instance_variable_set (:@attributes, { :brand.to_s => "Apple" } ) overwrites all attributes instead of adding
Background...
I'm writing a parser that looks at strings and tries to determine what products they might contain. I've created my own Token
class to help.
class Token < ActiveRecord::BaseWithoutTable
attr_accessor :regex
attr_accessor :values
end
Example of a Token:
Token.new(:regex => /apple iphone 4/, :values => { :brand => "Apple", :product => "iPhone", :version => 4})
(where the hash keys all correspond to database columns in the products table.)
Here is the problem: In my Parser
, when a Token
is found, I attempt to add the associated values to a Product
instance, like so:
token.values.each do |v|
attrib, value = v[0], v[1]
my_product.instance_variable_set(:@attributes, { at开发者_Python百科trib.to_s => value })
end
This works except that it seems as if I have to set all my attributes at the same time. If I do it in stages (ie: as I discover new tokens), it overwrites any unspecified attributes with nil
. Am I missing something? Is there a better way to do this?
Modify the existing value (if it exists) instead of overwriting it:
if attr = my_product.instance_variable_get :@attributes
attr[attrib.to_s] = value
else
my_product.instance_variable_get :@attributes, { attrib.to_s => value }
end
The use of instance_variable_set seems sketchy; why don't you have an accessor on the Product
itself?
class Product
def attributes
@attributes ||= {}
end
end
...
token.values.each do |attr,v|
my_product.attributes.merge!( attr.to_s => v )
end
If my_product
is an active_record
object, you can use write_attribute
instead of instance_variable_set
. Note that this will only write attributes i.e. database columns:
token.values.each do |v|
attrib, value = v[0], v[1]
my_product.write_attribute attrib.to_s, value # attrib.to_sym would work too
end
Also, if token.values
returns a Hash
, this is how you can iterate:
token.values.each do |k, v|
my_product.write_attribute k, v
end
精彩评论