Prefork Prefork - 4 months ago 22
Ruby Question

NoExistingObject exception when using Sequel::Model.plugin timestamps and touch

As part of a project, I have created a

Session
model to store the dates when it was first created and updated (using the
timestamps
plugin provided by Sequel). This is to facilitate server-side handling of session timeouts. The updated_at column can be updated using Sequel's
touch
plugin, which provides an equivalent method.

However, when trying to use it, I receive the following error

Sequel::NoExistingObject: Attempt to update object did not result in a single row modification (SQL: UPDATE `sessions` SET `updated_at` = '2016-07-01 12:17:06.373469' WHERE (`id` = 14))
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:2018:in `_update'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:2011:in `_update_columns'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:1965:in `block (2 levels) in _save'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:1146:in `around_update'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:1949:in `block in _save'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:1146:in `around_save'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:1935:in `_save'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:1587:in `block (2 levels) in save'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:2089:in `block in checked_transaction'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/database/transactions.rb:163:in `_transaction'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/database/transactions.rb:138:in `block in transaction'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/database/connecting.rb:251:in `block in synchronize'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/connection_pool/threaded.rb:105:in `hold'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/database/connecting.rb:251:in `synchronize'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/database/transactions.rb:104:in `transaction'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sequel-4.35.0/lib/sequel/model/base.rb:2089:in `checked_transaction'
... 7 levels...
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/console.rb:9:in `start'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/commands_tasks.rb:68:in `console'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/railties-4.2.6/lib/rails/commands.rb:17:in `<top (required)>'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:274:in `require'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:274:in `block in require'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:240:in `load_dependency'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:274:in `require'
from /Users/[FILTERED USERNAME]/Sync/RubymineProjects/[FILTERED PROJECT NAME]/bin/rails:9:in `<top (required)>'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:268:in `load'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:268:in `block in load'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:240:in `load_dependency'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.6/lib/active_support/dependencies.rb:268:in `load'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from /Users/[FILTERED USERNAME]/.rbenv/versions/2.3.1/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from -e:1:in `<main>'


I have already tried using the
Sequel::Model.update
and
Sequel::Model.update_all
methods, but to no avail (receiving the same error).

What is intriguing is that I am able to run the SQL qeury produced above via the command line, with both SQLite and MySQL.

==== app/models/session.rb ====

class Session < Sequel::Model
plugin :validation_helpers
plugin :timestamps
plugin :touch

def before_update
self.touch
end

def before_save
Session.where(user_id: self.user_id).delete
end

def self.sweep(time = 2.hours)
time = time.split.inject { |count, unit| count.to_i.send(unit) } if time.is_a?(String)
Session.where("updated_at < '#{time.ago.to_s(:db)}' OR created_at < '#{1.day.ago.to_s(:db)}'").delete
end

def validates
validates_presence [:user_id]
end
end


Thanks advance for any help provided.

Answer
def before_save
  Session.where(user_id: self.user_id).delete
end

This is going to delete all rows in the sessions table with the same user_id before saving the session, including the current row (if updating and not creating). If you only want to allow a single session, you should probably switch to using before_create. Also, don't forget to call super in your hooks.