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!