Reputation: 12670
I have an ActiveRecord model whose fields mostly come from the database. There are additional attributes, which come from a nested serialised blob of stuff. This has been done so that I can use these attributes from forms without having to jump through hoops (or so I thought in the beginning, anyway) while allowing forwards and backwards compatibility without having to write complicated migrations.
Basically I am doing this:
class Licence < ActiveRecord::Base
attr_accessor :load_worker_count
strip_attributes!
validates_numericality_of :load_worker_count,
:greater_than => 2, :allow_nil => true, :allow_blank => true
before_save :serialise_fields_into_properties
def serialise_fields_into_properties
...
end
def after_initialize
...
end
...
end
The problem I noticed was that I can't get empty values in :load_worker_count
to be accepted by the validator, because:
:allow_blank
, it fails validation complaining about it being blank:allow_blank
, it converts the blank to 0, which when fails on the :greater_than => 2
In tracking down why these blank values are getting to the validation stage in the first place, I discovered the root of the problem: strip_attributes!
only affects actual attributes, as returned by the attributes
method. So the values which should be nil at time of validation are not. So it feels like the root cause is that the synthetic attributes I added in aren't seen when setting which attributes to strip, so therefore I ask:
Is there a proper way of creating synthetic attributes which are recognised as proper attributes by other code which integrates with ActiveRecord?
Upvotes: 1
Views: 1910
Reputation: 3825
I assume you are talking of the strip_attributes
plugin; looking at the code, it uses the method attributes
, defined in active_record/base.rb, which uses @attributes
, which is initialized (in initialize
) as @attributes = attributes_from_column_definition
.
Maybe it's possible to hack ActiveRecord::Base somehow, but it would be a hard work: @attributes
is also used when getting/putting stuff from/to db, so you would have to do a lot of hacking.
There's a much simpler solution:
before_validate :serialise_fields_into_properties
...
def serialise_fields_into_properties
if load_worker_count.respond_to? :strip
load_worker_count = load_worker_count.blank? ? nil : load_worker_count.strip
end
...
end
After all, this is what strip_attributes! does.
Upvotes: 1
Reputation: 11069
Wouldn't it be easier to just use Rails' serialize macro here?
class License < ActiveRecord::Base
serialize :special_attributes
end
Now you can assign a hash or array or whatever you need to special_attributes and Rails will serialize it a text field in the database.
license = License.new
license.special_attributes = { :beer => true, :water => false }
This will keep your code clean and you don't have to worry about serializing/deserializing attributes yourself.
Upvotes: 1