Is it possible to override value in a block - ruby

New to Ruby so apologies in advance if this is a dumb question. I have a need to supply a value to a block but would like not to update everywhere called.
Let's say I have the following block:
{ base.call_my_test }
It get's passed to a function where I'd like to wrap it in another block but supply that other blocks value to it such as:
def test(&block)
MyTest.with_base_object do |base|
#neither of these work
block.instance_variable_set(:#base, base) # Doesn't work
block.define_singleton_method(:hi) do
return base
end
block.call <---- Is it possible to define base here?
end
end
In the above example, is it possible to define base on the block without passing as an argument? I tried instance_variable_set as well as define_singleton_method but I can't seem to get anything to be defined as it always says base is undefined.
Not sure if this is even possible but figured I'd ask.

First, blocks in Ruby are non-rigid Proc objects. That means that you can pass extra arguments, and anyone who doesn't expect arguments will silently ignore them.
def test(&block)
base = Object.new
block.call(base)
end
test { |x| puts "Works with an object #{x}." }
test { puts "Works without an object as well!" }
So if you control the block where you need base to be defined, then you can simply add the argument. Any other uses of the test function will simply ignore the extra argument, even if they're not declared to take one.
But this is assuming you control any of the blocks at all. It sounds like you have some blocks somewhere out there that you can't change, and you automagically want to inject the name base into their scope. You can change the class instance that a block is being invoked for by passing the block to instance_eval.
Buyer beware: This will completely change self. If the block in question tries to view self or call any other methods on the object (it thinks) it's enclosed in, it will fail.
class HasBase
attr_reader :base
def initialize(base)
#base = base
end
end
def test(&block)
base = Object.new
HasBase.new(base).instance_eval(&block)
end
test { puts "I can see my #{base} from here!" }
Depending on your specific needs, you can start augmenting this to work with whatever your callers need. For instance, you can write method_missing to fall back to the proc's original binding. But that's getting into some deep magic, and you probably don't want to play with that footgun in production for very long.

Related

Ruby: different values of self inside lambda when using instance_eval(&lambda) vs instance_eval { lambda.call }

I want to call a lambda changing it's self value. Searching for it, I arrived at instance_exec, which was supposed to get the job done.
However, after 1+ hour studying an example, I found this behavior I can't explain:
class Dummy
class << self
attr_accessor :my_lambda
end
self.my_lambda = proc { self }
def test
self.class.my_lambda.call
end
def test1
instance_eval(&self.class.my_lambda)
end
def test2
instance_eval { self.class.my_lambda.call }
end
end
Dummy.new.test
=> Dummy # I was expecting this, returns the class, lexical scope (where `self` was defined)
Dummy.new.test1
=> #<Dummy:0x00007fe982f8b678> # I was expecting this, `self` changed to the receiver of `instance_eval`, which is the instance, so `self` returned the instance.
Dummy.new.test2
=> Dummy # I can't understand this, at all. Why isn't it the same as test1?
So, why do we get different results from instance_eval(&self.class.my_lambda) vs instance_eval { self.class.my_lambda.call } ?
Also, since we're at the subject of calling Lambdas ... if I change the class level variable to this:
self.my_lambda = lambda { self }
then Dummy.new.test1 throws an exception ArgumentError: wrong number of arguments (given 1, expected 0), which I also can't understand why, since I know lambdas are strict about argument numbers, but we didn't pass any when we used instance_eval(&self.class.my_lambda)
Amazingly how, right away after writing this long question, I managed to answer it myself in one extra minute. I needed a rubber duck. Anyways.
I misunderstood instance_eval. It "Executes the given block within the context of the receiver (obj)."
So, the given block in #test2 instance_eval { self.class.my_lambda.call } was, indeed, executed with self set to the instance, but the lambda was still called within it, which doesn't change it's self.
However, in #test1, the ampersand & changed the proc to a block, and then it was passed to instance_eval, so it was the proc's own body that was executed with the self changed.

Ruby: Passing a Proc to method with no parameters

So this works(Pulled from Code-Academy):
def greeter
yield
end
phrase = Proc.new {
puts "Hello there!"
}
greeter(&phrase)
I understand what yield is doing, and understand what a Proc does. However "why" is this allowed?
IE: greeter method has no parameters defined, not even optional ones. So why does ruby allow us to pass something to it? (In this case a reference to a block of code (The Proc phrase).
I mean it's great that it does, but rule-wise this seems like it shouldn't be allowed?
&phrase is not a reference. It is the Ruby annotation for passing a block explicitly. Here , it is converting the proc to the implicit block for the method call. Since every method accepts a default block as an argument, your code works.

How do write two methods with different number of arguments in Ruby

I am trying to write this inside my class:
class << self
def steps
#steps.call
end
def transitions
#transitions.call
end
def steps(&steps)
#steps = steps
end
def transitions(&transitions)
#transitions = transitions
end
end
That won't work since in Ruby, I can't do this kind of method overloading. Is there a way around this?
You can kind of do this with method aliasing and mixins, but the way you handle methods with different signatures in Ruby is with optional arguments:
def steps(&block)
block.present? ? #steps = block : #steps.call
end
This sort of delegation is a code smell, though. It usually means there's something awkward about the interface you've designed. In this case, something like this is probably better:
def steps
#steps.call
end
def steps=(&block)
#steps = block
end
This makes it clear to other objects in the system how to use this interface since it follows convention. It also allows for other cases, like passing a block into the steps method for some other use:
def steps(&block)
#steps.call(&block)
end
Ruby does not support method overloading (see "Why doesn't ruby support method overloading?" for the reason). You can, however, do something like:
def run(args*)
puts args
end
args will then be an array of the arguments passed in.
You can also pass in a hash of options to handle arguments, or you can pass in nil when you don't want to supply arguments and handle nil in your method body.

Explain the usage of procs in an rspec context

Here is an expectation that utilizes a custom RSpec matcher, yield_variables:
specify { expect{ |p| [3,4,5].my_each(&p) }.to yield_variables [3,4,5] }
my yield_variables matcher's matches? method utilizes a custom class called Probe (Probe is a stripped down version of RSpec's yield probe):
...
def matches? block
ap Probe.probe block
# note i am inspecting what is returned by Probe.probe with ap
end
...
# Probe class is what all my questions are about!
class Probe
attr_accessor :yielded_args
def initialize
self.yielded_args = []
end
def self.probe(block)
probe = new
block.call(probe)
probe.yielded_args
end
def to_proc
Proc.new { |*args| yielded_args << args }
end
end
Now my ap inside matches? reports this: [3,4,5] That is what I expect. However, I have no idea how the Probe class works!!
Problem 1) the matches? block
Normally, the argument we pass to matches? is what we expect the subject to return. i.e, I expect [3,4,5] to be passed into block.
Instead, |p| [3,4,5].my_each(&p) is passed into block, as a proc. Why is this?
Problem 2) block.call(probe)
I'm a bit shakey on procs so please explain slowly. But basically, here we take a new instance of my Probe class and 'send' the block to it, as an argument. That's how I'd explain it to the best of my abilities, but I might have it totally wrong so please explain slowly.
Problem 3) How is to_proc called?
How on earth is .to_proc called automatically? I believe it's called automatically by block.call(probe). Is to_proc an automatically called method like initialize? Is it automatically called whenever the class is sent to a proc? (Btw, the phrase the class is sent to a proc doesn't even make 100% sense to me - please explain. The block isn't passed into the class as an argument anymore. Or if the block is passed as an argument the block.call syntax feels really weird and backwards)
Problem 4) to_proc
How does to_proc have access to the expectation's subject i.e. |p| [3,4,5].my_each(&p) ? How is Proc.new 's *args automatically populated with every single possible yield argument, even though I've only passed in |p| ? How does Proc.new loop along with my my_each, incrementally placing all my args in an array? How does Proc.new have any knowledge of the subject |p| [3,4,5].my_each(&p)? How?? Explain please, and thanks.
Block based marchers work a little differently to other matchers. Typically you want to do something that would not be possible if the expression you were interested in was evaluated and the result passed to you.
For example the raises_error matcher wants to execute the block itself to see that the correct exception is raised and the change matcher wants to evaluate some other expression before and after to see if it changes in the specified way. This is why you are passed a block rather than the value.
The block you are passing to expect takes 1 argument and uses this as the block in the call to my_each, so when your Probe.probe method calls the block it has to pass something as the value. You've phrased as "sending the block to the probe instance" but it is the other way around: the block is called using probe as its argument.
The block executes and calls my_each. Inside here p is the instance of Probe. Prefixing an argument with a & tells ruby that this argument should be used as the method's block (the method being my_each). If the argument is not already a proc ruby calls to_proc on it. This is how Probe#to_proc is called
Assuming that my_each behaves in a similar way to normal each it will call its block (ie the return value of to_proc) once for each of the values in the array.
Normally your matcher would then compare the return value from Probe.probe to the actual value.

