public
Description: Simplifying tests!
Homepage: http://www.nomedojogo.com/category/remarkable/
Clone URL: git://github.com/carlosbrando/remarkable.git
Click here to lend your support to: remarkable and make a donation at www.pledgie.com !
carlosbrando (author)
Thu Oct 29 13:32:03 -0700 2009
commit  e80ffb556c934f9806623898ad59684f1e8ee53d
tree    a3f090e58720b3b73ecee0ffdada76a74796b73e
parent  0ed320c910719926d007e04e811fabc16933085b
remarkable / remarkable
name age message
..
file CHANGELOG Thu May 28 00:51:16 -0700 2009 Updated CHANGELOG. [josevalim]
file LICENSE Thu Apr 09 03:46:57 -0700 2009 Added LICENSE files. [josevalim]
file README Sat Apr 25 04:42:30 -0700 2009 Ensure that optionals that can accept procs als... [josevalim]
file Rakefile Sun May 24 06:16:32 -0700 2009 Ruby 1.9 compatibility Signed-off-by: José Val... [iain]
directory lib/ Thu Oct 01 07:12:23 -0700 2009 Bump to 3.1.11 [carlosbrando]
directory locale/ Wed Mar 18 05:35:15 -0700 2009 More compatibility with rspec 1.2.0 [josevalim]
directory spec/ Thu Jul 16 10:25:18 -0700 2009 Allow allow_values_for and allow_mass_assignmen... [josevalim]
remarkable/README
= Remarkable

This is the core package of Remarkable. It provides a DSL for creating matchers
with I18n support, decouples messages from matchers logic, add rspec extra features,
create macros automatically and allow those macros to be configurable wth blocks.

== Macros

Each matcher in Remarkable is also available as a macro. So this matcher:

  it { should validate_numericality_of(:age, :greater_than => 18, :only_integer => true) }
  it { should validate_numericality_of(:age).greater_than(18).only_integer }

Can also be written as:

  should_validate_numericality_of :age, :greater_than => 18, :only_integer => true

Which can be also written as:

  should_validate_numericality_of :age do |m|
    m.only_integer
    m.greater_than 18
    # Or: m.greater_than = 18
  end

Choose your style!

== Disabled Macros

Remarkable adds the possibility to disable macros. So as you could do:

  xit { should validate_presence_of(:name) }

You can also do:

  xshould_validate_presence_of :name

And it will show in your specs output:

  "Example disabled: require name to be set"

== Pending macros

In Rspec you can mark some examples as pending:

  it "should have one manager" do
    pending("create managers resource")
  end

  it "should validate associated manager" do
    pending("create managers resource")
  end

To allow this to work with macros, we created the pending group:

  pending "create managers resource" do
    should_have_one :manager
    should_validate_associated :manager
  end

This outputs the same as above.

== I18n

All matchers come with I18n support. You can find an example locale file under
the locale folder of each project.

To change the locale, you have first to add your locale file:

  Remarkable.add_locale 'path/to/my_locale.yml'

And then:

  Remarkable.locale = :my_locale

Internationalization is powered by the I18n gem. If you are using it with Rails,
it will use the built in gem, otherwise you will have to install the gem by hand:

  gem sources -a http://gems.github.com
  sudo gem install svenfuchs-i18n

== Creating you own matcher

Create a new matcher is easy. Let's create validate_inclusion_of matcher for
ActiveRecord as an example. A first matcher version would be:

module Remarkable
  module ActiveRecord
    module Matchers
      class ValidateInclusionOfMatcher < Remarkable::ActiveRecord::Base
        arguments :attribute
        assertion :is_valid?

        optional :in
        optional :allow_blank, :allow_nil, :default => true

        protected

          def is_valid?
            @options[:in].each do |value|
              @subject.send(:"#{@attribute}=", value)
              return false, :value => value unless @subject.valid?
            end
            true
          end
      end

      def validate_inclusion_of(*args)
        ValidateInclusionOfMatcher.new(*args).spec(self)
      end
    end
  end
end

This creates a matcher which requires one attribute and has :in, :allow_blank
and :allow_nil as options. So you can call the matcher in the following way:

  should_validate_inclusion_of :size, :in => %w(S M L XL)
  should_validate_inclusion_of :size, :in => %w(S M L XL), :allow_blank => true

  it { should validate_inclusion_of(:size, :in => %w(S M L XL)).allow_nil(true) }
  it { should validate_inclusion_of(:size, :in => %w(S M L XL)).allow_nil }

  it { should validate_inclusion_of(:size, :in => %w(S M L XL)) }
  it { should validate_inclusion_of(:size, :in => %w(S M L XL), :allow_nil => true) }

The assertions methods (in this case, :is_valid?) makes the matcher pass when
it returns true and fail when returns false.

As you noticed, the matcher doesn't have any message on it. You add them on I18n
file. A file for this example would be:

  remarkable:
    active_record:
      validate_inclusion_of:
        description: "validate inclusion of {{attribute}}"
        expectations:
          is_valid: "to be valid when {{attribute}} is {{value}}"
        optionals:
          in:
            positive: "in {{inspect}}"
          allow_nil:
            positive: "allowing nil values"
            negative: "not allowing nil values"
          allow_blank:
            positive: "allowing blank values"
            negative: "allowing blank values"

The optionals are just added to the description message when they are supplied.
Look some description messages examples:

  should_validate_inclusion_of :size, :in => %w(S M L XL)
  #=> should validate inclusion of size in ["S", "M", "L", "XL"]

  should_validate_inclusion_of :size, :in => %w(S M L XL), :allow_nil => true
  #=> should validate inclusion of size in ["S", "M", "L", "XL"] and allowing nil values

  should_validate_inclusion_of :size, :in => %w(S M L XL), :allow_nil => false
  #=> should validate inclusion of size in ["S", "M", "L", "XL"] and not allowing nil values

Please notice that the arguments are available as interpolation option, as well
as the optionals.

The expectations message are set whenever one of the assertions returns false.
In this case, whenever the assertion fails, we are also returning a hash, with
the value that failed:

  return false, :value => value

This will tell remarkable to make value as interpolation option too.

Whenever you create all your matchers, you tell remarkable to add them to the
desired rspec example group:

  Remarkable.include_matchers!(Remarkable::ActiveRecord, Spec::Example::ExampleGroup)

== Working with collections

Finally, Remarkable also makes easy to deal with collections. The same matcher
could be easily extended to accept a collection of attributes instead of just one:

    should_validate_inclusion_of :first_size, :second_size, :in => %w(S M L XL)

For this we have just those two lines:

    arguments :attribute
    assertion :is_valid?

For:

    arguments :collection => :attributes, :as => :attribute
    collection_assertion :is_valid?

This means that the collection will be kept in the @attributes instance variable
and for each value in the collection, it will run the :is_valid? assertion.

Whenever running the assertion, it will also set the @attribute (in singular)
variable. In your I18n files, you just need to change your description:

      validate_inclusion_of:
        description: "validate inclusion of {{attributes}}"

And this will output:

  should_validate_inclusion_of :first_size, :second_size, :in => %w(S M L XL)
  #=> should validate inclusion of first size and second size in ["S", "M", "L", "XL"]

== More

This is just an overview of the API. You can add extra options to interpolation
by overwriting the interpolation_options methods, you can add callbacks after
initialize your matcher or before asserting and much more!