HornsAndHooves-flat_map 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/.metrics +17 -0
  4. data/.rspec +4 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +9 -0
  8. data/Gemfile +20 -0
  9. data/HornsAndHooves-flat_map.gemspec +29 -0
  10. data/LICENSE +21 -0
  11. data/README.markdown +214 -0
  12. data/Rakefile +15 -0
  13. data/lib/flat_map.rb +14 -0
  14. data/lib/flat_map/errors.rb +57 -0
  15. data/lib/flat_map/mapping.rb +124 -0
  16. data/lib/flat_map/mapping/factory.rb +21 -0
  17. data/lib/flat_map/mapping/reader.rb +12 -0
  18. data/lib/flat_map/mapping/reader/basic.rb +28 -0
  19. data/lib/flat_map/mapping/reader/formatted.rb +45 -0
  20. data/lib/flat_map/mapping/reader/formatted/formats.rb +28 -0
  21. data/lib/flat_map/mapping/reader/method.rb +25 -0
  22. data/lib/flat_map/mapping/reader/proc.rb +15 -0
  23. data/lib/flat_map/mapping/writer.rb +11 -0
  24. data/lib/flat_map/mapping/writer/basic.rb +25 -0
  25. data/lib/flat_map/mapping/writer/method.rb +28 -0
  26. data/lib/flat_map/mapping/writer/proc.rb +18 -0
  27. data/lib/flat_map/model_mapper.rb +195 -0
  28. data/lib/flat_map/model_mapper/persistence.rb +108 -0
  29. data/lib/flat_map/model_mapper/skipping.rb +45 -0
  30. data/lib/flat_map/open_mapper.rb +113 -0
  31. data/lib/flat_map/open_mapper/attribute_methods.rb +55 -0
  32. data/lib/flat_map/open_mapper/factory.rb +244 -0
  33. data/lib/flat_map/open_mapper/mapping.rb +123 -0
  34. data/lib/flat_map/open_mapper/mounting.rb +168 -0
  35. data/lib/flat_map/open_mapper/persistence.rb +178 -0
  36. data/lib/flat_map/open_mapper/skipping.rb +66 -0
  37. data/lib/flat_map/open_mapper/traits.rb +95 -0
  38. data/lib/flat_map/version.rb +3 -0
  39. data/spec/flat_map/errors_spec.rb +23 -0
  40. data/spec/flat_map/mapper/attribute_methods_spec.rb +36 -0
  41. data/spec/flat_map/mapper/callbacks_spec.rb +76 -0
  42. data/spec/flat_map/mapper/factory_spec.rb +285 -0
  43. data/spec/flat_map/mapper/mapping_spec.rb +98 -0
  44. data/spec/flat_map/mapper/mounting_spec.rb +142 -0
  45. data/spec/flat_map/mapper/persistence_spec.rb +152 -0
  46. data/spec/flat_map/mapper/skipping_spec.rb +91 -0
  47. data/spec/flat_map/mapper/targeting_spec.rb +156 -0
  48. data/spec/flat_map/mapper/traits_spec.rb +172 -0
  49. data/spec/flat_map/mapper/validations_spec.rb +72 -0
  50. data/spec/flat_map/mapper_spec.rb +9 -0
  51. data/spec/flat_map/mapping/factory_spec.rb +12 -0
  52. data/spec/flat_map/mapping/reader/basic_spec.rb +15 -0
  53. data/spec/flat_map/mapping/reader/formatted_spec.rb +62 -0
  54. data/spec/flat_map/mapping/reader/method_spec.rb +13 -0
  55. data/spec/flat_map/mapping/reader/proc_spec.rb +13 -0
  56. data/spec/flat_map/mapping/writer/basic_spec.rb +15 -0
  57. data/spec/flat_map/mapping/writer/method_spec.rb +13 -0
  58. data/spec/flat_map/mapping/writer/proc_spec.rb +13 -0
  59. data/spec/flat_map/mapping_spec.rb +123 -0
  60. data/spec/flat_map/open_mapper_spec.rb +19 -0
  61. data/spec/spec_helper.rb +7 -0
  62. data/tmp/metric_fu/_data/20131218.yml +6902 -0
  63. data/tmp/metric_fu/_data/20131219.yml +6726 -0
  64. metadata +220 -0
