What's the difference between "=" & "=>" and "#variable", "##variable" and ":variable" in ruby? - ruby-on-rails

I know these are the basics of rails but i still don't know the full difference between = sign and => and the difference between #some_variable, ##some_variable and :some_variable in rails.
Thanks.

OK.
The difference between the = and the => operators is that the first is assignment, the second represents an association in a hash (associative array). So { :key => 'val' } is saying "create an associative array, with :key being the key, and 'val' being the value". If you want to sound like a Rubyist, we call this the "hashrocket". (Believe it or not, this isn't the most strange operator in Ruby; we also have the <=>, or "spaceship operator".)
You may be confused because there is a bit of a shortcut you can use in methods, if the last parameter is a hash, you can omit the squiggly brackets ({}). so calling render :partial => 'foo' is basically calling the render method, passing in a hash with a single key/value pair. Because of this, you often see a hash as the last parameter to sort of have a poor man's optional parameters (you see something similar done in JavaScript too).
In Ruby, any normal word is a local variable. So foo inside a method is a variable scoped to the method level. Prefixing a variable with # means scope the variable to the instance. So #foo in a method is an instance level.
## means a class variable, meaning that ## variables are in scope of the class, and all instances.
: means symbol. A symbol in Ruby is a special kind of string that implies that it will be used as a key. If you are coming from C#/Java, they are similar in use to the key part of an enum. There are some other differences too, but basically any time you are going to treat a string as any sort of key, you use a symbol instead.

Wow, a that's a lot of different concepts together.
1) = is plain old assignment.
a = 4;
puts a
2) => is used to declare hashes
hash = {'a' => 1, 'b' => 2, 'c' => 3}
puts hash['b'] # prints 2
3) #var lets you access object instance variable.
class MyObject
def set_x(x)
#x = x
end
def get_x
#x
end
end
o = MyObject.new
o.set_x 3
puts o.get_x # prints 3
4) ##var lets you access class ('static') variables.
class MyObject
def set_x(x)
##x = x # now you can access '##x' from other MyObject instance
end
def get_x
##x
end
end
o1 = MyObject.new
o1.set_x 3
o2 = MyObject.new
puts o2.get_x # prints 3, even though 'set_x' was invoked on different object
5) I usually think of :var as special 'label' class. Example 2 can be rephrased like this
hash = {:a => 1, :b => 2, :c => 3}
puts hash[:b] # prints 2

Related

Ruby documentation over Symbol objects is misleading

