remarkable 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,2 +1,199 @@
1
- Remarkable
2
- ==========
1
+ = Remarkable
2
+
3
+ This is the core package of Remarkable. It provides a DSL for creating matchers
4
+ with I18n support, decoupling messages from matcher's logic and adding rspec
5
+ extra features.
6
+
7
+ == Macros
8
+
9
+ Each matcher in Remarkable is also available as a macro. So this matcher:
10
+
11
+ it { should validate_presence_of(:name) }
12
+
13
+ Can also be written as:
14
+
15
+ should_validate_presence_of :name
16
+
17
+ Remarkable adds the possibility to disable macros. So as you could do:
18
+
19
+ xit { should validate_presence_of(:name) }
20
+
21
+ You can also do:
22
+
23
+ xshould_validate_presence_of :name
24
+
25
+ And it will show in your specs output:
26
+
27
+ "Example disabled: require name to be set"
28
+
29
+ == Pending macros
30
+
31
+ In Rspec you can mark some examples as pending:
32
+
33
+ it "should have one manager" do
34
+ pending("create managers resource")
35
+ end
36
+
37
+ it "should validate associated manager" do
38
+ pending("create managers resource")
39
+ end
40
+
41
+ To allow this to work with macros, we created the pending group:
42
+
43
+ pending "create managers resource" do
44
+ should_have_one :manager
45
+ should_validate_associated :manager
46
+ end
47
+
48
+ This outputs the same as above.
49
+
50
+ == I18n
51
+
52
+ All matchers come with I18n support. You can find an example locale file under
53
+ the locale folder of each project.
54
+
55
+ To change the locale, you have first to add your locale file:
56
+
57
+ Remarkable.add_locale 'path/to/my_locale.yml'
58
+
59
+ And then:
60
+
61
+ Remarkable.locale = :my_locale
62
+
63
+ Internationalization is powered by the I18n gem. If you are using it with Rails,
64
+ it will use the built in gem, otherwise you will have to install the gem by hand:
65
+
66
+ gem sources -a http://gems.github.com
67
+ sudo gem install svenfuchs-i18n
68
+
69
+ == Creating you own matcher
70
+
71
+ Create a new matcher is easy. Let's create validate_inclusion_of matcher for
72
+ ActiveRecord as an example. A first matcher version would be:
73
+
74
+ module Remarkable
75
+ module ActiveRecord
76
+ module Matchers
77
+ class ValidateInclusionOfMatcher < Remarkable::ActiveRecord::Base
78
+ arguments :attribute
79
+ assertion :is_valid?
80
+
81
+ optional :in
82
+ optional :allow_blank, :allow_nil, :default => true
83
+
84
+ protected
85
+
86
+ def is_valid?
87
+ @options[:in].each do |value|
88
+ @subject.send(:"#{@attribute}=", value)
89
+ return false, :value => value unless @subject.valid?
90
+ end
91
+ true
92
+ end
93
+ end
94
+
95
+ def validate_inclusion_of(*args)
96
+ ValidateInclusionOfMatcher.new(*args).spec(self)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ This creates a matcher which requires one attribute and has :in, :allow_blank
103
+ and :allow_nil as options. So you can call the matcher in the following way:
104
+
105
+ should_validate_inclusion_of :size, :in => %w(S M L XL)
106
+ should_validate_inclusion_of :size, :in => %w(S M L XL), :allow_blank => true
107
+
108
+ it { should validate_inclusion_of(:size, :in => %w(S M L XL)).allow_nil(true) }
109
+ it { should validate_inclusion_of(:size, :in => %w(S M L XL)).allow_nil }
110
+
111
+ it { should validate_inclusion_of(:size, :in => %w(S M L XL)) }
112
+ it { should validate_inclusion_of(:size, :in => %w(S M L XL), :allow_nil => true) }
113
+
114
+ The assertions methods (in this case, :is_valid?) makes the matcher pass when
115
+ it returns true and fail when returns false.
116
+
117
+ As you noticed, the matcher doesn't have any message on it. You add them on I18n
118
+ file. A file for this example would be:
119
+
120
+ remarkable:
121
+ active_record:
122
+ validate_inclusion_of:
123
+ description: "validate inclusion of {{attribute}}"
124
+ expectations:
125
+ is_valid: "to be valid when {{attribute}} is {{value}}"
126
+ optionals:
127
+ in:
128
+ positive: "in {{inspect}}"
129
+ allow_nil:
130
+ positive: "allowing nil values"
131
+ negative: "not allowing nil values"
132
+ allow_blank:
133
+ positive: "allowing blank values"
134
+ negative: "allowing blank values"
135
+
136
+ The optionals are just added to the description message when they are supplied.
137
+ Look some description messages examples:
138
+
139
+ should_validate_inclusion_of :size, :in => %w(S M L XL)
140
+ #=> should validate inclusion of size in ["S", "M", "L", "XL"]
141
+
142
+ should_validate_inclusion_of :size, :in => %w(S M L XL), :allow_nil => true
143
+ #=> should validate inclusion of size in ["S", "M", "L", "XL"] and allowing nil values
144
+
145
+ should_validate_inclusion_of :size, :in => %w(S M L XL), :allow_nil => false
146
+ #=> should validate inclusion of size in ["S", "M", "L", "XL"] and not allowing nil values
147
+
148
+ Please notice that the arguments are available as interpolation option, as well
149
+ as the optionals.
150
+
151
+ The expectations message are set whenever one of the assertions returns false.
152
+ In this case, whenever the assertion fails, we are also returning a hash, with
153
+ the value that failed:
154
+
155
+ return false, :value => value
156
+
157
+ This will tell remarkable to make value as interpolation option too.
158
+
159
+ Whenever you create all your matchers, you tell remarkable to add them to the
160
+ desired rspec example group:
161
+
162
+ Remarkable.include_matchers!(Remarkable::ActiveRecord, Spec::Example::ExampleGroup)
163
+
164
+ == Working with collections
165
+
166
+ Finally, Remarkable also makes easy to deal with collections. The same matcher
167
+ could be easily extended to accept a collection of attributes instead of just one:
168
+
169
+ should_validate_inclusion_of :first_size, :second_size, :in => %w(S M L XL)
170
+
171
+ For this we have just those two lines:
172
+
173
+ arguments :attribute
174
+ assertion :is_valid?
175
+
176
+ For:
177
+
178
+ arguments :collection => :attributes, :as => :attribute
179
+ collection_assertion :is_valid?
180
+
181
+ This means that the collection will be kept in the @attributes instance variable
182
+ and for each value in the collection, it will run the :is_valid? assertion.
183
+
184
+ Whenever running the assertion, it will also set the @attribute (in singular)
185
+ variable. In your I18n files, you just need to change your description:
186
+
187
+ validate_inclusion_of:
188
+ description: "validate inclusion of {{attributes}}"
189
+
190
+ And this will output:
191
+
192
+ should_validate_inclusion_of :first_size, :second_size, :in => %w(S M L XL)
193
+ #=> should validate inclusion of first size and second size in ["S", "M", "L", "XL"]
194
+
195
+ == More
196
+
197
+ This is just an overview of the API. You can add extra options to interpolation
198
+ by overwriting the interpolation_options methods, you can add callbacks after
199
+ initialize your matcher or before asserting and much more!
data/lib/remarkable.rb CHANGED
@@ -11,10 +11,8 @@ require File.join(dir, 'remarkable', 'macros')
11
11
  require File.join(dir, 'remarkable', 'pending')
