remarkable 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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