I'm a newbie to Ruby and like others I had trouble wrapping my head around Ruby symbols. I know this subject has been brought up many times but I believe this post might slightly differ from the others. If not, my apologies. Take for instance this piece of code from the documentation.
module One
class Fred
end
$f1 = :Fred
end
module Two
Fred = 1
$f2 = :Fred
end
def Fred()
end
$f3 = :Fred
$f1.object_id #=> 2514190
$f2.object_id #=> 2514190
$f3.object_id #=> 2514190
My gripe is that it makes us think there is a link between the class, the module or the function and the :Fred symbol. No wonder people ask things like "can I assign a value to a symbol" or is the symbol a reference to another thing.
This code adds to the confusion :
class TestController < ApplicationController
layout :which_layout
def index
...
end
private
def which_layout
if condition
"layout1"
else
"layout2"
end
end
end
At first, I thought there was a reference to the function but in fact it's just that the behavior of the layout method will vary base on whether we'll pass a String (the template name) or a Symbol (call the method specified by the symbol) as stated by the documentation. (Does it look for a method.to_sym that is equivalent to the symbol we passed as argument?)
What I believe I've read however is that when creating a class, his symbol counterpart will be automatically created, that is :Fred will already exist in subsequent calls. But that's just it?
My question is : why did they have to include a class, a variable and a function to illustrate this? The context? Then why having the same name? Why not just do :
$f1 = :Fred
$f2 = :Fred
$f3 = :Fred
$f1.object_id #=> 2514190
$f2.object_id #=> 2514190
$f3.object_id #=> 2514190
When you use a symbol Ruby looks at list of existing symbols so when you reuse a symbol you are not creating a separate object in memory.
irb(main):006:0> :foo.object_id == :foo.object_id
=> true
You can contrast this with a string:
irb(main):007:0> "foo".object_id == "foo".object_id
=> false
This combined with the fact that they are so cheap to compare is what makes symbols so effective as hash keys.
What that pretty confusing example demonstrates is that symbols are not private to a scope - the table of symbols is global. It would be a bit less confusing if an instance variable was used instead of a global variable. I think it also attempts to demonstrate that module and class names are constants.
irb(main):016:0> Fred = Module.new do; end # this is the same as using the module keyword
irb(main):017:0> Fred.object_id != :Fred.object_id
=> true
Which means that Fred is a reference to the module while :Fred is a value (a symbol).
Symbols like strings are a value and thus cannot be a used as a reference. This is very much like true, false and nil which are singleton objects.
irb(main):008:0> true.class
=> TrueClass
irb(main):09:0> true.object_id == true.object_id
=> true
# you can even use the singletons as hash keys
irb(main):010:0> { true => 1, false => 2, nil => 3 }[true]
=> 1
The Rails example is not really that complicated. :which_layout is just an argument passed to the layout method. The layout method has a conditional which uses Object#send to dynamically call the :which_layout method if it exists. A string argument is instead used straight away to construct a glob.
My understanding is it's meant to illustrate the fact that all of the other "Fred's" do nothing to alter the fact that the symbol :Fred is unchanged in every context. Maybe changing the object_id list at the bottom of the example to something like this would make it more clear:
p One::Fred.object_id #=> 70222371662500
p Two::Fred.object_id #=> 3
p Fred().object_id #=> 8
p $f1.object_id #=> 2514190
p $f2.object_id #=> 2514190
p $f3.object_id #=> 2514190

Having a hash, set object properties in Ruby

Having a hashmap, such as:
{:foo => 1, :bar => 2}
in Ruby, is there an easy way to assign those values as properties of the object, to automatically cause this to happen:
obj.foo = 1
obj.bar = 2
To be precise, some Ruby-idiomatic way of doing:
hashmap.each { |k,v| obj.send("#{k}=", v) }
obj is an object that doesn't inherit ActiveModel and it's not a Struct and I can't control it's type as it's coming from a third party library.
I'm using Rails, so if the answer comes from Rails, that's acceptable.
What you have there is (almost) the most concise, readable, idiomatic solution already:
hashmap.each { |k,v| obj.send("#{k}=", v) }
There is only one thing left to improve:
hashmap.each { |k,v| obj.public_send("#{k}=", v) }
Use public_send instead of send to make it clear to others that you are using it only to pass a method name dynamically and not to circumvent access restrictions.
Maybe you could create an OpenStruct from your hash, do whatever you need with the OpenStruct and its attributes, then convert it back into a hash?
require 'ostruct'
h = {:foo => 1, :bar => 2}
o = OpenStruct.new(h)
o.foo # Output: => 1
o.bar # Output: => 2
# if necessary, convert it back into a hash
h = o.to_h
From the Ruby docs:
An OpenStruct is a data structure, similar to a Hash, that allows the
definition of arbitrary attributes with their accompanying values.
This is accomplished by using Ruby’s metaprogramming to define methods
on the class itself.
An OpenStruct utilizes Ruby’s method lookup structure to and find and
define the necessary methods for properties. This is accomplished
through the method method_missing and define_method.
This should be a consideration if there is a concern about the
performance of the objects that are created, as there is much more
overhead in the setting of these properties compared to using a Hash
or a Struct.
Doing this on mobile so let's give this a try:
class Something
attr_reader :foo, :bar
def initialize(hash = {})
foo = hash[:foo]
bar = hash[:bar]
end
end
obj = Something.new({foo: 1, bar: 2})
obj.foo = 1
obj.bar =2
class Klass
def initialize(h)
h.each { |iv, val| instance_variable_set("##{iv}", val) }
end
end
k = Klass.new(:foo => 1, :bar => 2)
#=> #<Klass:0x007ff1d9073118 #foo=1, #bar=2>
You can use method_missing if you need to set data dynamically.
class Foo
def method_missing(sym, *args)
super unless instance_variables.include?("##{sym}".to_sym)
instance_variable_get("##{sym}")
end
end
obj = Foo.new
h = {:foo => 1, :bar => 2}
h.each { |k, v| obj.instance_variable_set("##{k}", v) }
obj.foo
# => 1
obj.bar
# => 2
If you are using Rails, a minor improvement on your suggested solution would be to use Object#try from Active support extensions
hashmap.each {|k,v| obj.try "#{k}=", v }
As per documentation,
Invokes the public method whose name goes as first argument just like
public_send does, except that if the receiver does not respond to it
the call returns nil rather than raising an exception.

