flat_map 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@ module FlatMap
2
2
  # This module hosts all definitions required to define and use mapping
3
3
  # functionality within mapper classes. This includes mapping definition
4
4
  # methods and basic reading and writing methods.
5
- module BaseMapper::Mapping
5
+ module OpenMapper::Mapping
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  # Mapping class macros
@@ -65,7 +65,7 @@ module FlatMap
65
65
  # Send passed +params+ +write_from_params+ method of each
66
66
  # of the mappings of +self+.
67
67
  #
68
- # Overloaded in {BaseMapper::Mounting}.
68
+ # Overloaded in {OpenMapper::Mounting}.
69
69
  #
70
70
  # @param [Hash] params
71
71
  # @return [Hash] params
@@ -109,7 +109,7 @@ module FlatMap
109
109
  # @param [Symbol] name
110
110
  # @return [FlatMap::Mapping]
111
111
  def mapping(name)
112
- mappings.find{ |_mapping| _mapping.name == name }
112
+ mappings.find{ |mapping| mapping.name == name }
113
113
  end
114
114
 
115
115
  # Return a list of mappings associated to +self+.
@@ -6,7 +6,7 @@ module FlatMap
6
6
  #
7
7
  # Also, the +method_missing+ method is defined here to delegate the missing
8
8
  # method to the very first mounted mapper that responds to it.
9
- module BaseMapper::Mounting
9
+ module OpenMapper::Mounting
10
10
  extend ActiveSupport::Concern
11
11
 
12
12
  included do
@@ -21,14 +21,14 @@ module FlatMap
21
21
  # mapper.
22
22
  #
23
23
  # @param [*Object] args
24
- # @return [Array<FlatMap::BaseMapper::Factory>]
24
+ # @return [Array<FlatMap::OpenMapper::Factory>]
25
25
  def mount(*args, &block)
26
- mountings << FlatMap::BaseMapper::Factory.new(*args, &block)
26
+ mountings << FlatMap::OpenMapper::Factory.new(*args, &block)
27
27
  end
28
28
 
29
29
  # List of mountings (factories) of a class.
30
30
  #
31
- # @return [Array<FlatMap::BaseMapper>]
31
+ # @return [Array<FlatMap::OpenMapper>]
32
32
  def mountings
33
33
  @mountings ||= []
34
34
  end
@@ -68,14 +68,14 @@ module FlatMap
68
68
 
69
69
  # Return list of mappings to be saved before saving target of +self+
70
70
  #
71
- # @return [Array<FlatMap::BaseMapper>]
71
+ # @return [Array<FlatMap::OpenMapper>]
72
72
  def before_save_mountings
73
73
  nearest_mountings.select{ |mount| mount.save_order == :before }
74
74
  end
75
75
 
76
76
  # Return list of mappings to be saved after target of +self+ was saved
77
77
  #
78
- # @return [Array<FlatMap::BaseMapper>]
78
+ # @return [Array<FlatMap::OpenMapper>]
79
79
  def after_save_mountings
80
80
  nearest_mountings.reject{ |mount| mount.save_order == :before }
81
81
  end
@@ -83,16 +83,16 @@ module FlatMap
83
83
  # Return all mountings that are mouted on +self+ directly or through
84
84
  # traits.
85
85
  #
86
- # @return [Array<FlatMap::BaseMapper>]
86
+ # @return [Array<FlatMap::OpenMapper>]
87
87
  def nearest_mountings
88
88
  mountings.map{ |mount| mount.owned? ? mount.nearest_mountings : mount }.flatten
89
89
  end
90
90
 
91
91
  # Return a list of all mountings (mapper objects) associated with +self+.
92
92
  #
93
- # Overridden in {Traits}.
93
+ # Overridden in {Traits}. Left here for consistency.
94
94
  #
95
- # @return [Array<FlatMap::BaseMapper>]
95
+ # @return [Array<FlatMap::OpenMapper>]
96
96
  def mountings
