README in remarkable-3.0.0 vs README in remarkable-3.0.1

- old
+ new

@@ -1,2 +1,199 @@ -Remarkable -========== += Remarkable + +This is the core package of Remarkable. It provides a DSL for creating matchers +with I18n support, decoupling messages from matcher's logic and adding rspec +extra features. + +== Macros + +Each matcher in Remarkable is also available as a macro. So this matcher: + + it { should validate_presence_of(:name) } + +Can also be written as: + + should_validate_presence_of :name + +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!