It is really nice to be able to write out
#foo ||= "bar_default"
or
#foo ||= myobject.bar(args)
but I have been looking to see if there is a way to write something like
#foo ||= do
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
end
roughly equivalent in actually functional code to something like
#foo = if !#foo.nil?
#foo
else
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
end
And I suppose I could write my own global method like "getblock" to wrap and return the result of any general block, but I'm wondering if there is already a built-in way to do this.
You can use begin..end:
#foo ||= begin
# any statements here
end
or perhaps consider factoring the contents of the block into a separate method.
I usually write it like this:
#foo ||= (
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
)
#foo ||= unless #foo
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
end
Related
I often memoize Ruby methods using the begin ... end block syntax:
$memo = {}
def calculate(something)
$memo[something] ||= begin
perform_calculation(something)
end
end
However, there's a gotcha here. If I return early from the begin ... end block via a guard clause, the result is not memoized:
$memo = {}
def calculate(something)
$memo[something] ||= begin
return 'foo' if something == 'bar'
perform_calculation(something)
end
end
# does not memoize 'bar'; method will be run each time
I can avoid this by avoiding the return statement:
$memo = {}
def calculate(something)
$memo[something] ||= begin
if something == 'bar'
'foo'
else
perform_calculation(something)
end
end
end
This works, but I don't love it because:
it's easy to forget I'm not allowed to use return in this case.
with many conditions it clutters up the code as opposed to the guard clause.
Is there a better idiom for this besides just avoiding return?
As far as I know, begin...end cannot be short-circuited. You can do exactly what you're trying to do with procs though:
$memo = {}
def calculate(something)
$memo[something] ||= -> do
return 'foo' if something == 'bar'
perform_calculation(something)
end.call
end
That being said, I've never seen this done before, so it's certainly not idiomatic.
I'd add another layer:
def calculate(something)
$memo[something] ||= _calculate(something)
end
def _calculate(something)
return if something == 'bar'
perform_calculation(something) # or maybe inline this then
end
This has the additional benefit of providing you with a method you could call whenever you want to be sure to get a freshly computed result. I would spend some more time on the method naming though.
One way to tackle this is with meta-programming where you wrap the method after it's defined. This preserves any behaviour in it:
def memoize(method_name)
implementation = method(method_name)
cache = Hash.new do |h, k|
h[k] = implementation.call(*k)
end
define_method(method_name) do |*args|
cache[args]
end
end
This creates a closure variable which acts as a cache. That avoids the ugly global, but it also means you can't really clear out that cache if you need to, so if you pass in a large number of different arguments it could end up consuming a lot of memory. Be cautious! That functionality could be added if necessary by defining some auxiliary method like x_forget for any given method x.
Here's how it works:
def calculate(n)
return n if (n < 1)
n + 2
end
memoize(:calculate)
Then you can see:
10.times do |i|
p '%d=%d' % [ i % 5, calculate(i % 5) ]
end
# => "0=0"
# => "1=3"
# => "2=4"
# => "3=5"
# => "4=6"
# => "0=0"
# => "1=3"
# => "2=4"
# => "3=5"
# => "4=6"
I fear I don't understand the question correctly, as it would seem something quite simple would do.
$memo = {}
def calculate(something)
$memo[something] ||= something == 'bar' ? 'foo' : perform_calculation(something)
end
Let's try it.
def perform_calculation(something)
'baz'
end
calculate('bar')
#=> "foo"
$memo
#=> {"bar"=>"foo"}
calculate('baz')
#=> "baz"
$memo
#=> {"bar"=>"foo", "baz"=>"baz"}
calculate('bar')
#=> "foo"
$memo
#=> {"bar"=>"foo", "baz"=>"baz"}
I don't know a solution using return but for the guard clause in your example I would use case.
$memo = {}
def calculate(something)
$memo[something] ||= case something
when 'foo' then 'bar'
else perform_calculation(something)
end
end
You could use break statements in a #tap block:
def val
#val ||= default.tap do
break val1 if cond1
break val2 if cond2
break val3 if cond3
end
end
You could also use 1.6's #then or 1.5's #yield_self, but then don't forget to return the default value at the end of the block, or it'll default to nil. In your example it's not a problem since the default is only evaluated at the end:
def calculate(something)
#calculate ||= {}
return #calculate[something] if #calculate.key?(something)
#calculate[something] = something.then do |something|
break 'foo' if something == 'bar'
perform_calculation(something)
end
end
What is the difference between these snippets?
def config
#config ||= begin
if config_exists?
#config = return some value
else
{}
end
end
end
def config
#config ||= method
end
def method
if config_exists?
return some value
else
{}
end
end
I'm confused with the "begin ... end" block. Does it make any difference in the output? If not, then what is the use of the begin ... end block here?
First of all, you need to be aware that a defined method inherently includes the functionality of a begin ... end block.
In the context of exception handling, def method_name ... end is functionally equivalent to begin ... end. Both can include rescue statements for example.
The two blocks of code you have shared are actually identical, and there is no benefit in one over the other ... unless your method is needed in more than one place. In that case, you DRY up your code by putting the logic into a single method and calling it from multiple other places.
In your case, you can even omit the begin ... end block:
#config ||=
if config_exists?
return_some_value
else
{}
end
or, using the ternary if:
#config ||= config_exists? ? return_some_value : {}
Does it make any difference in output?
It could make a difference, because unlike def ... end, an begin ... end block doesn't create a new variable scope.
Here's a contrived example:
def foo
a = 456 # doesn't affect the other a
end
a = 123
b = foo
p a: a, b: b #=> {:a=>123, :b=>456}
Versus:
a = 123
b = begin
a = 456 # overwrites a
end
p a: a, b: b #=> {:a=>456, :b=>456}
Using ||= begin...end allows you to memoize the result of whatever is run in the begin...end. This is useful for caching the result of resource-intensive computation.
The only thing that will happen differently is if an exception is raised. For instance, let's say there is a problem in the config_exists? method call. If it raises an exception in the first example your #config var will be set to {}. In the second example if the same thing happens your program will crash.
As a side note, there is no need for the return keyword here. In fact the example should read as follows. This is assuming that I understand the intent.
def config
#config ||=
begin
if config_exists?
some_value
else
{}
end
rescue
{}
end
end
and
def config
#config ||= method
end
def method
if config_exists?
some_value
else
{}
end
end
Both examples are exactly the same, except if an exception is raised #config will still be set to = some_value in the first example.
Also, it should be noted that nothing will happen if #config already has a value. The ||= operators is the same as:
#config = some_value if #config.nil?
Only set the variable to this value if it is currently nil.
Hope this is helpful and that I am understanding your question correctly.
How do you conditionally execute something when overwriting super, while still returning the result of super? I am sure there is a cleaner way of writing this in Ruby
def my_method
result = super
if result.success?
my_other_method1
my_other_method2
if #my_field
#x = #y
end
end
result
end
I believe something can be done with block, but don't really understand them yet. Any pointers would be greatly appreciated.
If you're using ruby 1.9, you could use the Object#tap method to clean that up a bit.
def my_method
super.tap do |result|
if result.success?
my_other_method1
my_other_method2
if #my_field
#x = #y
end
end
end
end
You can do it like this:
def my_method
super || my_other_method
end
I have a question about Ruby blocks.
For example I have a Ruby Class:
class NewClass
def initialize
#a = 1
end
def some_method
puts #a
end
end
When I do something like that:
NewClass.new do |c|
c.some_method
end
Everything is good, but is there any possibilities to do that somehow like:
NewClass.new do
some_method
end
Any ideas?
Your current code will just ignore the block anyway since you don't yield to it. For what you are trying to do in your first example you need the yield self idiom in initialize.
For why you need a block variable in the first place, think about what the receiver for some_method would be in your second example. Without an explicit receiver it's the top level main (unless this code is part of some other class of course, where that enclosing class would be self). See Dave Thomas' blog post Changing self in Ruby (or Yehuda Katz' post as pointed out by Niklas B. in the comments) for more info on that topic (the comments clear up the "proc invocation" part).
Edit: all that said, this seems to work, but I prefer the yield self version and example 1:
class NewClass
def initialize
#a = 1
end
def some_method
puts "Hello: ##a"
end
def self.build(&block)
x = self.new
x.instance_eval(&block)
x
end
end
NewClass.build do
some_method
end
This allows you to execute the block without a block variable and will return the new instance of the class for assigning to a variable etc.
class NewClass
def initialize(&block)
#a = 1
instance_eval(&block)
end
def some_method
puts #a
end
end
NewClass.new do
some_method
end
Using instance_eval should do the job. But unless you know what you are doing (and it's not just out of laziness) I'd advice against it, and go with your original approach.
def initialize(&block)
#a = 1
self.instance_eval(&block) if block_given?
end
With the following code:
def index
#q = ""
#q = params[:search][:q] if params[:search]
q = #q
#search = Sunspot.search(User) do
keywords q
end
#users = #search.results
end
If #q is used instead of q, the search always returns results for an empty query (""). Why is this?
Is the #q variable unavailable to the do...end block?
It depends on how the block is being called. If it is called using the yield keyword or the Proc#call method, then you'll be able to use your instance variables in the block. If it's called using Object#instance_eval or Module#class_eval then the context of the block will be changed and you won't be able to access your instance variables.
#x = "Outside the class"
class Test
def initialize
#x = "Inside the class"
end
def a(&block)
block.call
end
def b(&block)
self.instance_eval(&block)
end
end
Test.new.a { #x } #=> "Outside the class"
Test.new.b { #x } #=> "Inside the class"
In your case, it looks like Sunspot.search is calling your block in a different context using instance_eval, because the block needs easy access to that keywords method.
As Jeremy says, Sunspot executes its search DSL in a new scope.
In order to use an instance variable in the Sunspot.search block, you'll need to pass it an argument. Something like this should work (not tested):
#q = params[:search][:q] if params[:search]
#search = Sunspot.search(User) do |query|
query.keywords #q
end
#users = #search.results
See here for a better explanation: http://groups.google.com/group/ruby-sunspot/msg/d0444189de3e2725