97
97
  @mountings ||= self.class.mountings.map{ |factory| factory.create(self) }
98
98
  end
@@ -110,7 +110,7 @@ module FlatMap
110
110
  # list of all mountings of the owner. This will allow separate traits
111
111
  # to share methods via method_missing pattern.
112
112
  #
113
- # @return [Array<FlatMap::BaseMapper>] mounted mappers (including traits)
113
+ # @return [Array<FlatMap::OpenMapper>] mounted mappers (including traits)
114
114
  def all_mountings
115
115
  return all_nested_mountings.unshift(self) unless owned?
116
116
  owner.all_mountings
@@ -119,7 +119,7 @@ module FlatMap
119
119
 
120
120
  # Return a list of mountings that are accessible by a named mapper.
121
121
  #
122
- # @return [Array<FlatMap::BaseMapper>]
122
+ # @return [Array<FlatMap::OpenMapper>]
123
123
  def all_nested_mountings
124
124
  mountings.dup.concat(mountings.map{ |mount| mount.send(:all_nested_mountings) }).flatten
125
125
  end
@@ -149,20 +149,20 @@ module FlatMap
149
149
  protected :all_nested_mappings
150
150
 
151
151
  # Delegate missing method to any mounted mapping that respond to it,
152
- # unless those methods are protected methods of FlatMap::BaseMapper.
152
+ # unless those methods are protected methods of FlatMap::OpenMapper.
153
153
  #
154
154
  # NOTE: :to_ary method is called internally by Ruby 1.9.3 when we call
155
155
  # something like [mapper].flatten. And we DO want default behavior
156
156
  # for handling this missing method.
157
157
  def method_missing(name, *args, &block)
158
158
  return super if name == :to_ary ||
159
- FlatMap::BaseMapper.protected_instance_methods.include?(name)
159
+ self.class.protected_instance_methods.include?(name)
160
160
 
161
161
  return self[name] if mapping(name).present?
162
162
 
163
- mount = all_mountings.find{ |_mount| _mount.respond_to?(name) }
164
- return super if mount.nil?
165
- mount.send(name, *args, &block)
163
+ mounting = all_mountings.find{ |mount| mount.respond_to?(name) }
164
+ return super if mounting.nil?
165
+ mounting.send(name, *args, &block)
166
166
  end
167
167
  end
168
168
  end
@@ -1,18 +1,37 @@
1
1
  module FlatMap
2
- # This module provides persistence functionality for mappers. Note
3
- # that term of persistence here does not imply storing information
4
- # in database or other place. This module provides methods for
5
- # saving operation as a work flow of applying parameters to mapper
6
- # and all of its mounted mappers in a right way, running callbacks,
7
- # etc.
8
- #
9
- # See {Mapper::Targeting} for a place where mapper targets are
10
- # actually get persisted / updated.
2
+ # This module provides some integration between mapper and its target,
3
+ # which is usually an ActiveRecord model, as well as some integration
4
+ # between mapper and Rails forms.
11
5
  #
12
6
  # In particular, validation and save methods are defined here. And
13
7
  # the <tt>save</tt> method itself is defined as a callback. Also, Rails
14
8
  # multiparam attributes extraction is defined within this module.
15
- module BaseMapper::Persistence
9
+ module OpenMapper::Persistence
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ define_callbacks :save
14
+ end
15
+
16
+ # ModelMethods class macros
17
+ module ClassMethods
18
+ # Create a new mapper object wrapped around new instance of its
19
+ # +target_class+, with a list of passed +traits+ applied to it.
20
+ #
21
+ # @param [*Symbol] traits
22
+ # @return [FlatMap::OpenMapper] mapper
23
+ def build(*traits, &block)
24
+ new(target_class.new, *traits, &block)
25
+ end
26
+
27
+ # Default target class for OpenMapper is OpenStruct.
28
+ #
29
+ # @return [Class] class
30
+ def target_class
31
+ OpenStruct
32
+ end
33
+ end
34
+
16
35
  # Write a passed set of +params+. Then try to save the model if +self+
