def foo(bar)
'return value'
end
foo 'bar' # => "return value"
def foo=(bar)
'return value'
end
foo = 'bar' # => "bar"
send :foo=, 'bar' # => "return value"
I want foo = 'bar' to return "return value" but not to use send for this purpose. How can I do this?
Update
I need a desired behavior in my gem. Here is an example:
car = Car.new
car.gear # => :first
car.next_gear # => :second
car.gear # => :second
car.gear = :fourth # => false
car.gear # => :second
car.gear = :third # => :third
car.gear # => :third
Assignments always return the right hand side of an assignment.
Have a look at the ruby documentation for details:
Methods that end with an equals sign indicate an assignment method.
For assignment methods the return value is ignored, the arguments are
returned instead.
Having said that, foo = bar also assigns to a local variable foo instead of using the foo= method. Again, this is defined in the ruby docs:
When using method assignment you must always have a receiver. If you
do not have a receiver Ruby assumes you are assigning to a local
variable
You can test that by running
local_variables #=> []
def foo=(bar);end
foo = 42
local_variables #=> [:foo]
You see that the local variable foo was created. Better use self.foo = 'bar'.
To address your specific problem with your gem: Follow Neil's advice and use an extra method like change_gear for what you want to do. He gave you good council in his comments.
It's a Ruby gotcha: the return value of accessor methods get ignored.
This code will make it more clear what is actually happening:
#!/usr/bin/env ruby
def foo(bar)
p "called :foo w/ #{bar.inspect}"
end
def foo=(bar)
p "called :foo= with #{bar.inspect}"
end
ret = (foo :bar1) # calls foo(bar)
p "ret: #{ret}" # "ret: called :foo w/ :bar1"
ret = (foo = :bar2) # assigns a local variable foo = 'bar2'
p "ret: #{ret}" # "ret: bar2"
ret = (send :foo=, :bar3) # calls foo=(bar), returns what p returns
p "ret: #{ret}" # "ret: called :foo= with :bar3"
ret = (self.foo = :bar4) # calls foo=(bar), returns ???
p "ret: #{ret}" # "ret: bar4"
Basically, the Ruby parser (in 2.1 at least) behaves as if self.foo= was calling an accessor method (even if it actually isn't assigning anything), and will always return the value passed to it irrespective of what you sent it, rather than the accessor's return value.
Demonstration:
#!/usr/bin/env ruby
class << self
attr_accessor :foo
def foo=(bar)
p "called :foo= with #{bar.inspect}"
#foo = :baz
end
end
ret = (self.foo = :bar)
p "ret: #{ret} vs #foo: #{#foo.inspect}"
Outputs:
"called :foo= with :bar"
"ret: bar vs #foo: :baz"
Edit: hat #tessi for the reference:
Methods that end with an equals sign indicate an assignment method. For assignment methods the return value is ignored, the arguments are returned instead.
I think the reason why it's failing is that local variable names take precedence over method names when they are defined.
So you need to use send so that self knows it's looking for a method instead of a variable.
You need to do this:
self.foo = 'bar'
Related
I'm learning ruby and my opinion about attr_reader is bad. Because you can change the value of particular instance variable from outside of class. For example:
class X
attr_reader :some_string
def initialize
#some_string = "abc"
end
end
tmp = X.new
puts tmp.some_string
tmp.some_string[0] = "aaaaaaaaa"
puts tmp.some_string
When you run this code you can see that the instance variable some_string has changed. So to avoid this I'm always making my own getter, and returning frozen object. For example:
class X
def initialize
#some_string = "abc"
end
def some_string
#some_string.freeze
end
end
tmp = X.new
puts tmp.some_string
tmp.some_string[0] = "aaaaaaaaa"
puts tmp.some_string
Now when you run the code, it throws an error saying can't modify frozen String: "abc", which is what I wanted. So my question is should I use attr_reader and is always returning frozen objects from getters bad practice?
attr_reader is not "bad", it simply does what it does, which is to return the value of the instance variable, which is a reference. Unlike attr_writer or attr_accessor it will not allow you to change the value of the instance variable (ie you can't change what the instance variable refers to)
The question is do you really want or need the reference. For example say you want to convert the value of some_string to upper case. You could use attr_reader to get the reference to some_string and then call upcase! on it like below. But having the reference allows you to call any method on the object including the []= method which maybe you don't want to allow. Note: []= is a method that manipulates the content of what some_string references it does not set #some_string to a new value, it still points to the same object, but the object it points to was manipulated.
class Foo
attr_reader :some_string
def initialize()
#some_string = "abc"
end
end
puts "Foo"
foo = Foo.new
some_string = foo.some_string
puts some_string #=> abc
some_string.upcase!
p foo # => #<Foo:0x0000563c38391ac8 #some_string="ABC">
puts some_string.object_id # => 60
some_string[0] = "x"
p foo # => #<Foo:0x0000563c38391ac8 #some_string="xBC">
puts some_string.object_id # => 60 ... ie same object different content
# foo.some_string = "ABC" is a runtime error
If you don't want to allow arbitrary methods to be called on an instance variable then you should not expose it using attr_reader, rather you should manipulate the instance variable via methods in your class. For example below I "delegate" the upcase! method to the instance variable #some_string, and I provide a string_value method to return a new string with the same value as the instance variable.
class Bar
def initialize()
#some_string = "abc"
end
def upcase!()
#some_string.upcase!
end
def string_value()
"#{#some_string}"
end
end
puts "Bar"
bar = Bar.new
p bar # => #<Bar:0x0000563c383915a0 #some_string="abc">
bar.upcase!
p bar # => #<Bar:0x0000563c383915a0 #some_string="ABC">
some_string = bar.string_value
p some_string # => "ABC"
some_string[0] = "x"
p bar # => #<Bar:0x0000563c383915a0 #some_string="ABC">
p some_string # => "xBC"
So I would say attr_reader is not bad, you might argue it is over used, I know I will often use it to "get" an instance variable when all I really need is some property on the instance variable.
A lot of developers try to use private attr_reader and use it inside the class or avoid using it at all
A good conversations about attr_reader are here and here
I'm trying to add an instance method foo to Ruby's Array class
so when it's invoked, the array's string elements are changed to string "foo".
This can be done easily by monkey patching Ruby's String and Array classes.
class String
def foo
replace "foo"
end
end
class Array
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'foo, 1, foo' as expected
Now I'm trying to rewrite the above using Ruby 2's refinements feature.
I'm using Ruby version 2.2.2.
The following works (in a file, eg. ruby test.rb, but not in irb for some reason)
module M
refine String do
def foo
replace "foo"
end
end
end
using M
s = ''
s.foo
puts s # you get 'foo'
However, I can't get it to work when adding foo onto the Array class.
module M
refine String do
def foo
replace "foo"
end
end
end
using M
module N
refine Array do
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
end
using N
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'a, 1, b', not 'foo, 1, foo' as expected
There're two issues:
After you refine a class with a new method, respond_to? does not work even when you can invoke
the method on an object. Try adding puts 'yes' if s.respond_to? :foo
as the last line in the second code snippet, you'll see 'yes' is not printed.
In my Array refinement, the String#foo is out of scope. If you remove if x.respond_to? :foo from
the Array#foo, you'll get the error undefined method 'foo' for "a":String (NoMethodError). So the question is: how do you make the String#foo refinement visible inside the Array#foo refinement?
How do I overcome these two issues so I can get this to work?
(Please don't offer alternative solutions that don't involve refinement, because this is a theoretical exercise so I can learn how to use refinement).
Thank you.
The respond_to? method does not work and this is documented
here.
The problem is that you can only activate a refinement at top-level
and they are lexical in scope.
One solution would be:
module N
refine String do
def foo
replace 'foobar'
end
end
refine Array do
def foo
self.each do |x|
x.foo rescue x
end
end
end
end
using N
a = ['a', 1, 'b']
p a.foo
puts a.join(", ") # foo, 1, foo
Taking up your example again, a simple solution could be to override the respond_to? method in refinement block :
module M
refine String do
def foo
replace "foo"
end
def respond_to?(name,all=false)
list_methods = self.methods.concat [:foo]
list_methods.include? name
end
end
refine Array do
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
end
using M
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'foo, 1, foo'
I was wondering how to pass a block to a method which will make the method return on yield.
The naive aproach doesn't work:
def run(&block)
block.call
end
run { return :foo } # => LocalJumpError
Wrapping in another proc has the same effect:
def run(&block)
proc { block.call }.call
end
run { return :bar } # => LocalJumpError
So I thought that the return statement is bound to the receiver of the current binding. However, trying it out with instance_eval proved me wrong:
class ProcTest
def run(&block)
puts "run: #{[binding.local_variables, binding.receiver]}"
instance_eval(&block)
end
end
pt = ProcTest.new
binding_inspector = proc { puts "proc: #{[binding.local_variables, binding.receiver]}" }
puts "main: #{[binding.local_variables, binding.receiver]}"
# => main: [[:pt, :binding_inspector], main]
binding_inspector.call
# => proc: [[:pt, :binding_inspector], main]
pt.run(&binding_inspector)
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => proc: [[:pt, :binding_inspector], #<ProcTest:0x007f4987b06508>]
pt.run { return :baz }
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => LocalJumpError
So the questions are:
How can this be done?
How is the return context tied to the return statement. Is this connection accessible via the language's API?
Was this implemented in such manner intentionally? If yes - why? If no - what are the obstacles to fix it?
I thought that the return statement is bound to the receiver of the current binding.
Only methods have an receiver. return is not a method:
defined? return #=> "expression"
Trying to invoke it as a method doesn't work:
def foo
send(:return, 123)
end
foo #=> undefined method `return'
trying it out with instance_eval proved me wrong
Though instance_eval evaluates the block in the context of the receiver (so you have access to the receivers instance methods and instance variables):
class MyClass
def foo(&block)
#var = 123
instance_eval(&block)
end
end
MyClass.new.foo { instance_variables }
#=> [:#var]
... it does not evaluate the block in the current binding (so you don't have access to any local variables):
class MyClass
def foo(&block)
var = 123
instance_eval(&block)
end
end
MyClass.new.foo { local_variables }
#=> []
How can this be done?
You could use eval, but that requires a string:
def foo
var = 123
eval yield
nil
end
foo { "return var * 2" }
#=> 246
Or by passing the binding to the block (again using eval):
def foo
var = 123
yield binding
nil
end
foo { |b| b.eval "return var * 2" }
#=> 246
return in a block returns from the enclosing method when the block is defined (ie, the closure in which the block is created). In your example, there is no enclosing block to return from, hence your exception.
This is easily demonstrated:
def foo(&block)
puts yield
puts "we won't get here"
end
def bar
foo { return "hi from the block"; puts "we never get here" }
puts "we never get here either"
end
puts bar # => "hi from the block" (only printed once; the puts in `foo` is not executed)
Return in a proc will immediately return out of the proc, not out of the method on the stack under the proc:
def foo(&block)
puts yield
puts "we will get here"
end
def bar
foo &->{ return "hi from the proc"; puts "we never get here" }
puts "we will get here too"
end
puts bar
# hi from the proc # puts from foo
# we will get here # puts from foo
# we will get here too # puts from bar
Because of these behaviors, there is no way to achieve your desired behavior, in which a return in the given block will execute a return in the method from which the block is invoked, unless the block was defined within that scope, since doing so would require one of the existing behaviors not work.
You could achieve something like this with throw...catch, which is kinda-sorta useful as a way to zip up the stack from an arbitrary depth, but you can't return arbitrary values with it:
def foo(&block)
yield
puts "we won't get here"
end
catch(:escape) do
foo &->{ throw :escape }
end
I can't figure out how to assign a function call to a ruby hash.
What I want to do is to assign a function to a hash key, and later
call this function using the classic hash lookout syntax.
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => Foo() }
puts "Executing"
test["foo"]
This code fails, function Foo is called after puts "Assign", during hash creation, and nothing happens after puts "Executing"
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => Foo }
puts "Executing"
test["foo"]
with this code I receive an uninitialized constant Foo (NameError).
Finally with
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => :Foo }
puts "Executing"
test["foo"]
I get not outputs.
Any suggestions?
Thanks to all for answres and suggestions.
What I'm going to do is to test
if a hash based approach to call function is faster than
an equivalent code based on if / case statements.
funcs["foo"].call
fatser than
if func_name == "foo" then
Foo()
elsif ...
...
end
or
case func_name
when "foo"
Foo()
when ...
...
end
Obviously for a big number of functions (~150) and hundreds of
calling cycles
you could use lambda's instead of methods. Two options here:
hash = {:foo => lambda { puts 'bar } }
hash[:foo].call
the second (more complicated) is this:
irb(main):001:0> class Hash
irb(main):002:1> alias :orig_anc :'[]'
irb(main):003:1>
irb(main):004:1* def [](key)
irb(main):005:2> if orig_anc(key).is_a? Proc
irb(main):006:3> orig_anc(key).call
irb(main):007:3> else
irb(main):008:3* orig_anc(key)
irb(main):009:3> end
irb(main):010:2> end
irb(main):011:1> end
=> nil
irb(main):012:0> h = {:hello => 'world', :foo => lambda { puts 'bar' }}
=> {:hello=>"world", :foo=>#<Proc:0x843224c#(irb):12 (lambda)>}
irb(main):013:0> h[:hello]
=> "world"
irb(main):014:0> h[:foo]
bar
=> nil
irb(main):015:0>
The second one just allows you to skip using 'call' method
There is no easy possibility to make your function execute simply by retrieving the hash key withput overriding Hash's [] method, as Vlad pointed out, i.e.
def foo
puts "hi"
end
... # magic
test["foo"] # outputs hi
won't work. What you can do, though, is assign the method reference using Object#method and then invoke it using call:
def foo
puts "hi"
end
test = { "foo" => method(:foo) }
test["foo"].call # => hi
First things first, start method names with lower case letters. Then, moving to first example, Ruby is eager so test = { "foo" => Foo() } Foo is called.
In the second example, variables starting with an uppercase letter are considered constants, so Ruby looks for one and do not bother looking for a method. Take into account that if foo where a method then foo would be called as in the first example.
Third example: test["foo"] returns :Foo, a symbol. Nowhere in your code Foo() is invoked.
*Suggestions
1.- Take a look at identifiers nomenclature in Ruby.
2.- You may took a look at lambda, Proc and Object#method
3.- If you have some money to spare and don't mind buying a pair of books, give Programming Ruby and Metaprogramming in Ruby a chance (both can be bought in PragProg.
You could try using the hash constructor with a block, so
def foo
puts "hi"
end
test = Hash.new do |hash, key|
send(key) if respond_to?(key)
end
test["foo"] # prints "hi", returns nil
So, I'd like to be able to make a call
x = MyClass.new('good morning', 'good afternoon', 'good evening', 'good night',
['hello', 'goodbye'])
that would add methods to the class whose values are the values of the arguments. So now:
p x.methods #> [m_greeting, a_greeting, e_greeting, n_greeting,
r_greeting, ...]
And
p x.m_greeting #> "good morning"
p x.r_greeting #> ['hello', 'goodbye']
I realize that this is sort of what instance variables are to do (and that if I wanted them immutable I could make them frozen constants) but, for reasons beyond my control, I need to make methods instead.
Thanks!
BTW: I tried
def initialize(*args)
i = 0
%w[m_a, m_b, m_c].each do |a|
self.class.send(:define_method, a.to_s, Proc.new { args[i] })
i+=1
end
end
But that ended up giving every method the value of the last argument.
I guess this solves the problem:
def initialize(*args)
#args = args
%w[m_a m_b m_c].each_with_index do |a, i|
eval "def #{a}; #args[#{i}]; end"
end
end
You can do what you want, like so:
class Foo
def initialize(*args)
methods = %w[m_greeting a_greeting e_greeting n_greeting r_greeting]
raise ArgumentError unless args.size == methods.size
args.zip(methods).each do |arg, method|
self.class.instance_eval do
define_method method do
arg
end
end
end
end
end
foo = Foo.new(1, 2, 3, 4, 5)
p foo.m_greeting # => 1
p foo.a_greeting # => 2
p foo.e_greeting # => 3
p foo.n_greeting # => 4
p foo.r_greeting # => 5
But this may not be the droid you're looking for: More than a few positional arguments can make code difficult to read. You might consider using OpenStruct. You'll have to write almost no code, and the constructor calls will be easier to read:
require 'ostruct'
class Foo < OpenStruct
end
foo = Foo.new(:m_greeting=>1,
:a_greeting=>2,
:e_greeting=>3,
:n_greeting=>4,
:r_greeting=>5)
p foo.m_greeting # => 1
p foo.a_greeting # => 2
p foo.e_greeting # => 3
p foo.n_greeting # => 4
p foo.r_greeting # => 5
Don't sweat mutability. If you feel the need to write code to protect yourself from mistakes, consider writing unit tests instead. Then the code can be unfettered with sundry checks and protections.
Your last loop would send the last argument to redefine the method for each of your m_a, m_b, m_c Try looping over the args and sending to the indexed method.
e.g.
def initialize(*args)
methods = %w[m_a m_b m_c]
args.each_with_index {|item,index|
self.class.send(:define_method, methods[index], lambda { item })
}
end
each_with_index comes from the Enumerable module: http://ruby-doc.org/core/classes/Enumerable.html#M003137