Ruby: Automatically set instance variable as method argument?

Are there any plans to implement ruby behavior similar to the CoffeeScript feature of specifying an instance variable name in a method argument list?
Like
class User
def initialize(#name, age)
# #name is set implicitly, but #age isn't.
# the local variable "age" will be set, just like it currently works.
end
end
I'm aware of this question: in Ruby can I automatically populate instance variables somehow in the initialize method? , but all the solutions (including my own) don't seem to fit the ruby simplicity philosophy.
And, would there be any downsides for having this behavior?
UPDATE
One of the reasons for this is the DRY (don't repeat yourself) philosophy of the ruby community. I often find myself needing to repeat the name of an argument variable because I want it to be assigned to the instance variable of the same name.
def initialize(name)
# not DRY
#name = name
end
One downside I can think of is that it may look as though a method is doing nothing if it has no body. If you're scanning quickly, this may look like a no-op. But I think given time, we can adapt.
Another downside: if you're setting other instance variables in the body, and you try to be readable by putting all the assignments at the beginning, it can take more cognitive "power" to see that there assignments also happening in the argument list. But I don't think this is any harder than, say, seeing a constant or method call and having to jump to its definition.
# notice: instance var assignments are happening in 2 places!
def initialize(#name)
#errors = []
end
After some pondering, I wondered if it's possible to actually get the argument names from a ruby method. If so, I could use a special argument prefix like "iv_" to indicate which args should be set as instance variables.
And it is possible: How to get argument names using reflection.
Yes! So I can maybe write a module to handle this for me. Then I got stuck because if I call the module's helper method, it doesn't know the values of the arguments because they're local to the caller. Ah, but ruby has Binding objects.
Here's the module (ruby 1.9 only):
module InstanceVarsFromArgsSlurper
# arg_prefix must be a valid local variable name, and I strongly suggest
# ending it with an underscore for readability of the slurped args.
def self.enable_for(mod, arg_prefix)
raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
mod.send(:include, self)
mod.instance_variable_set(:#instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
end
def slurp_args(binding)
defined_prefix = self.class.instance_variable_get(:#instance_vars_from_args_slurper_prefix)
method_name = caller[0][/`.*?'/][1..-2]
param_names = method(method_name).parameters.map{|p| p.last.to_s }
param_names.each do |pname|
# starts with and longer than prefix
if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
ivar_name = pname[defined_prefix.size .. -1]
eval "##{ivar_name} = #{pname}", binding
end
end
nil
end
end
And here's the usage:
class User
InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')
def initialize(iv_name, age)
slurp_args(binding) # this line does all the heavy lifting
p [:iv_name, iv_name]
p [:age, age]
p [:#name, #name]
p [:#age, #age]
end
end
user = User.new("Methuselah", 969)
p user
Output:
[:iv_name, "Methuselah"]
[:age, 969]
[:#name, "Methuselah"]
[:#age, nil]
#<User:0x00000101089448 #name="Methuselah">
It doesn't let you have an empty method body, but it is DRY. I'm sure it can be enhanced further by merely specifying which methods should have this behavior (implemented via alias_method), rather than calling slurp_args in each method - the specification would have to be after all the methods are defined though.
Note that the module and helper method name could probably be improved. I just used the first thing that came to mind.
Well, actually...
class User
define_method(:initialize) { |#name| }
end
User.new(:name).instance_variable_get :#name
# => :name
Works in 1.8.7, but not in 1.9.3. Now, just where did I learn about this...
I think you answered your own question, it does not fit the ruby simplicity philosophy. It would add additional complexity for how parameters are handled in methods and moves the logic for managing variables up into the method parameters. I can see the argument that it makes the code less readable a toss up, but it does strike me as not very verbose.
Some scenarios the # param would have to contend with:
def initialize( first, last, #scope, #opts = {} )
def search( #query, condition )
def ratchet( #*arg )
Should all of these scenarios be valid? Just the initialize? The #*arg seems particularly dicey in my mind. All these rules and exclusions make the Ruby language more complicated. For the benefit of auto instance variables, I do not think it would be worth it.

Resources