cucumber_factory 2.0.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|