ruby class#initialize manifold arguments - ruby

i don't understand.
this one works:
class Foo
def initialize(*args)
#variables = *args unless args[0].eql?(nil)
end
end
this one don't:
class Foo
def initialize(*args)
args[0].eql?(nil) ? #multiples = [3,5] : #multiples = *args
end
end
but this one works too:
class Foo
def initialize(*args)
args[0].eql?(nil) ? #multiples = [3,5] : #multiples = args
end
end
my question is about 'args' and '*args' between the second form and the third.
why is that ?

The splat operator (the star *), as you know, when used in method definitions, will capture an unlimited number of arguments and convert them to an array. However, its purpose IS DIFFERENT when used in a METHOD body. There's no point of putting the splat * in a method body when doing an assignment because
a) You'll get the same result as if you didn't put it.
class Foo
def initialize(*args)
#a = args
#b = *args
p #a == #b
end
end
Foo.new(1,2,3) #=> prints 'true'
b) You'll get strange errors like in your second example which probably produces an error because Ruby confuses the precedence of the operators. It will work if you put parentheses around the whole expression, however:
class Foo
def initialize(*args)
args[0].eql?(nil) ? #multiples = [3,5] : (#multiples = *args)
end
end
So to sum up, when doing an assignment of a splat to a variable, don't use the * .
Btw, a bit off topic, instead of doing args[0].eql?(nil), you can just do arg[0].nil? Also, instead of assigning #multiples twice, you can do something like:
#multiples = args[0].nil? ? [3,5] : args

Related

Ruby equivalent of lisp-like "apply"?

I would like to create an instance method that takes another instance method of its own class as a parameter, and then applies the passed method on the instance it's working on (known as self):
class MyClass
attr_reader :called_methods
def initialize
#called_methods = []
end
def my_first_method!
#called_methods << :my_first_method
self
end
def my_second_method!
#called_methods << :my_second_method
self
end
def my_strange_method!(secondary)
# Want to apply method whose name is given by secondary, to self
end
end
p MyClass.new.my_second_method!.my_strange_method!(:my_first_method!).called_methods
I suspect the unary & may be key, but all the web pages I can find on that operator involve calling methods on multiple objects, as when iterating over an Enumerable with #each or #map.
Use Object#public_send (or Object#send to apply protected/private method).
def my_strange_method!(secondary)
public_send(secondary)
self
end
p MyClass.new.
my_second_method!.
my_strange_method!(:my_first_method!).
called_methods
#⇒ [:my_second_method, :my_first_method]
There could be more defensive way to apply if and only the method is known:
def my_strange_method!(secondary)
raise ArgumentError.new("Unknown method #{secondary}") \
unless methods.include? secondary.to_s.to_sym
public_send(secondary)
end
p MyClass.new.
my_second_method!.
my_strange_method!(:my_first_method!).
called_methods
#⇒ [:my_second_method, :my_first_method]
p MyClass.new.
my_second_method!.
my_strange_method!(:inexisting_method!).
called_methods
#⇒ ArgumentError: Unknown method inexisting_method!
This is tagged with functional-programming so I'm going to offer a persistent (immutable) design -
class MyClass
attr_reader :called_methods
def initialize(m = [])
#called_methods = m
end
def my_first_method!
MyClass.new(called_methods + [ :first ])
end
def my_second_method!
MyClass.new(called_methods + [ :second ])
end
def my_strange_method!(secondary)
public_send secondary
end
end
MyClass.new.my_second_method!.my_strange_method!(:my_first_method!).called_methods
# [:second, :first]

Ruby map method with instance_variables

