Skip to content

Instantly share code, notes, and snippets.

@bdmac
Last active December 14, 2015 05:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bdmac/5032984 to your computer and use it in GitHub Desktop.
Save bdmac/5032984 to your computer and use it in GitHub Desktop.
Proposal to modify rails-core with optimistic locking enhancements or turn this into a gem.
# Automatically adds lock_version to model forms if the underlying
# model has locking_enabled.
module ActionView
module Helpers
module OptimisticLockingFormFor
def self.included(base)
base.alias_method_chain :form_for, :optimistic_locking
base.alias_method_chain :fields_for, :optimistic_locking
end
def form_for_with_optimistic_locking(record_or_name_or_array, *args, &block)
form_for_without_optimistic_locking(record_or_name_or_array, *args) do |form_with_locking|
build_lockable_form(form_with_locking, &block)
end
end
def fields_for_with_optimistic_locking(record_name, record_object = nil, options = {}, &block)
fields_for_with_optimistic_locking(record_name, record_object, options) do |fiels_for_with_locking|
build_lockable_form(fields_for_with_locking, &block)
end
end
private
def build_lockable_form(builder, &block)
lock_form = builder.object &&
builder.object.respond_to?(:locking_enabled?) &&
builder.object.locking_enabled? &&
!builder.object.new_record?
concat(content_tag(:div,
builder.hidden_field(builder.object.class.locking_column),
:style => 'margin:0;padding:0;display:inline').html_safe) if lock_form
yield builder
end
end
end
end
ActionView::Base.send :include, ActionView::Helpers::OptimisticLockingFormFor
# Automatically add data-params for the model's lock_version
# when lock_with: is specified in link_to.
# Usage: link_to(name, url_for(model), remote: true, method: :put, lock_with: model)
module ActionView
module Helpers
module LockVersionUrlHelper
def self.included(base)
base.alias_method_chain :convert_options_to_data_attributes, :optimistic_locking
end
private
def convert_options_to_data_attributes_with_optimistic_locking(options, html_options)
the_options = convert_options_to_data_attributes_without_optimistic_locking(options, html_options)
if the_options && the_options['lock_with'] && the_options['lock_with'].kind_of?(ActiveRecord::Base)
model_object = the_options.delete('lock_with')
if model_object.respond_to?(:locking_enabled?) && model_object.locking_enabled? && !model_object.new_record?
locking_column = model_object.class.locking_column
lock_version = model_object.send(locking_column.to_sym)
if the_options['data-params'].present?
the_options['data-params'] += "&#{locking_column}=#{lock_version}"
else
the_options['data-params'] = "#{locking_column}=#{lock_version}"
end
end
end
the_options
end
end
end
end
ActionView::Base.send :include, ActionView::Helpers::LockVersionUrlHelper
# Automatically permit lock_version whenever #permit is called on params.
# Note that this assumes the locking column is named lock_version which
# may not be true if overridden in the model class. Unfortunately there's
# no way I can see to determine the appropriate locking column to use from
# here with no access to the model in use.
#
# I'm least happy with this proposal...
module ActionController
module LockVersionParameters
def self.included(base)
base.alias_method_chain :permit, :optimistic_locking
end
def permit_with_optimistic_locking(*filters)
filters << 'lock_version'
permit_without_optimistic_locking(filters)
end
end
end
ActionController::Parameters.send :include, ActionController::LockVersionParameters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment