2010-05-25 3 views
0

По существу я задаюсь вопросом, как поместить обратные вызовы на объекты в рубин, так что, когда объект изменен в любом случае я могу автоматически вызвать другие изменения:Ruby: имеющие обратные вызовы на «ATTR» объекты

(EDIT: Я запутался в моем собственном примере! Не очень хороший знак ... Поскольку @proxy - это объект URI, он имеет свои собственные методы, изменение объекта URI с помощью собственных методов не вызывает мой собственный метод proxy= и обновляет объект @http)

class MyClass 
    attr_reader :proxy 
    def proxy=(string_proxy = "") 
    begin 
     @proxy = URI.parse("http://"+((string_proxy.empty?) ? ENV['HTTP_PROXY'] : string_proxy)) 
     @http = Net::HTTP::Proxy.new(@proxy.host,@proxy.port) 
    rescue 
     @http = Net::HTTP 
    end 
    end 
end 

m = MyClass.new 
m.proxy = "myproxy.com:8080" 
p m.proxy 
# => <URI: @host="myproxy.com" @port=8080> 

m.proxy.host = 'otherproxy.com' 
p m.proxy 
# => <URI: @host="otherproxy.com" @port=8080> 
# But accessing a website with @http.get('http://google.com') will still travel through myproxy.com as the @http object hasn't been changed when m.proxy.host was. 
+2

Что вы имеете в виду «как прокси = не вызывается»? Конечно, это называется. – sepp2k

+0

Я не понимаю, вы получаете 'Net :: HTTP' ...? Вы можете проверить его, если вы добавили 'attr_reader: http' и проверили' p m.http'. Что значит 'proxy =' не вызывается при изменении 'm.proxy'? Этот вызов функции - это единственное, что происходит - вы не можете напрямую изменять переменные экземпляра в Ruby. – Amadan

+0

Ahh pants, я смутил себя в своем примере! Метод 'proxy =' не будет вызываться, если дочерние объекты объекта URI @proxy будут изменены (см. Последние 4 строки моего примерного кода) - надеюсь, что это имеет смысл? –

ответ

0

Мне удалось понять это для себя!

# Unobtrusive modifications to the Class class. 
class Class 
    # Pass a block to attr_reader and the block will be evaluated in the context of the class instance before 
    # the instance variable is returned. 
    def attr_reader(*params,&block) 
    if block_given? 
     params.each do |sym| 
     # Create the reader method 
     define_method(sym) do 
      # Force the block to execute before we… 
     self.instance_eval(&block) 
      # … return the instance variable 
      self.instance_variable_get("@#{sym}") 
     end 
     end 
    else # Keep the original function of attr_reader 
     params.each do |sym| 
     attr sym 
     end 
    end 
    end 
end 

Если добавить этот код где-то будет распространить метод attr_reader так, что если вы сейчас сделать следующее:

attr_reader :special_attr { p "This happens before I give you @special_attr" } 

Это будет вызывать блок, прежде чем он дает @special_attr. Он выполняется в области экземпляра, поэтому вы можете использовать его, например, в классах, где атрибуты загружаются из Интернета. Если вы определяете метод, как get_details, который делает все извлечение и устанавливает @details_retrieved в true, то вы можете определить Аттрибут так:

attr_reader :name, :address, :blah { get_details if @details_retrieved.nil? } 
0

Ваша линия m.proxy = nil поднимет NoMethodError исключение, так как nil не делает реагировать на empty?. Таким образом, @http установлен в Net::HTTP, как в предложении rescue.

Это не имеет никакого отношения к обратным вызовам/сеттерам. Вы должны изменить свой код, чтобы делать то, что хотите (например, вызывать string_proxy.blank? при использовании activesupport).

+0

Извините, мне удалось запутать себя и задать неправильный вопрос (я упростил свой код, чтобы привести его в stackoverflow) - спасибо за вашу помощь! –