lib/remarkable/dsl/optionals.rb in remarkable-3.1.8 vs lib/remarkable/dsl/optionals.rb in remarkable-3.1.9

- old
+ new

@@ -1,15 +1,15 @@ -module Remarkable +module Remarkable module DSL # This module is responsable for create optional handlers and providing macro # configration blocks. Consider the matcher below: # - # class AllowValuesForMatcher < Remarkable::ActiveRecord::Base - # arguments :collection => :attributes, :as => :attribute - # + # class AllowValuesForMatcher < Remarkable::ActiveRecord::Base + # arguments :collection => :attributes, :as => :attribute + # # optional :message - # optional :in, :splat => true + # optional :in, :splat => true # optional :allow_nil, :allow_blank, :default => true # end # # This allow the matcher to be called as: # @@ -42,35 +42,35 @@ # m.to { @user.id } # end # # == I18n # - # Optionals will be included in description messages if you assign them - # properly on your locale file. If you have a validate_uniqueness_of - # matcher with the following on your locale file: - # - # description: validate uniqueness of {{attributes}} - # optionals: - # scope: - # positive: scoped to {{inspect}} - # case_sensitive: - # positive: case sensitive - # negative: case insensitive - # - # When invoked like below will generate the following messages: - # - # validate_uniqueness_of :project_id, :scope => :company_id - # #=> "validate uniqueness of project_id scoped to :company_id" - # - # validate_uniqueness_of :project_id, :scope => :company_id, :case_sensitive => true - # #=> "validate uniqueness of project_id scoped to :company_id and case sensitive" - # - # validate_uniqueness_of :project_id, :scope => :company_id, :case_sensitive => false - # #=> "validate uniqueness of project_id scoped to :company_id and case insensitive" + # Optionals will be included in description messages if you assign them + # properly on your locale file. If you have a validate_uniqueness_of + # matcher with the following on your locale file: # + # description: validate uniqueness of {{attributes}} + # optionals: + # scope: + # positive: scoped to {{inspect}} + # case_sensitive: + # positive: case sensitive + # negative: case insensitive + # + # When invoked like below will generate the following messages: + # + # validate_uniqueness_of :project_id, :scope => :company_id + # #=> "validate uniqueness of project_id scoped to :company_id" + # + # validate_uniqueness_of :project_id, :scope => :company_id, :case_sensitive => true + # #=> "validate uniqueness of project_id scoped to :company_id and case sensitive" + # + # validate_uniqueness_of :project_id, :scope => :company_id, :case_sensitive => false + # #=> "validate uniqueness of project_id scoped to :company_id and case insensitive" + # # == Interpolation options - # + # # The default interpolation options available are "inspect" and "value". Whenever # you use :splat => true, it also adds a new interpolation option called {{sentence}}. # # Given the following matcher call: # @@ -85,128 +85,128 @@ # positive: scoped to {{value}} # # Outputs: "validate uniqueness of project_id scoped to company_idproject_id" # # positive: scoped to {{value}} # # Outputs: "validate uniqueness of project_id scoped to company_id and project_id" - # - # == Interpolation keys # + # == Interpolation keys + # # Three keys are available to be used in I18n files and control how optionals - # are appended to your description: - # - # * <tt>positive</tt> - When the optional is given and it evaluates to true (everything but false and nil). - # * <tt>negative</tt> - When the optional is given and it evaluates to false (false or nil). - # * <tt>not_given</tt> - When the optional is not given. - # - module Optionals - - OPTIONAL_KEYS = [ :positive, :negative, :not_given ] - - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - - protected - - # Creates optional handlers for matchers dynamically. - # - # == Options - # - # * <tt>:default</tt> - The default value for this optional - # * <tt>:alias</tt> - An alias for this optional + # are appended to your description: + # + # * <tt>positive</tt> - When the optional is given and it evaluates to true (everything but false and nil). + # * <tt>negative</tt> - When the optional is given and it evaluates to false (false or nil). + # * <tt>not_given</tt> - When the optional is not given. + # + module Optionals + + OPTIONAL_KEYS = [ :positive, :negative, :not_given ] + + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + + protected + + # Creates optional handlers for matchers dynamically. + # + # == Options + # + # * <tt>:default</tt> - The default value for this optional + # * <tt>:alias</tt> - An alias for this optional # * <tt>:splat</tt> - Should be true if you expects multiple arguments - # * <tt>:block</tt> - Tell this optional can also receive blocks - # - # == Examples - # - # class AllowValuesForMatcher < Remarkable::ActiveRecord::Base - # arguments :collection => :attributes, :as => :attribute - # + # * <tt>:block</tt> - Tell this optional can also receive blocks + # + # == Examples + # + # class AllowValuesForMatcher < Remarkable::ActiveRecord::Base + # arguments :collection => :attributes, :as => :attribute + # # optional :message - # optional :in, :splat => true + # optional :in, :splat => true # optional :allow_nil, :allow_blank, :default => true # end - # - def optionals(*names) + # + def optionals(*names) options = names.extract_options! - + @matcher_optionals += names default = options[:default] ? "=#{options[:default].inspect}" : nil block = if options[:block] @matcher_optionals_block += names default ||= "=nil" ', &block' else nil - end - + end + splat = if options[:splat] @matcher_optionals_splat += names '*' else nil end - - names.each do |name| - class_eval <<-END, __FILE__, __LINE__ - def #{name}(#{splat}value#{default}#{block}) + + names.each do |name| + class_eval <<-END, __FILE__, __LINE__ + def #{name}(#{splat}value#{default}#{block}) @options ||= {} - #{"@options[:#{name}] ||= []" if splat} - @options[:#{name}] #{:+ if splat}= #{"block ||" if block} value - self + #{"@options[:#{name}] ||= []" if splat} + @options[:#{name}] #{:+ if splat}= #{"block ||" if block} value + self end def #{name}=(value) @options ||= {} - @options[:#{name}] = value + @options[:#{name}] = value self - end - END + end + END end - + class_eval %{ alias :#{options[:alias]} :#{names.last} alias :#{options[:alias]}= :#{names.last}= - } if options[:alias] - - # Call unique to avoid duplicate optionals. - @matcher_optionals.uniq! - end - alias :optional :optionals - - # Instead of appending, prepend optionals to the beginning of optionals + } if options[:alias] + + # Call unique to avoid duplicate optionals. + @matcher_optionals.uniq! + end + alias :optional :optionals + + # Instead of appending, prepend optionals to the beginning of optionals # array. This is important because the optionals declaration order - # changes how the description message is generated. - # - def prepend_optionals(*names) - current_optionals = @matcher_optionals.dup - @matcher_optionals = [] - optional(*names) - @matcher_optionals += current_optionals - @matcher_optionals.uniq! - end - alias :prepend_optional :prepend_optionals - - end - - # Overwrites description to support optionals. Check <tt>optional</tt> for - # more information. - # - def description(options={}) #:nodoc: - message = super(options) - message.strip! - - optionals = self.class.matcher_optionals.map do |optional| - if @options.key?(optional) + # changes how the description message is generated. + # + def prepend_optionals(*names) + current_optionals = @matcher_optionals.dup + @matcher_optionals = [] + optional(*names) + @matcher_optionals += current_optionals + @matcher_optionals.uniq! + end + alias :prepend_optional :prepend_optionals + + end + + # Overwrites description to support optionals. Check <tt>optional</tt> for + # more information. + # + def description(options={}) #:nodoc: + message = super(options) + message.strip! + + optionals = self.class.matcher_optionals.map do |optional| + if @options.key?(optional) value = @options[optional] - + defaults = [ (value ? :positive : :negative) ] - # If optional is a symbol and it's not any to any of the reserved symbols, search for it also + # If optional is a symbol and it's not any to any of the reserved symbols, search for it also defaults.unshift(value) if value.is_a?(Symbol) && !OPTIONAL_KEYS.include?(value) - defaults << '' + defaults << '' options = { :default => defaults, :inspect => value.inspect, :value => value.to_s } if self.class.matcher_optionals_splat.include?(optional) value = [ value ] unless Array === value @@ -214,16 +214,16 @@ end translate_optionals_with_namespace(optional, defaults.shift, options) else translate_optionals_with_namespace(optional, :not_given, :default => '') - end - end.compact - - message << ' ' << array_to_sentence(optionals) - message.strip! - message + end + end.compact + + message << ' ' << array_to_sentence(optionals) + message.strip! + message end def translate_optionals_with_namespace(optional, key, options={}) #:nodoc: scope = "#{matcher_i18n_scope}.optionals.#{optional}" @@ -234,10 +234,10 @@ parent_scope.delete_at(-3) translation = Remarkable.t key, options.merge!(:scope => parent_scope) return translation unless translation.empty? nil - end - - end - end -end + end + + end + end +end