I have an app where I need to prevent users from editing data while it is being edited by a different user. I'm trying to think of the best way to do it and wanted to ask for ideas. So far, I've created a settings model that stores application wide configuration on the db in key/value pairs. So, for the lock, I have a settings instance that's called LOCKED_TABLE_UID, and it stored the user_id of the user editing the table or null (nil) if the table is free.
>> lock = Setting.find_by_key('LOCKED_TABLE_UID')
Then, I implemented 2 methods in my application controller to acquire and release the lock:
# current_user returns the user currently logged in
def acquire_lock
lock = Setting.find_by_key("LOCKED_TABLE_UID")
if lock.value
# if lock taken, see if it's the current_user or someone else
if lock.value.to_i == current_user.id.to_i
return true
else
return false
end
else
# lock is free, assign it to this user
lock.value = current_user.id
return true if lock.save
end
end
def release_lock
lock = Setting.find_by_key("LOCKED_TABLE_UID")
if lock.value
# the lock belongs to current_user, so he can release it
if lock.value.to_i == current_user.id.to_i
lock.value = nil
return true if lock.save
else
# not your lock, go away
return false
end
else
# lock is free, quit bugging
return true
end
end
What I want is to create some kind of block code that contains the locking mechanism, something like this:
def some_crud_action
requires_locking do |lock|
if lock
# do some CRUD stuff here
else
# decline CRUD and give some error
end
end
end
I'd appreciate help on this - but I'm also open to other suggestions on how to accomplish all that, or some things I may have overlooked. This lock doesn't have to be atomic, but fairly basic and most important - that it works :) thanks.
Have you seen the ActiveRecord built-in locking feature?
I like the idea but see a big problem with your solution, and it is that you are getting and releasing locks of entire tables.
For a very small app that could be fine, but imagine if you have thousands of users trying to access to, say, the 'PRODUCTS' table and having to wait because someone is editing an entry totally unrelated to their own products.
Maybe you could have a finer grain approach and lock particular rows instead of tables. Then the lock would include the table name, row ID and user ID.
You're almost there. Create your require_locking? action as you see fit. Then process it with a before_filter.
I think acts_as_lockable_by gem is exactly doing what you asked for in simpler terms and less code. It's easily integrable with rails or even a bare ruby project.
With this gem, you get atomic
lock
,unlock
andrenew_lock
methods. Also, you get an auto expiringttl
locks, so if shit hits the fan and you could not unlock the resource it will be automatically unlocked for you!