1

I have the following table:

event_id  value   created_at        updated_at
1         15.2    2014/01/01 00:00  2014/01/01 00:00
2         15.5    2014/01/01 00:10  2014/01/01 00:10
3         15.9    2014/01/01 00:20  2014/01/01 00:20

However, if a new Event has same value as the previous (as in newest in time) Event, then the previous Event should have "updated_at" set to current time and no new Event should be created.

In the example above, if I do Event.new(:value => 15.9), then Event with id 3 should have its updated_at set to current time - and that should be the only change.

Any suggestions on how to accomplish this? I have tried fiddling with Active Record callbacks, but fail when aborting creation (using rollback). It is of course possible to solve using a special "constructor" method, but I'd like to avoid that.

Ciryon
  • 2,637
  • 3
  • 31
  • 33

4 Answers4

4
Event.where(value: params[:value]).first_or_create.touch

or in event.rb

before_save :update_if_existing

private 
def update_if_existing
  if event = Event.find_by(value: value)
    event.touch # updates the updated_at timestamp if the existing event
    false       # prevents the current event from being inserted into the db
  end
end
spickermann
  • 100,941
  • 9
  • 101
  • 131
  • Thanks, I'll accept this answer since it puts logic in the model object rather than the controller. – Ciryon May 06 '14 at 06:14
  • Thanks. Keep in mind that it still returns the new unsaved `Event` and not the `Event` that already exists. You may need to handle that in your controller. – spickermann May 06 '14 at 06:19
1

You can do this way, let's say @event is the object and before saving you want to check the value

unless @event.value == Event.last.value     
  @event.save
else
  Event.last.update_attributes(:updated_at => DateTime.now)
end

or you can do this in a single line using ternary operator

(@event.value == Event.last.value) ? (Event.last.update_attributes(:updated_at => DateTime.now)) : (@event.save)
Rajdeep Singh
  • 17,621
  • 6
  • 53
  • 78
1
Event.find_or_create_by_value(params[:value]).touch

This method will find event by value or create one if event with this value doesn't exist. Touch method will update updated_at timestamp for this record.

Mikhail Nikalyukin
  • 11,867
  • 1
  • 46
  • 70
0

Since your definition of Event is that no two events should have the same value - you should add a unique index on that field, which will also make any such operations much quicker.

Actually, since your event is not defined by its id but by its value, consider changing its primary key to value:

create_table(:event, :primary_key => 'value') do |t|
  t.column :userID, :decimal, :null => false
  ...
end

class Event
  set_primary_key :value
  ...
end

Now you can simply do:

Event.find_or_create_by_value(params[:value]).touch
Community
  • 1
  • 1
Uri Agassi
  • 36,848
  • 14
  • 76
  • 93
  • Two events in the table can have the same value, but not in sequence. I.e. 15.2, 15.5, 15.2 is ok. But not 15.2, 15.2. – Ciryon May 06 '14 at 06:27
  • @Ciryon - it was not apparent from your question, sorry. In that case, I think only RSB got it right... the rest of the answers look for it globally. – Uri Agassi May 06 '14 at 06:30