Say I have a class SomeClass with instance variables a and b.
def SomeClass
def initialize a, b
#a = a
#b = b
end
end
When I type into pry the following lines
someclass = SomeClass.new "one", "two"
someclass.instance_variables
As expected it prints an array with symbols
[:#a, :#b]
now, when I use
a.instance_variables.map do |var|
puts var
end
I expect it to print
:#a
:#b
but what I am getting is
#a
#b
Can someone please explain this behaviour ?
puts transparently calls to_s on arguments. Symbol#to_s returns symbol’s name (strings w/out preceding colon.) On the other hand, Array#to_s calls inspect on nested elements, that’s why you see colons while putting array’s instance.
Look:
▶ :a.to_s
#=> "a"
▶ [:a,:b,:c].to_s
#=> "[:a, :b, :c]"
This is exactly what you yield calling puts.
If you just want to print the symbols as
:#a
:#b
use 'p var', as p calls inspect on its argument unlike puts which calls to_s

Calling super without arguments

According to the documentation for modules and classes, calling super (without arguments or parentheses) calls the parent method with the same arguments:
When used without any arguments super uses the arguments given to the subclass method.
Assigning a new value to the "argument variable" seems to alter this behavior:
class MyClass
def foo(arg)
puts "MyClass#foo(#{arg.inspect})"
end
end
class MySubclass < MyClass
def foo(arg)
puts "MySubclass#foo(#{arg.inspect})"
super
arg = 'new value'
super
end
end
MySubclass.new.foo('inital value')
Output:
MySubclass#foo("inital value")
MyClass#foo("inital value")
MyClass#foo("new value") # <- not the argument given to MySubclass#foo
Is this expected?
Update
This seems to be the expected behavior for positional and keyword arguments, but it doesn't work for block arguments:
class MyClass
def foo(&block)
puts "MyClass#foo { #{block.call.inspect} }"
end
end
class MySubclass < MyClass
def foo(&block)
puts "MySubclass#foo { #{block.call.inspect} }"
super
block = Proc.new { 'new value' }
super
end
end
MySubclass.new.foo { 'initial value' }
Output:
MySubclass#foo { "initial value" }
MyClass#foo { "initial value" }
MyClass#foo { "initial value" }
Lets take one example from the Ruby core:
Keyword2
class Base
def single(a) a end
def double(a, b) [a,b] end
def array(*a) a end
def optional(a = 0) a end
def keyword(**a) a end
end
class Keyword2 < Base
def keyword(foo: "keyword2")
foo = "changed1"
x = super
foo = "changed2"
y = super
[x, y]
end
end
Now, see the test case :-
def test_keyword2
assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
end
Above example exactly mathes the keyword documentation.
Called with no arguments and no empty argument list, super calls the appropriate method with the same arguments, and the same code block, as those used to call the current method. Called with an argument list or arguments, it calls the appropriate methods with exactly the specified arguments (including none, in the case of an empty argument list indicated by empty parentheses).
same arguments means it is saying the current values of argument variables.test_super.rb files contains all the varieties of stuffs we can do with super in Ruby.
No, it work with block too (taken from core) :
a = Class.new do
def foo
yield
end
end
b = Class.new(a) do
def foo
super{
"b"
}
end
end
b.new.foo{"c"} # => "b"
But, have no idea why the below is giving "c"? This is actually the updated question of the OP:
c = Class.new do
def foo(&block)
block.call
end
end
d = Class.new(c) do
def foo(&block)
block = -> { "b" }
super
end
end
d.new.foo{"c"} # => "c"
It seems to be the expected behavior, based on the RubySpec anyway.
module RestArgsWithSuper
class A
def a(*args)
args
end
end
class B < A
def a(*args)
args << "foo"
super
end
end
end
(language/fixtures/super.rb).
It's then expected that the arguments are modified:
it "passes along modified rest args when they weren't originally empty" do
Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"]
end
(language/super_spec.rb)
It's the expected behaviour. Technically, arg is the same argument, it just points to another value.
This answer might explain it better: https://stackoverflow.com/a/1872159/163640

How to "replace" number objects in-place in Ruby

I'm attempting to adjust a variable inside a Ruby/Rails function. Standard stuff for many other languages.
In c:
void change(int *io){
*io = 1;
}
Now we all know that Ruby is pass by reference (smiley face). This bit of code works perfectly:
def tester()
value = 'dave'
test_replace(value)
p value.to_s;
end
def test_replace(ioValue)
ioValue.replace 'test'
end
The output is 'test'.
So the problem is: fixed numbers don't have a replace method. They are not passed the same way as other values.
So my question is: how do I "io" an "int" in Ruby?
def tester()
value = 10
test_replace(value)
p value.to_s;
end
def test_replace(ioValue)
ioValue.replace 15
end
Which raises
undefined method `replace' for 10:Fixnum
#Linuxios is correct in his explanation about pass by value, but his work-arounds are pretty awkward. It's very easy to box a fixnum with a SimpleDelegator that supports a replace:
class NumberBox < SimpleDelegator
alias_method :replace, :__setobj__
class <<self; alias_method :[], :new; end
end
def tester()
value = NumberBox[10]
test_replace(value)
p value.to_s;
end
def test_replace(ioValue)
ioValue.replace 15
end
In the above value will behave exactly like the object it delegates to, established at initialization (with ::new or the NumberBox::[]) except it also supports the __setobj__ method and its alias replace which binds a new object as the delegatee.
There are really two ways to do this:
Wrap in an object
Wrap in an array
Create a mutable-int proxy object
Option two is easy, and I won't give an example. Option 1 would look like this:
class IntRef < BasicObject
def initialize(i)
#int = i
end
def replace(v)
#int = v
end
def method_missing(name, *args, &block)
#int.send(name, *args, &block)
end
end
Or option three: A proxy object!
class MutableInt < BasicObject
def initialize(i)
#int = i
end
def method_missing(name, *args, &block)
v = #int.send(name, *args, &block)
if(v.is_a?(::Fixnum))
#int = v
return self
end
v
end
end
WARNING: Anything, anything you do to this proxy sticks and changes the object. Beware.
Now we all know that Ruby is pass by reference
On the contrary, Ruby is pass by value only. No pass by reference. Same as Java, Python, etc.

Ruby Design Pattern Question - Classes/Modules Inheritance

Right now I have:
module A
class B
def initialize
#y = 'foo'
end
end
end
module A
class C < B
def initialize
#z = 'buzz'
end
end
end
How can I have it so when I instantiate C #y is still set equal to 'foo'? Do I have to repeat that in the initialize under C? I am a following a bad pattern? Should #y be a class variable or just a constant under the module? Any help would be appreciated!
class A::C < B
def initialize( x, y )
super # With no parens or arguments, this passes along whatever arguments
# were passed to this initialize; your initialize signature must
# therefore match that of the parent class
#z = 'buzz'
end
end
Or, as #EnabrenTane pointed out, you can explicitly pass along whatever arguments you know the super class will be expecting.
For more on inheritance, see the section on Inheritance and Messages in the old-but-free online version of the Pickaxe book.
You need the super keyword. It calls your parents definition of the same method.
I added params just in case. Note, to pass params B#initialize will have to take optional params as well.
module A
class C < B
def initialize(params = nil)
super(params) # calls B#initialize passing params
#z = 'buzz'
end
end
end

Resources