12
12
  require File.join(dir, 'remarkable', 'core_ext', 'array')
13
13
 
14
- # Loads rspec files only if spec is defined
15
14
  if defined?(Spec)
16
15
  require File.join(dir, 'remarkable', 'rspec')
17
16
  end
18
17
 
19
- # Add Remarkable default locale file
20
18
  Remarkable.add_locale File.join(dir, '..', 'locale', 'en.yml')
@@ -2,15 +2,16 @@ module Remarkable
2
2
  class Base
3
3
  include Remarkable::Messages
4
4
  extend Remarkable::DSL
5
-
5
+
6
+ # Optional to provide spec binding to matchers.
6
7
  def spec(binding)
7
8
  @spec = binding
8
9
  self
9
10
  end
10
11
 
11
- private
12
+ protected
12
13
 
13
- # Returns the subject class if it's not one.
14
+ # Returns the subject class unless it's a class object.
14
15
  def subject_class
15
16
  nil unless @subject
16
17
  @subject.is_a?(Class) ? @subject : @subject.class
@@ -25,7 +26,7 @@ module Remarkable
25
26
 
26
27
  # Iterates over the collection given yielding the block and return false
27
28
  # if any of them also returns false.
28
- def assert_matcher_for(collection)
29
+ def assert_matcher_for(collection) #:nodoc:
29
30
  collection.each do |item|
