Ruby on Rails - Array of Hashes - Display value based on key - ruby-on-rails

I have an array of hashes saved to a Rails 5 Postgres DB (Ruby 2.3.1). I’m able to display this on my show.html.erb page like so:
<%= #item.yearly_interest_totals %>
This displays:
[
"{:financial_year=>\"2017\", :total=>\"120.08\"}",
"{:financial_year=>\"2018\", :total=>\"237.32\"}",
"{:financial_year=>\"2019\", :total=>\"163.75\"}",
"{:financial_year=>\"2020\", :total=>\"87.95\"}",
"{:financial_year=>\"2021\", :total=>\"15.38\"}"
]
Also on this page I have a variable <%= fin_year %> which displays 2017.
I’m trying to display the value corresponding to this fin_year key in the view, with the following code, but it is giving me a no implicit conversion of Symbol into Integer error…
<%= #item.yearly_interest_totals.detect do |t|
t[:financial_year] == fin_year end [:total] %>
Could somebody please explain why i'm receiving this error?
Update
Both the hash key and local variable being named the same is confusing, I have changed the local variable to fin_year.
<%= fin_year.class %> is producing String
<%= #item.yearly_interest_totals.class %> is producing Array
<%= #item.yearly_interest_totals[0][:financial_year].class %> is returning a "no implicit conversion of Symbol into Integer" error...

