flat_map 0.0.3 → 0.1.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.
@@ -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