The Problem
There is a pattern that I find myself to be frequently using, so I'd like to dry it up. I have stuff like this:
class InfoGatherer
def foo
true
end
def people
unless #people
#people = # Long and complex calculation (using foo)
end
#people
end
end
I'd like to dry this up to look like this:
class InfoGatherer
extend AttrCalculator
def foo
true
end
attr_calculator(:people) { # Long and complex calculation (using foo) }
end
To accomplish this, I defined a module AttrCalculator to extend into InfoGatherer. Here's what I tried:
module AttrCalculator
def attr_calculator(variable_name_symbol)
variable_name = "##{variable_name_symbol}"
define_method variable_name_symbol do
unless instance_variable_defined?(variable_name)
instance_variable_set(variable_name, block.call)
end
instance_variable_get(variable_name)
end
end
end
Unfortunately, when I try something as simple as InfoGatherer.new.people, I get:
NameError: undefined local variable or method `foo' for InfoGatherer:Class
Well, that's odd. Why is block running in the scope of InfoGatherer:Class, rather than its instance InfoGatherer.new?
The Research
I know I can't use yield, because that would try to catch the wrong block, as seen here.
I attempted to use self.instance_exec(block) in the place of block.call above, but then I received a new error:
LocalJumpError: no block given
Huh? I see the same error in this SO question, but I'm already using bracket notation, so the answers there don't seem to apply.
I also tried to use class_eval, but I'm not sure how to call block inside of a string. This certainly doesn't work:
class_eval("
def #{variable_name_symbol}
unless #{variable_name}
#{variable_name} = #{block.call}
end
#{variable_name}
end
")
That use case is called memoization. It can be done easily like:
def people
#people ||= # Long and complex calculation (using foo)
end
You shouldn't go into the mess like you are.
The problem was that, inside the define_method, self was surprisingly InfoGatherer, rather than an instance of InfoGatherer. So I was on the right track with self.instance_exec(block).
The working solution is self.instance_exec(&block) (note the ampersand). I guess the interpreter doesn't recognize that block is a block unless you label it as such? If anyone can explain this better than me, please do.
As a side note, this is not the best way to solve this particular problem. See #sawa's answer for a clean way to memoize complicated calculations.
To expand on the last persons
def people(varariable = nil)
#people ||= ComplexCalculation.new(variable).evaluate
end
class ComplexCalculation
def initialize(variable)
#variable = variable
end
def evaluate(variable)
#stuff
end
end
By extracting this class you are isolating that complexity and will have a much better experience.
Related
module Access
def last
self[-1]
end
def start_end
self[0] + last
end
end
module StringExt
refine String do
include Access
end
end
using StringExt
puts 'abcd'.last # => d
puts 'abcd'.start_end
When a class being refined with too many connected methods, I think it is better to extract them to a module. However, in above example which demos a problem when one method calls another(see the last statement), and it produces following error.
in 'start_end': undefined local variable or method 'last' for "abcd":String (NameError)
Similar issue was solved using a global variable, which also works for my example. But I'm seeking another better way to organize inter-called methods being refined and avoid a global thing.
How would advice a better way to organize those methods?
Here's a general pattern I ended up using. Basically I found no workaround for using global identifiers at some level. But this can be done fairly cleanly by making those globals classes/modules. This will be more clear as an example:
module StringPatches
def self.non_empty?(string)
!string.empty?
end
def non_empty?
StringPatches.non_empty?(self)
end
def non_non_empty?
!StringPatches.non_empty?(self)
end
refine String do
include StringPatches
end
end
class Foo
using StringPatches
puts "asd".non_empty? # => true
puts "asd".non_non_empty? # => false
end
The class methods on StringPatches don't get exported to using. But since classes/modules are constants (globals) they can be accessed from anywhere.
I would like to make something like this:
class Result<<ActiveRecord::Base
def condensation
#some code here that calculates #winner and #looser and #condresalut
def winner
#winner
end
def looser
#looser
end
def showresault
#condresalut
end
end
end
so that I can call res.condensation.winner and res.condensation.looser and res.condensation.showresault.
What is the best way to do it? Apparently this way it does not work, I got nils.
It is indeed possible to do so. Not sure what the intent is, as that has been asked, but not sure if that question was clarified.
However Jay Fields has a well visited blog entry that shows how to define a method inside a method.
class Class
def def_each(*method_names, &block)
method_names.each do |method_name|
define_method method_name do
instance_exec method_name, &block
end
end
end
end
Your methods themselves inside your definition though are likely better served using the attr_reader technique.
As far as calling nested defined methods:
def testing
def testing2
'it worked'
end
end
puts testing::testing2
Thogh as Alex D reminds me in the comments, the scope operator is a deception.
I don't think you can get there from here.
Ruby allows us to define methods inside methods, but the inner methods are not exposed, or available directly.
The inner methods are only available from within the outer method, so, in your example, winner, looser and showresault are only accessible from inside condensation.
You could create lambdas or procs and return them, en masse, as closures, which would give you access to the internal values inside condensation, but, really, it seems as if you're confusing the use of a class vs. a method and trying to make a method behave like a class with its accessors. Instead, I'd probably create a class within a class, and go from there.
def condensation
#condensation ||= Struct.new(:winner, :looser, :showresult).new
end
def winner
#winner ||= condensation.winner
end
def winner=(winner)
#winner = winner
end
... and so on
I changed resault by result, and I wanted to change showresult with show_result
You can calculate winner like this:
def calculate_winner
# something using winner= method
end
as far as I understand 'send' method, this
some_object.some_method("im an argument")
is same as this
some_object.send :some_method, "im an argument"
So what is the point using 'send' method?
It can come in handy if you don't know in advance the name of the method, when you're doing metaprogramming for example, you can have the name of the method in a variable and pass it to the send method.
It can also be used to call private methods, although this particular usage is not considered to be a good practice by most Ruby developers.
class Test
private
def my_private_method
puts "Yay"
end
end
t = Test.new
t.my_private_method # Error
t.send :my_private_method #Ok
You can use public_send though to only be able to call public methods.
In addition to Intrepidd's use cases, it is convenient when you want to route different methods on the same receiver and/or arguments. If you have some_object, and want to do different things on it depending on what foo is, then without send, you need to write like:
case foo
when blah_blah then some_object.do_this(*some_arguments)
when whatever then some_object.do_that(*some_arguments)
...
end
but if you have send, you can write
next_method =
case foo
when blah_blah then :do_this
when whatever then :do_that
....
end
some_object.send(next_method, *some_arguments)
or
some_object.send(
case foo
when blah_blah then :do_this
when whatever then :do_that
....
end,
*some_arguments
)
or by using a hash, even this:
NextMethod = {blah_blah: :do_this, whatever: :do_that, ...}
some_object.send(NextMethod[:foo], *some_arguments)
In addition to everyone else's answers, a good use case would be for iterating through methods that contain an incrementing digit.
class Something
def attribute_0
"foo"
end
def attribute_1
"bar"
end
def attribute_2
"baz"
end
end
thing = Something.new
3.times do |x|
puts thing.send("attribute_#{x}")
end
#=> foo
# bar
# baz
This may seem trivial, but it's occasionally helped me keep my Rails code and templates DRY. It's a very specific case, but I think it's a valid one.
The summing briefly up what was already said by colleagues: send method is a syntax sugar for meta-programming. The example below demonstrates the case when native calls to methods are likely impossible:
class Validator
def name
'Mozart'
end
def location
'Salzburg'
end
end
v = Validator.new
'%name% was born in %location%'.gsub (/%(?<mthd>\w+)%/) do
# v.send :"#{Regexp.last_match[:mthd]}"
v.send Regexp.last_match[:mthd].to_sym
end
=> "Mozart was born in Salzburg"
I like this costruction
Object.get_const("Foo").send(:bar)
Sometimes we end up writing several methods something like:
module XyzGateway
module Defaults
def pull_sample asynch=true
'N/A'
end
def is_pull_available?
false
end
def is_push_available?
true
end
def connect params
logger.debug "Invalid gateway(#{self.id}), could not resolve its type. #{ap self}"
end
def gateway_init
end
def disconnect
end
end
end
I am simply looking for a way to avoid these def and end keywords in such cases, is there any way out? In my case above, these are default behaviors and I would love if I could avoid these def, end.
Edit: yes, actually I do have a module XyzGateway::Defaults for all these.
You cannot avoid them, except by using define_method:
define_method :is_pull_available? { false }
If your goal is just to shorten your code though, you can put the whole method on one line, which isn't so bad for extremely short methods (the fourth method here is probably a bit too long and condensing it like this hurts readability, IMO):
def pull_sample(asynch = true); 'N/A'; end
def is_pull_available?; false; end
def is_push_available?; true; end
def connect params; logger.debug "Invalid gateway(#{self.id}), could not resolve its type. #{ap self}"; end
def gateway_init; end
def disconnect; end
For static methods you can define hash and use it to define methods:
methods_to_define = { 'pull_sample' => 'N/A', 'is_pull_available?' => false,
'is_push_available?' => true, 'gateway_init' => nil, 'disconnect' => nil }
methods_to_define.each_pair do |key, value|
define_method(key) { value }
end
I have this problem too sometimes. It can even make me feel like I'm getting too clever for Ruby. I know that isn't the case. In reality, I think I'm just using the language in a non-ideal way.
When I have a series of one line methods, which I find to be the ultimate conclusion to just about any object oriented refactor, I figure I have also locked in the design. This is probably a fine thing to do if the code has matured around the problem, but probably a bad thing to do prematurely. For that reason, I try to keep the methods a little more willy-nilly and with some meat on the bones.
I also find, that when I have a series of one line methods, I'm probably getting closer to realizing one facet of the beauty of lisp. But Ruby simple doesn't seem like the right place to be doing that, I argue.
So instead, I would prefer to be Ruby like. Well, what does that mean?
I've seen a lot of people do this.
def is_pull_available?; false end
Knowing to leave off the second ; is a sign of someone who is at least familiar with that aspect of the Ruby grammar.
This is fast and easy to do, but still kinda yuk.
So what's a Ruby programmar to do? One perhaps with a little extra free time? Well, maybe they can create a DSL. That's all they've really done so far anyway, might as well present it that much more elegantly.
So perhaps we turn
def is_pull_available?
false
end
into
pull_available false
All you really need to do to make this happen is...
def self.pull_available(value)
define_method(:is_pull_available?) { value }
end
And either throw that in a base class or mix it in from a module.
I think this is really a step you will want to reserve for when you really want to lock in the domain logic and accentuate it. The more you polish it up, the more you are going to feel poorly when it changes.
Metaprogramming in Ruby is probably a great book to read if you are interested in this sorta stuff.
How about this?
class Module
def simple_method meth, value
define_method(meth){value}
end
end
class A
simple_method :is_pull_available?, false
end
Or
class Module
def simple_method hash
hash.each do |key, value|
define_method(key){value}
end
end
end
class A
# Ruby 1.9 only
simple_method is_pull_available?: false
end
Learning ruby. I'm under the impression that boolean attributes should be named as follows:
my_boolean_attribute?
However, I get syntax errors when attempting to do the following:
class MyClass
attr_accessor :my_boolean_attribute?
def initialize
:my_boolean_attribute? = false
end
end
Apparently ruby is hating the "?". Is this the convention? What am I doing wrong?
Edit: three-years later; the times, they are a-changin'…
Julik's answer is the simplest and best way to tackle the problem these days:
class Foo
attr_accessor :dead
alias_method :dead?, :dead # will pick up the reader method
end
My answer to the original question follows, for posterity…
The short version:
You can't use a question mark in the name of an instance variable.
The longer version:
Take, for example, attr_accessor :foo — it's simply conceptually a bit of syntactic sugar for the following:
def foo
#foo
end
def foo=(newfoo)
#foo = newfoo
end
Furthermore, the question-mark suffix is mostly just a convention to indicate that the return value of a method is a boolean.
The best approximation I can make of what you're going for here…
class MyClass
def initialize
#awesome = true
end
def awesome?
#awesome
end
end
In this case, there may be a case to be made for using attr_accessor — after all, it may be explicit that you're working directly with a boolean attribute. Generally, I save the question-mark suffix for when I am implementing a method whose boolean return value is based on slightly more complex conditions than just the value of an attribute.
Cheers!
Edit, two years later, after a recent comment:
Ruby enforces certain naming conventions. Symbols in Ruby can't have question marks. Thus invocations of :my_boolean_attribute? both will fail with a NameError. Edit: not correct, just use the quoted syntax for a symbol, e.g., :"my_attribute?"
Symbols are immutable, attempting to assign to one will throw a SyntaxError.
The easiest way to quickly add a "question method" is to use aliasing for your reader method
class Foo
attr_accessor :dead
alias_method :dead?, :dead # will pick up the reader method
end
The attr_accessor symbol implies that the variable name is #my_boolean_attribute, so that's what you should be setting (not the symbol).
Also, you can't use ? for variables, just method names.
? is convention for methodnames, not variables. You can't use an instance variable named #foo?, however you could use a variable named #foo and name the (manually created) getter method foo? if you wanted to.
Monkey-patching metaprogramming - maybe it can be made more elegant, this is only a quick draft, and I haven't done metaprogramming for a little while...
# inject the convenience method into the definition of the Object class
class Object
def Object::bool_attr(attrname)
class_eval { define_method(attrname.to_s,
lambda { instance_variable_get('#' + attrname.to_s.chop) }) }
class_eval { define_method(attrname.to_s.chop+"=",
lambda { |x| instance_variable_set('#'+attrname.to_s.chop, x) }) }
end
end
### somewhere later
class MyClass
bool_attr :my_boolean_attribute?
def initialize
#my_boolean_attribute = true
end
end
# yet even more later
foo = MyClass.new
bar = MyClass.new
foo.my_boolean_attribute = 1
puts foo.my_boolean_attribute?
puts bar.my_boolean_attribute?
With this approach, you can be DRY and get the nice questionmark too. You just might need to pick a better name than "bool_attr", like, "bool_attr_accessor" or something similar.
The definitions that I made are a bit cranky, in a sense that the question mark is present in the original symbol. Probably a cleaner approach would be to avoid the questionmark in the symbol name and append it during the definition of the method - should be less confusing.
Oh, and almost forgot to include the obligatory link: Seeing metaclasses clearly
I looked through the answers, and while the accepted answer is on-target, it introduces "extra" noise in the class. The way I'd suggest solving this issue is:
class Animal
attr_writer :can_swim
def initialize(animal_type_name)
#can_swim = true
#animal_type_name = animal_type_name
end
def can_swim?
#can_swim
end
def to_s
#animal_type_name
end
end
dog = Animal.new('Dog in a bag')
dog.can_swim = false
puts "Can this #{dog} Swim? --- [#{dog_without_legs.can_swim? ? 'YEP!' : 'NOPE!'}]"