I\'m not sure if I should be updating records this way or if I\'m missing something.
I have a table with 5 columns (not including timestamps and id) 3 of which are d
Here is two approaches.
First you can extend Available
with exact method you need:
def self.find_or_create_by_room_id_and_bookdate_and_source(room_id, bookdate, source, &block)
obj = self.find_by_room_id_and_bookdate_and_source( room_id, bookdate, source ) || self.new(:room_id => room_id, :bookdate => bookdate, :source => source)
yield obj
obj.save
end
usage
Available.find_or_create_by_room_id_and_bookdate_and_source(room.id, (params[:date].to_date)+index, data.class.to_s) do |c|
c.price = night.price
c.spots = night.spots
end
This is awkward. So for being more flexible you can create update_or_create_by...
method for ActiveRecord
using method_missing
magic:
class ActiveRecord::Base
def self.method_missing(method_id, *args, &block)
method_name = method_id.to_s
if method_name =~ /^update_or_create_by_(.+)$/
update_or_create($1, *args, &block)
else
super
end
end
def self.update_or_create(search, *args, &block)
parameters = search.split("_and_")
params = Hash[ parameters.zip(args) ]
obj = where(params).first || self.new(params)
yield obj
obj.save
obj
end
end
So now you can use it:
Available.update_or_create_by_id_and_source(20, "my_source") do |a|
a.whatever = "coooool"
end
I think the simplest way is using Ruby's tap method, like this:
def self.parse_data(params,data)
data.beds.each do |bed|
room = Room.find_or_create_room(bed.title, params[:id])
bed.nights.each_with_index do |night,index|
Available.find_or_initialize_by(room_id: room.id).tap do |available|
available.bookdate = (params[:date].to_date) + index
available.source = data.class.to_s
available.price = night.price
available.save
end
end
end
end
find_or_initialize_by
finds or intitializes a record, then returns it. We then tap into it, make our updates and save it to the database.
Actually, there is a way without any hacking. Instead of find_or_create_by you can use find_or_initialize_by and set updated atributes with tap
Available.find_or_initialize_by_room_id_and_bookdate_and_source(
room.id,
(params[:date].to_date)+index,
data.class.to_s#
).tap do |a|
a.price = night.price
a.spots = night.spots
end.save!
Initially this can seems cluttered, but it is doing exactly what you asked for. Find the record, instanciate it if not found and update atributes. this could be called "find_and_update_or_create_by", fortunatelly nobody did that. ;) Hope this help.