Why does subclassing ActiveSupport::TimeZone break its class-level [] operator? - ruby-on-rails

With some trepidation, I decided to subclass ActiveSupport::TimeZone, but I can't get very far because when I call [] on my subclass it fails to invoke the superclass's [] method. I thought it might have had something to with the fact that the operator overload happens in a class method, so I tried this:
Loading development environment (Rails 4.2.1)
2.2.1 :001 > class Foo; def Foo.[](f); f; end; end
=> :[]
2.2.1 :002 > Foo['baz']
=> "baz"
2.2.1 :003 > class Bar < Foo; end
=> nil
2.2.1 :004 > Bar['baz']
=> "baz"
So that worked exactly as I thought it would. But if that works, why does this fail?
2.2.1 :005 > ActiveSupport::TimeZone["America/New_York"]
=> #<ActiveSupport::TimeZone:0x007f80835c18a8 #name="America/New_York", #utc_offset=nil, #tzinfo=#<TZInfo::TimezoneProxy: America/New_York>, #current_period=#<TZInfo::TimezonePeriod: #<TZInfo::TimezoneTransitionDefinition: #<TZInfo::TimeOrDateTime: 1425798000>,#<TZInfo::TimezoneOffset: -18000,3600,EDT>>,#<TZInfo::TimezoneTransitionDefinition: #<TZInfo::TimeOrDateTime: 1446357600>,#<TZInfo::TimezoneOffset: -18000,0,EST>>>>
2.2.1 :006 > class T < ActiveSupport::TimeZone; end
=> nil
2.2.1 :007 > T["America/New_York"]
NoMethodError: undefined method `[]' for nil:NilClass

The method relies on calling [] on an #lazy_zones_map instance variable, which your subclass does not have.
Edit: an example of the behavior:
class Foo
#foo = []
def self.[](arg)
#foo[arg]
end
end
class Bar < Foo
end
Foo[0] #=> nil
Bar[0] #=> undefined method `[]` for nil:NilClass

Related

rspec test fails code in rails controller

In my application_controller.rb, i have a line of code as follows:
def index
CaseStatus.order(:application_source).pluck(:application_source).uniq!
end
In my rspec code, i have a line of code that visits the index path of application_controller as follows
visit applications_path
When i run the code directly, it works perfectly but when it visits application_controller.rb via rspec, i get an error which says
NoMethodError:
undefined method `compact' for nil:NilClass
Not sure while i get this error via rspec and capybara but if i run the code as
def index
CaseStatus.order(:application_source).pluck(:application_source)
end
It executes perfectly with no errors. Kinda confused what the uniq! breaks in the code that suddenly the result becomes nil.
i get this error
Failure/Error: #application_channels = CaseStatus.order(:application_source).pluck(:application_source).uniq!.compact if CaseStatus.order(:application_source).present?
NoMethodError:
undefined method `compact' for nil:NilClass
# ./app/controllers/loan_applications_controller.rb:53:in `index'
I do not think uniq! is the method you would like to use in this case, see:
Returns nil if no changes are made (that is, no duplicates are found).
https://ruby-doc.org/core-2.2.0/Array.html#method-i-uniq-21
So it works like this:
2.3.1 :008 > a = [1,2,3,3,nil].uniq!
=> [1, 2, 3, nil]
2.3.1 :009 > a = [1,2,3,nil].uniq!
=> nil
2.3.1 :010 >
on the other hand uniq works like:
2.3.1 :010 > a = [1,2,3,3,nil].uniq
=> [1, 2, 3, nil]
2.3.1 :011 > a = [1,2,3,nil].uniq
=> [1, 2, 3, nil]
and on the output of uniq it is safe to run compact to remove nil values.

Difference between Ruby’s Hash and ActiveSupport’s HashWithIndifferentAccess

What is the difference between Ruby’s Hash and ActiveSupport’s HashWithIndifferentAccess? Which is the best for dynamic hashes?
Below is the simple example that will show you difference between simple ruby hash & a "ActiveSupport::HashWithIndifferentAccess"
HashWithIndifferentAccess allows us to access hash key as a symbol or string
Simple Ruby Hash
$ irb
2.2.1 :001 > hash = {a: 1, b:2}
=> {:a=>1, :b=>2}
2.2.1 :002 > hash[:a]
=> 1
2.2.1 :003 > hash["a"]
=> nil
ActiveSupport::HashWithIndifferentAccess
2.2.1 :006 > hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1, b:2)
NameError: uninitialized constant ActiveSupport
from (irb):6
from /home/synerzip/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
2.2.1 :007 > require 'active_support/core_ext/hash/indifferent_access'
=> true
2.2.1 :008 > hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1, b:2)
=> {"a"=>1, "b"=>2}
2.2.1 :009 > hash[:a]
=> 1
2.2.1 :010 > hash["a"]
=> 1
class HashWithIndifferentAccess is inherited from ruby "Hash" & above special behavior is added in it.
In Ruby Hash:
hash[:key]
hash["key"]
are different. In HashWithIndifferentAccess as the name suggests, you can access key either way.
Quoting official documentation to this:
Implements a hash where keys :foo and "foo" are considered to be the
same.
and
Internally symbols are mapped to strings when used as keys in the
entire writing interface (calling []=, merge, etc). This mapping
belongs to the public interface. For example, given:
hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
You are
guaranteed that the key is returned as a string:
hash.keys # => ["a"]

