lib/classproxy/classproxy.rb in classproxy-0.7.4 vs lib/classproxy/classproxy.rb in classproxy-0.7.9

- old
+ new

@@ -30,14 +30,14 @@ # @example Store Github API name in a different attribute # class GithubUser # include ClassProxy # # fallback_fetch { |args| Octokit.user(args[:login]) } - # after_fallback_fetch do |model, obj| + # after_fallback_fetch do |obj| # # obj is what `fallback_fetch` returns - # model.name = obj.name - # model.login = obj.login + # self.name = obj.name + # self.login = obj.login # end # # attr_accessor :name, :login # end def after_fallback_fetch(&block); @after_fallback_method = block; end @@ -48,11 +48,11 @@ # @example Load method using uppercase # class GithubUser # include ClassProxy # # fallback_fetch { |args| Octokit.user(args[:login]) } - # after_fallback_fetch { |model, obj| model.name = obj.name; model.login = obj.login } + # after_fallback_fetch { |obj| self.name = obj.name; self.login = obj.login } # # attr_accessor :name, :followers :login # # proxy_methods :name, :followers, uppercase_login: lambda { login.upcase } # end @@ -99,30 +99,38 @@ end private def run_fallback(args, _self=nil) - fallback_obj = @fallback_fetch[args] + obj = _self || self.new + fallback_obj = obj.instance_exec args, &@fallback_fetch + # Use the after_fallback_method - obj = _self || self.new - @after_fallback_method[obj, fallback_obj] if @after_fallback_method.is_a?(Proc) + obj.instance_exec fallback_obj, &@after_fallback_method if @after_fallback_method.is_a? Proc # Go through the keys of the return object and try to use setters if fallback_obj and obj and fallback_obj.respond_to? :keys and fallback_obj.keys.respond_to? :each fallback_obj.keys.each do |key| - next unless obj.respond_to? "#{key}=" and obj.send(key) == nil - obj.send("#{key}=", fallback_obj.send(key)) + next unless obj.respond_to? "#{key}=" + + # check if its set to something else + get_method = obj.respond_to?("no_proxy_#{key}") ? "no_proxy_#{key}" : key + if obj.respond_to? get_method and obj.send(get_method) == nil + obj.send("#{key}=", fallback_obj.send(key)) + end end end return obj end def proxy_method(method_name, proc=nil) self.class_eval do - alias_method "no_proxy_#{method_name}".to_sym, method_name + unless self.instance_methods.include? "no_proxy_#{method_name}".to_sym + alias_method "no_proxy_#{method_name}".to_sym, method_name + end define_method(method_name) do |*args| # Use the no_proxy one first v = self.send("no_proxy_#{method_name}".to_sym, *args) @@ -131,13 +139,15 @@ # Since AR calls the getter method when using the setter method # to establish the dirty attribute, since the getter is being replaced # here and the setter is being used when appropriate, the @mutex_in_call_for # prevents endless recursion. - if v == nil and @mutex_in_call_for != method_name - @mutex_in_call_for = method_name + @mutex_in_call_for ||= [] + if v == nil and not @mutex_in_call_for.include? method_name + @mutex_in_call_for << method_name method = "_run_fallback_#{method_name}".to_sym + if self.respond_to?(method) v = if self.method(method).arity == 1 # Callback method is expecting to receive the fallback object fallback_fetch_method = self.class.instance_variable_get(:@fallback_fetch) fallback_obj = fallback_fetch_method[self] @@ -146,25 +156,20 @@ # Callback method doesn't need the fallback object self.send(method) end else # This method has no callback, so just run the fallback - args = {} - (self.methods - self.class.methods).each do |key| - next if key[-1] == '=' # don't run for set - hkey = key.to_s.gsub(/^no_proxy_/, '') - args[hkey.to_sym] = self.send(key) - end - self.class.send :run_fallback, args, self + args_class = ArgsClass.new(self) + self.class.send :run_fallback, args_class, self # The value might have changed, so check here v = self.send("no_proxy_#{method_name}".to_sym) end # Set the defaults when this class responds to the same method self.send("#{method_name}=".to_sym, v) if v and self.respond_to?("#{method_name}=") - @mutex_in_call_for = nil + @mutex_in_call_for.delete method_name end return v end end @@ -175,6 +180,27 @@ end def self.included(receiver) receiver.extend ClassMethods end -end + + # This class makes methods accessible as a hash key, useful to pass + # as a fallback_fetch argument + class ArgsClass < BasicObject + def initialize(object) + @target = object + end + + def [](key) + @target.respond_to?(key) ? @target.send(key) : @target[key] + end + + def inspect + "ArgsClass [#{@target.inspect}] " + + (@target.methods - @target.class.methods).join(', ') + end + + def target + @target + end + end +end \ No newline at end of file