2014-02-11 2 views
2

Я ищу способ (или, возможно, лучшую практику), связать различные методы с строковым объектом, фактически не открывая класс String. Это связано с тем, что преобразования, которые я хочу применить к объекту String, являются специфически специфичными для проекта, и я не вижу причин, чтобы с ним обмениваться глобальным пространством.Цепные методы на String без основных расширений

Есть ли способ достичь этого? Может быть, какая-то обертка? Я экспериментировал с gsub! на переменную экземпляра, но gsub !, если gsub не выбрасывает ниль на несоответствие, поэтому он останавливает цепочку, которую я хочу достичь.

В принципе, мне нужно, чтобы быть в состоянии сделать:

"my_string".transformation1.transformation2.transformation3

и получить, чтобы сохранить все эти преобразования пространств имён для моего приложения. Есть идеи?

+2

всегда может добавить к экземпляру. –

+0

@DaveNewton Я мог бы, но это было бы дорого, потому что все эти преобразования одинаковы. –

+0

... Затем добавьте его в строку или используйте реализацию «озабоченность», вероятно, также немного дороже. –

ответ

4

Вы можете добавить метод экземпляра, но вы будете нуждаться, чтобы прыгать через обруч цепи методы:

str = 'now is the time' 

def str.foo(target, replacement) 
    self.gsub(target, replacement) 
end 

def str.bar 
    self.reverse 
end 

str.singleton_methods # => [:foo, :bar] 
str.foo('ow', 'ever') # => "never is the time" 
str.bar # => "emit eht si won" 

Это не будет работать:

str.foo('ow', 'ever').bar 

# ~> NoMethodError 
# ~> undefined method `bar' for "never is the time":String 

Вы можете использовать методы типа «удар», которые мутируют исходный объект:

str = 'now is the time' 

def str.foo(target, replacement) 
    self.gsub!(target, replacement) 
    self 
end 

def str.bar 
    self.reverse! 
    self 
end 

str.singleton_methods # => [:foo, :bar] 

str.foo('ow', 'ever').bar # => "emit eht si reven" 

Будьте осторожны, хотя. Это не будет работать для каждого типа объектов, только те, которые позволяют мутировать.

И тот факт, что добавленные методы доступны только этой конкретной переменной, действительно ограничивает; Это бесполезное повторное использование той же функциональности. Вы можете clone переменную и назначить этот клон новой переменной, но заменяя значение с чем-то другом становится грязным:

str2 = str.clone 
str.object_id # => 70259449706680 
str2.object_id # => 70259449706520 
str.singleton_methods # => [:foo] 
str2.singleton_methods # => [:foo] 

str2 = 'all good men' 
str2.foo('ll', '') # => 

# ~> NoMethodError 
# ~> undefined method `foo' for "all good men":String 

Лично я хотел бы сделать это с помощью подкласса:

class MyString < String 
def foo(s, t) 
    self.gsub(s, t) 
end 
end 

str = MyString.new('now is the time') 
str.foo('ow', 'ever') # => "never is the time" 

str2 = 'all good men' 
str2.foo('ll', '') # => 

# ~> NoMethodError 
# ~> undefined method `foo' for "all good men":String 

Если вы на Ruby v2 +, посмотрите на ответ @ mdesantis, используя уточнения. Они были введены для решения такого рода проблем. Если вы < v2.0, я бы пошел маршрут подкласса или согласился с необходимостью изменять String.

+0

Я пошел с подклассификацией. Гораздо чище и очень привязано к моему приложению. –

7

Если вы используете Ruby,> = 2.0 вы можете использовать refinements:

module MyRefinements 
    refine String do 
    def transformation1 
     # ... 
     self 
    end 
    def transformation2 
     # ... 
     self 
    end 
    def transformation3 
     # ... 
     self 
    end 
    end 
end 

# Either in global scope (active until the end-of-file) 
# or in lexical scope (active until the lexical scope end) 
# More on this in the refinements docs scope section 

# global scope 
using MyRefinements 
"test".transformation1.transformation2.transformation3 #=> "test" 

# lexical scope 
begin 
    using MyRefinements 
    "test".transformation1.transformation2.transformation3 #=> "test" 
end 
+0

+1. Я забыл об уточнениях. Данг эти новомодные вещи. :-) –

+0

@theTinMan Я должен признать, что я никогда не использовал их в производственном коде: D просто для экспериментов; но я нашел их довольно умными, я буду использовать их при следующей возможности. – mdesantis

+0

Спасибо @mdesantis. Мне они тоже понравились, но в итоге я пошел с подклассом. –

Смежные вопросы