30
31
  return false unless yield(item)
31
32
  end
@@ -39,7 +40,7 @@ module Remarkable
39
40
  # assert_contains(['a', '1'], 'a') => passes
40
41
  # assert_contains(['a', '1'], /not there/) => fails
41
42
  #
42
- def assert_contains(collection, x) # :nodoc:
43
+ def assert_contains(collection, x)
43
44
  collection = [collection] unless collection.is_a?(Array)
44
45
 
45
46
  case x
@@ -10,20 +10,19 @@ module Remarkable
10
10
  :matcher_collection_assertions, :before_assert_callbacks, :after_initialize_callbacks
11
11
  ] unless self.const_defined?(:ATTR_READERS)
12
12
 
13
- def self.extended(base)
14
- # Load modules
13
+ def self.extended(base) #:nodoc:
15
14
  base.extend Assertions
16
15
  base.send :include, Callbacks
17
16
  base.send :include, Matches
18
17
  base.send :include, Optionals
19
18
 
20
- # Set the default value for matcher_arguments
19
+ # Initialize matcher_arguments hash with names as an empty array
21
20
  base.instance_variable_set('@matcher_arguments', { :names => [] })
22
21
  end
23
22
 
24
23
  # Make Remarkable::Base DSL inheritable.
25
24
  #
26
- def inherited(base)
25
+ def inherited(base) #:nodoc:
27
26
  base.class_eval do
28
27
  class << self
29
28
  attr_reader *ATTR_READERS
@@ -24,7 +24,7 @@ module Remarkable
24
24
  # validate_presence_of is a matcher declared as:
25
25
  #
26
26
  # class ValidatePresenceOfMatcher < Remarkable::Base
27
- # arguments :collection => :attributes
27
+ # arguments :collection => :attributes, :as => :attribute
28
28
  # end
29
29
  #
30
30
  # In this case, Remarkable provides an API that enables you to easily
@@ -107,7 +107,7 @@ END
107
107
  # For example, validate_presence_of can be written as:
108
108
  #
109
109
  # class ValidatePresenceOfMatcher < Remarkable::Base
110
- # arguments :collection => :attributes
110
+ # arguments :collection => :attributes, :as => :attribute
111
111
  # collection_assertions :allow_nil?
112
112
  #
113
113
  # protected
@@ -168,8 +168,7 @@ END
168
168
  end
169
169
  alias :assertion :assertions
170
170
 
171
- # Class method that accepts a block or a Hash that will overwrite
172
- # instance method default_options.
171
+ # Class method that accepts a block or a hash to set matcher's default options.
173
172
  #
174
173
  def default_options(hash = {}, &block)
175
174
  if block_given?
@@ -2,13 +2,13 @@ module Remarkable
2
2
  module DSL
3
3
  module Callbacks
4
4
 
5
- def self.included(base)
5
+ def self.included(base) #:nodoc:
6
6
  base.extend ClassMethods
7
7
  end
8
8
 
9
9
  module ClassMethods
10
10
  protected
11
- # Class method that accepts a block which is called after initialization.
11
+ # Class method that accepts a block or a symbol which is called after initialization.
12
12
  #
13
13
  def after_initialize(symbol=nil, &block)
14
14
  if block_given?
@@ -18,7 +18,7 @@ module Remarkable
18
18
  end
19
19
  end
20
20
 
