Related
As a ruby newbie I am trying to understand a snippet of code in our baseline. Could someone please do that for me ? The snippet appears below
%w{word1 word2}.each { |att| define_method(att.to_sym) { return nil }}
In the context where this line will be run, two methods will be defined
def word1
return nil
end
def word2
return nil
end
For example
class MyClass
%w{word1 word2}.each { |att| define_method(att.to_sym) { return nil }}
end
After my_class.rb file will be loaded you will be able to consume generated methods
test = MyClass.new
test.word1
# or
test.word2
Like jdv said in the comments, for tutorials you might be better of on other websites. Here are all references needed to understand the piece of code provided:
Percent strings, used in %w{word1 word2}
Percent Strings
Besides %(...) which creates a String, the % may
create other types of object. As with strings, an uppercase
letter allows interpolation and escaped characters while a
lowercase letter disables them.
These are the types of percent strings in ruby:
%i: Array of Symbols
%q: String
%r: Regular Expression
%s: Symbol
%w: Array of Strings
%x: Backtick (capture subshell result)
For the two array forms of percent string, if you wish to
include a space in one of the array entries you must escape
it with a “\” character:
%w[one one-hundred\ one]
#=> ["one", "one-hundred one"]
If you are using “(”, “[”, “{”, “<” you must close it with
“)”, “]”, “}”, “>” respectively. You may use most other
non-alphanumeric characters for percent string delimiters
such as “%”, “|”, “^”, etc.
Array#each
each {|item| block} → ary
each → Enumerator
Calls the given block once for each element in self,
passing that element as a parameter. Returns the array
itself.
If no block is given, an Enumerator is returned.
a = [ "a", "b", "c" ]
a.each {|x| print x, " -- " }
produces:
a -- b -- c --
Module#define_method
define_method(symbol, method) → symbol
define_method(symbol) { block } → symbol
Defines an instance method in the receiver. The method
parameter can be a Proc, a Method or an UnboundMethod
object. If a block is specified, it is used as the method
body. This block is evaluated using instance_eval.
class A
def fred
puts "In Fred"
end
def create_method(name, &block)
self.class.define_method(name, &block)
end
define_method(:wilma) { puts "Charge it!" }
end
class B < A
define_method(:barney, instance_method(:fred))
end
a = B.new
a.barney
a.wilma
a.create_method(:betty) { p self }
a.betty
produces:
In Fred
Charge it!
#<B:0x401b39e8>
String#to_sym
to_sym → symbol
Returns the Symbol corresponding to str, creating the
symbol if it did not previously exist. See Symbol#id2name.
"Koala".intern #=> :Koala
s = 'cat'.to_sym #=> :cat
s == :cat #=> true
s = '#cat'.to_sym #=> :#cat
s == :#cat #=> true
This can also be used to create symbols that cannot be
represented using the :xxx notation.
'cat and dog'.to_sym #=> :"cat and dog"
%w{word1 word2} = creating an array that looks like this ['word1', 'word2']
.each = iterating through each value in the array
{} = this is a code block each value in the array will be run through this block
|attr| = block parameter. each value in the array will be placed here
define_method = define a method from the argument
(att.to_sym) = the name of the new method. this will be word1 and then word2
{ return nil } = the body of the new method
So what is happening is you are defining two new methods. One method called word1 and another called word2. Each of these methods will have a body of return nil. They will look like this:
def word1
return nil
end
def word2
return nil
end
I am looking to check a variabe for its type based on the value held in another variable but am struggling with it. I'm completely new to ruby but can anyone tell me how to have the value of a variable interpreted in the expression? My current code looks like:-
if variable.is_a?("#{variable_type}")
puts variable
end
Where variable could contain anything and variable_type contains the type of a variable like String or Fixnum. But currently this code gives me TypeError: Class or module required. Any thoughts?
Your code sends a String object to the #is_a? method and the #is_a method expects a Class.
For example, String vs. "String":
variable = "Hello!"
variable_type = String
"#{variable_type}" # => "String"
# your code:
if variable.is_a?("#{variable_type}")
puts variable
end
#is_a? expects the actual Class (String, Fixnum, etc') - as you can see in the documentation for #is_a?.
You can adjust your code in two ways:
pass the Class, without the string.
convert the string to the class using Module.const_get.
here is an example:
variable = "Hello!"
variable_type = String
"#{variable_type}" # => "String"
# passing the actual class:
if variable.is_a?(variable_type)
puts variable
end
# or,
# converting the string to a the type:
if variable.is_a?( Module.const_get( variable_type.to_s ) )
puts variable
end
Just a little example:
variable = 1
variable_type = String
puts variable if variable.is_a?(variable_type)
#=> nil
variable_type = Integer
puts variable if variable.is_a?(variable_type)
#=> 1
Or when your variable_type is a string:
variable_type = 'Integer'
puts variable if variable.is_a?(Object.const_get(variable_type))
#=> 1
TypeError: Class or module required
It means, that to use is_a? varibale_type should hold a class name (any).
Therefore if you hold anything else except for class name in variable_type it will give you this error.
a = :a
variable_type = Symbol
a if a.is_a? variable_type
# => :a
If variable type is a string, you will have to use Module#const_get:
variable_type = 'Symbol'
a if a.is_a? Object.const_get(variable_type)
# => :a
I don't understand completely how named parameters in Ruby 2.0 work.
def test(var1, var2, var3)
puts "#{var1} #{var2} #{var3}"
end
test(var3:"var3-new", var1: 1111, var2: 2222) #wrong number of arguments (1 for 3) (ArgumentError)
it's treated like a hash. And it's very funny because to use named parameters in Ruby 2.0 I must set default values for them:
def test(var1: "var1", var2: "var2", var3: "var3")
puts "#{var1} #{var2} #{var3}"
end
test(var3:"var3-new", var1: 1111, var2: 2222) # ok => 1111 2222 var3-new
which very similar to the behaviour which Ruby had before with default parameters' values:
def test(var1="var1", var2="var2", var3="var3")
puts "#{var1} #{var2} #{var3}"
end
test(var3:"var3-new", var1: 1111, var2: 2222) # ok but ... {:var3=>"var3-new", :var1=>1111, :var2=>2222} var2 var3
I know why is that happening and almost how it works.
But I'm just curious, must I use default values for parameters if I use named parameters?
And, can anybody tell me what's the difference between these two then?
def test1(var1="default value123")
#.......
end
def test1(var1:"default value123")
#.......
end
I think that the answer to your updated question can be explained with explicit examples. In the example below you have optional parameters in an explicit order:
def show_name_and_address(name="Someone", address="Somewhere")
puts "#{name}, #{address}"
end
show_name_and_address
#=> 'Someone, Somewhere'
show_name_and_address('Andy')
#=> 'Andy, Somewhere'
The named parameter approach is different. It still allows you to provide defaults but it allows the caller to determine which, if any, of the parameters to provide:
def show_name_and_address(name: "Someone", address: "Somewhere")
puts "#{name}, #{address}"
end
show_name_and_address
#=> 'Someone, Somewhere'
show_name_and_address(name: 'Andy')
#=> 'Andy, Somewhere'
show_name_and_address(address: 'USA')
#=> 'Someone, USA'
While it's true that the two approaches are similar when provided with no parameters, they differ when the user provides parameters to the method. With named parameters the caller can specify which parameter is being provided. Specifically, the last example (providing only the address) is not quite achievable in the first example; you can get similar results ONLY by supplying BOTH parameters to the method. This makes the named parameters approach much more flexible.
The last example you posted is misleading. I disagree that the behavior is similar to the one before. The last example passes the argument hash in as the first optional parameter, which is a different thing!
If you do not want to have a default value, you can use nil.
If you want to read a good writeup, see "Ruby 2 Keyword Arguments".
As of Ruby 2.1.0, you no longer have to set default values for named parameters. If you omit the default value for a parameter, the caller will be required to provide it.
def concatenate(val1: 'default', val2:)
"#{val1} #{val2}"
end
concatenate(val2: 'argument')
#=> "default argument"
concatenate(val1: 'change')
#=> ArgumentError: missing keyword: val2
Given:
def test1(var1="default value123")
var1
end
def test2(var1:"default value123")
var1
end
They'll behave the same way when not passed an argument:
test1
#=> "default value123"
test2
#=> "default value123"
But they'll behave much differently when an argument is passed:
test1("something else")
#=> "something else"
test2("something else")
#=> ArgumentError: wrong number of arguments (1 for 0)
test1(var1: "something else")
#=> {:var1=>"something else"}
test2(var1: "something else")
#=> "something else"
I agree with you that it's weird to require default values as the price for using named parameters, and evidently the Ruby maintainers agree with us! Ruby 2.1 will drop the default value requirement as of 2.1.0-preview1.
This is present in all the other answers, but I want to extract this essence.
There are four kinds of parameter:
Required
Optional
Positional
def PR(a)
def PO(a=1)
Keyword
def KR(a:)
def KO(a:1)
When defining a function, positional arguments are specified before keyword arguments, and required arguments before optional ones.
irb(main):006:0> def argtest(a,b=2,c:,d:4)
irb(main):007:1> p [a,b,c,d]
irb(main):008:1> end
=> :argtest
irb(main):009:0> argtest(1,c: 3)
=> [1, 2, 3, 4]
irb(main):010:0> argtest(1,20,c: 3,d: 40)
=> [1, 20, 3, 40]
EDIT: the required keyword argument (without a default value) is new as of Ruby 2.1.0, as mentioned by others.
Leaving this here because it helped me a lot.
Example
Suppose you have this:
def foo(thing, to_print)
if to_print
puts thing
end
end
# this works
foo("hi", true)
# hi
# => nil
so you try adding the argument names, like so:
foo(thing: "hi", to_print: true)
# foo(thing: "hi", to_print: true)
# ArgumentError: wrong number of arguments (given 1, expected 2)
# from (pry):42:in `foo'
but unfortunately it errors.
Solution
Just add a : to the end of each argument:
def foo2(thing:, to_print:)
if to_print
puts thing
end
end
foo2(thing: "hi", to_print: true)
# hi
# => nil
And it works!
According to "Ruby 2.0.0 by Example" you must have defaults:
In Ruby 2.0.0, keyword arguments must have defaults, or else must be captured by **extra at the end.
def test(a = 1, b: 2, c: 3)
p [a,b,c]
end
test #=> [1,2,3]
test 10 #=> [10,2,3]
test c:30 #=> [1,2,30] <- this is where named parameters become handy.
You can define the default value and the name of the parameter and then call the method the way you would call it if you had hash-based "named" parameters but without the need to define defaults in your method.
You would need this in your method for each "named parameter" if you were using a hash.
b = options_hash[:b] || 2
as in:
def test(a = 1, options_hash)
b = options_hash[:b] || 2
c = options_hash[:c] || 3
p [a,b,c]
end
You can define named parameters like
def test(var1: var1, var2: var2, var3: var3)
puts "#{var1} #{var2} #{var3}"
end
If you don't pass one of the parameters, then Ruby will complain about an undefined local variable or method.
This question already has answers here:
How to dynamically create a local variable?
(4 answers)
Closed 7 years ago.
I'm interested in dynamically setting local variables in Ruby. Not creating methods, constants, or instance variables.
So something like:
args[:a] = 1
args.each_pair do |k,v|
Object.make_instance_var k,v
end
puts a
> 1
I want locally variables specifically because the method in question lives in a model and I dont want to pollute the global or object space.
As an additional information for future readers, starting from ruby 2.1.0 you can using binding.local_variable_get and binding.local_variable_set:
def foo
a = 1
b = binding
b.local_variable_set(:a, 2) # set existing local variable `a'
b.local_variable_set(:c, 3) # create new local variable `c'
# `c' exists only in binding.
b.local_variable_get(:a) #=> 2
b.local_variable_get(:c) #=> 3
p a #=> 2
p c #=> NameError
end
As stated in the doc, it is a similar behavior to
binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")
The problem here is that the block inside each_pair has a different scope. Any local variables assigned therein will only be accessible therein. For instance, this:
args = {}
args[:a] = 1
args[:b] = 2
args.each_pair do |k,v|
key = k.to_s
eval('key = v')
eval('puts key')
end
puts a
Produces this:
1
2
undefined local variable or method `a' for main:Object (NameError)
In order to get around this, you could create a local hash, assign keys to this hash, and access them there, like so:
args = {}
args[:a] = 1
args[:b] = 2
localHash = {}
args.each_pair do |k,v|
key = k.to_s
localHash[key] = v
end
puts localHash['a']
puts localHash['b']
Of course, in this example, it's merely copying the original hash with strings for keys. I'm assuming that the actual use-case, though, is more complex.
interesting, you can change a local variable but you cannot set it:
def test
x=3
eval('x=7;')
puts x
end
test =>
7
def test
eval('x=7;')
puts x
end
test =>
NameError: undefined local variable or method `x' for main:Object
This is the only reason why Dorkus Prime's code works.
I suggest you use the hash (but keep reading for other alternatives).
Why?
Allowing arbitrary named arguments makes for extremely unstable code.
Let's say you have a method foo that you want to accept these theoretical named arguments.
Scenarios:
The called method (foo) needs to call a private method (let's call it bar) that takes no arguments. If you pass an argument to foo that you wanted to be stored in local variable bar, it will mask the bar method. The workaround is to have explicit parentheses when calling bar.
Let's say foo's code assigns a local variable. But then the caller decides to pass in an arg with the same name as that local variable. The assign will clobber the argument.
Basically, a method's caller must never be able to alter the logic of the method.
Alternatives
An alternate middle ground involves OpenStruct. It's less typing than using a hash.
require 'ostruct'
os = OpenStruct.new(:a => 1, :b => 2)
os.a # => 1
os.a = 2 # settable
os.foo # => nil
Note that OpenStruct allows you access non-existent members - it'll return nil. If you want a stricter version, use Struct instead.
This creates an anonymous class, then instantiates the class.
h = {:a=>1, :b=>2}
obj = Struct.new(* h.keys).new(* h.values)
obj.a # => 1
obj.a = 2 # settable
obj.foo # NoMethodError
since you don't want constants
args = {}
args[:a] = 1
args[:b] = 2
args.each_pair{|k,v|eval "##{k}=#{v};"}
puts #b
2
you might find this approach interesting ( evaluate the variables in the right context)
fn="b*b"
vars=""
args.each_pair{|k,v|vars+="#{k}=#{v};"}
eval vars + fn
4
In Ruby, is it possible to pass by reference a parameter with value-type semantics (e.g. a Fixnum)?
I'm looking for something similar to C#'s 'ref' keyword.
Example:
def func(x)
x += 1
end
a = 5
func(a) #this should be something like func(ref a)
puts a #should read '6'
Btw. I know I could just use:
a = func(a)
You can accomplish this by explicitly passing in the current binding:
def func(x, bdg)
eval "#{x} += 1", bdg
end
a = 5
func(:a, binding)
puts a # => 6
Ruby doesn't support "pass by reference" at all. Everything is an object and the references to those objects are always passed by value. Actually, in your example you are passing a copy of the reference to the Fixnum Object by value.
The problem with the your code is, that x += 1 doesn't modify the passed Fixnum Object but instead creates a completely new and independent object.
I think, Java programmers would call Fixnum objects immutable.
In Ruby you can't pass parameters by reference. For your example, you would have to return the new value and assign it to the variable a or create a new class that contains the value and pass an instance of this class around. Example:
class Container
attr_accessor :value
def initialize value
#value = value
end
end
def func(x)
x.value += 1
end
a = Container.new(5)
func(a)
puts a.value
You can try following trick:
def func(x)
x[0] += 1
end
a = [5]
func(a) #this should be something like func(ref a)
puts a[0] #should read '6'
http://ruby-doc.org/core-2.1.5/Fixnum.html
Fixnum objects have immediate value. This means that when they are assigned or
passed as parameters, the actual object is passed, rather than a reference to
that object.
Also Ruby is pass by value.
However, it seems that composite objects, like hashes, are passed by reference:
fp = {}
def changeit(par)
par[:abc] = 'cde'
end
changeit(fp)
p fp
gives
{:abc=>"cde"}