diff --git a/forminate.gemspec b/forminate.gemspec index 517a12c..e607edb 100644 --- a/forminate.gemspec +++ b/forminate.gemspec @@ -20,7 +20,6 @@ Gem::Specification.new do |spec| spec.add_dependency 'active_attr', '>= 0.8', '<= 0.13' spec.add_dependency 'activesupport', '>= 3.0.2', '< 6' - spec.add_dependency 'client_side_validations', '~> 12.1' spec.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1' spec.add_development_dependency 'rake', '~> 10.1' diff --git a/lib/forminate/client_side_validations.rb b/lib/forminate/client_side_validations.rb deleted file mode 100644 index e49cfe0..0000000 --- a/lib/forminate/client_side_validations.rb +++ /dev/null @@ -1,124 +0,0 @@ -module ActiveModel - class Validator - # To make use of validators present on associated object, we need to be - # able to specify the original object that a given validator came from. - attr_accessor :target_object - end -end - -module Forminate - # This module makes Forminate work with bcardarella's client_side_validations - # gem (https://github.com/bcardarella/client_side_validations). - # - # The majority of the methods contained here are overrides or alternative - # versions of methods in the client_side_validations gem. For the most part, - # they follow the shape and ideas of the original implementation, usually adding - # the ability to send messages to an associated object rather than the Forminate - # object itself. - module ClientSideValidations - extend ActiveSupport::Concern - - # Where the magic happens. This overrides the client_side_validations gem's - # method of the same name, using Forminate's #association_validators rather - # than ActiveModel's _validators method. It still calls super so that it - # includes validations placed directly on the Forminate object. - def client_side_validation_hash(force = nil) - hash = super - assoc_hash = association_validators.reduce({}) do |assoc_hash, (attr, validators)| - unless [nil, :block].include?(attr) - - validator_hash = validators.reduce(Hash.new { |h,k| h[k] = []}) do |kind_hash, validator| - model = validator.target_object - model_pattern = /^#{model.class.name.underscore}_/ - target_attr = attr.to_s.sub(model_pattern, '').to_sym - - if force.is_a? Hash - relevant_force = force.select { |k, v| k.to_s.match model_pattern } - assoc_force = relevant_force.reduce({}) do |assoc_force, (key, value)| - key = key.to_s.sub(model_pattern, '').to_sym - assoc_force.merge({ key => value }) - end - else - assoc_force = force - end - - if _can_use_for_client_side_validation?(model, target_attr, validator, assoc_force) - if client_side_hash = validator.client_side_hash(model, target_attr, extract_force_option(target_attr, assoc_force)) - kind_hash[validator.kind] << client_side_hash.except(:on, :if, :unless) - end - end - - kind_hash - end - - if validator_hash.present? - assoc_hash.merge!(attr => validator_hash) - else - assoc_hash - end - else - assoc_hash - end - end - hash.merge assoc_hash - end - - private - - # Constructs the associated validators for use with client_side_validations. - # This is meant to mimic ActiveModel's #_validators method that the - # client_side_validations gem relies on for construction the client side - # validation hash. - def association_validators - associations.reduce({}) do |assoc_validators, (name, object)| - if validate_assoc?(name) && object.respond_to?(:_validators) - object._validators.each do |attr, validators| - new_validators = validators.reduce([]) do |new_validators, validator| - new_validator = validator.dup - new_validator.target_object = object - new_validators << new_validator - end - assoc_validators["#{name}_#{attr}".to_sym] = new_validators - end - end - assoc_validators - end - end - - # Alternative version of the client_side_validation gem's - # #can_use_for_client_side_validation? method, allowing the target 'model' - # to be passed in as an argument. - def _can_use_for_client_side_validation?(model, attr, validator, force) - if validator_turned_off?(attr, validator, force) - result = false - else - result = ((model.respond_to?(:new_record?) && validator.options[:on] == (model.new_record? ? :create : :update)) || validator.options[:on].nil?) - result = result && validator.kind != :block - - if validator.options[:if] || validator.options[:unless] - if result = can_force_validator?(attr, validator, force) - if validator.options[:if] - result = result && _run_conditional(model, validator.options[:if]) - end - if validator.options[:unless] - result = result && !_run_conditional(model, validator.options[:unless]) - end - end - end - end - - result - end - - # Alternative version of the client_side_validation gem's #run_conditional - # method, allowing the target 'model' to be passed in as an argument. - def _run_conditional(model, method_name_or_proc) - case method_name_or_proc - when Proc - method_name_or_proc.call(model) - else - model.send(method_name_or_proc) - end - end - end -end diff --git a/lib/forminate/version.rb b/lib/forminate/version.rb index 298c2df..ddac7cb 100644 --- a/lib/forminate/version.rb +++ b/lib/forminate/version.rb @@ -1,3 +1,3 @@ module Forminate - VERSION = '0.2.2' + VERSION = '0.3.0' end diff --git a/spec/forminate/client_side_validations.rb b/spec/forminate/client_side_validations.rb deleted file mode 100644 index e49cfe0..0000000 --- a/spec/forminate/client_side_validations.rb +++ /dev/null @@ -1,124 +0,0 @@ -module ActiveModel - class Validator - # To make use of validators present on associated object, we need to be - # able to specify the original object that a given validator came from. - attr_accessor :target_object - end -end - -module Forminate - # This module makes Forminate work with bcardarella's client_side_validations - # gem (https://github.com/bcardarella/client_side_validations). - # - # The majority of the methods contained here are overrides or alternative - # versions of methods in the client_side_validations gem. For the most part, - # they follow the shape and ideas of the original implementation, usually adding - # the ability to send messages to an associated object rather than the Forminate - # object itself. - module ClientSideValidations - extend ActiveSupport::Concern - - # Where the magic happens. This overrides the client_side_validations gem's - # method of the same name, using Forminate's #association_validators rather - # than ActiveModel's _validators method. It still calls super so that it - # includes validations placed directly on the Forminate object. - def client_side_validation_hash(force = nil) - hash = super - assoc_hash = association_validators.reduce({}) do |assoc_hash, (attr, validators)| - unless [nil, :block].include?(attr) - - validator_hash = validators.reduce(Hash.new { |h,k| h[k] = []}) do |kind_hash, validator| - model = validator.target_object - model_pattern = /^#{model.class.name.underscore}_/ - target_attr = attr.to_s.sub(model_pattern, '').to_sym - - if force.is_a? Hash - relevant_force = force.select { |k, v| k.to_s.match model_pattern } - assoc_force = relevant_force.reduce({}) do |assoc_force, (key, value)| - key = key.to_s.sub(model_pattern, '').to_sym - assoc_force.merge({ key => value }) - end - else - assoc_force = force - end - - if _can_use_for_client_side_validation?(model, target_attr, validator, assoc_force) - if client_side_hash = validator.client_side_hash(model, target_attr, extract_force_option(target_attr, assoc_force)) - kind_hash[validator.kind] << client_side_hash.except(:on, :if, :unless) - end - end - - kind_hash - end - - if validator_hash.present? - assoc_hash.merge!(attr => validator_hash) - else - assoc_hash - end - else - assoc_hash - end - end - hash.merge assoc_hash - end - - private - - # Constructs the associated validators for use with client_side_validations. - # This is meant to mimic ActiveModel's #_validators method that the - # client_side_validations gem relies on for construction the client side - # validation hash. - def association_validators - associations.reduce({}) do |assoc_validators, (name, object)| - if validate_assoc?(name) && object.respond_to?(:_validators) - object._validators.each do |attr, validators| - new_validators = validators.reduce([]) do |new_validators, validator| - new_validator = validator.dup - new_validator.target_object = object - new_validators << new_validator - end - assoc_validators["#{name}_#{attr}".to_sym] = new_validators - end - end - assoc_validators - end - end - - # Alternative version of the client_side_validation gem's - # #can_use_for_client_side_validation? method, allowing the target 'model' - # to be passed in as an argument. - def _can_use_for_client_side_validation?(model, attr, validator, force) - if validator_turned_off?(attr, validator, force) - result = false - else - result = ((model.respond_to?(:new_record?) && validator.options[:on] == (model.new_record? ? :create : :update)) || validator.options[:on].nil?) - result = result && validator.kind != :block - - if validator.options[:if] || validator.options[:unless] - if result = can_force_validator?(attr, validator, force) - if validator.options[:if] - result = result && _run_conditional(model, validator.options[:if]) - end - if validator.options[:unless] - result = result && !_run_conditional(model, validator.options[:unless]) - end - end - end - end - - result - end - - # Alternative version of the client_side_validation gem's #run_conditional - # method, allowing the target 'model' to be passed in as an argument. - def _run_conditional(model, method_name_or_proc) - case method_name_or_proc - when Proc - method_name_or_proc.call(model) - else - model.send(method_name_or_proc) - end - end - end -end diff --git a/spec/forminate/client_side_validations_spec.rb b/spec/forminate/client_side_validations_spec.rb deleted file mode 100644 index f67a41f..0000000 --- a/spec/forminate/client_side_validations_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' -require 'client_side_validations' -require 'forminate/client_side_validations' - -describe Forminate::ClientSideValidations do - subject(:model) { model_class.new } - - let :model_class do - Class.new do - include Forminate - include Forminate::ClientSideValidations - - attribute :total - attribute :tax - - attributes_for :dummy_user - attributes_for :dummy_book, validate: false - attributes_for :dummy_credit_card, validate: :require_credit_card? - - validates_numericality_of :total - - def self.name - 'Cart' - end - - def calculate_total - self.total = dummy_book.price || 0.0 - end - - def require_credit_card? - dummy_book.price && dummy_book.price > 0.0 - end - end - end - - describe '#client_side_validation_hash' do - it 'constructs a hash of validations and messages for use with the client_side_validations gem' do - expected_hash = { - total: { - numericality: [{ messages: { numericality: 'is not a number' } }] - }, - dummy_user_email: { - presence: [{ message: "can't be blank" }] - } - } - expect(model.client_side_validation_hash).to eq(expected_hash) - expected_hash = { - total: { - numericality: [{ messages: { numericality: 'is not a number' } }] - }, - dummy_user_email: { - presence: [{ message: "can't be blank" }] - }, - dummy_credit_card_number: { - presence: [{ message: "can't be blank" }], - length: [{ - messages: { - minimum: 'is too short (minimum is 12 characters)', - maximum: 'is too long (maximum is 19 characters)' - }, - minimum: 12, - maximum: 19 - }] - } - } - model.dummy_book_price = 12.95 - expect(model.client_side_validation_hash).to eq(expected_hash) - end - end -end