@@ -0,0 +1,66 @@
1
+ module FlatMap
2
+ # This helper module provides helper functionality that allow to
3
+ # exclude specific mapper from a processing chain.
4
+ module OpenMapper::Skipping
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :ActiveRecord
8
+
9
+ # Mark self as skipped, i.e. it will not be subject of
10
+ # validation and saving chain.
11
+ #
12
+ # @return [Object]
13
+ def skip!
14
+ @_skip_processing = true
15
+ end
16
+
17
+ # Remove "skip" mark from +self+ and "destroyed" flag from
18
+ # the target.
19
+ #
20
+ # @return [Object]
21
+ def use!
22
+ @_skip_processing = nil
23
+ end
24
+
25
+ # Return +true+ if +self+ was marked for skipping.
26
+ #
27
+ # @return [Boolean]
28
+ def skipped?
29
+ !!@_skip_processing
30
+ end
31
+
32
+ # Override {FlatMap::OpenMapper::Persistence#valid?} to
33
+ # force it to return +true+ if +self+ is marked for skipping.
34
+ #
35
+ # @param [Symbol] context useless context parameter to make it compatible with
36
+ # ActiveRecord models.
37
+ #
38
+ # @return [Boolean]
39
+ def valid?(context = nil)
40
+ skipped? || super
41
+ end
42
+
43
+ # Override {FlatMap::OpenMapper::Persistence#save} method to
44
+ # force it to return +true+ if +self+ is marked for skipping.
45
+ #
46
+ # @return [Boolean]
47
+ def save
48
+ skipped? || super
49
+ end
50
+
51
+ # Override {FlatMap::OpenMapper::Persistence#shallow_save} method
52
+ # to make it possible to skip traits.
53
+ #
54
+ # @return [Boolean]
55
+ def shallow_save
56
+ skipped? || super
57
+ end
58
+
59
+ # Mark self as used and then delegated to original
60
+ # {FlatMap::OpenMapper::Persistence#write}.
61
+ def write(*)
62
+ use!
63
+ super
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,95 @@
1
+ module FlatMap
2
+ # This small module allows mappers to define traits, which technically
3
+ # means mounting anonymous mappers, attached to host one.
4
+ #
5
+ # Also, FlatMap::OpenMapper::Mounting#mountings completely overridden
6
+ # here to support special trait behavior.
7
+ module OpenMapper::Traits
8
+ extend ActiveSupport::Concern
9
+
10
+ # Traits class macros
11
+ module ClassMethods
12
+ # Define a trait for a mapper class. In implementation terms, a trait
13
+ # is nothing more than a mounted mapper, owned by a host mapper.
14
+ # It shares all mappings with it.
15
+ # The block is passed as a body of the anonymous mapper class.
16
+ #
17
+ # @param [Symbol] name
18
+ def trait(name, &block)
19
+ base_class = self < FlatMap::Mapper ? FlatMap::Mapper : FlatMap::OpenMapper
20
+ mapper_class = Class.new(base_class, &block)
21
+ mapper_class_name = "#{ancestors.first.name}#{name.to_s.camelize}Trait"
22
+ mapper_class.singleton_class.send(:define_method, :name){ mapper_class_name }
23
+ mount mapper_class, :trait_name => name
24
+ end
25
+ end
26
+
27
+ # Override the original {FlatMap::OpenMapper::Mounting#mountings}
28
+ # method to filter out those traited mappers that are not required for
29
+ # trait setup of +self+. Also, handle any inline extension that may be
30
+ # defined on the mounting mapper, which is attached as a singleton trait.
31
+ #
32
+ # @return [Array<FlatMap::OpenMapper>]
33
+ def mountings
34
+ @mountings ||= begin
35
+ mountings = self.class.mountings.reject do |factory|
36
+ factory.traited? && !factory.required_for_any_trait?(traits)
37
+ end
38
+ mountings.concat(singleton_class.mountings)
39
+ mountings.map{ |factory| factory.create(self, *traits) }
40
+ end
41
+ end
42
+
43
+ # Return a list of all mountings that represent full picture of +self+, i.e.
44
+ # +self+ and all traits, including deeply nested, that are mounted on self
45
+ #
46
+ # @return [Array<FlatMap::OpenMapper>]
47
+ def self_mountings
48
+ mountings.select(&:owned?).map{ |mount| mount.self_mountings }.flatten.concat [self]
49
+ end
50
+
51
+ # Try to find trait mapper with name that corresponds to +trait_name+
52
+ # Used internally to manipulate such mappers (for example, skip some traits)
53
+ # in some scenarios.
54
+ #
55
+ # @param [Symbol] trait_name
56
+ # @return [FlatMap::OpenMapper, nil]
57
+ def trait(trait_name)
58
+ self_mountings.find{ |mount| mount.class.name.underscore =~ /#{trait_name}_trait$/ }
59
+ end
60
+
61
+ # Return :extension trait, if present
62
+ #
63
+ # @return [FlatMap::OpenMapper]
64
+ def extension
65
+ trait(:extension)
66
+ end
67
+
68
+ # Return only mountings that are actually traits for host mapper.
69
+ #
70
+ # @return [Array<FlatMap::OpenMapper>]
71
+ def trait_mountings
72
+ result = mountings.select{ |mount| mount.owned? }
73
+ # mapper extension has more priority then traits, and
74
+ # has to be processed first.
75
+ result.unshift(result.pop) if result.length > 1 && result[-1].extension?
76
+ result
77
+ end
78
+ protected :trait_mountings
79
+
80
+ # Return only mountings that correspond to external mappers.
81
+ #
82
+ # @return [Array<FlatMap::OpenMapper>]
83
+ def mapper_mountings
84
+ mountings.select{ |mount| !mount.owned? }
85
+ end
86
+ protected :mapper_mountings
87
+
88
+ # Return +true+ if +self+ is extension of host mapper.
89
+ #
90
+ # @return [Boolean]
91
+ def extension?
92
+ owned? && self.class.name =~ /ExtensionTrait$/
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,3 @@
1
+ module FlatMap # :nodoc:
2
+ VERSION = "0.2.0" # :nodoc:
3
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Errors do
4
+ let(:errors) {
5
+ described_class.new(
6
+ double( 'mapper', :suffixed? => true,
7
+ :suffix => 'foo',
8
+ 'attr_foo' => 2)
9
+ )
10
+ }
11
+
12
+ it "preserved errors should appear on #empty? call exactly once" do
13
+ errors.preserve :base, 'an error'
14
+ errors.should_not be_empty
15
+ errors[:base].should == ['an error']
16
+ expect{ errors.empty? }.not_to change{ errors[:base].length }
17
+ end
18
+
19
+ it "should add error to mapper with suffix" do
20
+ errors.add(:attr, 'an error')
21
+ errors[:attr_foo].should == ['an error']
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module AttributeMethodsSpec
5
+ class SpecMapper < Mapper
6
+ map :attr_a, :attr_b
7
+ end
8
+ end
9
+
10
+ describe 'Attribute Methods' do
11
+ let(:target){ OpenStruct.new }
12
+ let(:mapper){ AttributeMethodsSpec::SpecMapper.new(target) }
13
+
14
+ before do
15
+ target.attr_a = 'a'
16
+ target.attr_b = 'b'
17
+ end
18
+
19
+ it 'should be able to read values via method calls' do
20
+ mapper.attr_a.should == 'a'
21
+ mapper.attr_b.should == 'b'
22
+ end
23
+
24
+ it 'should be able to write values via method calls' do
25
+ mapper.attr_a = 'A'
26
+ mapper.attr_b = 'B'
27
+ target.attr_a.should == 'A'
28
+ target.attr_b.should == 'B'
29
+ end
30
+
31
+ it 'should still raise for unknown or private method calls' do
32
+ expect{ mapper.undefined_method }.to raise_error(NoMethodError)
33
+ expect{ mapper.attribute_methods }.to raise_error(NoMethodError)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module CallbacksSpec
5
+ class MountMapper < Mapper
6
+ map :attr_c
7
+
8
+ set_callback :validate, :before, :set_c
9
+
10
+ def set_c
11
+ self.attr_c = 'mounted before validate'
12
+ end
13
+ end
14
+
15
+ class SpecMapper < Mapper
16
+ map :attr_a
17
+
18
+ set_callback :save, :before, :set_a
19
+
20
+ trait :with_b do
21
+ map :attr_b
22
+
23
+ set_callback :validate, :before, :set_b
24
+
25
+ def set_b
26
+ self.attr_b = 'before validate'
27
+ end
28
+ end
29
+
30
+ def set_a
31
+ self.attr_a = 'before save'
32
+ end
33
+
34
+ mount :mount,
35
+ :mapper_class_name => 'FlatMap::CallbacksSpec::MountMapper',
36
+ :target => lambda{ |_| OpenStruct.new }
37
+ end
38
+ end
39
+
40
+ describe 'Callbacks' do
41
+ let(:mapper) do
42
+ CallbacksSpec::SpecMapper.new(OpenStruct.new, :with_b) do
43
+ set_callback :validate, :before, :extension_set_b
44
+
45
+ def extension_set_b
46
+ self.attr_b = 'extension value'
47
+ end
48
+ end
49
+ end
50
+
51
+ it 'should call callbacks once' do
52
+ mapper.should_receive(:set_a).once
53
+ mapper.save
54
+ end
55
+
56
+ specify 'validation callbacks' do
57
+ mapper.valid?
58
+ mapper.attr_a.should be_nil
59
+ mapper.attr_b.should == 'before validate'
60
+ mapper.attr_c.should == 'mounted before validate'
61
+ end
62
+
63
+ specify 'save callbacks' do
64
+ mapper.save
65
+ mapper.attr_a.should == 'before save'
66
+ end
67
+
68
+ context 'extension trait and named traits' do
69
+ it 'should process extension first' do
70
+ mapper.extension.should_receive(:extension_set_b).once.and_call_original
71
+ mapper.valid?
72
+ mapper.attr_b.should == 'before validate'
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,285 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ describe OpenMapper::Factory do
5
+ let(:trait_class){ Class.new(FlatMap::OpenMapper) }
6
+ let(:mapper){ OpenMapper.build }
7
+
8
+ let(:mount_factory){ OpenMapper::Factory.new(:spec_mount, :traits => :used_traits) }
9
+ let(:trait_factory){ OpenMapper::Factory.new(trait_class, :trait_name => :a_trait) }
10
+ let(:open_factory){ OpenMapper::Factory.new(:some_mount, :open => true) }
11
+
12
+ context 'when used for a trait' do
13
+ subject{ trait_factory }
14
+
15
+ it{ should be_traited }
16
+ its(:name){ should be_nil }
17
+ its(:trait_name){ should == :a_trait }
18
+ its(:mapper_class){ should == trait_class }
19
+ end
20
+
21
+ context 'when used for a mounted mapper' do
22
+ subject{ mount_factory }
23
+
24
+ it{ should_not be_traited }
25
+ its(:name){ should == :spec_mount }
26
+ its(:trait_name){ should be_nil }
27
+ its(:traits){ should == [:used_traits] }
28
+ end
29
+
30
+ context 'when used for an open mapper' do
31
+ it "should have descendant of OpenMapper as mapper_class" do
32
+ open_factory.mapper_class.should < OpenMapper
33
+ open_factory.mapper_class.name.should == 'SomeMountMapper'
34
+ end
35
+
36
+ it "should create and instance of OpenMapper with open struct as a target" do
37
+ mounted = open_factory.create(mapper)
38
+ mounted.target.should be_a(OpenStruct)
39
+ end
40
+ end
41
+
42
+ describe 'behavior' do
43
+ let(:target) { mapper.target }
44
+ let(:other_target) { Object.new }
45
+
46
+ describe '#mapper_class for mounted mappers' do
47
+ class ::SpecMountMapper < FlatMap::ModelMapper; end
48
+ class OpenMapper::Factory::SpecMountMapper < FlatMap::OpenMapper; end
49
+
50
+ it "should be able to fetch class name from name" do
51
+ mount_factory.mapper_class.should == ::SpecMountMapper
52
+ end
53
+
54
+ it "should be able to fetch class from mapper_class" do
55
+ factory = OpenMapper::Factory.new(
56
+ :spec_mount,
57
+ :mapper_class => FlatMap::OpenMapper::Factory::SpecMountMapper
58
+ )
59
+ factory.mapper_class.should == ::FlatMap::OpenMapper::Factory::SpecMountMapper
60
+ end
61
+
62
+ it "should use options if specified" do
63
+ factory = OpenMapper::Factory.new(
64
+ :spec_mount,
65
+ :mapper_class_name => 'FlatMap::OpenMapper::Factory::SpecMountMapper'
66
+ )
67
+ factory.mapper_class.should == ::FlatMap::OpenMapper::Factory::SpecMountMapper
68
+ end
69
+ end
70
+
71
+ describe '#fetch_target_from' do
72
+ it "should return owner's target for traited factory" do
73
+ trait_factory.fetch_target_from(mapper).should == target
74
+ end
75
+
76
+ context 'explicit target' do
77
+ it "should use explicitly specified if applicable" do
78
+ factory = OpenMapper::Factory.new(:spec_mount, :target => other_target)
79
+ factory.fetch_target_from(mapper).should == other_target
80
+ end
81
+
82
+ it "should call Proc and pass owner target to it if Proc is specified as :target" do
83
+ factory = OpenMapper::Factory.new(:spec_mount, :target => lambda{ |obj| obj.foo })
84
+ target.should_receive(:foo).and_return(other_target)
85
+ factory.fetch_target_from(mapper).should == other_target
86
+ end
87
+
88
+ it "should call a method if Symbol is used" do
89
+ factory = OpenMapper::Factory.new(:spec_mount, :target => :foo)
90
+ mapper.should_receive(:foo).and_return(other_target)
91
+ factory.fetch_target_from(mapper).should == other_target
92
+ end
93
+ end
94
+
95
+ context 'target from association' do
96
+ before do
97
+ target.stub(:is_a?).and_call_original
98
+ target.stub(:is_a?).with(ActiveRecord::Base).and_return(true)
99
+ end
100
+
101
+ let(:has_one_current_reflection) {
102
+ double('reflection', :macro => :has_one, :options => {:is_current => true})
103
+ }
104
+ let(:has_one_reflection) {
105
+ double('reflection', :macro => :has_one, :options => {})
106
+ }
107
+ let(:belongs_to_reflection) {
108
+ double('reflection', :macro => :belongs_to)
109
+ }
110
+ let(:has_many_reflection) {
111
+ double('reflection', :macro => :has_many, :name => :spec_mounts)
112
+ }
113
+
114
+ it "should refer to effective name for has_one_current association" do
115
+ # Note: has_one_current is not part of Rails
116
+ mount_factory.should_receive(:reflection_from_target).
117
+ with(target).
118
+ and_return(has_one_current_reflection)
119
+ target.should_receive(:effective_spec_mount).and_return(other_target)
120
+ mount_factory.fetch_target_from(mapper).should == other_target
121
+ end
122
+
123
+ it "should refer to existing association object if possible, " \
124
+ "and build it if it is absent for :has_one" do
125
+ mount_factory.should_receive(:reflection_from_target).
126
+ with(target).
127
+ and_return(has_one_reflection)
128
+ target.should_receive(:spec_mount).and_return(nil)
129
+ target.should_receive(:build_spec_mount).and_return(other_target)
130
+ mount_factory.fetch_target_from(mapper).should == other_target
131
+ end
132
+
133
+ it "should refer to existing association object if possible, " \
134
+ "and build it if it is absent for :belongs_to" do
135
+ mount_factory.should_receive(:reflection_from_target).
136
+ with(target).
137
+ and_return(belongs_to_reflection)
138
+ target.should_receive(:spec_mount).and_return(nil)
139
+ target.should_receive(:build_spec_mount).and_return(other_target)
140
+ mount_factory.fetch_target_from(mapper).should == other_target
141
+ end
142
+
143
+ it "should always build a new record for :has_many association" do
144
+ mount_factory.should_receive(:reflection_from_target).
145
+ with(target).
146
+ and_return(has_many_reflection)
147
+ target.should_receive(:association).with(:spec_mounts)
148
+ target.stub_chain(:association, :build).and_return(other_target)
149
+ mount_factory.fetch_target_from(mapper).should == other_target
150
+ end
151
+
152
+ describe 'reflection_from_target' do
153
+ before{ target.stub(:is_a?).with(ActiveRecord::Base).and_return(true) }
154
+
155
+ it 'should first refer to singular association' do
156
+ target.stub_chain(:class, :reflect_on_association).
157
+ with(:spec_mount).
158
+ and_return(has_one_reflection)
159
+ mount_factory.reflection_from_target(target).should == has_one_reflection
160
+ end
161
+
162
+ it 'should use collection association if singular does not exist' do
163
+ target.stub_chain(:class, :reflect_on_association).
164
+ with(:spec_mount).
165
+ and_return(nil)
166
+ target.stub_chain(:class, :reflect_on_association).
167
+ with(:spec_mounts).
168
+ and_return(has_many_reflection)
169
+ mount_factory.reflection_from_target(target).should == has_many_reflection
170
+ end
171
+ end
172
+ end
173
+
174
+ context 'target from name' do
175
+ it 'should simply send method to owner target' do
176
+ target.should_receive(:spec_mount).and_return(other_target)
177
+ mount_factory.fetch_target_from(mapper).should == other_target
178
+ end
179
+ end
180
+ end
181
+
182
+ describe '#create' do
183
+ specify 'traited factory should create an owned mapper' do
184
+ new_one = trait_factory.create(mapper)
185
+ new_one.owner.should == mapper
186
+ end
187
+
188
+ context 'mounted mapper' do
189
+ let(:mount_class){ Class.new(Mapper) }
190
+ let(:factory){ mount_factory }
191
+
192
+ before do
193
+ factory.stub(:mapper_class).and_return(mount_class)
194
+ factory.stub(:fetch_target_from).and_return(other_target)
195
+ end
196
+
197
+ it 'should combine traits' do
198
+ mount_class.should_receive(:new).
199
+ with(other_target, :used_traits, :another_trait).
200
+ and_call_original
201
+ factory.create(mapper, :another_trait)
202
+ end
203
+
204
+ it 'should properly set properties' do
205
+ new_one = factory.create(mapper)
206
+ new_one.host .should == mapper
207
+ new_one.name .should == :spec_mount
208
+ new_one.save_order.should == :after
209
+ new_one.suffix.should be_nil
210
+ end
211
+
212
+ context 'when suffix is defined' do
213
+ let(:factory){ OpenMapper::Factory.new(:spec_mount, :suffix => :foo) }
214
+
215
+ it "should adjust properties with suffix" do
216
+ new_one = factory.create(mapper)
217
+ new_one.name .should == :spec_mount_foo
218
+ new_one.suffix.should == :foo
219
+ end
220
+ end
221
+
222
+ context 'when extension is present' do
223
+ let(:extension){ Proc.new{} }
224
+ let(:factory){ OpenMapper::Factory.new(:spec_mount, &extension) }
225
+
226
+ it "should pass it to mapper initialization" do
227
+ mount_class.should_receive(:new).
228
+ with(other_target, &extension).
229
+ and_call_original
230
+ new_one = factory.create(mapper)
231
+ end
232
+ end
233
+
234
+ describe 'save order' do
235
+ before do
236
+ mapper.stub(:is_a?).and_call_original
237
+ mapper.stub(:is_a?).with(ModelMapper).and_return(true)
238
+ end
239
+
240
+ it 'should be :before for belongs_to association' do
241
+ factory.stub(:reflection_from_target).
242
+ and_return(double('reflection', :macro => :belongs_to))
243
+ factory.fetch_save_order(mapper).should == :before
244
+ end
245
+
246
+ it 'should be :after for other cases' do
247
+ factory.stub(:reflection_from_target).
248
+ and_return(double('reflection', :macro => :has_one))
249
+ factory.fetch_save_order(mapper).should == :after
250
+ end
251
+
252
+ context 'when explicitly set' do
253
+ let(:factory){ OpenMapper::Factory.new(:spec_mount, :save => :before) }
254
+
255
+ it 'should fetch from options, if possible' do
256
+ new_one = factory.create(mapper)
257
+ new_one.save_order.should == :before
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#required_for_any_trait?' do
265
+ let(:mapper_class) do
266
+ Class.new(FlatMap::Mapper) do
267
+ trait(:trait_a) {
268
+ trait(:trait_b) {
269
+ trait(:trait_c) {
270
+ } } }
271
+ end
272
+ end
273
+ let(:factory_for_b){ mapper_class.mountings.first.mapper_class.mountings.first }
274
+
275
+ it "should be required for nested trait" do
276
+ factory_for_b.required_for_any_trait?([:trait_c]).should be true
277
+ end
278
+
279
+ it "should not be required for top trait" do
280
+ factory_for_b.required_for_any_trait?([:trait_a]).should be false
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end