21
- # Class method that accepts a block which is called before assertion.
21
+ # Class method that accepts a block or a symbol which is called before assertion.
22
22
  #
23
23
  def before_assert(symbol=nil, &block)
24
24
  if block_given?
@@ -29,7 +29,7 @@ module Remarkable
29
29
  end
30
30
  end
31
31
 
32
- def run_after_initialize_callbacks
32
+ def run_after_initialize_callbacks #:nodoc:
33
33
  self.class.after_initialize_callbacks.each do |method|
34
34
  if method.is_a?(Proc)
35
35
  instance_eval &method
@@ -39,7 +39,7 @@ module Remarkable
39
39
  end
40
40
  end
41
41
 
42
- def run_before_assert_callbacks
42
+ def run_before_assert_callbacks #:nodoc:
43
43
  self.class.before_assert_callbacks.each do |method|
44
44
  if method.is_a?(Proc)
45
45
  instance_eval &method
@@ -3,7 +3,7 @@ module Remarkable
3
3
  module Matches
4
4
 
5
5
  # For each instance under the collection declared in <tt>arguments</tt>,
6
- # this method will call each method declared in <tt>assertions</tt>.
6
+ # this method will call each method declared in <tt>collection_assertions</tt>.
7
7
  #
8
8
  # As an example, let's assume you have the following matcher:
9
9
  #
@@ -49,14 +49,8 @@ module Remarkable
49
49
  {}
50
50
  end
51
51
 
52
- # Overwrites default_i18n_options to provide collection interpolation,
53
- # arguments and optionals to interpolation options.
54
- #
55
- # Their are appended in the reverse order above. So if you have an optional
56
- # with the same name as an argument, the argument overwrites the optional.
57
- #
58
- # All values are provided calling inspect, so what you will have in your
59
- # I18n available for interpolation is @options[:allow_nil].inspect.
52
+ # Overwrites default_i18n_options to provide arguments and optionals
53
+ # to interpolation options.
60
54
  #
61
55
  # If you still need to provide more other interpolation options, you can
62
56
  # do that in two ways:
@@ -76,7 +70,7 @@ module Remarkable
76
70
  #
77
71
  # In both cases, :real_value will be available as interpolation option.
78
72
  #
79
- def default_i18n_options
73
+ def default_i18n_options #:nodoc:
80
74
  i18n_options = {}
81
75
 
82
76
  @options.each do |key, value|
@@ -95,13 +89,11 @@ module Remarkable
95
89
  i18n_options.update(super)
96
90
  end
97
91
 
98
- # Methods that return collection_name and object_name as a Hash for
99
- # interpolation.
92
+ # Method responsible to add collection as interpolation.
100
93
  #
101
- def collection_interpolation
94
+ def collection_interpolation #:nodoc:
102
95
  options = {}
103
96
 
104
- # Add collection to options
105
97
  if collection_name = self.class.matcher_arguments[:collection]
106
98
  collection_name = collection_name.to_sym
107
99
  collection = instance_variable_get("@#{collection_name}")
@@ -115,15 +107,15 @@ module Remarkable
115
107
  options
116
108
  end
117
109
 
118
- # Helper that send the methods given and create a expectation message if
119
- # any returns false.
110
+ # Send the assertion methods given and create a expectation message
111
+ # if any of those methods returns false.
120
112
  #
121
113
  # Since most assertion methods ends with an question mark and it's not
122
114
  # readable in yml files, we remove question and exclation marks at the
123
115
  # end of the method name before translating it. So if you have a method
124
116
  # called is_valid? on I18n yml file we will check for a key :is_valid.
125
117
  #
126
- def send_methods_and_generate_message(methods)
118
+ def send_methods_and_generate_message(methods) #:nodoc:
127
119
  methods.each do |method|
128
120
  bool, hash = send(method)
129
121
 
@@ -4,7 +4,7 @@ module Remarkable
4
4
 
5
5
  OPTIONAL_KEYS = [ :positive, :negative, :not_given ]
6
6
 
7
- def self.included(base)
7
+ def self.included(base) #:nodoc:
8
8
  base.extend ClassMethods
9
9
  end