ActiveRecord::Relation getting methods added to it - Rails 3.2

I'm seeing very strange behavior in Rails 3.2.20.
I created an example app with 2 simple models.
class Bro < ActiveRecord::Base
attr_accessible :cool_story
scope :hey_bro, -> do
where(nil)
end
end
class Dawg < ActiveRecord::Base
attr_accessible :where_you_at
end
From irb:
🐈 jonesdeini#scooty_puff_jr ~/example$ rails c
Loading development environment (Rails 3.2.20)
1.9.3-p125 :001 > Dawg.where(nil).respond_to? :hey_bro
=> false
1.9.3-p125 :002 > Bro.hey_bro
Bro Load (0.6ms) SELECT "bros".* FROM "bros"
=> []
1.9.3-p125 :003 > Dawg.where(nil).respond_to? :hey_bro
=> false
1.9.3-p125 :004 > Bro.where(nil).hey_bro
Bro Load (0.8ms) SELECT "bros".* FROM "bros"
=> []
1.9.3-p125 :005 > Dawg.where(nil).respond_to? :hey_bro
=> true
Can someone please explain what is going on here?
The complete code is here: https://github.com/jonesdeini/rails3.2-weirdness
Update:
The strangeness can be traced to here:
https://github.com/rails/rails/blob/v3.2.20/activerecord/lib/active_record/relation/delegation.rb#L26
super returns true after a scope has been added to a where. (probably better explained by the output in this gist https://gist.github.com/jonesdeini/dbe3124b2c31bd31ed31)

protection level for model attributes in rails

Suppose the following situation
class User < ActiveRecord::Base
private
def password= p
self[:password] = p
end
def password
self[:password]
end
end
If anyone with access to the Rails console can do:
Loading development environment (Rails 4.0.0)
2.0.0p247 :001 > User
=> User(id: integer, name:string, password:string)
2.0.0p247 :002 > u = User.find(1)
=> #<User id: 1, name: "Jack", password: "da6c253ffe0975ca1ddd92865ff3d5f0">
2.0.0p247 :003 > u.password = "123"
NoMethodError: private method 'password' called for #<User:0xa9145b0>
2.0.0p247 :004 > u[:password] = "123"
=> "123"
2.0.0p247 :005 > u
=> #<User id: 1, name: "Jack", password: "123">
2.0.0p247 :005 > u.save
=> true
Why does this happen? How can I encapsulate critical fields?
I am guessing that password is attr_accessible in the model. When a field is attr_accessible, Rails automatically lets you read and write to the field. You have a private password method that overwrites the Rails password and password= methods, but you did not overwrite the [] and []= methods as well. You can either overwrite the [] and []= methods or make it so password is not attr_accessible.
Here is a code example of how to overwrite the [] method:
class User < ActiveRecord::Base
def [](word)
puts "I am the master of: #{word}"
end
def []=(key, value)
puts "Fluffy monsters"
end
end
With this revised code, here is what the [] method will return:
>> u[:password] = "123"
=> nil
# prints "Fluffy monsters" in the console
>> u[:password]
=> nil
# prints "I am the master of: password" in the console

opening Array in ruby on rails console v. irb

I wish to make my code a little more readable by calling #rando on any array and retrieve a random element (rando because a rand() method already exists and I don't want there to be any confusion).
So I opened up the class and wrote a method:
class Array
def rando
self[ rand(length) ]
end
end
This seems far too straightforward.
When I open up irb, and type arr = %w(hi bye) and then arr.rando I get either hi or bye back. That's expected. However, in my rails console, when I do the same thing, I get ArgumentError: wrong number of arguments (1 for 0)
I've been tracing Array up the rails chain and can't figure it out. Any idea?
FWIW, I'm using rails 2.3.11 and ruby 1.8.7
Works fine in my case :
Loading development environment (Rails 3.0.3)
ruby-1.9.2-p180 :001 > class Array
ruby-1.9.2-p180 :002?> def rando
ruby-1.9.2-p180 :003?> self[ rand(length) ]
ruby-1.9.2-p180 :004?> end
ruby-1.9.2-p180 :005?> end
=> nil
ruby-1.9.2-p180 :006 > arr = %w(hi bye)
=> ["hi", "bye"]
ruby-1.9.2-p180 :007 > arr.rando
=> "bye"

Resources