17
36
  # passes validation. Saving is performed in a transaction.
18
37
  #
@@ -63,6 +82,13 @@ module FlatMap
63
82
  before_res && target_res && after_res
64
83
  end
65
84
 
85
+ # Return +true+ since OpenStruct is always 'saved'.
86
+ #
87
+ # @return [true]
88
+ def save_target
89
+ true
90
+ end
91
+
66
92
  # Perform target save with callbacks call
67
93
  #
68
94
  # @return [Boolean]
@@ -70,10 +96,17 @@ module FlatMap
70
96
  run_callbacks(:save){ save_target }
71
97
  end
72
98
 
99
+ # Return +true+ if target was updated.
100
+ #
101
+ # @return [Boolean]
102
+ def persisted?
103
+ target != OpenStruct.new
104
+ end
105
+
73
106
  # Send <tt>:save</tt> method to all mountings in list. Will return +true+
74
107
  # only if all savings are positive.
75
108
  #
76
- # @param [Array<FlatMap::BaseMapper>] mountings
109
+ # @param [Array<FlatMap::OpenMapper>] mountings
77
110
  # @return [Boolean]
78
111
  def save_mountings(mountings)
79
112
  mountings.map{ |mount| mount.save }.all?
@@ -128,9 +161,9 @@ module FlatMap
128
161
 
129
162
  next if param_keys.empty?
130
163
 
131
- args = param_keys.inject([]) do |values, _key|
132
- value = params.delete _key
133
- type = _key[/\(\d+(\w*)\)/, 1]
164
+ args = param_keys.inject([]) do |values, key|
165
+ value = params.delete key
166
+ type = key[/\(\d+(\w*)\)/, 1]
134
167
  value = value.send("to_#{type}") unless type.blank?
135
168
 
136
169
  values.push value
@@ -1,7 +1,11 @@
1
1
  module FlatMap
2
2
  # This helper module provides helper functionality that allow to
3
3
  # exclude specific mapper from a processing chain.
4
- module BaseMapper::Skipping
4
+ module OpenMapper::Skipping
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :ActiveRecord
8
+
5
9
  # Mark self as skipped, i.e. it will not be subject of
6
10
  # validation and saving chain.
7
11
  #
@@ -25,7 +29,7 @@ module FlatMap
25
29
  !!@_skip_processing
26
30
  end
27
31
 
28
- # Override {FlatMap::BaseMapper::Persistence#valid?} to
32
+ # Override {FlatMap::OpenMapper::Persistence#valid?} to
29
33
  # force it to return +true+ if +self+ is marked for skipping.
30
34
  #
31
35
  # @param [Symbol] context useless context parameter to make it compatible with
@@ -36,7 +40,7 @@ module FlatMap
36
40
  skipped? || super
37
41
  end
38
42
 
39
- # Override {FlatMap::BaseMapper::Persistence#save} method to
43
+ # Override {FlatMap::OpenMapper::Persistence#save} method to
40
44
  # force it to return +true+ if +self+ is marked for skipping.
41
45
  #
42
46
  # @return [Boolean]
@@ -44,7 +48,7 @@ module FlatMap
44
48
  skipped? || super
45
49
  end
46
50
 
47
- # Override {FlatMap::BaseMapper::Persistence#shallow_save} method
51
+ # Override {FlatMap::OpenMapper::Persistence#shallow_save} method
48
52
  # to make it possible to skip traits.
49
53
  #
50
54
  # @return [Boolean]
@@ -53,7 +57,7 @@ module FlatMap
53
57
  end
54
58
 
55
59
  # Mark self as used and then delegated to original
56
- # {FlatMap::BaseMapper::Persistence#write}.
60
+ # {FlatMap::OpenMapper::Persistence#write}.
57
61
  def write(*)
