Module Remarkable::DSL::Assertions::ClassMethods
In: lib/remarkable/dsl/assertions.rb

Methods

Protected Instance methods

It sets the arguments your matcher receives on initialization.

Options

  • :collection - if a collection is expected.
  • :as - how each item of the collection will be available.
  • :block - tell the matcher can receive blocks as argument and store
                      them under the variable given.
    

Note: the expected block cannot have arity 1. This is already reserved for macro configuration.

Examples

Let‘s see for each example how the arguments declarion reflects on the matcher API:

  arguments :assign
  # Can be called as:
  #=> should_assign :task
  #=> should_assign :task, :with => Task.new

This is roughly the same as:

  def initialize(assign, options = {})
    @assign   = name
    @options = options
  end

As you noticed, a matcher can always receive options on initialization. If you have a matcher that accepts only options, for example, have_default_scope you just need to call arguments:

  arguments
  # Can be called as:
  #=> should_have_default_scope :limit => 10

  arguments :collection => :assigns, :as => :assign
  # Can be called as:
  #=> should_assign :task1, :task2
  #=> should_assign :task1, :task2, :with => Task.new

  arguments :collection => :assigns, :as => :assign, :block => :buildeer
  # Can be called as:
  #=> should_assign :task1, :task2
  #=> should_assign(:task1, :task2){ Task.new }

The block will be available under the instance variable @builder.

I18n

All the parameters given to arguments are available for interpolation in I18n. So if you have the following declarion:

  class InRange < Remarkable::Base
    arguments :range, :collection => :names, :as => :name

You will have {{range}}, {{names}} and {{name}} available for I18n messages:

  in_range:
    description: "have {{names}} to be on range {{range}}"

Before a collection is sent to I18n, it‘s transformed to a sentence. So if the following matcher:

  in_range(2..20, :username, :password)

Has the following description:

  "should have username and password in range 2..20"

[Source]

     # File lib/remarkable/dsl/assertions.rb, line 119
119:           def arguments(*names)
120:             options = names.extract_options!
121:             args = names.dup
122: 
123:             @matcher_arguments[:names] = names
124: 
125:             if collection = options.delete(:collection)
126:               @matcher_arguments[:collection] = collection
127: 
128:               if options[:as]
129:                 @matcher_arguments[:as] = options.delete(:as)
130:               else
131:                 raise ArgumentError, 'You gave me :collection as option but have not give me :as as well'
132:               end
133: 
134:               args          << "*#{collection}"
135:               get_options    = "#{collection}.extract_options!"
136:               set_collection = "@#{collection} = #{collection}"
137:             else
138:               args          << 'options={}'
139:               get_options    = 'options'
140:               set_collection = ''
141:             end
142: 
143:             if block = options.delete(:block)
144:               block = :block unless block.is_a?(Symbol)
145:               @matcher_arguments[:block] = block
146:             end
147: 
148:             # Blocks are always appended. If they have arity 1, they are used for

149:             # macro configuration, otherwise, they are stored in the :block variable.

150:             #

151:             args << "&block"
152: 
153:             assignments = names.map do |name|
154:               "@#{name} = #{name}"
155:             end.join("\n  ")
156: 
157:             class_eval "def initialize(\#{args.join(',')})\n_builder, block = block, nil if block && block.arity == 1\n\#{assignments}\n\#{\"@\#{block} = block\" if block}\n@options = default_options.merge(\#{get_options})\n\#{set_collection}\nrun_after_initialize_callbacks\n_builder.call(self) if _builder\nend\n", __FILE__, __LINE__
158:           end
assertion(*methods, &block)

Alias for assertions

Declares the assertions that are run once per matcher.

Examples

A matcher that checks if an element is included in an array can be done just with:

  class IncludedMatcher < Remarkable::Base
    arguments :value
    assertion :is_included?

    protected
      def is_included?
        @subject.include?(@value)
      end
  end

Whenever the matcher is called, the :is_included? action is automatically triggered. Each assertion must return true or false. In case it‘s false it will seach for an expectation message on the I18n file. In this case, the error message would be on:

  included:
    description: "check {{value}} is included in the array"
    expectations:
      is_included: "{{value}} is included in the array"

In case of failure, it will output:

  "Expected {{value}} is included in the array"

Notice that on the yml file the question mark is removed for readability.

Shortcut declaration

You can shortcut declaration by giving a name and block to assertion method:

  class IncludedMatcher < Remarkable::Base
    arguments :value

    assertion :is_included? do
      @subject.include?(@value)
    end
  end

[Source]

     # File lib/remarkable/dsl/assertions.rb, line 243
243:           def assertions(*methods, &block)
244:             if block_given?
245:               define_method methods.last, &block
246:               protected methods.last
247:             end
248: 
249:             @matcher_single_assertions += methods
250:           end
collection_assertion(*methods, &block)

Declare the assertions that are runned for each element in the collection. It must be used with arguments methods in order to work properly.

Examples

The example given in assertions can be transformed to accept a collection just doing:

  class IncludedMatcher < Remarkable::Base
    arguments :collection => :values, :as => :value
    collection_assertion :is_included?

    protected
      def is_included?
        @subject.include?(@value)
      end
  end

All further consideration done in assertions are also valid here.

[Source]

     # File lib/remarkable/dsl/assertions.rb, line 191
191:           def collection_assertions(*methods, &block)
192:             define_method methods.last, &block if block_given?
193:             @matcher_collection_assertions += methods
194:           end

Class method that accepts a block or a hash to set matcher‘s default options. It‘s called on matcher initialization and stores the default value in the @options instance variable.

Examples

  default_options do
    { :name => @subject.name }
  end

  default_options :message => :invalid

[Source]

     # File lib/remarkable/dsl/assertions.rb, line 265
265:           def default_options(hash = {}, &block)
266:             if block_given?
267:               define_method :default_options, &block
268:             else
269:               class_eval "def default_options; #{hash.inspect}; end"
270:             end
271:           end

[Validate]