cucumber_factory 2.0.1 → 2.3.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +0 -11
- data/CHANGELOG.md +51 -0
- data/Gemfile.cucumber-1.3 +9 -6
- data/Gemfile.cucumber-1.3.lock +57 -19
- data/Gemfile.cucumber-2.4 +4 -0
- data/Gemfile.cucumber-2.4.lock +12 -1
- data/Gemfile.cucumber-3.0 +4 -0
- data/Gemfile.cucumber-3.0.lock +12 -1
- data/Gemfile.cucumber-3.1 +4 -0
- data/Gemfile.cucumber-3.1.lock +12 -1
- data/README.md +32 -1
- data/lib/cucumber_factory.rb +1 -0
- data/lib/cucumber_factory/build_strategy.rb +99 -48
- data/lib/cucumber_factory/factory.rb +107 -35
- data/lib/cucumber_factory/update_strategy.rb +30 -0
- data/lib/cucumber_factory/version.rb +1 -1
- data/spec/assets/file.txt +1 -0
- data/spec/assets/file2.txt +1 -0
- data/spec/assets/symlink.txt +1 -0
- data/spec/cucumber_factory/factory/build_strategy_spec.rb +12 -14
- data/spec/cucumber_factory/steps_spec.rb +572 -403
- data/spec/spec_helper.rb +6 -0
- data/spec/support/database.rb +9 -0
- data/spec/support/factories/factories.rb +46 -0
- data/spec/support/models/job_offer.rb +1 -3
- data/spec/support/models/movie.rb +1 -0
- data/spec/support/models/opera.rb +2 -3
- data/spec/support/models/payment.rb +13 -8
- data/spec/support/uploaders/attachment_uploader.rb +3 -0
- metadata +8 -3
- data/spec/support/factory_bot_mock.rb +0 -17
data/lib/cucumber_factory.rb
CHANGED
@@ -7,79 +7,115 @@ module CucumberFactory
|
|
7
7
|
class << self
|
8
8
|
|
9
9
|
def from_prose(model_prose, variant_prose)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
variants = variants_from_prose(variant_prose)
|
11
|
+
factory = factory_bot_factory(model_prose, variants)
|
12
|
+
|
13
|
+
if factory
|
14
|
+
factory.compile # Required to load inherited traits!
|
15
|
+
strategy = factory_bot_strategy(factory, model_prose, variants)
|
16
|
+
transient_attributes = factory_bot_transient_attributes(factory, variants)
|
15
17
|
else
|
16
|
-
|
18
|
+
strategy = alternative_strategy(model_prose, variants)
|
19
|
+
transient_attributes = []
|
17
20
|
end
|
18
21
|
|
19
|
-
|
20
|
-
|
22
|
+
[strategy, transient_attributes]
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_model_class(model_prose)
|
26
|
+
underscored_model_name(model_prose).camelize.constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def variants_from_prose(variant_prose)
|
32
|
+
if variant_prose.present?
|
33
|
+
variants = /\((.*?)\)/.match(variant_prose)[1].split(/\s*,\s*/)
|
34
|
+
variants.collect { |variant| variant.downcase.gsub(" ", "_").to_sym }
|
21
35
|
else
|
22
|
-
|
23
|
-
machinist_strategy(model_class, variants) ||
|
24
|
-
active_record_strategy(model_class) ||
|
25
|
-
ruby_object_strategy(model_class)
|
36
|
+
[]
|
26
37
|
end
|
27
38
|
end
|
28
39
|
|
29
|
-
|
40
|
+
def factory_bot_factory(model_prose, variants)
|
41
|
+
return unless factory_bot_class
|
30
42
|
|
31
|
-
|
32
|
-
|
33
|
-
factory_class ||= ::FactoryGirl if defined?(FactoryGirl)
|
34
|
-
return unless factory_class
|
43
|
+
factory_name = factory_name_from_prose(model_prose)
|
44
|
+
factory = factory_bot_class.factories[factory_name]
|
35
45
|
|
36
|
-
|
37
|
-
|
46
|
+
if factory.nil? && variants.present?
|
47
|
+
factory = factory_bot_class.factories[variants[0]]
|
48
|
+
end
|
38
49
|
|
39
|
-
factory
|
50
|
+
factory
|
51
|
+
end
|
40
52
|
|
41
|
-
|
53
|
+
def factory_bot_strategy(factory, model_prose, variants)
|
54
|
+
return unless factory
|
55
|
+
|
56
|
+
factory_name = factory_name_from_prose(model_prose)
|
57
|
+
if factory_bot_class.factories[factory_name].nil? && variants.present?
|
42
58
|
factory_name, *variants = variants
|
43
59
|
end
|
44
60
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
61
|
+
new(factory.build_class) do |attributes|
|
62
|
+
# Cannot have additional scalar args after a varargs
|
63
|
+
# argument in Ruby 1.8 and 1.9
|
64
|
+
args = []
|
65
|
+
args += variants
|
66
|
+
args << attributes
|
67
|
+
factory_bot_class.create(factory_name, *args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def factory_bot_transient_attributes(factory, variants)
|
72
|
+
return [] unless factory
|
73
|
+
|
74
|
+
factory_attributes = factory_bot_attributes(factory, variants)
|
75
|
+
class_attributes = factory.build_class.attribute_names.map(&:to_sym)
|
76
|
+
|
77
|
+
factory_attributes - class_attributes
|
78
|
+
end
|
79
|
+
|
80
|
+
def factory_bot_attributes(factory, variants)
|
81
|
+
traits = factory_bot_traits(factory, variants)
|
82
|
+
factory.with_traits(traits.map(&:name)).definition.attributes.names
|
83
|
+
end
|
84
|
+
|
85
|
+
def factory_bot_traits(factory, variants)
|
86
|
+
factory.definition.defined_traits.select do |trait|
|
87
|
+
variants.include?(trait.name.to_sym)
|
54
88
|
end
|
89
|
+
end
|
55
90
|
|
91
|
+
def alternative_strategy(model_prose, variants)
|
92
|
+
model_class = parse_model_class(model_prose)
|
93
|
+
machinist_strategy(model_class, variants) ||
|
94
|
+
active_record_strategy(model_class) ||
|
95
|
+
ruby_object_strategy(model_class)
|
56
96
|
end
|
57
97
|
|
58
98
|
def machinist_strategy(model_class, variants)
|
59
|
-
|
60
|
-
|
61
|
-
new(model_class) do |attributes|
|
62
|
-
if variants.present?
|
63
|
-
variants.size == 1 or raise 'Machinist only supports a single variant per blueprint'
|
64
|
-
model_class.make(variants.first.to_sym, attributes)
|
65
|
-
else
|
66
|
-
model_class.make(attributes)
|
67
|
-
end
|
68
|
-
end
|
99
|
+
return unless model_class.respond_to?(:make)
|
69
100
|
|
101
|
+
new(model_class) do |attributes|
|
102
|
+
if variants.present?
|
103
|
+
variants.size == 1 or raise 'Machinist only supports a single variant per blueprint'
|
104
|
+
model_class.make(variants.first, attributes)
|
105
|
+
else
|
106
|
+
model_class.make(attributes)
|
107
|
+
end
|
70
108
|
end
|
71
109
|
end
|
72
110
|
|
73
111
|
def active_record_strategy(model_class)
|
74
|
-
|
75
|
-
|
76
|
-
new(model_class) do |attributes|
|
77
|
-
model = model_class.new
|
78
|
-
CucumberFactory::Switcher.assign_attributes(model, attributes)
|
79
|
-
model.save!
|
80
|
-
model
|
81
|
-
end
|
112
|
+
return unless model_class.respond_to?(:create!)
|
82
113
|
|
114
|
+
new(model_class) do |attributes|
|
115
|
+
model = model_class.new
|
116
|
+
CucumberFactory::Switcher.assign_attributes(model, attributes)
|
117
|
+
model.save!
|
118
|
+
model
|
83
119
|
end
|
84
120
|
end
|
85
121
|
|
@@ -89,6 +125,21 @@ module CucumberFactory
|
|
89
125
|
end
|
90
126
|
end
|
91
127
|
|
128
|
+
def factory_bot_class
|
129
|
+
factory_class = ::FactoryBot if defined?(FactoryBot)
|
130
|
+
factory_class ||= ::FactoryGirl if defined?(FactoryGirl)
|
131
|
+
factory_class
|
132
|
+
end
|
133
|
+
|
134
|
+
def factory_name_from_prose(model_prose)
|
135
|
+
underscored_model_name(model_prose).to_s.underscore.gsub('/', '_').to_sym
|
136
|
+
end
|
137
|
+
|
138
|
+
def underscored_model_name(model_prose)
|
139
|
+
# don't use \w which depends on the system locale
|
140
|
+
model_prose.gsub(/[^A-Za-z0-9_\/]+/, "_")
|
141
|
+
end
|
142
|
+
|
92
143
|
end
|
93
144
|
|
94
145
|
|
@@ -2,21 +2,25 @@ module CucumberFactory
|
|
2
2
|
module Factory
|
3
3
|
class Error < StandardError; end
|
4
4
|
|
5
|
-
ATTRIBUTES_PATTERN = '( with the .+?)?( (?:which|who|that) is .+?)?'
|
5
|
+
ATTRIBUTES_PATTERN = '( with the .+?)?( (?:which|who|that) is .+?)?' # ... with the year 1979 which is science fiction
|
6
6
|
TEXT_ATTRIBUTES_PATTERN = ' (?:with|and) these attributes:'
|
7
|
+
UPDATE_ATTR_PATTERN = '(?: (?:has|belongs to)( the .+?))?(?:(?: and| but|,)*( is .+?))?' # ... belongs to the collection "Fantasy" and is trending
|
8
|
+
TEXT_UPDATE_ATTR_PATTERN = '(?: and|,)* has these attributes:'
|
7
9
|
|
8
|
-
RECORD_PATTERN = 'there is an? (.+?)( \(.+?\))?'
|
9
|
-
NAMED_RECORD_PATTERN = '(?:"([^\"]*)"|\'([^\']*)\') is an? (.+?)( \(.+?\))?'
|
10
|
+
RECORD_PATTERN = 'there is an? (.+?)( \(.+?\))?' # Given there is a movie (comedy)
|
11
|
+
NAMED_RECORD_PATTERN = '(?:"([^\"]*)"|\'([^\']*)\') is an? (.+?)( \(.+?\))?' # Given "LotR" is a movie
|
12
|
+
RECORD_UPDATE_PATTERN = 'the (.+?) (above|".+?"|\'.+?\')' # Given the movie "LotR" ...
|
10
13
|
|
11
14
|
NAMED_RECORDS_VARIABLE = :'@named_cucumber_factory_records'
|
12
15
|
|
13
16
|
VALUE_INTEGER = /\d+/
|
14
17
|
VALUE_DECIMAL = /[\d\.]+/
|
15
18
|
VALUE_STRING = /"[^"]*"|'[^']*'/
|
19
|
+
VALUE_FILE = /file:#{VALUE_STRING}/
|
16
20
|
VALUE_ARRAY = /\[[^\]]*\]/
|
17
21
|
VALUE_LAST_RECORD = /\babove\b/
|
18
22
|
|
19
|
-
VALUE_SCALAR = /#{VALUE_STRING}|#{VALUE_DECIMAL}|#{VALUE_INTEGER}/
|
23
|
+
VALUE_SCALAR = /#{VALUE_STRING}|#{VALUE_DECIMAL}|#{VALUE_INTEGER}|#{VALUE_FILE}/
|
20
24
|
|
21
25
|
CLEAR_NAMED_RECORDS_STEP_DESCRIPTOR = {
|
22
26
|
:kind => :Before,
|
@@ -39,6 +43,12 @@ module CucumberFactory
|
|
39
43
|
:block => lambda { |a1, a2, a3, a4| CucumberFactory::Factory.send(:parse_creation, self, a1, a2, a3, a4) }
|
40
44
|
}
|
41
45
|
|
46
|
+
UPDATE_STEP_DESCRIPTOR = {
|
47
|
+
:kind => :And,
|
48
|
+
:pattern => /^#{RECORD_UPDATE_PATTERN}#{UPDATE_ATTR_PATTERN}$/,
|
49
|
+
:block => lambda { |a1, a2, a3, a4| CucumberFactory::Factory.send(:parse_update, self, a1, a2, a3, a4) }
|
50
|
+
}
|
51
|
+
|
42
52
|
NAMED_CREATION_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES = {
|
43
53
|
:kind => :Given,
|
44
54
|
:pattern => /^#{NAMED_RECORD_PATTERN}#{ATTRIBUTES_PATTERN}#{TEXT_ATTRIBUTES_PATTERN}?$/,
|
@@ -53,6 +63,13 @@ module CucumberFactory
|
|
53
63
|
:priority => true
|
54
64
|
}
|
55
65
|
|
66
|
+
UPDATE_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES = {
|
67
|
+
:kind => :And,
|
68
|
+
:pattern => /^#{RECORD_UPDATE_PATTERN}#{UPDATE_ATTR_PATTERN}#{TEXT_UPDATE_ATTR_PATTERN}$/,
|
69
|
+
:block => lambda { |a1, a2, a3, a4, a5| CucumberFactory::Factory.send(:parse_update, self, a1, a2, a3, a4, a5) },
|
70
|
+
:priority => true
|
71
|
+
}
|
72
|
+
|
56
73
|
class << self
|
57
74
|
|
58
75
|
def add_steps(main)
|
@@ -61,6 +78,8 @@ module CucumberFactory
|
|
61
78
|
add_step(main, CREATION_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES)
|
62
79
|
add_step(main, NAMED_CREATION_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES)
|
63
80
|
add_step(main, CLEAR_NAMED_RECORDS_STEP_DESCRIPTOR)
|
81
|
+
add_step(main, UPDATE_STEP_DESCRIPTOR)
|
82
|
+
add_step(main, UPDATE_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES)
|
64
83
|
end
|
65
84
|
|
66
85
|
private
|
@@ -100,15 +119,31 @@ module CucumberFactory
|
|
100
119
|
end
|
101
120
|
|
102
121
|
def parse_creation(world, raw_model, raw_variant, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil)
|
103
|
-
build_strategy = BuildStrategy.from_prose(raw_model, raw_variant)
|
122
|
+
build_strategy, transient_attributes = CucumberFactory::BuildStrategy.from_prose(raw_model, raw_variant)
|
104
123
|
model_class = build_strategy.model_class
|
124
|
+
attributes = parse_attributes(world, model_class, raw_attributes, raw_boolean_attributes, raw_multiline_attributes, transient_attributes)
|
125
|
+
record = build_strategy.create_record(attributes)
|
126
|
+
remember_record_names(world, record, attributes)
|
127
|
+
record
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse_update(world, raw_model, raw_name, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil)
|
131
|
+
model_class = CucumberFactory::BuildStrategy.parse_model_class(raw_model)
|
132
|
+
attributes = parse_attributes(world, model_class, raw_attributes, raw_boolean_attributes, raw_multiline_attributes)
|
133
|
+
record = resolve_associated_value(world, model_class, model_class, model_class, raw_name)
|
134
|
+
CucumberFactory::UpdateStrategy.new(record).assign_attributes(attributes)
|
135
|
+
remember_record_names(world, record, attributes)
|
136
|
+
record
|
137
|
+
end
|
138
|
+
|
139
|
+
def parse_attributes(world, model_class, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil, transient_attributes = [])
|
105
140
|
attributes = {}
|
106
141
|
if raw_attributes.try(:strip).present?
|
107
142
|
raw_attribute_fragment_regex = /(?:the |and |with |but |,| )+(.*?) (#{VALUE_SCALAR}|#{VALUE_ARRAY}|#{VALUE_LAST_RECORD})/
|
108
143
|
raw_attributes.scan(raw_attribute_fragment_regex).each do |fragment|
|
109
144
|
attribute = attribute_name_from_prose(fragment[0])
|
110
145
|
value = fragment[1]
|
111
|
-
attributes[attribute] = attribute_value(world, model_class, attribute, value)
|
146
|
+
attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
|
112
147
|
end
|
113
148
|
unused_raw_attributes = raw_attributes.gsub(raw_attribute_fragment_regex, '')
|
114
149
|
if unused_raw_attributes.present?
|
@@ -126,49 +161,74 @@ module CucumberFactory
|
|
126
161
|
# DocString e.g. "first name: Jane\nlast name: Jenny\n"
|
127
162
|
if raw_multiline_attributes.is_a?(String)
|
128
163
|
raw_multiline_attributes.split("\n").each do |fragment|
|
129
|
-
raw_attribute, value = fragment.split(': ')
|
164
|
+
raw_attribute, value = fragment.split(': ', 2)
|
130
165
|
attribute = attribute_name_from_prose(raw_attribute)
|
131
|
-
value = "\"#{value}\"" unless matches_fully?(value, VALUE_ARRAY)
|
132
|
-
attributes[attribute] = attribute_value(world, model_class, attribute, value)
|
166
|
+
value = "\"#{value}\"" unless matches_fully?(value, /#{VALUE_ARRAY}|#{VALUE_FILE}/)
|
167
|
+
attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
|
133
168
|
end
|
134
169
|
# DataTable e.g. in raw [["first name", "Jane"], ["last name", "Jenny"]]
|
135
170
|
else
|
136
171
|
raw_multiline_attributes.raw.each do |raw_attribute, value|
|
137
172
|
attribute = attribute_name_from_prose(raw_attribute)
|
138
|
-
value = "\"#{value}\"" unless matches_fully?(value, VALUE_ARRAY)
|
139
|
-
attributes[attribute] = attribute_value(world, model_class, attribute, value)
|
173
|
+
value = "\"#{value}\"" unless matches_fully?(value, /#{VALUE_ARRAY}|#{VALUE_FILE}/)
|
174
|
+
attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
|
140
175
|
end
|
141
176
|
end
|
142
177
|
end
|
143
|
-
|
144
|
-
remember_record_names(world, record, attributes)
|
145
|
-
record
|
178
|
+
attributes
|
146
179
|
end
|
147
180
|
|
148
|
-
def attribute_value(world, model_class, attribute, value)
|
149
|
-
|
150
|
-
|
151
|
-
if matches_fully?(value, VALUE_ARRAY)
|
152
|
-
|
153
|
-
|
154
|
-
elsif
|
155
|
-
|
156
|
-
value = CucumberFactory::Switcher.find_last(association.klass) or raise Error, "There is no last #{attribute}"
|
157
|
-
elsif matches_fully?(value, VALUE_STRING)
|
158
|
-
value = unquote(value)
|
159
|
-
value = get_named_record(world, value) || transform_value(world, value)
|
160
|
-
elsif matches_fully?(value, VALUE_INTEGER)
|
161
|
-
value = value.to_s
|
162
|
-
value = get_named_record(world, value) || transform_value(world, value)
|
163
|
-
else
|
164
|
-
raise Error, "Cannot set association #{model_class}##{attribute} to #{value}."
|
165
|
-
end
|
181
|
+
def attribute_value(world, model_class, transient_attributes, attribute, value)
|
182
|
+
associated, association_class = resolve_association(attribute, model_class, transient_attributes)
|
183
|
+
|
184
|
+
value = if matches_fully?(value, VALUE_ARRAY)
|
185
|
+
array_values = unquote(value).scan(VALUE_SCALAR)
|
186
|
+
array_values.map { |v| attribute_value(world, model_class, transient_attributes, attribute, v) }
|
187
|
+
elsif associated
|
188
|
+
resolve_associated_value(world, model_class, association_class, attribute, value)
|
166
189
|
else
|
167
|
-
|
190
|
+
resolve_scalar_value(world, model_class, attribute, value)
|
168
191
|
end
|
169
192
|
value
|
170
193
|
end
|
171
194
|
|
195
|
+
def resolve_association(attribute, model_class, transient_attributes)
|
196
|
+
return unless model_class.respond_to?(:reflect_on_association)
|
197
|
+
|
198
|
+
association = model_class.reflect_on_association(attribute)
|
199
|
+
association_class = nil
|
200
|
+
|
201
|
+
if association
|
202
|
+
association_class = association.klass unless association.polymorphic?
|
203
|
+
associated = true
|
204
|
+
elsif transient_attributes.include?(attribute.to_sym)
|
205
|
+
klass_name = attribute.to_s.camelize
|
206
|
+
if Object.const_defined?(klass_name)
|
207
|
+
association_class = klass_name.constantize
|
208
|
+
associated = true
|
209
|
+
end
|
210
|
+
else
|
211
|
+
associated = false
|
212
|
+
end
|
213
|
+
[associated, association_class]
|
214
|
+
end
|
215
|
+
|
216
|
+
def resolve_associated_value(world, model_class, association_class, attribute, value)
|
217
|
+
if matches_fully?(value, VALUE_LAST_RECORD)
|
218
|
+
raise(Error, "Cannot set last #{model_class}##{attribute} for polymorphic associations") unless association_class.present?
|
219
|
+
|
220
|
+
CucumberFactory::Switcher.find_last(association_class) || raise(Error, "There is no last #{attribute}")
|
221
|
+
elsif matches_fully?(value, VALUE_STRING)
|
222
|
+
value = unquote(value)
|
223
|
+
get_named_record(world, value) || transform_value(world, value)
|
224
|
+
elsif matches_fully?(value, VALUE_INTEGER)
|
225
|
+
value = value.to_s
|
226
|
+
get_named_record(world, value) || transform_value(world, value)
|
227
|
+
else
|
228
|
+
raise Error, "Cannot set association #{model_class}##{attribute} to #{value}."
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
172
232
|
def resolve_scalar_value(world, model_class, attribute, value)
|
173
233
|
if matches_fully?(value, VALUE_STRING)
|
174
234
|
value = unquote(value)
|
@@ -177,6 +237,9 @@ module CucumberFactory
|
|
177
237
|
value = value.to_i
|
178
238
|
elsif matches_fully?(value, VALUE_DECIMAL)
|
179
239
|
value = BigDecimal(value)
|
240
|
+
elsif matches_fully?(value, VALUE_FILE)
|
241
|
+
path = File.path("./#{file_value_to_path(value)}")
|
242
|
+
value = File.new(path)
|
180
243
|
else
|
181
244
|
raise Error, "Cannot set attribute #{model_class}##{attribute} to #{value}."
|
182
245
|
end
|
@@ -184,11 +247,20 @@ module CucumberFactory
|
|
184
247
|
end
|
185
248
|
|
186
249
|
def unquote(string)
|
250
|
+
# This method removes quotes or brackets from the start and end from a string
|
251
|
+
# Examples: 'single' => single, "double" => double, [1, 2, 3] => 1, 2, 3
|
187
252
|
string[1, string.length - 2]
|
188
253
|
end
|
189
254
|
|
255
|
+
def file_value_to_path(string)
|
256
|
+
# file paths are marked with a special keyword and enclosed with quotes.
|
257
|
+
# Example: file:"/path/image.png"
|
258
|
+
# This will extract the path (/path/image.png) from the text fragment above
|
259
|
+
unquote string.sub(/\Afile:/, '')
|
260
|
+
end
|
261
|
+
|
190
262
|
def full_regexp(partial_regexp)
|
191
|
-
Regexp.new(
|
263
|
+
Regexp.new('\\A(?:' + partial_regexp.source + ')\\z', partial_regexp.options)
|
192
264
|
end
|
193
265
|
|
194
266
|
def matches_fully?(string, partial_regexp)
|
@@ -208,7 +280,7 @@ module CucumberFactory
|
|
208
280
|
end
|
209
281
|
|
210
282
|
def attribute_name_from_prose(prose)
|
211
|
-
prose.downcase.gsub(
|
283
|
+
prose.downcase.gsub(' ', '_').to_sym
|
212
284
|
end
|
213
285
|
|
214
286
|
def remember_record_names(world, record, attributes)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CucumberFactory
|
2
|
+
|
3
|
+
class UpdateStrategy
|
4
|
+
|
5
|
+
def initialize(record)
|
6
|
+
@record = record
|
7
|
+
end
|
8
|
+
|
9
|
+
def assign_attributes(attributes)
|
10
|
+
active_record_strategy(attributes) ||
|
11
|
+
ruby_object_strategy(attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def active_record_strategy(attributes)
|
17
|
+
return unless @record.respond_to?(:save!)
|
18
|
+
|
19
|
+
CucumberFactory::Switcher.assign_attributes(@record, attributes)
|
20
|
+
@record.save!
|
21
|
+
end
|
22
|
+
|
23
|
+
def ruby_object_strategy(attributes)
|
24
|
+
attributes.each do |name, value|
|
25
|
+
@record.send("#{name}=".to_sym, value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|