58
62
  use!
59
63
  super
@@ -1,7 +1,10 @@
1
1
  module FlatMap
2
2
  # This small module allows mappers to define traits, which technically
3
3
  # means mounting anonymous mappers, attached to host one.
4
- module BaseMapper::Traits
4
+ #
5
+ # Also, FlatMap::OpenMapper::Mounting#mountings completely overridden
6
+ # here to support special trait behavior.
7
+ module OpenMapper::Traits
5
8
  extend ActiveSupport::Concern
6
9
 
7
10
  # Traits class macros
@@ -13,7 +16,7 @@ module FlatMap
13
16
  #
14
17
  # @param [Symbol] name
15
18
  def trait(name, &block)
16
- base_class = self < FlatMap::Mapper ? FlatMap::Mapper : FlatMap::EmptyMapper
19
+ base_class = self < FlatMap::Mapper ? FlatMap::Mapper : FlatMap::OpenMapper
17
20
  mapper_class = Class.new(base_class, &block)
18
21
  mapper_class_name = "#{ancestors.first.name}#{name.to_s.camelize}Trait"
19
22
  mapper_class.singleton_class.send(:define_method, :name){ mapper_class_name }
@@ -21,19 +24,17 @@ module FlatMap
21
24
  end
22
25
  end
23
26
 
24
- # Override the original {FlatMap::BaseMapper::Mounting#mountings}
27
+ # Override the original {FlatMap::OpenMapper::Mounting#mountings}
25
28
  # method to filter out those traited mappers that are not required for
26
29
  # trait setup of +self+. Also, handle any inline extension that may be
27
30
  # defined on the mounting mapper, which is attached as a singleton trait.
28
31
  #
29
- # @return [Array<FlatMap::BaseMapper>]
32
+ # @return [Array<FlatMap::OpenMapper>]
30
33
  def mountings
31
34
  @mountings ||= begin
32
- mountings = self.class.mountings.
33
- reject{ |factory|
34
- factory.traited? &&
35
- !factory.required_for_any_trait?(traits)
36
- }
35
+ mountings = self.class.mountings.reject do |factory|
36
+ factory.traited? && !factory.required_for_any_trait?(traits)
37
+ end
37
38
  mountings.concat(singleton_class.mountings)
38
39
  mountings.map{ |factory| factory.create(self, *traits) }
39
40
  end
@@ -42,7 +43,7 @@ module FlatMap
42
43
  # Return a list of all mountings that represent full picture of +self+, i.e.
43
44
  # +self+ and all traits, including deeply nested, that are mounted on self
44
45
  #
45
- # @return [Array<FlatMap::BaseMapper>]
46
+ # @return [Array<FlatMap::OpenMapper>]
46
47
  def self_mountings
47
48
  mountings.select(&:owned?).map{ |mount| mount.self_mountings }.flatten.concat [self]
48
49
  end
@@ -52,21 +53,21 @@ module FlatMap
52
53
  # in some scenarios.
53
54
  #
54
55
  # @param [Symbol] trait_name
55
- # @return [FlatMap::BaseMapper, nil]
56
+ # @return [FlatMap::OpenMapper, nil]
56
57
  def trait(trait_name)
