3

I want to update an AR object, using an attribute name that I access from another source (in the real data, there are lots).

class DailyScore < ActiveRecord::Base
    attr_accessor :mail, :date, :sun, :express, :average
end

array = ['mail','date','sun']
thing = DailyScore.new

How can I turn each item in the array into a symbol, and update the record accordingly?

Ideally, I'd like something like this:

array.each do |field|
    thing.field = 1.0 #Float
end

UPDATE (with fuller code example):

def sort_and_deliver_scores(dates)

  # dates example: ["2016-01-01","2016-01-12","2016-01-05"]

  dates.each do |dateString|

    params = {}
    params[:date] = dateString
    so_far = 0.0

    ["mail","express","sun"].each do |paper|

      # get all records that correspond to this newspaper
        @dailyscores = MyModel
                       .pluck(:paper,:score, :date)
                       .select{|b| b[0]==paper}

      # add up the scores in all records for each paper that day  

      today = @dailyscores.map{|c| c[1]}.inject(:+)

      todaysScoresByPaper = (today/@dailyscores.size).round(1)
      so_far += todaysScoresByPaper

      params[paper.to_sym] = todaysScoresByPaper    

    end

    params[:average] = (so_far / 3).round(1)
    save_record_as_daily_score_object(params)
  end
end


def save_record_as_daily_score_object(params)
        ds = DailyScore.where(date: params[:date]).first_or_create!
        ds.update!(params)
end

When I check using pry, the params are fine, with Float values stored in the hash, but for some reason, when I save the record, ONLY the :average field gets saved, and all others are nil.

UPDATE 2:

tasks.rake

task :seed_scores => :environment do

         # "select_a_few_dates" sends a few dates as strings
         sort_and_deliver_scores(Score.all.select_a_few_dates)

end

score.rb

def save_record_as_daily_score_object(params)
        ds = DailyScore.where(date: params[:date]).first_or_create!
        ds.update!(params)
        binding.pry

end

PRY output:

[1] pry(main)> ds
=> #<DailyScore:0x0000010add5ff8
 id: 3876,
 mail: nil,
 average: -3.4,
 sun: nil,
 express: nil,
 date: nil,
 created_at: 2016-05-15 09:17:55 UTC,
 updated_at: 2016-05-15 12:45:50 UTC>
[2] pry(main)> params
 => {:date=>"2015-09-02",
 :mail=>-0.6,
 :express=>-0.1,
 :sun=>-3.2,
 :average=>-3.4}

It seems that creating a DailyScore object by dateString is not working, and for some reason, only the :average attribute is being saved.

Jonathan_W
  • 638
  • 9
  • 24
  • Can you post your pry output? That might be revealing. – Michael Gaskill May 15 '16 at 10:01
  • Done. Could it be that there is a fault in the model itself? It was created via a generator and then did padrino rake db:migrate as usual. Is it possible that it is related to the environment variable in some way? This code is executed inside a rake task. – Jonathan_W May 15 '16 at 12:49
  • @MichaelGaskill I reposted the Pry issue as a separate question as it doesn't really fall under the remit of this one. – Jonathan_W May 16 '16 at 14:01
  • I actually realized that it was you, about 10 seconds after I made the comment. I was sure that I'd found someone with a similar issue to what you were experiencing. But, alas, it simply wasn't so. – Michael Gaskill May 16 '16 at 14:02

3 Answers3

6

You can use the ActiveRecord hash interface, which would look like this, using your array variable:

array.each do |field|
  thing[field.to_sym] = "I'll tell you later"
end

thing.save

Alternatively, you can use the params hash appraoch. Rails uses the params hash to perform a similar operation. You can simulate this functionality in this way:

array = ['title','date','height']
values = ['King Lear', '2016-05-13', 13.5]
attributes_hash = {}

array.map {|elem| elem.to_sym }.each_with_index do |field, i|
  attributes_hash[field] = values[i]
end

thing.update(attributes_hash)

The benefit of this approach is that you can update an entire hash of values at one time. If you want to bypass validations and other checks, you can use update_attributes, instead.

For more information, see:

Community
  • 1
  • 1
Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43
  • Thanks for this, but it isn't working for me, I can't see why. I'll add my code in full to the question, perhaps the error is elsewhere. – Jonathan_W May 15 '16 at 09:19
  • 1
    I much prefer this over the `send` method, if you're able to set the attributes directly and do not need to call special setter method. – Joshua Pinter Nov 08 '22 at 22:10
3

This will do the trick :)

array = ['title', 'date', 'height']
thing = Thing.new
array.each do |field|
 thing.send("#{field}=", 'your value')
end
# save object or do whatever

send invokes a method defined by a symbol or (as is our case) a string.

nicohvi
  • 2,270
  • 2
  • 28
  • 42
1

you can update this like below code

array = ['title','date','height']
thing = Thing.new
array.each do |field|
   thing[field] = "I'll tell you later"
end
thing.save
Amol Udage
  • 2,917
  • 19
  • 27