Class: Ripar::Combinder
- Inherits:
- BasicObject
- Defined in:
- lib/ripar/combinder.rb
Overview
This is the part that lets an instance_eval-style block access variables defined outside of it. Arguably that’s a horrible idea, really.
Implements method_missing to figure out which of binding.self and obj can handle the missing method. Can have somewhat weird side effect of making variables assigned to lambdas (or anything implementing call actually) directly callable.
TODO @binding.eval(‘self’) might define method_missing, so we may actually have to attempt to call the method to see if it’s missing.
Defined Under Namespace
Modules: BindingNiceness Classes: AmbiguousMethod, BindingWrapper
Instance Method Summary collapse
-
#__inside__ ⇒ Object
for disambiguating outside variables vs method calls, otherwise Ruby interpreter will find the outside variable name, and use that instead of the method call.
-
#__outside__ ⇒ Object
access the outside of the block, ie its binding.
-
#initialize(obj, saved_binding) ⇒ Combinder
constructor
A new instance of Combinder.
-
#method_missing(meth, *args, &blk) ⇒ Object
long method, but we want to keep BasicObject really empty.
- #respond_to?(meth, include_all = false) ⇒ Boolean
Constructor Details
#initialize(obj, saved_binding) ⇒ Combinder
Returns a new instance of Combinder.
22 23 24 25 |
# File 'lib/ripar/combinder.rb', line 22 def initialize( obj, saved_binding ) @obj, @binding = obj, saved_binding @binding.extend BindingNiceness end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *args, &blk) ⇒ Object
long method, but we want to keep BasicObject really empty. TODO could split into private __methods
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ripar/combinder.rb', line 31 def method_missing( meth, *args, &blk ) if @obj.respond_to?( meth ) && (@binding.self.methods - ::Object.instance_methods).include?( meth ) begin return @obj.__ambiguous_method__( @binding.self, meth, *args, &blk ) rescue ::NoMethodError => ex unless ::Object::RUBY_VERSION == '2.0.0' # for some reason, any references to ex.message fail here for 2.0.0 # so only for other versions just double-check versions that it was in fact caused by __ambiguous_method__ # otherwise just raise whatever was missing. ::Kernel.raise unless ex. =~ /__ambiguous_method__/ end ::Kernel.raise AmbiguousMethod, "method :#{meth} exists on both #{@binding.self.inspect} (outside) and #{@obj.inspect} (inside)", ex.backtrace[3..-1] end end if @binding.local_variables.include?( meth ) # This branch is only necessary to do lambda calls with (), # because variables in the binding are already, well, part of the binding. # So they are picked up before the code ever calls method_missing bound_value = @binding.eval meth.to_s if bound_value.respond_to?( :call ) # It's a local variable, but it's been forced to come here by (). So call it. bound_value.call(*args) else # assume that the user really wants to call the object's method # rather than access the outside variable. @obj.send meth, *args, &blk end elsif @binding.self.respond_to?( meth ) @binding.self.send meth, *args, &blk else @obj.send meth, *args, &blk end end |
Instance Method Details
#__inside__ ⇒ Object
for disambiguating outside variables vs method calls, otherwise Ruby interpreter will find the outside variable name, and use that instead of the method call. You could also force the method call with (), but that is sometimes ugly.
77 78 79 |
# File 'lib/ripar/combinder.rb', line 77 def __inside__ @obj end |
#__outside__ ⇒ Object
access the outside of the block, ie its binding.
110 111 112 |
# File 'lib/ripar/combinder.rb', line 110 def __outside__ BindingWrapper.new @binding end |
#respond_to?(meth, include_all = false) ⇒ Boolean
67 68 69 70 71 |
# File 'lib/ripar/combinder.rb', line 67 def respond_to?( meth, include_all = false ) # ::Kernel.puts "Combinder#respond_to #{meth}" # ::Kernel.puts "Combinder local variables #{@binding.local_variables}" return @binding.local_variables.include?( meth ) || @binding.self.respond_to?( meth, include_all ) || @obj.respond_to?( meth, include_all ) end |