This is somewhat of an esoteric situation that I am not even sure is possible. I have a method that is called by a lot of methods:
def called_by_a_lot_of_methods
# Work
end
I am introducing a feature in which the called_by_a_lot_of_methods will do stuff depends on the caller method, and my current solution is to change all these methods to pass their names when calling called_by_a_lot_of_methods:
def some_method
called_by_a_lot_of_methods(method_name: some_method)
end
Or, alternatively:
def some_method
called_by_a_lot_of_methods(method_name: __method__)
end
But this is becoming tedious, and I was wondering if I can give the method_name parameter a default value:
def called_by_a_lot_of_methods(method_name: __method__)
# Work
end
This does not work because __method__ evaluates immediately giving called_by_a_lot_of_methods, which is obviously not what I want. The question then is, is there a way in Ruby to defer the evaluation of the argument until a useful time when I know it should give the correct result, which is the outer caller method? Thus saving me from having to pass the argument everywhere?
Related
def foo(a)
# some computation
bar(a, b)
end
def bar(a,b)
# some computation
baz(a, b, c)
end
def baz(a, b ,c)
print a
end
How to avoid passing same parameter (a) to all methods? One way is to make parameter a as an instance variable. What is the right way to do this?
There is no single solution which works in every case, and often, passing the parameters explicitly is simply the best choice, especially when it comes to debugging.
You can use instance variables, but I would not introduce a new instance variable for the sole reason not to avoid passing it down the call chain. It should have its own value in addition.
One approach which I can see often in projects, is to us a single parameter hash, where the new parameters are added:
def foo(a)
....
bar({a:a, b:b})
end
def bar(par)
....
par[:c] = c
end
def baz(par)
par[:a]+par[:b]+par[:c]
end
This is not without drawbacks either. If you, for instance, forget to "add" a necessary parameter, this will either yield wrong results or throw an exception (depending on how to used them), while forgetting them explicitly via the parameter list, would be found at compile time already.
I would make the decisiion depending on the length of the parameter lists involved. If the number of parameters to a methods is larger than 5, I would start thinking, whether a different design might be more appropriate.
Answering the question stated: yes, ruby is a stateful OO language, having mutable everything. So in ruby one might introduce instance variables:
class MyClass
def foo(a)
#a = a
# some computation
bar(b)
end
def bar(b)
#b = b
# some computation
baz(c)
end
def baz(c)
print [#a, #b, c].inspect
end
end
In the real life, the best practice would be to avoid using instance variables wherever possible, and pass everything across methods. That might save you days of debugging in the future.
Looking for a little wisdom from fellow Rubyists. For a while now, I've used the following for convenience in some of my applications, and I'm wondering if there's a language feature I'm just missing:
class Object
def as_block
yield
end
end
There are other uses, but the normal case for me is a conditional assignment that requires a little non-trivial logic. Two obvious ways to do this:
# via a second method:
def foo
#foo ||= set_foo
end
# via conditional logic:
def foo
if #foo
#foo
else
# do complicated stuff
end
end
Both of these approaches seem kind of ugly: in the first case, #set_foo seems extraneous, and the second just looks kind of nasty. So, instead, I like this:
def foo
#foo ||= as_block do
# do complicated stuff
end
end
The problem here (aside from monkey patching Object) is that it's really a dependency (on the monkey patch) that looks like a language feature. That is, something like this really shouldn't be in (say) a Rails initializer---it seems like it should be in a gem, so the dependency can be managed correctly. Then I'm packaging an entire gem to run five lines of code to monkey patch Object...
So, my questions:
1. Anyone else use this, or something like it?
2. Has the Ruby team ever considered including something like this by default? It seems like a really easy way to use blocks as plain old expressions, but it's not there (as far as I know) which makes me wonder if there's some reason for not including it, or...
3. Is there already some better way of doing this that I'm just unaware of?
Thanks!
-E
What you're looking for is begin ... end. This isn't the same thing as a block or Proc, as it's not an object you can pass around or a closure which creates a new scope, but it should serve your purpose just fine:
def foo
#foo ||= begin
# do complicated stuff
end
end
You could use a lambda:
def foo
#foo ||= lambda do
# do complicated stuff
end.call
end
Note that it is important to call the lambda to actually execute the expression, ie
def foo
#foo ||= lambda do
# do complicated stuff
end
end
will return a lambda rather than your evaluated expression.
I understand that we use the ampersand in front of the method's argument in order to avoid using yield, which would output an error if it doesn't find a block in its way.
However, I've seen that sometimes we use the & in front of the argumenent when we call a method, in order to transform the proc back into a block. It eventually happens that there are 2 ampersands, one for turning blocks into procs, and the second for the opposite.
How's that?! When do we need to put the ampersand to getting back a block from a proc, and why?
I hope I've been clear, beacause not being english mother tongue could make harsh to pose difficult questions for me.
Example
I took this example from the slides of CodeSchool, which can be found on http://courseware.codeschool.com/ruby_bits_2/ruby_bits_2_level_1.pdf.
class Timeline
attr_accessor :tweets
def each(&block)
tweets.each(&block)
end
end
I understand that we use the ampersand in front of the method's
argument in order to avoid using yield, which would output an error if
it doesn't find a block in its way.
Your understanding is wrong.
You use &block as the last argument to capture the block into a Proc, in particular to store it and use it later on. When calling a method, &block does the reverse, i.e. specifies that the block corresponds to the given block. This is the same idea as def foo(*args) and foo(*args).
The example you gave is basically equivalent to:
def each
tweets.each{|t| yield y}
end
As Yehuda Katz explains at the end of this talk, you should think of a "block" as a Proc with a special argument position and a dedicated syntax to specify it.
It's common to use them together. This is the case because blocks can't be used as parameters, while procs can. So if you need to use the block as a parameter within the method's body, you must turn it into a proc.
Within that method, you might want to pass the proc to another method that only accepts a block, so you have to do the conversion once again.
For example:
def each(&block) # block to proc
posts.each(&block) # proc to block
end
I'm writing a simple dsl in ruby. Few weeks ago I stumbled upon some blog post, which show how to transform code like:
some_method argument do |book|
book.some_method_on_book
book.some_other_method_on_book :with => argument
end
into cleaner code:
some_method argument do
some_method_on_book
some_other_method_on_book :with => argument
end
I can't remember how to do this and I'm not sure about downsides but cleaner syntax is tempting. Does anyone have a clue about this transformation?
def some_method argument, &blk
#...
book.instance_eval &blk
#...
end
UPDATE: However, that omits book but don't let you use the argument. To use it transparently you must transport it someway. I suggest to do it on book itself:
class Book
attr_accessor :argument
end
def some_method argument, &blk
#...
book.argument = argument
book.instance_eval &blk
#...
end
some_method 'argument' do
some_method_on_book
some_other_method_on_book argument
end
Take a look at this article http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation — there is an overview of the method (specifically stated in the context of its downsides and possible solution to them), plus there're several useful links for further reading.
Basically, it's about using instance_eval to execute the block in the desirable context.
Speaking about downside of this technique:
So what's the problem with it? Well, the problem is that blocks are
generally closures. And you expect them to actually be full closures.
And it's not obvious from the point where you write the block that
that block might not be a full closure. That's what happens when you
use instance_eval: you reset the self of that block into something
else - this means that the block is still a closure over all local
variables outside the block, but NOT for method calls. I don't even
know if constant lookup is changed or not.
Using instance_eval changes the rules for the language in a way that
is not obvious when reading a block. You need to think an extra step
to figure out exactly why a method call that you can lexically see
around the block can actually not be called from inside of the block.
Check out the docile gem. It takes care of all the sharp edges, making this very easy for you.
One question that ran through my mind was how does the Ruby interpreter know that a method exists on a object if the definition is yet to be interpreted? Like, wouldn't it matter whether you define the method first than use it, rather than use it then define it?
It doesn't know, and it doesn't care - until execution. When a method call statement is executed, the interpreter looks to see if the class (object, not code!) has the named function. If it does not, it looks up the ancestor tree. If it does not find any, it calls the method_missing method. If that is not defined, you get your error.
If your function call does not get executed, you will not get any errors.
The interpreter doesn't know about undefined methods ahead of time, for example:
o = Object.new
o.foo # => Raises NoMethodError.
class Object
def foo
puts "Foo!"
end
end
o.foo # => prints "Foo!", since the method is defined.
However, Ruby has a neat feature called method_missing which let's the receiver of a method call take the method name and arguments as separate arguments and handle accordingly as long as no defined method already handles the call.
def o.method_missing(sym, *args)
puts "OK: #{sym}(#{args.inspect})"
# Do something depending on the value of 'sym' and args...
end
o.bar(1, 2, 3) #=> OK: bar(1, 2, 3)
"Method missing" is used by things like active record find methods and other places where it could make sense to have "dynamically defined" functions.
The problem is, the interpreter tried to find it when you use it, and since it won't be there, it may fail.
In ( some ) compiled languages, it doesn't matter, because while compiling, the compiler may say "I'll look for this on a second pass" but I don't think this is the case with Ruby.