问题
I have an ActiveRecord object that has a before_create
hook that generates a SHA hash and stores the result in an attribute of the object:
before_create :store_api_id
attr_reader :api_id
def store_api_id
self.api_id = generate_api_id
end
private
def generate_api_id
Digest::SHA1.hexdigest([Time.now.nsec, rand].join).encode('UTF-8')
end
This works in that the api_id attribute is created and stored in the database as text (which is forced by the call to .encode('UTF-8')
otherwise SQLite will try to store the result as binary data.
However the following specs are failing:
it "should be available as ad.api_id" do
@ad.save!
@ad.api_id.should_not be_nil
end
it "should match the directly accessed attribute" do
@ad.save!
@ad.api_id.should == @ad.attributes['api_id']
end
I can get the correct hash by using ad.api_id_before_type_cast
and ad.attributes['api_id']
but not when using ad.api_id
.
Ad.find_by_api_id('string api id')
also works as expected, but still returns null when calling .api_id
on the returned object.
I have double-checked the type in the database as follows:
sqlite> select typeof(api_id) from ads;
text
Here is an example rails console
session on a fresh Rails 3.2.2 / ruby 1.9.3-p125 app:
Loading development environment (Rails 3.2.2)
irb(main):001:0> ex = Example.new
=> #<Example id: nil, api_id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> ex.save! (0.1ms) begin transaction
SQL (7.3ms) INSERT INTO "examples" ("api_id", "created_at", "updated_at")
VALUES (?, ?, ?) [["api_id", "fc83bb94420cf8fb689b9b33195318778d771c4e"],
["created_at", Fri, 23 Mar 2012 10:17:24 UTC +00:00],
["updated_at", Fri, 23 Mar 2012 10:17:24 UTC +00:00]]
(1.0ms) commit transaction
=> true
irb(main):003:0> ex.api_id
=> nil
irb(main):004:0> ex.api_id_before_type_cast
=> "fc83bb94420cf8fb689b9b33195318778d771c4e"
irb(main):005:0> ex.attributes['api_id']
=> "fc83bb94420cf8fb689b9b33195318778d771c4e"
irb(main):006:0>
回答1:
As I wrote above, using attr_readonly
instead of attr_reader
to protect the attribute fixed this issue for me, and in this case is actually closer to what I want.
As the API docs note:
Attributes listed as readonly will be used to create a new record but update operations will ignore these fields.
来源:https://stackoverflow.com/questions/9837126/attribute-available-before-type-cast-but-nil-when-accessed-directly