remarkable 3.0.8 → 3.0.9
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/CHANGELOG +22 -0
- data/README +18 -5
- data/lib/remarkable/base.rb +3 -1
- data/lib/remarkable/dsl.rb +23 -6
- data/lib/remarkable/dsl/assertions.rb +353 -147
- data/lib/remarkable/dsl/callbacks.rb +20 -2
- data/lib/remarkable/dsl/optionals.rb +152 -64
- data/lib/remarkable/i18n.rb +30 -7
- data/lib/remarkable/macros.rb +4 -1
- data/lib/remarkable/matchers.rb +12 -4
- data/lib/remarkable/messages.rb +7 -2
- data/lib/remarkable/pending.rb +10 -1
- data/lib/remarkable/rspec.rb +0 -2
- data/lib/remarkable/version.rb +1 -1
- data/spec/dsl/assertions_spec.rb +1 -1
- data/spec/dsl/optionals_spec.rb +220 -37
- data/spec/locale/en.yml +3 -1
- data/spec/macros_spec.rb +1 -1
- data/spec/matchers/be_a_person_matcher.rb +5 -1
- data/spec/matchers/collection_contain_matcher.rb +2 -2
- data/spec/matchers/contain_matcher.rb +2 -2
- data/spec/matchers/single_contain_matcher.rb +4 -3
- metadata +2 -3
- data/lib/remarkable/dsl/matches.rb +0 -139
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
* Added support to blocks configuration. All Remarkable matcher and macros can
|
2
|
+
now be configured using a block:
|
3
|
+
|
4
|
+
should_accept_nested_attributes_for :tasks do |m|
|
5
|
+
m.allow_destroy
|
6
|
+
m.accept(:name => 'cool')
|
7
|
+
m.reject(:name => '')
|
8
|
+
end
|
9
|
+
|
10
|
+
* Added support to {{sentence}} as interpolation option in optionals.
|
11
|
+
Previously we had:
|
12
|
+
|
13
|
+
validate_uniqueness_of :id, :scope => [:project_id, :company_id]
|
14
|
+
# Description: "should require unique attributes for id scoped to [:project_id, :company_id]"
|
15
|
+
|
16
|
+
Now with the new sentence option, we can have:
|
17
|
+
|
18
|
+
validate_uniqueness_of :id, :scope => [:project_id, :company_id]
|
19
|
+
# Description: "should require unique attributes for id scoped to project_id and company_id"
|
20
|
+
|
21
|
+
* Added support to splat and block to optionals
|
22
|
+
|
1
23
|
* Added namespace lookup to optionals and expectations. For example, in ActiveRecord
|
2
24
|
several matchers have :allow_nil and :allow_blank as options. So you can store
|
3
25
|
the translation at:
|
data/README
CHANGED
@@ -1,18 +1,31 @@
|
|
1
1
|
= Remarkable
|
2
2
|
|
3
3
|
This is the core package of Remarkable. It provides a DSL for creating matchers
|
4
|
-
with I18n support,
|
5
|
-
|
4
|
+
with I18n support, decouples messages from matchers logic, add rspec extra features,
|
5
|
+
create macros automatically and allow those macros to be configurable wth blocks.
|
6
6
|
|
7
7
|
== Macros
|
8
8
|
|
9
9
|
Each matcher in Remarkable is also available as a macro. So this matcher:
|
10
|
-
|
11
|
-
it { should
|
10
|
+
|
11
|
+
it { should validate_numericality_of(:age, :greater_than => 18, :only_integer => true) }
|
12
|
+
it { should validate_numericality_of(:age).greater_than(18).only_integer }
|
12
13
|
|
13
14
|
Can also be written as:
|
14
15
|
|
15
|
-
|
16
|
+
should_validate_numericality_of :age, :greater_than => 18, :only_integer => true
|
17
|
+
|
18
|
+
Which can be also written as:
|
19
|
+
|
20
|
+
should_validate_numericality_of :age do |m|
|
21
|
+
m.only_integer
|
22
|
+
m.greater_than 18
|
23
|
+
# Or: m.greater_than = 18
|
24
|
+
end
|
25
|
+
|
26
|
+
Choose your style!
|
27
|
+
|
28
|
+
== Disabled Macros
|
16
29
|
|
17
30
|
Remarkable adds the possibility to disable macros. So as you could do:
|
18
31
|
|
data/lib/remarkable/base.rb
CHANGED
data/lib/remarkable/dsl.rb
CHANGED
@@ -1,19 +1,36 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
2
|
require File.join(dir, 'dsl', 'assertions')
|
3
3
|
require File.join(dir, 'dsl', 'optionals')
|
4
|
-
require File.join(dir, 'dsl', 'matches')
|
5
4
|
require File.join(dir, 'dsl', 'callbacks')
|
6
5
|
|
7
|
-
module Remarkable
|
6
|
+
module Remarkable
|
7
|
+
# The DSL module is responsable for all Remarkable convenience methods.
|
8
|
+
# It has three main submodules:
|
9
|
+
#
|
10
|
+
# * <tt>Assertions</tt> - adds a class methods to define matcher initialization and assertions,
|
11
|
+
# allowing matches? to be hidden from the matcher developer and dealing
|
12
|
+
# with I18n in the expectations messages;
|
13
|
+
#
|
14
|
+
# * <tt>Callbacks</tt> - provides API for after_initialize and before_assert callbacks;
|
15
|
+
#
|
16
|
+
# * <tt>Optionals</tt> - add an optionals DSL, which is also used for the auto configuring blocks
|
17
|
+
# and dynamic descriptions.
|
18
|
+
#
|
8
19
|
module DSL
|
9
|
-
ATTR_READERS = [
|
10
|
-
:
|
20
|
+
ATTR_READERS = [
|
21
|
+
:matcher_arguments,
|
22
|
+
:matcher_optionals,
|
23
|
+
:matcher_optionals_splat,
|
24
|
+
:matcher_optionals_block,
|
25
|
+
:matcher_single_assertions,
|
26
|
+
:matcher_collection_assertions,
|
27
|
+
:before_assert_callbacks,
|
28
|
+
:after_initialize_callbacks
|
11
29
|
] unless self.const_defined?(:ATTR_READERS)
|
12
30
|
|
13
31
|
def self.extended(base) #:nodoc:
|
14
|
-
base.
|
32
|
+
base.send :include, Assertions
|
15
33
|
base.send :include, Callbacks
|
16
|
-
base.send :include, Matches
|
17
34
|
base.send :include, Optionals
|
18
35
|
|
19
36
|
# Initialize matcher_arguments hash with names as an empty array
|
@@ -1,182 +1,388 @@
|
|
1
1
|
module Remarkable
|
2
|
-
module DSL
|
3
|
-
module
|
2
|
+
module DSL
|
3
|
+
# This module is responsable to create a basic matcher structure using a DSL.
|
4
|
+
#
|
5
|
+
# A matcher that checks if an element is included in an array can be done
|
6
|
+
# just with:
|
7
|
+
#
|
8
|
+
# class IncludedMatcher < Remarkable::Base
|
9
|
+
# arguments :value
|
10
|
+
# assertion :is_included?
|
11
|
+
#
|
12
|
+
# protected
|
13
|
+
# def is_included?
|
14
|
+
# @subject.include?(@value)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# As you have noticed, the DSL also allows you to remove the messages from
|
19
|
+
# matcher. Since it will look for it on I18n yml file.
|
20
|
+
#
|
21
|
+
# If you want to create a matcher that accepts multile values to be tested,
|
22
|
+
# you just need to do:
|
23
|
+
#
|
24
|
+
# class IncludedMatcher < Remarkable::Base
|
25
|
+
# arguments :collection => :values, :as => :value
|
26
|
+
# collection_assertion :is_included?
|
27
|
+
#
|
28
|
+
# protected
|
29
|
+
# def is_included?
|
30
|
+
# @subject.include?(@value)
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Notice that the :is_included? logic didn't have to change, because Remarkable
|
35
|
+
# handle this automatically for you.
|
36
|
+
#
|
37
|
+
module Assertions
|
38
|
+
|
39
|
+
def self.included(base) # :nodoc:
|
40
|
+
base.extend ClassMethods
|
41
|
+
end
|
42
|
+
|
43
|
+
module ClassMethods
|
4
44
|
|
5
|
-
|
45
|
+
protected
|
6
46
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
47
|
+
# It sets the arguments your matcher receives on initialization.
|
48
|
+
#
|
49
|
+
# == Options
|
50
|
+
#
|
51
|
+
# * <tt>:collection</tt> - if a collection is expected.
|
52
|
+
# * <tt>:as</tt> - how each item of the collection will be available.
|
53
|
+
# * <tt>:block</tt> - tell the matcher can receive blocks as argument and store
|
54
|
+
# them under the variable given.
|
55
|
+
#
|
56
|
+
# Note: the expected block cannot have arity 1. This is already reserved
|
57
|
+
# for macro configuration.
|
58
|
+
#
|
59
|
+
# == Examples
|
60
|
+
#
|
61
|
+
# Let's see for each example how the arguments declarion reflects on
|
62
|
+
# the matcher API:
|
63
|
+
#
|
64
|
+
# arguments :assign
|
65
|
+
# # Can be called as:
|
66
|
+
# #=> should_assign :task
|
67
|
+
# #=> should_assign :task, :with => Task.new
|
68
|
+
#
|
69
|
+
# This is roughly the same as:
|
70
|
+
#
|
71
|
+
# def initialize(assign, options = {})
|
72
|
+
# @assign = name
|
73
|
+
# @options = options
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# As you noticed, a matcher can always receive options on initialization.
|
77
|
+
# If you have a matcher that accepts only options, for example,
|
78
|
+
# have_default_scope you just need to call <tt>arguments</tt>:
|
79
|
+
#
|
80
|
+
# arguments
|
81
|
+
# # Can be called as:
|
82
|
+
# #=> should_have_default_scope :limit => 10
|
83
|
+
#
|
84
|
+
# arguments :collection => :assigns, :as => :assign
|
85
|
+
# # Can be called as:
|
86
|
+
# #=> should_assign :task1, :task2
|
87
|
+
# #=> should_assign :task1, :task2, :with => Task.new
|
88
|
+
#
|
89
|
+
# arguments :collection => :assigns, :as => :assign, :block => :buildeer
|
90
|
+
# # Can be called as:
|
91
|
+
# #=> should_assign :task1, :task2
|
92
|
+
# #=> should_assign(:task1, :task2){ Task.new }
|
93
|
+
#
|
94
|
+
# The block will be available under the instance variable @builder.
|
95
|
+
#
|
96
|
+
# == I18n
|
97
|
+
#
|
98
|
+
# All the parameters given to arguments are available for interpolation
|
99
|
+
# in I18n. So if you have the following declarion:
|
100
|
+
#
|
101
|
+
# class InRange < Remarkable::Base
|
102
|
+
# arguments :range, :collection => :names, :as => :name
|
103
|
+
#
|
104
|
+
# You will have {{range}}, {{names}} and {{name}} available for I18n
|
105
|
+
# messages:
|
106
|
+
#
|
107
|
+
# in_range:
|
108
|
+
# description: "have {{names}} to be on range {{range}}"
|
109
|
+
#
|
110
|
+
# Before a collection is sent to I18n, it's transformed to a sentence.
|
111
|
+
# So if the following matcher:
|
112
|
+
#
|
113
|
+
# in_range(2..20, :username, :password)
|
114
|
+
#
|
115
|
+
# Has the following description:
|
116
|
+
#
|
117
|
+
# "should have username and password in range 2..20"
|
118
|
+
#
|
119
|
+
def arguments(*names)
|
120
|
+
options = names.extract_options!
|
121
|
+
args = names.dup
|
122
|
+
|
123
|
+
@matcher_arguments[:names] = names
|
63
124
|
|
64
|
-
|
125
|
+
if collection = options.delete(:collection)
|
126
|
+
@matcher_arguments[:collection] = collection
|
65
127
|
|
66
|
-
|
67
|
-
|
128
|
+
if options[:as]
|
129
|
+
@matcher_arguments[:as] = options.delete(:as)
|
130
|
+
else
|
131
|
+
raise ArgumentError, 'You gave me :collection as option but have not give me :as as well'
|
132
|
+
end
|
68
133
|
|
69
|
-
|
70
|
-
|
134
|
+
args << "*#{collection}"
|
135
|
+
get_options = "#{collection}.extract_options!"
|
136
|
+
set_collection = "@#{collection} = #{collection}"
|
71
137
|
else
|
72
|
-
|
138
|
+
args << 'options={}'
|
139
|
+
get_options = 'options'
|
140
|
+
set_collection = ''
|
73
141
|
end
|
74
142
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
143
|
+
if block = options.delete(:block)
|
144
|
+
block = :block unless block.is_a?(Symbol)
|
145
|
+
@matcher_arguments[:block] = block
|
146
|
+
end
|
147
|
+
|
148
|
+
# Blocks are always appended. If they have arity 1, they are used for
|
149
|
+
# macro configuration, otherwise, they are stored in the :block variable.
|
150
|
+
#
|
151
|
+
args << "&block"
|
152
|
+
|
153
|
+
assignments = names.map do |name|
|
154
|
+
"@#{name} = #{name}"
|
155
|
+
end.join("\n ")
|
156
|
+
|
157
|
+
class_eval <<-END, __FILE__, __LINE__
|
158
|
+
def initialize(#{args.join(',')})
|
159
|
+
_builder, block = block, nil if block && block.arity == 1
|
160
|
+
#{assignments}
|
161
|
+
#{"@#{block} = block" if block}
|
162
|
+
@options = default_options.merge(#{get_options})
|
163
|
+
#{set_collection}
|
164
|
+
run_after_initialize_callbacks
|
165
|
+
_builder.call(self) if _builder
|
166
|
+
end
|
167
|
+
END
|
82
168
|
end
|
83
169
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
170
|
+
# Declare the assertions that are runned for each element in the collection.
|
171
|
+
# It must be used with <tt>arguments</tt> methods in order to work properly.
|
172
|
+
#
|
173
|
+
# == Examples
|
174
|
+
#
|
175
|
+
# The example given in <tt>assertions</tt> can be transformed to
|
176
|
+
# accept a collection just doing:
|
177
|
+
#
|
178
|
+
# class IncludedMatcher < Remarkable::Base
|
179
|
+
# arguments :collection => :values, :as => :value
|
180
|
+
# collection_assertion :is_included?
|
181
|
+
#
|
182
|
+
# protected
|
183
|
+
# def is_included?
|
184
|
+
# @subject.include?(@value)
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
# All further consideration done in <tt>assertions</tt> are also valid here.
|
189
|
+
#
|
190
|
+
def collection_assertions(*methods, &block)
|
191
|
+
define_method methods.last, &block if block_given?
|
192
|
+
@matcher_collection_assertions += methods
|
88
193
|
end
|
194
|
+
alias :collection_assertion :collection_assertions
|
89
195
|
|
90
|
-
|
91
|
-
|
92
|
-
|
196
|
+
# Declares the assertions that are run once per matcher.
|
197
|
+
#
|
198
|
+
# == Examples
|
199
|
+
#
|
200
|
+
# A matcher that checks if an element is included in an array can be done
|
201
|
+
# just with:
|
202
|
+
#
|
203
|
+
# class IncludedMatcher < Remarkable::Base
|
204
|
+
# arguments :value
|
205
|
+
# assertion :is_included?
|
206
|
+
#
|
207
|
+
# protected
|
208
|
+
# def is_included?
|
209
|
+
# @subject.include?(@value)
|
210
|
+
# end
|
211
|
+
# end
|
212
|
+
#
|
213
|
+
# Whenever the matcher is called, the :is_included? action is automatically
|
214
|
+
# triggered. Each assertion must return true or false. In case it's false
|
215
|
+
# it will seach for an expectation message on the I18n file. In this
|
216
|
+
# case, the error message would be on:
|
217
|
+
#
|
218
|
+
# included:
|
219
|
+
# description: "check {{value}} is included in the array"
|
220
|
+
# expectations:
|
221
|
+
# is_included: "{{value}} is included in the array"
|
222
|
+
#
|
223
|
+
# In case of failure, it will output:
|
224
|
+
#
|
225
|
+
# "Expected {{value}} is included in the array"
|
226
|
+
#
|
227
|
+
# Notice that on the yml file the question mark is removed for readability.
|
228
|
+
#
|
229
|
+
# == Shortcut declaration
|
230
|
+
#
|
231
|
+
# You can shortcut declaration by giving a name and block to assertion
|
232
|
+
# method:
|
233
|
+
#
|
234
|
+
# class IncludedMatcher < Remarkable::Base
|
235
|
+
# arguments :value
|
236
|
+
#
|
237
|
+
# assertion :is_included? do
|
238
|
+
# @subject.include?(@value)
|
239
|
+
# end
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
def assertions(*methods, &block)
|
243
|
+
if block_given?
|
244
|
+
define_method methods.last, &block
|
245
|
+
protected methods.last
|
246
|
+
end
|
93
247
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
248
|
+
@matcher_single_assertions += methods
|
249
|
+
end
|
250
|
+
alias :assertion :assertions
|
251
|
+
|
252
|
+
# Class method that accepts a block or a hash to set matcher's default
|
253
|
+
# options. It's called on matcher initialization and stores the default
|
254
|
+
# value in the @options instance variable.
|
255
|
+
#
|
256
|
+
# == Examples
|
257
|
+
#
|
258
|
+
# default_options do
|
259
|
+
# { :name => @subject.name }
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# default_options :message => :invalid
|
263
|
+
#
|
264
|
+
def default_options(hash = {}, &block)
|
265
|
+
if block_given?
|
266
|
+
define_method :default_options, &block
|
267
|
+
else
|
268
|
+
class_eval "def default_options; #{hash.inspect}; end"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# This method is responsable for connecting <tt>arguments</tt>, <tt>assertions</tt>
|
274
|
+
# and <tt>collection_assertions</tt>.
|
275
|
+
#
|
276
|
+
# It's the one that executes the assertions once, executes the collection
|
277
|
+
# assertions for each element in the collection and also responsable to set
|
278
|
+
# the I18n messages.
|
279
|
+
#
|
280
|
+
def matches?(subject)
|
281
|
+
@subject = subject
|
282
|
+
|
283
|
+
run_before_assert_callbacks
|
284
|
+
|
285
|
+
send_methods_and_generate_message(self.class.matcher_single_assertions) &&
|
286
|
+
assert_matcher_for(instance_variable_get("@#{self.class.matcher_arguments[:collection]}") || []) do |value|
|
287
|
+
instance_variable_set("@#{self.class.matcher_arguments[:as]}", value)
|
288
|
+
send_methods_and_generate_message(self.class.matcher_collection_assertions)
|
102
289
|
end
|
290
|
+
end
|
103
291
|
|
104
|
-
|
105
|
-
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
# class ValidatePresenceOfMatcher < Remarkable::Base
|
110
|
-
# arguments :collection => :attributes, :as => :attribute
|
111
|
-
# collection_assertions :allow_nil?
|
112
|
-
#
|
113
|
-
# protected
|
114
|
-
# def allow_nil?
|
115
|
-
# # matcher logic
|
116
|
-
# end
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# Then we call it as:
|
120
|
-
#
|
121
|
-
# should validate_presence_of(:email, :password)
|
292
|
+
protected
|
293
|
+
|
294
|
+
# You can overwrite this instance method to provide default options on
|
295
|
+
# initialization.
|
122
296
|
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
#
|
297
|
+
def default_options
|
298
|
+
{}
|
299
|
+
end
|
300
|
+
|
301
|
+
# Overwrites default_i18n_options to provide arguments and optionals
|
302
|
+
# to interpolation options.
|
128
303
|
#
|
129
|
-
# If
|
130
|
-
#
|
304
|
+
# If you still need to provide more other interpolation options, you can
|
305
|
+
# do that in two ways:
|
131
306
|
#
|
132
|
-
#
|
133
|
-
# arguments :collection => :attributes
|
307
|
+
# 1. Overwrite interpolation_options:
|
134
308
|
#
|
135
|
-
#
|
136
|
-
#
|
137
|
-
# end
|
309
|
+
# def interpolation_options
|
310
|
+
# { :real_value => real_value }
|
138
311
|
# end
|
139
312
|
#
|
140
|
-
#
|
141
|
-
# it fails, it will use I18n API to find the proper failure message:
|
313
|
+
# 2. Return a hash from your assertion method:
|
142
314
|
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
# Or you can set the message in the instance variable @expectation in the
|
148
|
-
# assertion method if you don't want to rely on I18n API.
|
315
|
+
# def my_assertion
|
316
|
+
# return true if real_value == expected_value
|
317
|
+
# return false, :real_value => real_value
|
318
|
+
# end
|
149
319
|
#
|
150
|
-
#
|
151
|
-
# aliased as <tt>collection_assertion</tt>.
|
320
|
+
# In both cases, :real_value will be available as interpolation option.
|
152
321
|
#
|
153
|
-
def
|
154
|
-
|
155
|
-
|
322
|
+
def default_i18n_options #:nodoc:
|
323
|
+
i18n_options = {}
|
324
|
+
|
325
|
+
@options.each do |key, value|
|
326
|
+
i18n_options[key] = value.inspect
|
327
|
+
end if @options
|
328
|
+
|
329
|
+
# Also add arguments as interpolation options.
|
330
|
+
self.class.matcher_arguments[:names].each do |name|
|
331
|
+
i18n_options[name] = instance_variable_get("@#{name}").inspect
|
332
|
+
end
|
333
|
+
|
334
|
+
# Add collection interpolation options.
|
335
|
+
i18n_options.update(collection_interpolation)
|
336
|
+
|
337
|
+
# Add default options (highest priority). They should not be overwritten.
|
338
|
+
i18n_options.update(super)
|
156
339
|
end
|
157
|
-
alias :collection_assertion :collection_assertions
|
158
340
|
|
159
|
-
#
|
160
|
-
# are called just once. In other words, it does not iterate through the
|
161
|
-
# collection given in arguments.
|
341
|
+
# Method responsible to add collection as interpolation.
|
162
342
|
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
343
|
+
def collection_interpolation #:nodoc:
|
344
|
+
options = {}
|
345
|
+
|
346
|
+
if collection_name = self.class.matcher_arguments[:collection]
|
347
|
+
collection_name = collection_name.to_sym
|
348
|
+
collection = instance_variable_get("@#{collection_name}")
|
349
|
+
options[collection_name] = array_to_sentence(collection) if collection
|
350
|
+
|
351
|
+
object_name = self.class.matcher_arguments[:as].to_sym
|
352
|
+
object = instance_variable_get("@#{object_name}")
|
353
|
+
options[object_name] = object if object
|
354
|
+
end
|
355
|
+
|
356
|
+
options
|
168
357
|
end
|
169
|
-
alias :assertion :assertions
|
170
358
|
|
171
|
-
#
|
359
|
+
# Send the assertion methods given and create a expectation message
|
360
|
+
# if any of those methods returns false.
|
361
|
+
#
|
362
|
+
# Since most assertion methods ends with an question mark and it's not
|
363
|
+
# readable in yml files, we remove question and exclation marks at the
|
364
|
+
# end of the method name before translating it. So if you have a method
|
365
|
+
# called is_valid? on I18n yml file we will check for a key :is_valid.
|
172
366
|
#
|
173
|
-
def
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
367
|
+
def send_methods_and_generate_message(methods) #:nodoc:
|
368
|
+
methods.each do |method|
|
369
|
+
bool, hash = send(method)
|
370
|
+
|
371
|
+
unless bool
|
372
|
+
parent_scope = matcher_i18n_scope.split('.')
|
373
|
+
matcher_name = parent_scope.pop
|
374
|
+
lookup = :"expectations.#{method.to_s.gsub(/(\?|\!)$/, '')}"
|
375
|
+
|
376
|
+
hash = { :scope => parent_scope, :default => lookup }.merge(hash || {})
|
377
|
+
@expectation ||= Remarkable.t "#{matcher_name}.#{lookup}", default_i18n_options.merge(hash)
|
378
|
+
|
379
|
+
return false
|
380
|
+
end
|
178
381
|
end
|
179
|
-
|
382
|
+
|
383
|
+
return true
|
384
|
+
end
|
385
|
+
|
180
386
|
|
181
387
|
end
|
182
388
|
end
|