# Copyright (c) 2007 James Coglan # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. module Flagger module Constants # Negative word prefixes that can be used to search for false flags NegativePrefixes = %w(in im un non dis de ir not_) # Sets of true/false values for different column types BooleanValueSets = { :boolean => {:true => true, :false => false}, :integer => {:true => 1, :false => 0}, :float => {:true => 1, :false => 0}, :decimal => {:true => 1, :false => 0}, :string => {:true => "1", :false => "0"}, :text => {:true => "1", :false => "0"} } end module ClassMethods def column_type(column_name) column = self.new.column_for_attribute(column_name.to_s) column.nil? ? nil : column.type.to_s.downcase.intern end def determine_condition_builder respond_to?(:construct_conditions_from_arguments, true) ? :construct_conditions_from_arguments : :construct_attributes_from_arguments end def method_missing_with_boolean_finders(method_id, *args, &block) method_missing_without_boolean_finders(method_id, *args, &block) rescue StandardError => e method_name = method_id.to_s if match = /^([_a-zA-Z]\w*)$/.match(method_name) attribute_names = match.captures.last.split(/_and_|_but_/) attributes = [] values = [] attribute_names.each do |name| name_without_prefix = name.gsub(Regexp.new("^(#{Flagger::Constants::NegativePrefixes.join('|')})", Regexp::IGNORECASE), '') name_with_prefixes = Flagger::Constants::NegativePrefixes.collect { |prefix| prefix + name } if all_attributes_exists?(name) attributes << name values << Flagger::Constants::BooleanValueSets[column_type(name)][:true] elsif all_attributes_exists?(name_without_prefix) attributes << name_without_prefix values << Flagger::Constants::BooleanValueSets[column_type(name_without_prefix)][:false] elsif name_with_prefixes.delete_if { |x| !all_attributes_exists?(x) }.size.nonzero? attributes << name_with_prefixes.first values << Flagger::Constants::BooleanValueSets[column_type(name_with_prefixes.first)][:false] else raise e end end condition_builder = determine_condition_builder conditions = __send__(condition_builder, attributes, values) options = {:conditions => conditions} options = args.first.update(options) if args.first && args.first.is_a?(Hash) set_readonly_option!(options) find_every(options) else raise e end end alias_method(:method_missing_without_boolean_finders, :method_missing) alias_method(:method_missing, :method_missing_with_boolean_finders) end module InstanceMethods def method_missing_with_mark_as(method_id, *args, &block) method_missing_without_mark_as(method_id, *args, &block) rescue StandardError => e method_name = method_id.to_s if match = /^mark_as_([_a-zA-Z]\w*)\!?$/.match(method_name) attribute_names = match.captures.last.split(/_and_|_but_/).sort attributes = attribute_names.collect do |name| name_without_prefix = name.gsub(Regexp.new("^(#{Flagger::Constants::NegativePrefixes.join('|')})", Regexp::IGNORECASE), '') name_with_prefixes = Flagger::Constants::NegativePrefixes.collect { |pref| pref + name } if has_attribute?(name) {:name => name, :value => Flagger::Constants::BooleanValueSets[self.class.column_type(name)][:true]} elsif has_attribute?(name_without_prefix) {:name => name_without_prefix, :value => Flagger::Constants::BooleanValueSets[self.class.column_type(name_without_prefix)][:false]} elsif name_with_prefixes.delete_if { |x| !has_attribute?(x) }.size.nonzero? {:name => name_with_prefixes.first, :value => Flagger::Constants::BooleanValueSets[self.class.column_type(name_with_prefixes.first)][:false]} else raise e end end bang = /\!$/.match(method_name) ? '!' : '' attributes.sort_by { |attr| attr[:name] }.each do |attr| unless __send__("#{attr[:name]}") == attr[:value] if /\!$/.match(method_name) && !new_record? update_attribute(attr[:name], attr[:value]) else __send__("#{attr[:name]}=", attr[:value]) end case attr[:value] when Flagger::Constants::BooleanValueSets[self.class.column_type(attr[:name])][:false] Flagger::Constants::NegativePrefixes.each do |prefix| method_name_with_prefix = "after_mark_as_#{prefix}#{attr[:name]}#{bang}" __send__(method_name_with_prefix) if respond_to?(method_name_with_prefix, true) if (attr[:name].match(Regexp.new('^' + prefix))) method_name_without_prefix = "after_mark_as_#{attr[:name].gsub(Regexp.new('^' + prefix), '')}#{bang}" __send__(method_name_without_prefix) if respond_to?(method_name_without_prefix, true) end end when Flagger::Constants::BooleanValueSets[self.class.column_type(attr[:name])][:true] __send__("after_mark_as_#{attr[:name]}#{bang}") if respond_to?("after_mark_as_#{attr[:name]}#{bang}", true) end end end else raise e end end alias_method(:method_missing_without_mark_as, :method_missing) alias_method(:method_missing, :method_missing_with_mark_as) end end