57
58
  self_mountings.find{ |mount| mount.class.name.underscore =~ /#{trait_name}_trait$/ }
58
59
  end
59
60
 
60
61
  # Return :extension trait, if present
61
62
  #
62
- # @return [FlatMap::BaseMapper]
63
+ # @return [FlatMap::OpenMapper]
63
64
  def extension
64
65
  trait(:extension)
65
66
  end
66
67
 
67
68
  # Return only mountings that are actually traits for host mapper.
68
69
  #
69
- # @return [Array<FlatMap::BaseMapper>]
70
+ # @return [Array<FlatMap::OpenMapper>]
70
71
  def trait_mountings
71
72
  result = mountings.select{ |mount| mount.owned? }
72
73
  # mapper extension has more priority then traits, and
@@ -78,7 +79,7 @@ module FlatMap
78
79
 
79
80
  # Return only mountings that correspond to external mappers.
80
81
  #
81
- # @return [Array<FlatMap::BaseMapper>]
82
+ # @return [Array<FlatMap::OpenMapper>]
82
83
  def mapper_mountings
83
84
  mountings.select{ |mount| !mount.owned? }
84
85
  end
@@ -1,3 +1,3 @@
1
1
  module FlatMap # :nodoc:
2
- VERSION = "0.0.3" # :nodoc:
2
+ VERSION = "0.1.0" # :nodoc:
3
3
  end
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module FlatMap
4
- describe BaseMapper::Factory do
5
- let(:trait_class){ Class.new(Mapper) }
6
- let(:target){ Object.new }
7
- let(:other_target){ Object.new }
8
- let(:mapper){ Class.new(Mapper).new(target) }
9
- let(:mount_factory){ BaseMapper::Factory.new(:spec_mount, :traits => :used_traits) }
10
- let(:trait_factory){ BaseMapper::Factory.new(trait_class, :trait_name => :a_trait) }
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
11
 
12
12
  context 'when used for a trait' do
13
13
  subject{ trait_factory }
@@ -27,21 +27,36 @@ module FlatMap
27
27
  its(:traits){ should == [:used_traits] }
28
28
  end
29
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
+
30
42
  describe 'behavior' do
43
+ let(:target) { mapper.target }
44
+ let(:other_target) { Object.new }
45
+
31
46
  describe '#mapper_class for mounted mappers' do
32
- class ::SpecMountMapper < Mapper; end
33
- class BaseMapper::Factory::SpecMountMapper; end
47
+ class ::SpecMountMapper < FlatMap::ModelMapper; end
48
+ class OpenMapper::Factory::SpecMountMapper < FlatMap::OpenMapper; end
34
49
 
35
50
  it "should be able to fetch class name from name" do
36
51
  mount_factory.mapper_class.should == ::SpecMountMapper
37
52
  end
38
53
 
39
54
  it "should use options if specified" do
40
- factory = BaseMapper::Factory.new(
55
+ factory = OpenMapper::Factory.new(
41
56
  :spec_mount,
42
- :mapper_class_name => 'FlatMap::BaseMapper::Factory::SpecMountMapper'
57
+ :mapper_class_name => 'FlatMap::OpenMapper::Factory::SpecMountMapper'
43
58
  )
44
- factory.mapper_class.should == ::FlatMap::BaseMapper::Factory::SpecMountMapper
59
+ factory.mapper_class.should == ::FlatMap::OpenMapper::Factory::SpecMountMapper
45
60
  end
46
61
  end
47
62
 
@@ -52,19 +67,28 @@ module FlatMap
52
67
 
53
68
  context 'explicit target' do
54
69
  it "should use explicitly specified if applicable" do
55
- factory = BaseMapper::Factory.new(:mount, :target => other_target)
70
+ factory = OpenMapper::Factory.new(:spec_mount, :target => other_target)
56
71
  factory.fetch_target_from(mapper).should == other_target
57
72
  end
58
73
 
59
74
  it "should call Proc and pass owner target to it if Proc is specified as :target" do
60
- factory = BaseMapper::Factory.new(:mount, :target => lambda{ |obj| obj.foo })
75
+ factory = OpenMapper::Factory.new(:spec_mount, :target => lambda{ |obj| obj.foo })
61
76
  target.should_receive(:foo).and_return(other_target)
62
77
  factory.fetch_target_from(mapper).should == other_target
63
78
  end
79
+
80
+ it "should call a method if Symbol is used" do
81
+ factory = OpenMapper::Factory.new(:spec_mount, :target => :foo)
82
+ mapper.should_receive(:foo).and_return(other_target)
83
+ factory.fetch_target_from(mapper).should == other_target
84
+ end
64
85
  end
65
86
 
66
87
  context 'target from association' do
67
- before{ target.stub(:kind_of?).with(ActiveRecord::Base).and_return(true) }
88
+ before do
89
+ target.stub(:is_a?).and_call_original
90
+ target.stub(:is_a?).with(ActiveRecord::Base).and_return(true)
91
+ end
68
92
 
69
93
  let(:has_one_current_reflection) {
70
94
  double('reflection', :macro => :has_one, :options => {:is_current => true})
@@ -153,16 +177,6 @@ module FlatMap
153
177
  new_one.owner.should == mapper
154
178
  end
155
179
 
156
- context 'mounted empty mapper' do
157
- class ::SpecEmptyMountMapper < EmptyMapper; end
158
- let(:factory){ BaseMapper::Factory.new(:spec_empty_mount) }
159
-
160
- it 'should not call fetch_target_from' do
161
- factory.should_not_receive(:fetch_target_from)
162
- factory.create(mapper)
163
- end
164
- end
165
-
166
180
  context 'mounted mapper' do
167
181
  let(:mount_class){ Class.new(Mapper) }
168
182
  let(:factory){ mount_factory }
@@ -188,7 +202,7 @@ module FlatMap
188
202
  end
189
203
 
190
204
  context 'when suffix is defined' do
191
- let(:factory){ BaseMapper::Factory.new(:spec_mount, :suffix => :foo) }
205
+ let(:factory){ OpenMapper::Factory.new(:spec_mount, :suffix => :foo) }
192
206
 
193
207
  it "should adjust properties with suffix" do
194
208
  new_one = factory.create(mapper)
@@ -199,7 +213,7 @@ module FlatMap
199
213
 
200
214
  context 'when extension is present' do
201
215
  let(:extension){ Proc.new{} }
202
- let(:factory){ BaseMapper::Factory.new(:spec_mount, &extension) }
216
+ let(:factory){ OpenMapper::Factory.new(:spec_mount, &extension) }
203
217
 
204
218
  it "should pass it to mapper initialization" do
205
219
  mount_class.should_receive(:new).
@@ -210,26 +224,31 @@ module FlatMap
210
224
  end
211
225
 
212
226
  describe 'save order' do
213
- context 'when explicitly set' do
214
- let(:factory){ BaseMapper::Factory.new(:spec_mount, :save => :before) }
215
-
216
- it 'should fetch from options, if possible' do
217
- new_one = factory.create(mapper)
218
- new_one.save_order.should == :before
219
- end
227
+ before do
228
+ mapper.stub(:is_a?).and_call_original
229
+ mapper.stub(:is_a?).with(ModelMapper).and_return(true)
220
230
  end
221
231
 
222
232
  it 'should be :before for belongs_to association' do
223
- mount_factory.stub(:reflection_from_target).
224
- and_return(double('reflection', :macro => :belongs_to))
233
+ factory.stub(:reflection_from_target).
234
+ and_return(double('reflection', :macro => :belongs_to))
225
235
  factory.fetch_save_order(mapper).should == :before
226
236
  end
227
237
 
228
238
  it 'should be :after for other cases' do
229
- mount_factory.stub(:reflection_from_target).
230
- and_return(double('reflection', :macro => :has_one))
239
+ factory.stub(:reflection_from_target).
240
+ and_return(double('reflection', :macro => :has_one))
231
241
  factory.fetch_save_order(mapper).should == :after
232
242
  end
243
+
244
+ context 'when explicitly set' do
245
+ let(:factory){ OpenMapper::Factory.new(:spec_mount, :save => :before) }
246
+
247
+ it 'should fetch from options, if possible' do
248
+ new_one = factory.create(mapper)
249
+ new_one.save_order.should == :before
250
+ end
251
+ end
233
252
  end
234
253
  end
235
254
  end