Module: ProxyMethod::ClassMethods

Defined in:
lib/proxy_method.rb

Constant Summary collapse

DEFAULT_PROXY_MESSAGE =
'Disabled by proxy_method'
DEFAULT_PREFIX =
'unproxied_'

Instance Method Summary collapse

Instance Method Details

#proxiedObject

Return a proxied version of this class.

If the class has previously been “unproxied”, this returns a copy where all proxies are re-enabled.



243
244
245
# File 'lib/proxy_method.rb', line 243

def proxied
  self.dup.send(:reproxy!)
end

#proxy_class_method(*original_method_names, &proxy_block) ⇒ Object

Proxy one or more inherited class methods, so that they are not used directly. Given this base class:

class Animal
  def self.create
    'created'
  end

  def destroy
    'destroyed'
  end
end

The simplest implementation is to pass just a single method name:

class Dog < Animal
  proxy_class_method :create
end

Dog.create  
# => RuntimeError: Disabled by proxy_method

Dog.destroy  
# => 'destroyed'

Or multiple method names:

class Dog < Animal
  proxy_class_method :create, :destroy
end

Dog.create  
# => RuntimeError: Disabled by proxy_method

Dog.destroy  
# => RuntimeError: Disabled by proxy_method

With a custom error message:

class Dog < Animal
  proxy_class_method :create, raise: 'Disabled!'
end

Dog.create  
# => RuntimeError: Disabled!

You can still access the unproxied version by prefixing ‘unproxied_’ to the method name:

Dog.unproxied_create  
# => 'created'

And you can change the prefix for unproxied versions:

class Dog < Animal
  proxy_class_method :create, prefix: 'original_'
end

Dog.original_create  
# => 'created'

Finally, you can actually proxy the method, by providing an alternative block of code to run:

class Dog < Animal
  proxy_class_method(:create) do |object, method_name, *args, &block|
    "indirectly #{object.send(method_name)}"
  end
end

Dog.create  
# => 'indirectly created'


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/proxy_method.rb', line 80

def proxy_class_method(*original_method_names, &proxy_block)
  options = if original_method_names.last.is_a?(Hash)
    original_method_names.pop
  else
    {}
  end

  original_method_names = Array(original_method_names).flatten

  error_message = options[:raise] || DEFAULT_PROXY_MESSAGE
  prefix = options[:prefix] || DEFAULT_PREFIX

  original_method_names.each do |original_method_name|
    proxied_class_methods.merge!(original_method_name => prefix)
    new_method_name = :"#{prefix}#{original_method_name}"

    self.singleton_class.send(:alias_method, new_method_name, original_method_name)
    define_singleton_method(original_method_name) do |*args, &block|
      if proxy_class_methods_enabled?
        if proxy_block
          proxy_block.call(self.unproxied, original_method_name, *args, &block)
        else
          raise error_message
        end
      else
        send(new_method_name, *args, &block)
      end
    end
  end
end

#proxy_instance_method(*original_method_names, &proxy_block) ⇒ Object Also known as: proxy_method

Proxy one or more inherited instance methods, so that they are not used directly. Given this base class:

class Animal
  def save
    'saved'
  end

  def update
    'updated'
  end
end

The simplest implementation is to pass just a single method name:

class Dog < Animal
  proxy_instance_method :save
end

Dog.new.save  
# => RuntimeError: Disabled by proxy_method

Dog.new.upate  
# => 'updated'

Or use the shorthand form:

class Dog < Animal
  proxy_method :save
end

Or multiple method names:

class Dog < Animal
  proxy_method :save, :update
end

Dog.new.save  
# => RuntimeError: Disabled by proxy_method

Dog.new.update  
# => RuntimeError: Disabled by proxy_method

With a custom error message:

class Dog < Animal
  proxy_method :save, raise: 'Disabled!'
end

Dog.new.save  
# => RuntimeError: Disabled!

You can still access the unproxied version by prefixing ‘unproxied_’ to the method name:

Dog.new.unproxied_save  
# => 'saved'

And you can change the prefix for unproxied versions:

class Dog < Animal
  proxy_method :save, prefix: 'original_'
end

Dog.new.original_save  
# => 'saved'

Finally, you can actually proxy the method, by providing an alternative block of code to run:

class Dog < Animal
  proxy_method(:save) do |object, method_name, *args, &block|
    "indirectly #{object.send(method_name)}"
  end
end

Dog.new.save  
# => 'indirectly saved'


192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/proxy_method.rb', line 192

def proxy_instance_method(*original_method_names, &proxy_block)
  options = if original_method_names.last.is_a?(Hash)
    original_method_names.pop
  else
    {}
  end

  original_method_names = Array(original_method_names).flatten

  error_message = options[:raise] || DEFAULT_PROXY_MESSAGE
  prefix = options[:prefix] || DEFAULT_PREFIX

  original_method_names.each do |original_method_name|
    proxied_instance_methods.merge!(original_method_name => prefix)
    new_method_name = :"#{prefix}#{original_method_name}"

    alias_method new_method_name, original_method_name

    define_method(original_method_name) do |*args, &block|
      if proxy_instance_methods_enabled?
        if proxy_block
          proxy_block.call(self.unproxied, original_method_name, *args, &block)
        else
          raise error_message
        end
      else
        send(new_method_name, *args, &block)
      end
    end
  end
end

#unproxiedObject

Return an unproxied version of this class.

This returns a copy of the class where all proxies are disabled. This is sometimes necessary when a proxied method is being called by a different method outside your control.



233
234
235
# File 'lib/proxy_method.rb', line 233

def unproxied
  self.dup.send(:unproxy!)
end