Ruby's bitwise shift operator "<<" confusion in conjuction with array

I am studying Ruby and can't figure this out. I have an exercise where I have to add a method into a Library class which contains games. Each game is an instance of a class Game. So the solution is the following:
class Library
attr_accessor :games
def initialize(games)
self.games = games
end
def has_game?(search_game)
for game in games
return true if game == search_game
end
false
end
def add_game(game)
#games << game
end
end
I can't understand how << works in this case. Is this a bitwise left shift? Does Library class just assumes that games is an array, I believe I can pass anything to the Library class when I am initialising, single game or an array of games?
When you have:
#games << game
<< is actually a method. If it is a method, you ask, why isn't it written in the normal way:
#games.<<(game)
? You could, in fact, write it that way and it would work fine. Many Ruby methods have names that are symbols. A few others are +, -, **, &, || and %. Ruby knows you'd prefer writing 2+3 instead of 2.+(3), so she let's you do the former (and then quietly converts it to the latter). This accommodation is often referred to as "syntactic sugar".
<< is one of #games' methods (and game is <<'s argument), because #games is the receiver of the method << (technically :<<). Historically, it's called the "receiver" because with OOP you "send" the method to the receiver.
To say that << is a method of #games means that << is an instance method of #games's class. Thus, we have:
#games.methods.include?(:<<) #=> true
#games.class.instance_methods.include?(:<<) #=> true
I expect #games is an instance of the class Array. Array's instance methods are listed here. For example:
#games = [1,2,3]
#games.class #=> [1,2,3].class => Array
#games << 4 #=> [1,2,3,4]
#games.<<(5) #=> [1,2,3,4,5]
On the other hand, suppose #games were an instance of Fixnum. For example:
#games = 7
#games.class #=> 7.class => Fixnum
which in binary looks like this:
#games.to_s(2) #=> "111"
Then:
#games << 2 #=> 28
28.to_s(2) #=> "11100"
because << is an instance method of the class Fixnum.
As a third example, suppose #games were a hash:
#games = { :a => 1 }
#games.class #=> { :a => 1 }.class => Hash
Then:
#games << { :b => 2 }
#=> NoMethodError: undefined method `<<' for {:a=>1}:Hash
The problem is clear from the error message. The class Hash has no instance method <<.
One last thing: consider the method Object#send. Recall that, at the outset, I said that methods are sent to receivers. Instead of writing:
#games = [1,2,3]
#games << 4 #=> [1,2,3,4]
you could write:
#games.send(:<<, 4) #=> [1, 2, 3, 4]
This is how you should think of methods and receivers. Because send is an instance method of the class Object, and all objects inherit Object's instance methods, we see that send is "sending" a method (:<<, which can alternatively be expressed as a string, "<<") and its arguments (here just 4) to the receiver.
There are times, incidentally, when you must use send to invoke a method. For one, send works with private and protected methods. For another, you can use send when you want to invoke a method dynamically, where the name of the method is the value of a variable.
In sum, to understand what method does in:
receiver.method(*args)
look for the doc for the instance method method in receiver's class. If you Google "ruby array", for example, the first hit will likely be the docs for the class Array.
It's not a bitwise left shift, it's "shovel" operator. You, probably, know that in Ruby operators are implemented as methods, i.e. when you write
1 + 1
what is actually going on:
1.+(1)
The same is true for "shovel" (<<) operator, it's just a method on Array class, that appends its arguments to the end of the array and returns the array itself, so it can be chained:
>> arr = []
=> []
>> arr << 1
=> [1]
>> arr
=> [1]
>> arr << 2 << 3
=> [1, 2, 3]
It looks like #games is an array. The shift operator for an array adds an element to the end of the array, similar to array#push.
ary << obj → ary
Append—Pushes the given object on to the end of this array. This expression returns the array itself, so several appends may be chained together.

