0

I am getting:

 syntax error, unexpected '=', expecting keyword_end diamond.send(field) = fields[field]

But I don't see why though. I am trying to dynamically assigning values.

Here is the code:

  def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|

      diamond = find_by_id(row["id"]) || new

      fields = row.to_hash

      # assign non float attributes to columns
      ['cust_ref', 'ags_1st_number', 'ags_ending_number', 'gold_cut_grade', 
      'polish_grade', 'symmetry_grade', 'color_grade', 'fluor_desc', 
      'clarity_grade', 'girdle_min_max_percentage', 'diameter_min_max', 
      'girdle_condition', 'proportion_grade', 'comment_1', 'comment_2', 
      'comment_3', 'comment_4', 'is_non_ideal', 'key_to_symbols', 'shape'].each do |field|
     diamond.send(field) = fields[field]
    end
  end
end
1
  • You need to write diamond.send(field + '=', fields[field]) I think.. Because the way you are assigning is not valid. Commented Jul 1, 2014 at 18:31

3 Answers 3

4

That's because you are calling the getter and not the setter.

To use send, you need to distinguish between the attr_accessor's given methods. So, for example:

class Test
   attr_accessor :field
end

t = Test.new

t.send(:field)
#=> nil
t.send(:field=, 'lalala')
#=>'lalala'
t.send(:field)
#=>'lalala'

And this works with strings too.

[EDIT] As mentioned by @jordan, this also allows you to do string interpolation, using or not symbols. So the next lines would also work:

a = 'field='
t.send(:"#{a}", 'lalala')
t.send(a)
b = 'field'
t.send("#{b}=", 'lalala')

And so on.

Sign up to request clarification or add additional context in comments.

9 Comments

Important to note that if you have the name of the attribute (e.g. "field" or :field) in a variable, you can add the equals sign using ordinary string (or symbol) interpolation: t.send(:"#{attr_name}=", 'lalala').
"#{attr_name}=" evaluates a string whereas :"#{attr_name}" evaluates to a symbol. send will take either, although IIRC in old versions of Ruby it took only symbols. In practice it doesn't make much of a difference, so it's mostly a matter of taste.
@ArupRakshit To do interpolation in a symbol you need the quotation marks. If attr_name has a value of "foo", then :"#{attr_name}=" evaluates to the symbol :foo=. :#{attr_name}= is a syntax error.
@Jordan Makes sense now.. If version of Ruby don't support #send with first argument as a string, then only :"#{attr}=" is needed to do.. otherwise not.. Perfect, got now what you meant to say..
@AfonsoTsukamoto It's true that symbols are more memory-efficient in certain circumstances (e.g. when you have a lot of duplicate strings that are in different places in memory, although copy-on-write in Ruby 2.0 mitigates this a lot). However it's important to remember that they're never garbage-collected, and so can become a problem if, say, you have code creating lots of long symbols with different names. There was even a Rails vulnerability relating to this, because its YAML parser automatically instantiated symbols of arbitrary length. Symbols are great, but they're not a silver bullet.
|
1

The syntax you are proposing is not a valid assignment syntax in Ruby.

You can define assignment methods in Ruby (check http://joeyates.info/2012/01/16/ruby-bareword-assignment-and-method-calls-with-implicit-self/ for a more deep-in explanation of assignment).

class Diamond
  def proportion_grade=(arg)
    @proportion_grade = arg
  end
end

If you declared an attr writer or accessor, this is done for you

class Diamond
  attr_accessor :proportion_grade
end

so what you want to call is the method proportion_grade=, not really proportion_grade

the correct way would be:

['all', 'those', 'arguments'].each do |field|
   diamond.send("#{field}=", fields[field])
end

Comments

0

It seems diamond.send(field) is a method and you shouldn't treat a method call like a variable that is being declared. Declare another variable if you want to save fields[field] as a variable. Delete the "= fields[field] from the line and just put diamond.send(field) on its own line, or diamond.send(field + '=' + fields[field]) as per @Arup Rakshit.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.