ruby instance_variable_get returns nil - ruby-on-rails

I've a problem with the instance_variable_get method cause it's always returns nil object with one of my object instance.
Here is my code:
logger.info "ASDF: " + #d_tree.inspect
logger.info "ASDF: " + #d_tree.instance_variable_get(:#content);
and the output is:
ASDF: #<DTree id: 11, parent_id: nil, content: "bababababa", subsidiary_info: "", deep_info: "blabla", title: "hello", direction: 1, created_at: "2010-10-26 19:27:32", updated_at: "2010-11-01 23:14:31", color: 2, cell_color: 2, howtoinfo: "howtoinfooo", textinfo: "textInfooo", locationinfo: "locationInfoooo", productinfo: "productinfoooo">
TypeError (can't convert nil into String):
/app/controllers/d_trees_controller.rb:38:in `+'
According to the inspect the object seems to be fine, but the instance_variable_get returns a nil object
Thanks for your help!

instance_variable_get(arg)
It should return the value of the instance variable or nil if the instance variable is not set.
for example
we define the following class
class Velpradeep
def initialize(mark1, mark2)
#m, #s = mark1, mark2
end
end
During creation of the object of the class
obj = Velpradeep.new(98,96)
Then we can access the instance variables by using :
irb(main):046:0> obj.instance_variable_get(:#m)
=> 98
Access the undefined instance variables defined in the initialize method
irb(main):047:0> obj.instance_variable_get(:#p)
=> nil # provides nil bcz the instance variable is not defined
If you want to access the variable before you need to set the instance variable using
instance_variable_set()
example :
irb(main):048:0> obj.instance_variable_set(:#p, 99)
=> 99
Then we can use, it will return the value of the instance variable....
irb(main):049:0> obj.instance_variable_get(:#p)
=> 99

Although it's considered bad form to grab instance variables like this directly, as using attr_accessor is the preferred method, the problem in this particular instance is that there is no instance variable called #content. What you have appears to be an ActiveRecord attribute.
These are fetched using a different mechanism:
#d_tree.attributes[:content]
Generally this is even a little redundant as this will needlessly create a copy of the attributes hash. The typical way to access is:
#d_tree.content
These values are actually located in the #attributes instance variable managed by ActiveRecord.

instance_variable_get returns nil if the variable is uninitialized (or if it has been set to nil, of course).
So, obviously, #content hasn't been initialized yet.
Why it hasn't been initialized is impossible to tell, since you haven't shown us any actual code yet.
It looks like #d_tree might be an ActiveRecord object, in which case the solution is rather simple: ActiveRecord objects don't keep their state in instance variables, they are backed by a database.

Related

Why does Ruby keep the relationship to a hash when initializing with variable?

maybe I am used to the initialState from React, but I tried doing something similar in Ruby and I noticed it updates the hash everywhere I initialized it.
Here's an example:
iniatializing_hash = {likes_count: 0, comments_count: 0}
hash_that_uses_initializer = {first: iniatializing_hash, second: iniatializing_hash}
# update first
hash_that_uses_initializer[:first][:likes_count] += 1
hash_that_uses_initializer[:first][:comments_count] += 1
# expect both first and second were updated
puts "first: #{hash_that_uses_initializer[:first].inspect} and second: #{hash_that_uses_initializer[:second].inspect}"
# returns first: {:likes_count=>1, :comments_count=>1} and second: {:likes_count=>1, :comments_count=>1}
I'm guessing this happens because it keeps the connection to the place in memory the hash is loaded.
Is there another elegant way to use an 'initialState' hash in Ruby?
Thanks!
Hashes are passed by reference. When you assign iniatializing_hash, it does not assign a copy of the hash - it assigns a pointer. When you change the values of the hash that you assigned to :first, you're also changing the values of the hash at :second because they're both the same hash.
You can prove they're the same location in memory by calling object_id method on the object:
echo('same hash') if iniatializing_hash.object_id == hash_that_uses_initializer[:first].object_id == hash_that_uses_initializer[:second].object_id
If you want a copy, then you need a deep copy.
def deep_copy(initial_hash)
Marshal.load(Marshal.dump(initial_hash))
end
hash_that_uses_initializer = {first: deep_copy(iniatializing_hash), second: deep_copy(iniatializing_hash)}
Now the hash stored at :first and the :second will be completely different from each other and different from iniatializing_hash

For a hash of objects in ruby, hash.keys.index(obj) works but hash.has_key?(obj) does not. Why?

Here's my Rails class
class SkinnyEmployee
include ActiveModel::Validations
attr_accessor :uid, :name
validates :uid, :presence => true
def initialize(id, name)
#uid = id
#name = name
end
def ==(other)
puts "Calling =="
raise ArgumentError.new("other is nil or bad in "+self.to_s) if other.nil? or !other.instance_of?(SkinnyEmployee)
return (self.class == other.class && self.uid == other.uid)
end
alias :eql? :==
end
I have a hash of SkinnyEmployee objects. E.g.,
skinny_hash = {SkinnyEmployee.new("123", "xyz") => 1, SkinnyEmployee.new("456", "abc") => 2}
I have another SkinnyEmployee object that I want to look up. E.g.,
entry = SkinnyEmployee.new("456", "abc")
When I do
skinny_hash.keys.index(entry)
I get 1, as expected. But when I do
skinny_hash.has_key?(entry)
I get false.
Why is that? Doesn't has_key? also use == or eql? to find whether a key exists in a hash?
Thanks much for the help!
First, this drove me nuts. What you're doing looked absolutely correct to me, and, as you already know, doesn't work.
I can take you part of the way to a solution:
http://ruby-doc.org/core-2.0.0/Object.html#method-i-hash
quoting:
Generates a Fixnum hash value for this object. This function must have the property that a.eql?(b) implies a.hash == b.hash.
The hash value is used along with eql? by the Hash class to determine if two objects reference the same hash key. Any hash value that exceeds the capacity of a Fixnum will be truncated before being used.
I added:
def hash
1
end
to your SkinnyEmployee Class, and has_key? started returning true. Obviously that's not a solution, but I'm thinking it at least puts you on the path to one.
You have overwritten the eql? method used by Array#index but not the hash method used by Hash#has_key?.
From Ruby docs for Object#hash
Generates a Fixnum hash value for this object. This function must have the property that a.eql?(b) implies a.hash == b.hash.
The Object#hash and Object#eql? methods return equal if and only if the objects occupy the same space in memory. Some classes like Array overwrite both methods to return true if the compared array's have same elements.
For your case you can define the hash method like:
def hash
"#{self.class}_#{self.uid}".hash
end
This would satisfy the docs criteria for hash method given above.
That is happening because the object you are using as a key and they one you are using to search the key are different.
Every time you call SkinnyEmployee.new it will create a new, different, object. For example
employee_1 = SkinnyEmployee.new("123", "xyz")
employee_2 = SkinnyEmployee.new("123", "xyz")
employee_1 == employee_1 #=> true
employee_2 == employee_2 #=> true
employee_2 == employee_1 #=> false
If you call object_id on both employee_1 and employee_2 you will notice that it gives you different id's.
Using has_key? will check for the exact same object, and that won't be the case if you use SkinnyEmployee.new("456", "abc").
You would need a way to retrieve the exact same object, store it in a variable or in the DB, you are using as a key and use it as an attribute for has_key? for it to work.
Hope this can help you.

Ruby and Rails Simple Question on Expression

Hey there, is there a quick Ruby (or Rails) expression that returns nil if an object does not have a value ?
For instance, if self.name is null in database, it should return nil. If it's a string, it should return it. I'm thinking of unless, but maybe there is something better.
Also, if you have a function like get_profession(type), and type can be "first", "second" or "third", what would be an elegant way of inquiring self.first or self.second or self.third(according to type parameter) and return nil unless self.first(or other type) has a string value ?
As you see, the second question closely relates to the first.
In Ruby, if a variable does not have a value, it is nil by default. To answer your second question, you can use the send method to call your "type" methods. Something like,
def get_profession(type)
# Making sure correct type is passed.
raise RuntimeError unless ['first', 'second', 'third'].include?(type.to_s)
send(type)
end
Send expects a method in a string as parameter, which it calls on the same object. In your case if the response of your "type" method has no value, it would be by default be nil.
it actually returns nil if there was no value. have you actually tried it yet? if an object doesn't have any value it SHOULD return nil
need to add though that your attribute should have a corresponding column name in the database..otherwise it will be an undefined method call.
e.g.
Company.name < -- should return nil if there's a name column in your companies table but has no value
if you don't have a name column it'll say undefined method name.
It is common and cleaner to just check for a field if you're planning to display it like so:
<%= #person.profession if #person.profession %>
or the other way around
<%= #person.profession unless #person.profession.nil? %>

Unwanted symbol to string conversion of hash key

When I assign in my controller
#my_hash = { :my_key => :my_value }
and test that controller by doing
get 'index'
assigns(:my_hash).should == { :my_key => :my_value }
then I get the following error message:
expected: {:my_key=>:my_value},
got: {"my_key"=>:my_value} (using ==)
Why does this automatic symbol to string conversion happen? Why does it affect the key of the hash?
It may end up as a HashWithIndifferentAccess if Rails somehow gets ahold of it, and that uses string keys internally. You might want to verify the class is the same:
assert_equal Hash, assigns(:my_hash).class
Parameters are always processed as the indifferent access kind of hash so you can retrieve using either string or symbol. If you're assigning this to your params hash on the get or post call, or you might be getting converted.
Another thing you can do is freeze it and see if anyone attempts to modify it because that should throw an exception:
#my_hash = { :my_key => :my_value }.freeze
You might try calling "stringify_keys":
assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys
AHA! This is happening not because of Rails, per se, but because of Rspec.
I had the same problem testing the value of a Hashie::Mash in a controller spec (but it applies to anything that quacks like a Hash)
Specifically, in a controller spec, when you call assigns to access the instance variables set in the controller action, it's not returning exactly the instance variable you set, but rather, a copy of the variable that Rspec stores as a member of a HashWithIndifferentAccess (containing all the assigned instance variables). Unfortunately, when you stick a Hash (or anything that inherits from Hash) into a HashWithIndifferentAccess, it is automatically converted to an instance of that same, oh-so-convenient but not-quite-accurate class :)
The easiest work-around is to avoid the conversion by accessing the variable directly, before it's converted "for your convenience", using: controller.view_assigns['variable_name'] (note: the key here must be a string, not a symbol)
So the test in the original post should pass if it were changed to:
get 'index'
controller.view_assigns['my_hash'].should == { :my_key => :my_value }
(of course, .should is no longer supported in new versions of RSpec, but just for comparison I kept it the same)
See this article for further explanation:
http://ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie.html
I know this is old, but if you are upgrading from Rails-3 to 4, your controller tests may still have places where Hash with symbol keys was used but compared with the stringified version, just to prevent the wrong expectation.
Rails-4 has fixed this issue: https://github.com/rails/rails/pull/5082 .
I suggest updating your tests to have expectations against the actual keys.
In Rails-3 the assigns method converts your #my_hash to HashWithIndifferentAccess that stringifies all the keys -
def assigns(key = nil)
assigns = #controller.view_assigns.with_indifferent_access
key.nil? ? assigns : assigns[key]
end
https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/action_dispatch/testing/test_process.rb#L7-L10
Rails-4 updated it to return the original keys -
def assigns(key = nil)
assigns = {}.with_indifferent_access
#controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
key.nil? ? assigns : assigns[key]
end
https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/testing/test_process.rb#L7-L11
You can also pass your Hash object to the initializer of HashWithIndifferentAccess.
You can use HashWithIndifferentAccess.new as Hash init:
Thor::CoreExt::HashWithIndifferentAccess.new( to: 'mail#somehost.com', from: 'from#host.com')

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