Equivalent of .try() for a hash to avoid "undefined method" errors on nil? [duplicate]

This question already has answers here:
How to avoid NoMethodError for nil elements when accessing nested hashes? [duplicate]
(4 answers)
Closed 7 years ago.
In Rails we can do the following in case a value doesn't exist to avoid an error:
#myvar = #comment.try(:body)
What is the equivalent when I'm digging deep into a hash and don't want to get an error?
#myvar = session[:comments][#comment.id]["temp_value"]
# [:comments] may or may not exist here
In the above case, session[:comments]try[#comment.id] doesn't work. What would?
You forgot to put a . before the try:
#myvar = session[:comments].try(:[], #comment.id)
since [] is the name of the method when you do [#comment.id].
The announcement of Ruby 2.3.0-preview1 includes an introduction of Safe navigation operator.
A safe navigation operator, which already exists in C#, Groovy, and
Swift, is introduced to ease nil handling as obj&.foo. Array#dig and
Hash#dig are also added.
This means as of 2.3 below code
account.try(:owner).try(:address)
can be rewritten to
account&.owner&.address
However, one should be careful that & is not a drop in replacement of #try. Take a look at this example:
> params = nil
nil
> params&.country
nil
> params = OpenStruct.new(country: "Australia")
#<OpenStruct country="Australia">
> params&.country
"Australia"
> params&.country&.name
NoMethodError: undefined method `name' for "Australia":String
from (pry):38:in `<main>'
> params.try(:country).try(:name)
nil
It is also including a similar sort of way: Array#dig and Hash#dig. So now this
city = params.fetch(:[], :country).try(:[], :state).try(:[], :city)
can be rewritten to
city = params.dig(:country, :state, :city)
Again, #dig is not replicating #try's behaviour. So be careful with returning values. If params[:country] returns, for example, an Integer, TypeError: Integer does not have #dig method will be raised.
The most beautiful solution is an old answer by Mladen Jablanović, as it lets you to dig in the hash deeper than you could with using direct .try() calls, if you want the code still look nice:
class Hash
def get_deep(*fields)
fields.inject(self) {|acc,e| acc[e] if acc}
end
end
You should be careful with various objects (especially params), because Strings and Arrays also respond to :[], but the returned value may not be what you want, and Array raises exception for Strings or Symbols used as indexes.
That is the reason why in the suggested form of this method (below) the (usually ugly) test for .is_a?(Hash) is used instead of (usually better) .respond_to?(:[]):
class Hash
def get_deep(*fields)
fields.inject(self) {|acc,e| acc[e] if acc.is_a?(Hash)}
end
end
a_hash = {:one => {:two => {:three => "asd"}, :arr => [1,2,3]}}
puts a_hash.get_deep(:one, :two ).inspect # => {:three=>"asd"}
puts a_hash.get_deep(:one, :two, :three ).inspect # => "asd"
puts a_hash.get_deep(:one, :two, :three, :four).inspect # => nil
puts a_hash.get_deep(:one, :arr ).inspect # => [1,2,3]
puts a_hash.get_deep(:one, :arr, :too_deep ).inspect # => nil
The last example would raise an exception: "Symbol as array index (TypeError)" if it was not guarded by this ugly "is_a?(Hash)".
The proper use of try with a hash is #sesion.try(:[], :comments).
#session.try(:[], :comments).try(:[], commend.id).try(:[], 'temp_value')
Update: As of Ruby 2.3 use #dig
Most objects that respond to [] expect an Integer argument, with Hash being an exception that will accept any object (such as strings or symbols).
The following is a slightly more robust version of Arsen7's answer that supports nested Array, Hash, as well as any other objects that expect an Integer passed to [].
It's not fool proof, as someone may have created an object that implements [] and does not accept an Integer argument. However, this solution works great in the common case e.g. pulling nested values from JSON (which has both Hash and Array):
class Hash
def get_deep(*fields)
fields.inject(self) { |acc, e| acc[e] if acc.is_a?(Hash) || (e.is_a?(Integer) && acc.respond_to?(:[])) }
end
end
It can be used the same as Arsen7's solution but also supports arrays e.g.
json = { 'users' => [ { 'name' => { 'first_name' => 'Frank'} }, { 'name' => { 'first_name' => 'Bob' } } ] }
json.get_deep 'users', 1, 'name', 'first_name' # Pulls out 'Bob'
say you want to find params[:user][:email] but it's not sure whether user is there in params or not. Then-
you can try:
params[:user].try(:[], :email)
It will return either nil(if user is not there or email is not there in user) or otherwise the value of email in user.
As of Ruby 2.3 this gets a little easier. Instead of having to nest try statements or define your own method you can now use Hash#dig (documentation).
h = { foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) #=> 1
h.dig(:foo, :zot) #=> nil
Or in the example above:
session.dig(:comments, #comment.id, "temp_value")
This has the added benefit of being more like try than some of the examples above. If any of the arguments lead to the hash returning nil then it will respond nil.
#myvar = session.fetch(:comments, {}).fetch(#comment.id, {})["temp_value"]
From Ruby 2.0, you can do:
#myvar = session[:comments].to_h[#comment.id].to_h["temp_value"]
From Ruby 2.3, you can do:
#myvar = session.dig(:comments, #comment.id, "temp_value")
Another approach:
#myvar = session[:comments][#comment.id]["temp_value"] rescue nil
This might also be consider a bit dangerous because it can hide too much, personally I like it.
If you want more control, you may consider something like:
def handle # just an example name, use what speaks to you
raise $! unless $!.kind_of? NoMethodError # Do whatever checks or
# reporting you want
end
# then you may use
#myvar = session[:comments][#comment.id]["temp_value"] rescue handle
When you do this:
myhash[:one][:two][:three]
You're just chaining a bunch of calls to a "[]" method, an the error occurs if myhash[:one] returns nil, because nil doesn't have a [] method. So, one simple and rather hacky way is to add a [] method to Niclass, which returns nil: i would set this up in a rails app as follows:
Add the method:
#in lib/ruby_extensions.rb
class NilClass
def [](*args)
nil
end
end
Require the file:
#in config/initializers/app_environment.rb
require 'ruby_extensions'
Now you can call nested hashes without fear: i'm demonstrating in the console here:
>> hash = {:foo => "bar"}
=> {:foo=>"bar"}
>> hash[:foo]
=> "bar"
>> hash[:doo]
=> nil
>> hash[:doo][:too]
=> nil
Andrew's answer didn't work for me when I tried this again recently. Maybe something has changed?
#myvar = session[:comments].try('[]', #comment.id)
The '[]' is in quotes instead of a symbol :[]
Try to use
#myvar = session[:comments][#comment.id]["temp_value"] if session[:comments]

can you pass self to lambda in rails?

I want to define a class method that has access to a local variable. So this would be different for each instance of the class. I know you can make a class method dynamic with lambda like when you use it with named_scope. But can this be done for values that are specific to an instance?
In detail it is the has_attached_file method for the paperclip plugin in rails. I want to pass a lambda for the styles hash so that the image styles can be based off of attributes of the object stored in the DB. Is this possible?
Disclaimer: First, the question (Can you pass self to lambda?) and the problem you're trying to solve (dynamic styles with paperclip) don't fully match up. I won't answer the original question because it's not entirely related to your problem, and rampion took a valiant stab at it.
I'll instead answer your paperclip question.
In detail it is the has_attached_file method for the paperclip plugin in rails. I want to pass a lambda for the styles hash so that the image styles can be based off of attributes of the object stored in the DB. Is this possible?
Yes, it is possible. In paperclip, the :styles option can take a Proc. When the attachment is initialized, if a Proc was used, the attachment itself is passed to the Proc. The attachment has a reference to the associated ActiveRecord object, so you can use that to determine your dynamic styles.
For example, your has_attached_file declaration might look something like this (assuming a User and avatar scenario where the user can customize the size of their avatar):
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => lambda { |attachment|
user = attachment.instance
dimensions = "#{user.avatar_width}x#{user.avatar_height}#"
{ :custom => dimensions }
}
end
Ok, you're being unclear.
Local variables in ruby begin with a lowercase letter (like foo, bar, or steve), and are lexically scoped (like C variables). They have nothing to do with "an instance of a class"
Instance variables in ruby begin with an # sigil (like #foo, #bar, or #carl), and are in scope whenever the current value of self is the object they are stored in.
If you want a method that can access the instance variables of an object directly, that's called an instance method. For example, battle_cry and initialize are both instance methods:
class Character
def initialize(name)
#name=name
end
def battle_cry
#name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
A class method, by contrast, is a method for a Class object, and doesn't have access to any of the instance variables of that object. In the above example,
default is a class method.
If you want a (class or instance) method that triggers a change in or gets a value from the current scope, ruby uses a type of callback called a block.
class Character
ATTACKS = [ "Ho!", "Haha!", "Guard!", "Turn!", "Parry!", "Dodge!", "Spin!", "Ha", "THRUST!" ]
def attack
ATTACKS.inject(0) { |dmg, word| dmg + yield(word) }
end
end
person = Character.default
puts person.battle_cry
num_attacks = 0;
damage = person.attack do |saying|
puts saying
num_attacks += 1
rand(3)
end
puts "#{damage} points of damage done in #{num_attacks} attacks"
In the above example, attack uses the yield keyword to call the block passed
to it. When we call attack, then, the local variable num_attacks is still
in scope in the block we pass it (delimited here by do ... end), so we can
increment it. attack is able to pass values into the block, here
they are captured into the saying variable. The block also passes values
back to the method, which show up as the return value of yield.
The word lambda in ruby usually means the lambda keyword, which is used
to make blocks into freestanding, function like objects (which themselves are usually
referred to as lambdas, procs, or Procs).
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
So I think what you're asking is whether you can pass a Proc in place of a Hash
for an argument to a method. And the answer is "it depends". If the method only
ever uses the #[] method, then yes:
class Character
attr_accessor :stats
def set_stats(stats)
#stats = stats
end
end
frank = Character.new("Victor Frankenstein")
frank.set_stats({ :str => 7, :dex => 14, :con => 9, :int => 19, :wis => 7, :cha => 11 })
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
However, it might use some other Hash specific methods, or call the same key multiple times,
which can produce weird results:
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1
In which case, you may be better off caching the requests in an intermediate hash. This is fairly easy,
since a Hash can have an initializer block. So if we change the above to:
monster.set_stats(Hash.new do |stats_hash, stat_name|
stats_hash[stat_name] = rand(20)
end)
monster.stats[:dex] #=> 3
monster.stats[:dex] #=> 3
The results are cached in the hash
To see more about Hash block initializers, see ri Hash::new:
-------------------------------------------------------------- Hash::new
Hash.new => hash
Hash.new(obj) => aHash
Hash.new {|hash, key| block } => aHash
------------------------------------------------------------------------
Returns a new, empty hash. If this hash is subsequently accessed
by a key that doesn't correspond to a hash entry, the value
returned depends on the style of new used to create the hash. In
the first form, the access returns nil. If obj is specified, this
single object will be used for all default values. If a block is
specified, it will be called with the hash object and the key, and
should return the default value. It is the block's responsibility
to store the value in the hash if required.
h = Hash.new("Go Fish")
h["a"] = 100
h["b"] = 200
h["a"] #=> 100
h["c"] #=> "Go Fish"
# The following alters the single default object
h["c"].upcase! #=> "GO FISH"
h["d"] #=> "GO FISH"
h.keys #=> ["a", "b"]
# While this creates a new default object each time
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
h["c"] #=> "Go Fish: c"
h["c"].upcase! #=> "GO FISH: C"
h["d"] #=> "Go Fish: d"
h.keys #=> ["c", "d"]

Resources