Module: Remarkable::DSL::Optionals

Defined in:
lib/remarkable/dsl/optionals.rb

Overview

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

  optional :message
  optional :in, :splat => true
  optional :allow_nil, :allow_blank, :default => true
end

This allow the matcher to be called as:

it { should allow_values_for(:email).in("[email protected]", "[email protected]").message(:invalid).allow_nil }

It also allow macros to be configured with blocks:

should_allow_values_for :email do |m|
  m.message :invalid
  m.allow_nil
  m.in "[email protected]"
  m.in "[email protected]"
end

Which could be also writen as:

should_allow_values_for :email do |m|
  m.message = :invalid
  m.allow_nil = true
  m.in = [ "[email protected]", "[email protected]" ]
end

The difference between the them are: 1) optional= always require an argument even if :default is given. 2) optional= always overwrite all previous values even if :splat is given.

Blocks can be also given when :block => true is set:

should_set_session :user_id do |m|
  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"

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:

validate_uniqueness_of :id, :scope => [ :company_id, :project_id ]

The following yml setting and outputs are:

scope:
  positive: scoped to {{inspect}}
  # Outputs: "validate uniqueness of project_id scoped to [ :company_id, :project_id ]"

  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

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.

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

OPTIONAL_KEYS =
[ :positive, :negative, :not_given ]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:



104
105
106
# File 'lib/remarkable/dsl/optionals.rb', line 104

def self.included(base) #:nodoc:
  base.extend ClassMethods
end

Instance Method Details

#description(options = {}) ⇒ Object

Overwrites description to support optionals. Check optional for more information.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/remarkable/dsl/optionals.rb', line 196

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
      defaults.unshift(value) if value.is_a?(Symbol) && !OPTIONAL_KEYS.include?(value)
      defaults << ''

      options = { :default => defaults, :inspect => value.inspect, :value => value.to_s }

      if self.class.matcher_optionals_splat.include?(optional)
        value = [ value ] unless Array === value
        options[:sentence] = array_to_sentence(value, true)
      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

#translate_optionals_with_namespace(optional, key, options = {}) ⇒ Object

:nodoc:



227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/remarkable/dsl/optionals.rb', line 227

def translate_optionals_with_namespace(optional, key, options={}) #:nodoc:
  scope = "#{matcher_i18n_scope}.optionals.#{optional}"

  translation = Remarkable.t key, options.merge!(:scope => scope)
  return translation unless translation.empty?

  parent_scope = scope.split('.')
  parent_scope.delete_at(-3)
  translation = Remarkable.t key, options.merge!(:scope => parent_scope)
  return translation unless translation.empty?

  nil
end