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 %>
I have a serialized column
class TestSerialize < ActiveRecord::Base
serialize :parameters, Array
end
When I tried to assign String
a = TestSerialize.new
a.parameters = "inappropriate type"
as expected I got
ActiveRecord::SerializationTypeMismatch:Attribute was supposed to be a Array, but was a String. -- "inappropriate type".
But when I tried to reassign "parameters" field, I still got the same error with the previous assigned value
a.parameters = []
ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Array, but was a String. -- "inappropriate type"
Is it expected behaviour?
2.2.3 :003 > a = TestSerialize.new
=> #<TestSerialize id: nil, parameters: [], first_name: {}, last_name: "", created_at: nil, updated_at: nil>
2.2.3 :004 > a.parameters
=> []
2.2.3 :005 > a.parameters = "test"
ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Array, but was a String. -- "test"
2.2.3 :006 > a
ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Array, but was a String. -- "test"
2.2.3 :007 > a.parameters
ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Array, but was a String. -- "test"
2.2.3 :008 > a.first_name
=> {}
2.2.3 :009 > reload!
Reloading...
=> true
2.2.3 :010 > a
ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Array, but was a String. -- "test"
Got the answer from Rails team
https://github.com/rails/rails/issues/23449
I have a model that looks like this:
class WorkRequest < ActiveRecord::Base
attr_accessible :upload, :assigned_to_staff
serialize :assigned_to_staff, Array
before_save :set_old_staff
def set_old_staff
#old_staff = self.assigned_to_staff_was
end
def staff_changed?
!self.assigned_to_staff.empty? && self.assigned_to_staff != #old_staff
end
end
I'm trying to make use of self.assigned_to_was to track when a staff assignment change takes place. I'm noticing that the serialized field behaves differently than a regular field. Console output below shows differing behavior in :upload (text string field) and the serialized :assigned_to_staff:
1.9.2-p320 :002 > wr.upload
=> nil
1.9.2-p320 :003 > wr.upload_was
=> nil
1.9.2-p320 :004 > wr.upload = "Yes"
=> "Yes"
1.9.2-p320 :005 > wr.upload_was
=> nil
compared to:
1.9.2-p320 :006 > wr.assigned_to_staff
=> []
1.9.2-p320 :007 > wr.assigned_to_staff_was
=> []
1.9.2-p320 :008 > wr.assigned_to_staff << User.last.name
User Load (0.2ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
=> ["last5, first5"]
1.9.2-p320 :009 > wr.assigned_to_staff_was
=> ["last5, first5"]
Can anyone explain this discrepancy and or suggest a workaround?
It appears that serialization doesn't fully implement all methods of the host class. Overrides are provided for getters and setters, but not concatenation.
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.
I am having problem updating the embedded documents in mongodb.
I have a following scenario.
A User model has address as the embedded docs.
I am able to embed the address to the parent model ie; User model but i still cant figure out how to update the address embedded even though i have the _id of the address embedded
Please help
Thanks
You have to retrieve the embedded document from the parent and then make the update operation, e.g:
address = user.address
address.update_attributes(:street => "foo")
There's another solution. If there's a many-to-many relationship between the Person and Preference classes, then:ruby-1.9.2-p0 > Person.count
=> 0
ruby-1.9.2-p0 > Preference.count
=> 0
ruby-1.9.2-p0 > person = Person.create
=> #< Person _id: 4cd353e92b58af214b000006, preference_ids: []>
ruby-1.9.2-p0 > pref = Preference.create
=> #< Preference _id: 4cd353ee2b58af214b000007, person_ids: [], name: nil>
ruby-1.9.2-p0 >
ruby-1.9.2-p0 > person.preferences << pref
=> true
ruby-1.9.2-p0 > Preference.first.people.count
=> 1
ruby-1.9.2-p0 > Person.first.preferences.count
=> 1
ruby-1.9.2-p0 >
ruby-1.9.2-p0 > person.preferences.first.name = 'foobar'
=> "foobar"
ruby-1.9.2-p0 > person.preferences.first.save
=> true
ruby-1.9.2-p0 > pref.reload
=> #< Preference _id: 4cd353ee2b58af214b000007, person_ids: [BSON::ObjectId('4cd353e92b58af214b000006')], name: "foobar">
ruby-1.9.2-p0 > pref.name
=> "foobar"