The problem appears to be that the values for the keys :financial_year in your array of hashes are strings (e.g. "2017"), but your value for the variable financial_year is a fixnum/integer (e.g 2017). Try making them consistent to compare, such as:
<%= #item.yearly_interest_totals.detect do |t|
t[:financial_year] == financial_year.to_s end [:total] %>
Here is output from the Rails console comparing the two:
Running via Spring preloader in process 15647
Loading development environment (Rails 4.2.7.1)
2.3.3 :001 > item_yearly_interest_totals = [{ financial_year: "2017", total: "120.08" }, { financial_year: "2018", total: "237.32" }, { financial_year: "2019", total: "163.75" }, { financial_year: "2020", total: "87.95" }, { financial_year: "2021", total: "15.38" }]
=> [{:financial_year=>"2017", :total=>"120.08"}, {:financial_year=>"2018", :total=>"237.32"}, {:financial_year=>"2019", :total=>"163.75"}, {:financial_year=>"2020", :total=>"87.95"}, {:financial_year=>"2021", :total=>"15.38"}]
2.3.3 :002 > financial_year = 2017
=> 2017
2.3.3 :003 > item_yearly_interest_totals.detect do |t|
2.3.3 :004 > t[:financial_year] == financial_year end [:total]
NoMethodError: undefined method `[]' for nil:NilClass
.
.
.
2.3.3 :005 > item_yearly_interest_totals.detect do |t|
2.3.3 :006 > t[:financial_year] == financial_year.to_s end [:total]
=> "120.08"
2.3.3 :007 >
UPDATE (02-20-2017)
I don't completely understand where the distinction within Rails lies or is occurring that is the source of your issue, but even though you execute #item.yearly_interest_totals[0].class and you get Hash, you can't seem to access the values using a hash key (e.g. [:financial_year], ["financial_year"], etc.).
After some digging, I found this:
Rails access hash value
and the accepted answer led me to try JSON.parse, which I was able to get working, albeit with .each rather than .detect. This time I did, in a Rails 5 app, create an Item model, used Postgres, and seeded a single Item. What I still did not do is create a controller or any views. I executed my code through the Rails console. So, if you duplicate my code and it does not work for you, the problem may lie there, within the controller and views.
Ultimately, there is still some discovery to be done regarding this hash/JSON distinction and how implementation leads it to manifest as one or the other.
app/models/item.rb
class Item < ApplicationRecord
validates :name, presence: true
end
db/migrate/20170220221004_enable_hstore_extension.rb
class EnableHstoreExtension < ActiveRecord::Migration
def change
enable_extension 'hstore'
end
end
db/migrate/20170220221129_create_item.rb
class CreateItem < ActiveRecord::Migration[5.0]
def change
create_table :items do |t|
t.string :name, null: false, index: { unique: true }
t.hstore :yearly_interest_totals, array: true
t.timestamps null: false
end
end
end
db/seeds.rb
Item.create(name: 'Sample Item', yearly_interest_totals: [{ financial_year: "2017", total: "120.08" }, { financial_year: "2018", total: "237.32" }, { financial_year: "2019", total: "163.75" }, { financial_year: "2020", total: "87.95" }, { financial_year: "2021", total: "15.38" }])
And here is the code as it is executed in the Rails console:
Running via Spring preloader in process 19764
Loading development environment (Rails 5.0.1)
2.4.0 :001 > #item = Item.first
Item Load (1.4ms) SELECT "items".* FROM "items" ORDER BY "items"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Item id: 1, name: "Sample Item", yearly_interest_totals: [{"total"=>"120.08", "financial_year"=>"2017"}, {"total"=>"237.32", "financial_year"=>"2018"}, {"total"=>"163.75", "financial_year"=>"2019"}, {"total"=>"87.95", "financial_year"=>"2020"}, {"total"=>"15.38", "financial_year"=>"2021"}], created_at: "2017-02-20 22:25:14", updated_at: "2017-02-20 22:25:14">
2.4.0 :002 > #item.class
=> Item(id: integer, name: string, yearly_interest_totals: hstore, created_at: datetime, updated_at: datetime)
2.4.0 :003 > #item.yearly_interest_totals.class
=> Array
2.4.0 :004 > #item.yearly_interest_totals[0].class
=> Hash
2.4.0 :005 > financial_year = 2017
=> 2017
2.4.0 :006 > financial_year.class
=> Integer
2.4.0 :007 > selected_year_interest_total = nil
=> nil
2.4.0 :008 > selected_year_interest_total.class
=> NilClass
2.4.0 :009 > #item.yearly_interest_totals.each do |t|
2.4.0 :010 > puts JSON.parse(t["financial_year"]).class
2.4.0 :011 > if JSON.parse(t["financial_year"]) == financial_year
2.4.0 :012?> selected_year_interest_total = JSON.parse(t["total"])
2.4.0 :013?> end
2.4.0 :014?> end
Integer
Integer
Integer
Integer
Integer
=> [{"total"=>"120.08", "financial_year"=>"2017"}, {"total"=>"237.32", "financial_year"=>"2018"}, {"total"=>"163.75", "financial_year"=>"2019"}, {"total"=>"87.95", "financial_year"=>"2020"}, {"total"=>"15.38", "financial_year"=>"2021"}]
2.4.0 :015 > selected_year_interest_total
=> 120.08
2.4.0 :016 > selected_year_interest_total.class
=> Float

I dunno about Rails 5 but maybe this will help, Rails 4, assuming that financial_year is a variable and I am understanding the question correctly:
<% #item.yearly_interest_totals.each do |t| %>
<%= t['total'] == financial_year %>
<% end %>

Related

Searching UNIX timestamp gives differing results

The following queries give different results, the result for both should be two. I'm using timestamp columns in my db (postgres), and am searching for objects where their end_at column is less than or equal to a given UNIX timestamp.
puts object.time_records.where('time_records.end_at <= ?', object.time_records.second.end_at).count #=> 2 (Correct)
puts object.time_records.where('time_records.end_at <= ?', DateTime.strptime(object.time_records.second.end_at.to_i.to_s, '%s')).count # => 1 (Incorrect)
puts object.time_records.where('time_records.end_at <= ?', Time.at(object.time_records.second.end_at.to_i)).count # => 1 (Incorrect)
If I seed some data, the timestamp used in the query might be, for example:
1473024092
Then if I print the timestamps for the object:
puts object.time_records.pluck(:end_at).map(&:to_i)
I get the following results:
1472419292
1473024092
1473628892
1474233692
As can be seen from these, the correct result should be two. If anyone has encountered something similar I'd appreciate a pointer in the right direction.
For what it's worth, this is occurring in specs I'm writing for a gem. I've tried varying combinations of in_time_zone and .utc for parsing and converting to the timestamp, and they all offer the same result. Even converting to a timestamp and straight back to a Time, and testing for equality results in false, when to_s is equal for both.
I ran an example in irb:
2.3.0 :001 > now = Time.now
=> 2016-08-28 21:58:43 +0100
2.3.0 :002 > timestamp = now.to_i
=> 1472417923
2.3.0 :003 > parsed_timestamp = Time.at(timestamp)
=> 2016-08-28 21:58:43 +0100
2.3.0 :004 > now.eql?(parsed_timestamp)
=> false
2.3.0 :005 > now == parsed_timestamp
=> false
2.3.0 :006 > now === parsed_timestamp
=> false
2.3.0 :007 > now.class
=> Time
2.3.0 :008 > parsed_timestamp.class
=> Time
The issue was fractional times. UNIX timestamps are to the second, so when converting to_i, the milliseconds are discarded.
Setting the precision of the timestamp columns resolved this issue:
class CreateTimeRecords < ActiveRecord::Migration
def change
create_table :time_records do |t|
t.belongs_to :object, index: true, null: false
t.datetime :start_at, null: false, index: true, precision: 0
t.datetime :end_at, null: false, index: true, precision: 0
t.timestamps null: false
end
end
end

ActiveRecord class (enum) behaving incorrectly with patch in config/initializers

I'm using ruby 2.2.3 and rails 4.2.4.
Background: I need the ability to re-use values across enums within the same model so I have patched in the latest version of Active Record's enum.rb as config/initializers/enum_patch.rb since they are adding prefix/suffix support in Rails 5 which will enable this functionality.
Issue: After patching in the code, Enum is no longer working correctly. See below:
class GatheringSession < ActiveRecord::Base
GatheringStates = %i(ready running finished errored)
enum :gathering_state => GatheringStates
...
end
Console (no patch). Correct behaviour:
2.2.3 :001 > gs2 = GatheringSession.last
=> #<GatheringSession id: 120, gathering_state: 2 ... >
2.2.3 :002 > gs2.gathering_state
=> "finished"
2.2.3 :003 > gs2.ready?
=> false
2.2.3 :004 > gs2.finished?
=> true
2.2.3 :005 > gs2.ready!
=> true
2.2.3 :007 > gs2.ready?
=> true
2.2.3 :008 > gs2.finished?
=> false
2.2.3 :009 > gs2.finished!
=> true
2.2.3 :010 > gs2.finished?
=> true
Console (patch):
2.2.3 :001 > gs2 = GatheringSession.last
=> #<GatheringSession id: 120, gathering_state: 2 ... >
2.2.3 :002 > gs2.gathering_state
=> 2
2.2.3 :003 > gs2.ready?
=> false
2.2.3 :004 > gs2.finished?
=> false
2.2.3 :005 > gs2.ready!
(0.2ms) BEGIN
SQL (0.8ms) UPDATE `gathering_sessions` SET `gathering_state` = 'ready', `updated_at` = '2015-10-31 00:28:36' WHERE `gathering_sessions`.`id` = 120
Mysql2::Error: Incorrect integer value: 'ready' for column 'gathering_state' at row 1: UPDATE `gathering_sessions` SET `gathering_state` = 'ready', `updated_at` = '2015-10-31 00:28:36' WHERE `gathering_sessions`.`id` = 120
(0.1ms) ROLLBACK
ActiveRecord::StatementInvalid: Mysql2::Error: Incorrect integer value: 'ready' for column 'gathering_state' at row 1: UPDATE `gathering_sessions` SET `gathering_state` = 'ready', `updated_at` = '2015-10-31 00:28:36' WHERE `gathering_sessions`.`id` = 120
...
from /Users/william/.rvm/gems/ruby-2.2.3/gems/activerecord-4.2.4/lib/active_record/persistence.rb:263:in `update!'
from /Users/william/code/repo/config/initializers/enum_patch.rb:193:in `block (4 levels) in enum'
After doing a little bit of testing, I've realized the problem is not the new code that I've patched in, but the fact that I'm patching in code period. Even when using the same code that is shipped with Rails 4.2.4 (here) I get the same (incorrect) behaviour.
Does anyone have an idea why this is happening and how I can fix it?

Big Decimal Stored as 60.00, Returns as 0.0?

I am customizing a Spree 2.3 application in Rails 4. When I save a price.amount, it appears to save correctly in the database, but when I retrieve it, it is wrapped in the BigDecimal class and returns 0.
How can I store, or retrieve, the correct value?
# in the form
<%= number_field_tag :amount %>
# controller action
#variant.price = params[:amount]
# appears in PostgresQL database as type 'numeric'
id: 35, amount: 60.00
# retrieved in Rails console as BigDecimal class instance
# looks like the wrong amount
2.1.1 :001 > Spree::Price.find_by_id(35).amount
=> #<BigDecimal:105806d20,'0.0',9(27)>
# converts to 0.0
2.1.1 :002 > Spree::Price.find_by_id(35).amount.to_f
=> 0.0
# testing data retrieval in the console
2.1.1 :003 > ActiveRecord::Base.connection.execute("SELECT * FROM spree_prices WHERE id=35").first
=> {"id"=>"35", "variant_id"=>"35", "amount"=>"60.00", "currency"=>"USD", "deleted_at"=>nil}
2.1.1 :004 > Spree::Price.find(35)
=> #<Spree::Price id: 35, variant_id: 35, amount: #<BigDecimal:109ec4a28,'0.0',9(27)>, currency: "USD", deleted_at: nil>
The problem was in a typo in the price model decorator. The pure PostgresQL doesn't trigger the callback, but using the 'find' method does. This accounts for the seemingly impossible results above.
#original price_decorator.rb
after_initialize :set_defaults
def set_defaults
self.amount = 0.0
end
#corrected price_decorator.rb
after_initialize :set_defaults
def set_defaults
self.amount ||= 0.0
end

Why does rails change the type of the attribute _was value after it is set?

I have an attribute in my model that is stored as text but interpreted as a rational. I have this method to handle that:
def start
read_attribute(:start).to_r
end
When I set the start attribute to a new value, the start_was helper method returns a string, instead of a rational, but before I do so, it returns the correct value. Why?
Loading development environment (Rails 3.2.8)
1.9.3p194 :001 > d = Day.find(55)
Day Load (8.7ms) SELECT "days".* FROM "days" WHERE "days"."id" = ? LIMIT 1 [["id", 55]]
=> #<Day id: 55, date: "2012-03-30", start: "1/2", finish: "2/2", created_at: "2012-09-18 15:16:42", updated_at: "2012-09-19 08:20:41", day_year_id: 1>
1.9.3p194 :002 > d.start_was
=> (1/2)
1.9.3p194 :003 > d.start=0
=> 0
1.9.3p194 :004 > d.start_was
=> "1/2"
I think the reason is this method in ActiveModel (activemodel-3.2.8\lib\active_model\dirty.rb)
# Handle <tt>*_was</tt> for +method_missing+.
def attribute_was(attr)
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
As you see, if attribute was not actually changed it just calls its own getter, in your case hitting your start method which does the transformation. But if the attribute is actually changed, it reads its raw value from the changed_attributes storage.

Ruby on Rails question regarding scope

I have the following named scope in my model
class User
scope :single_action, where("parent_id = ?", nil)
end
Even though there are records with parent_id with nil values, they do not seem to be available.
ruby-1.9.2-p0 > User.select("name")
=> [#<User parent_id: nil, name: "John">, #<Task parent_id: 1, name: "Felix">, #<Task parent_id: nil, name: "John Felix">]
I tried using the active record query to do the same, but it also returns empty resultset
ruby-1.9.2-p0 > User.where("parent_id = ?",nil)
=> []
I have defined few other scopes which seems to be working fine.
My Rails version is 3.0.7 and Ruby version is Ruby 1.9.2
Could you please help me to solve this problem.
Thanks :)
Change that to :
User.where(:parent_id => nil)
You can see the difference (you were actually trying to match the string 'NULL' instead of checking for the value being NULL):
ruby-1.9.2-p180 :031 > User.where("remember_token = ?", nil)
=> []
ruby-1.9.2-p180 :032 > User.where(:remember_token => nil)
=> [#<User id: 232255501, .......
ruby-1.9.2-p180 :029 > User.where(:remember_token => nil).to_sql
=> "SELECT `users`.* FROM `users` WHERE (`users`.`remember_token` IS NULL)"
ruby-1.9.2-p180 :030 > User.where("remember_token = ?", nil).to_sql
=> "SELECT `users`.* FROM `users` WHERE (remember_token = NULL)

Resources