10
10
 
@@ -111,7 +111,7 @@ module Remarkable
111
111
  # Overwrites description to support optionals. Check <tt>optional</tt> for
112
112
  # more information.
113
113
  #
114
- def description(options={})
114
+ def description(options={}) #:nodoc:
115
115
  message = super(options)
116
116
  message.strip!
117
117
 
@@ -3,7 +3,7 @@ module Remarkable
3
3
 
4
4
  protected
5
5
 
6
- def method_missing(method_id, *args, &block)
6
+ def method_missing(method_id, *args, &block) #:nodoc:
7
7
  if method_id.to_s =~ /^(should_not|should)_(.+)/
8
8
  should_or_should_not_method_missing($1, $2, caller, *args, &block)
9
9
  elsif method_id.to_s =~ /^x(should_not|should)_(.+)/
@@ -13,7 +13,7 @@ module Remarkable
13
13
  end
14
14
  end
15
15
 
16
- def should_or_should_not_method_missing(should_or_should_not, method, calltrace, *args, &block)
16
+ def should_or_should_not_method_missing(should_or_should_not, method, calltrace, *args, &block) #:nodoc:
17
17
  it {
18
18
  begin
19
19
  send(should_or_should_not, send(method, *args, &block))
@@ -24,7 +24,7 @@ module Remarkable
24
24
  }
25
25
  end
26
26
 
27
- def disabled_method_missing(should_or_should_not, method, *args, &block)
27
+ def disabled_method_missing(should_or_should_not, method, *args, &block) #:nodoc:
28
28
  description = get_description_from_matcher(should_or_should_not, method, *args, &block)
29
29
  xexample(description)
30
30
  end
@@ -33,7 +33,7 @@ module Remarkable
33
33
  # deduct the description from the matcher name, but it will be shown in
34
34
  # english.
35
35
  #
36
- def get_description_from_matcher(should_or_should_not, method, *args, &block)
36
+ def get_description_from_matcher(should_or_should_not, method, *args, &block) #:nodoc:
37
37
  verb = should_or_should_not.to_s.gsub('_', ' ')
38
38
 
39
39
  desc = Remarkable::Matchers.send(method, *args, &block).spec(self).description
@@ -6,7 +6,6 @@ module Remarkable
6
6
 
7
7
  # Helper that includes required Remarkable modules into the given klass.
8
8
  def self.include_matchers!(base, klass)
9
- # Add Remarkable macros core module
10
9
  klass.send :extend, Remarkable::Macros
11
10
 
12
11
  if defined?(base::Matchers)
@@ -7,7 +7,7 @@ module Remarkable
7
7
  PendingSandbox.new(description, self).instance_eval(&block)
8
8
  end
9
9
 
10
- class PendingSandbox < Struct.new(:description, :spec)
10
+ class PendingSandbox < Struct.new(:description, :spec) #:nodoc:
11
11
  include Macros
12
12
 
13
13
  def example(mather_description=nil)
@@ -2,7 +2,7 @@
2
2
  #
3
3
  module Spec #:nodoc:
4
4
  module Matchers #:nodoc:
5
- # Provides I18n on should and should_not.
5
+ # Overwrites to provide I18n on should and should_not.
6
6
  #
7
7
  def self.generated_description
8
8
  return nil if last_should.nil?
@@ -13,7 +13,7 @@ module Spec #:nodoc:
13
13
 
14
14
  module Example #:nodoc:
15
15
  module ExampleGroupMethods #:nodoc:
16
- # Provides I18n on example disabled message.
16
+ # Overwrites to provide I18n on example disabled message.
17
17
  #
18
18
  def xexample(description=nil, opts={}, &block)
19
19
  disabled = Remarkable.t 'remarkable.core.example_disabled', :default => 'Example disabled'
@@ -1,3 +1,3 @@
1
1
  module Remarkable
2
- VERSION = '3.0.0' unless self.const_defined?('VERSION')
2
+ VERSION = '3.0.1' unless self.const_defined?('VERSION')
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remarkable
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Brando
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-04-09 00:00:00 +02:00
13
+ date: 2009-04-10 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency