flat_map 0.0.3

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.
Files changed (64) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +31 -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/LICENSE +20 -0
  10. data/README.markdown +211 -0
  11. data/Rakefile +15 -0
  12. data/flat_map.gemspec +30 -0
  13. data/lib/flat_map.rb +9 -0
  14. data/lib/flat_map/base_mapper.rb +95 -0
  15. data/lib/flat_map/base_mapper/attribute_methods.rb +54 -0
  16. data/lib/flat_map/base_mapper/factory.rb +238 -0
  17. data/lib/flat_map/base_mapper/mapping.rb +123 -0
  18. data/lib/flat_map/base_mapper/mounting.rb +168 -0
  19. data/lib/flat_map/base_mapper/persistence.rb +145 -0
  20. data/lib/flat_map/base_mapper/skipping.rb +62 -0
  21. data/lib/flat_map/base_mapper/traits.rb +94 -0
  22. data/lib/flat_map/empty_mapper.rb +29 -0
  23. data/lib/flat_map/errors.rb +57 -0
  24. data/lib/flat_map/mapper.rb +213 -0
  25. data/lib/flat_map/mapper/skipping.rb +45 -0
  26. data/lib/flat_map/mapper/targeting.rb +130 -0
  27. data/lib/flat_map/mapping.rb +124 -0
  28. data/lib/flat_map/mapping/factory.rb +21 -0
  29. data/lib/flat_map/mapping/reader.rb +12 -0
  30. data/lib/flat_map/mapping/reader/basic.rb +28 -0
  31. data/lib/flat_map/mapping/reader/formatted.rb +45 -0
  32. data/lib/flat_map/mapping/reader/formatted/formats.rb +28 -0
  33. data/lib/flat_map/mapping/reader/method.rb +25 -0
  34. data/lib/flat_map/mapping/reader/proc.rb +15 -0
  35. data/lib/flat_map/mapping/writer.rb +11 -0
  36. data/lib/flat_map/mapping/writer/basic.rb +25 -0
  37. data/lib/flat_map/mapping/writer/method.rb +28 -0
  38. data/lib/flat_map/mapping/writer/proc.rb +18 -0
  39. data/lib/flat_map/version.rb +3 -0
  40. data/spec/flat_map/empty_mapper_spec.rb +36 -0
  41. data/spec/flat_map/errors_spec.rb +23 -0
  42. data/spec/flat_map/mapper/attribute_methods_spec.rb +36 -0
  43. data/spec/flat_map/mapper/callbacks_spec.rb +76 -0
  44. data/spec/flat_map/mapper/factory_spec.rb +258 -0
  45. data/spec/flat_map/mapper/mapping_spec.rb +98 -0
  46. data/spec/flat_map/mapper/mounting_spec.rb +142 -0
  47. data/spec/flat_map/mapper/skipping_spec.rb +91 -0
  48. data/spec/flat_map/mapper/targeting_spec.rb +156 -0
  49. data/spec/flat_map/mapper/traits_spec.rb +172 -0
  50. data/spec/flat_map/mapper/validations_spec.rb +72 -0
  51. data/spec/flat_map/mapper_spec.rb +9 -0
  52. data/spec/flat_map/mapping/factory_spec.rb +12 -0
  53. data/spec/flat_map/mapping/reader/basic_spec.rb +15 -0
  54. data/spec/flat_map/mapping/reader/formatted_spec.rb +62 -0
  55. data/spec/flat_map/mapping/reader/method_spec.rb +13 -0
  56. data/spec/flat_map/mapping/reader/proc_spec.rb +13 -0
  57. data/spec/flat_map/mapping/writer/basic_spec.rb +15 -0
  58. data/spec/flat_map/mapping/writer/method_spec.rb +13 -0
  59. data/spec/flat_map/mapping/writer/proc_spec.rb +13 -0
  60. data/spec/flat_map/mapping_spec.rb +123 -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 +184 -0
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module MappingSpec
5
+ SpecTarget = Struct.new(:attr_a, :attr_b, :attr_c, :attr_d)
6
+
7
+ class SpecMapper < Mapper
8
+ # explicit mapping
9
+ map :mapped_attr_a => :attr_a
10
+
11
+ # implicit mapping
12
+ map :attr_b
13
+
14
+ # implicit and explicit with options in one call
15
+ map :attr_c, :mapped_attr_d => :attr_d,
16
+ :reader => :read_mappings,
17
+ :writer => :write_mappings
18
+
19
+ def read_mappings(mapping)
20
+ "#{mapping.name}-#{target.send(mapping.target_attribute)}"
21
+ end
22
+
23
+ def write_mappings(mapping, value)
24
+ target.send("#{mapping.target_attribute}=", value.upcase)
25
+ end
26
+ end
27
+
28
+ class EmptyMapper < Mapper; end
29
+ end
30
+
31
+ describe 'Mapping' do
32
+ context 'defining mappings' do
33
+ it "should use Factory for defining mappings" do
34
+ MappingSpec::EmptyMapper.should_receive(:define_mappings).once.
35
+ with({:attr_a => :attr_a, :mapped_attr_b => :attr_b}, {:writer => false}).
36
+ and_call_original
37
+ Mapping::Factory.should_receive(:new).
38
+ with(:attr_a, :attr_a, :writer => false).
39
+ and_call_original
40
+ Mapping::Factory.should_receive(:new).
41
+ with(:mapped_attr_b, :attr_b, :writer => false).
42
+ and_call_original
43
+
44
+ MappingSpec::EmptyMapper.class_eval do
45
+ map :attr_a, :mapped_attr_b => :attr_b, :writer => false
46
+ end
47
+ end
48
+ end
49
+
50
+ specify 'mapper class should have defined mappings' do
51
+ MappingSpec::SpecMapper.should have(4).mappings
52
+ MappingSpec::SpecMapper.mappings.all?{ |m| m.is_a?(Mapping::Factory) }.should be_true
53
+ end
54
+
55
+ context "for initialized mapper" do
56
+ let(:target){ MappingSpec::SpecTarget.new('a', 'b', 'c', 'd') }
57
+ let(:mapper){ MappingSpec::SpecMapper.new(target) }
58
+
59
+ it "should be able to access mapping by its name" do
60
+ mapper.mapping(:mapped_attr_a).should be_a(FlatMap::Mapping)
61
+ mapper.mapping(:not_defined).should be_nil
62
+ end
63
+
64
+ describe 'reading and writing' do
65
+ it "should be able to read from target via brackets" do
66
+ mapper[:mapped_attr_a].should == 'a'
67
+ end
68
+
69
+ it 'should be able to write to target via brackets' do
70
+ mapper[:attr_b] = 'B'
71
+ target.attr_b.should == 'B'
72
+ end
73
+
74
+ it '#read should read all mappings to a hash' do
75
+ mapper.read.should == {
76
+ :mapped_attr_a => 'a',
77
+ :attr_b => 'b',
78
+ :attr_c => 'attr_c-c',
79
+ :mapped_attr_d => 'mapped_attr_d-d'
80
+ }
81
+ end
82
+
83
+ it '#write should assign values using mappings, ignoring invalid ones' do
84
+ mapper.write \
85
+ :mapped_attr_a => 'A',
86
+ :attr_b => 'B',
87
+ :attr_c => 'new-c',
88
+ :mapped_attr_d => 'new-d'
89
+
90
+ target.attr_a.should == 'A'
91
+ target.attr_b.should == 'B'
92
+ target.attr_c.should == 'NEW-C'
93
+ target.attr_d.should == 'NEW-D'
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module MountingSpec
5
+ MountTarget = Struct.new(:attr_a, :attr_b)
6
+
7
+ class MountMapper < Mapper
8
+ map :attr_a
9
+
10
+ trait :with_b do
11
+ map :mapped_attr_b => :attr_b
12
+ end
13
+
14
+ def a_method
15
+ 'a value'
16
+ end
17
+ end
18
+
19
+ class HostMapper < Mapper
20
+ map :host_attr, :reader => :attr_value, :writer => false
21
+
22
+ mount :spec_mount,
23
+ :traits => :with_b,
24
+ :target => lambda{ |obj| MountingSpec.mount_target },
25
+ :mapper_class_name => 'FlatMap::MountingSpec::MountMapper'
26
+
27
+ mount :spec_mount_before,
28
+ :target => lambda{ |obj| MountingSpec.mount_target },
29
+ :mapper_class_name => 'FlatMap::MountingSpec::MountMapper',
30
+ :save => :before
31
+
32
+ def attr_value(*)
33
+ 'attr'
34
+ end
35
+ end
36
+
37
+ class EmptyMapper < Mapper; end
38
+
39
+ def self.mount_target
40
+ @mount_target ||= MountTarget.new('a', 'b')
41
+ end
42
+
43
+ def self.reset_mount_target
44
+ @mount_target = nil
45
+ end
46
+ end
47
+
48
+ module MountingSuffixSpec
49
+ class SpecMapper < Mapper
50
+ mount_options = {
51
+ :suffix => 'foo',
52
+ :mapper_class_name => 'FlatMap::MountingSuffixSpec::MountMapper',
53
+ :target => lambda{ |_| OpenStruct.new } }
54
+ mount :mount, mount_options do
55
+ mount :nested,
56
+ :mapper_class_name => 'FlatMap::MountingSuffixSpec::NestedMapper',
57
+ :target => lambda{ |_| OpenStruct.new }
58
+ end
59
+ end
60
+
61
+ class MountMapper < Mapper
62
+ map :attr_mount
63
+ end
64
+
65
+ class NestedMapper < Mapper
66
+ map :attr_nested
67
+ end
68
+ end
69
+
70
+ describe 'Mounting' do
71
+ let(:mapper){ MountingSpec::HostMapper.new(Object.new) }
72
+ let(:mounting){ mapper.mounting(:spec_mount) }
73
+
74
+ after{ MountingSpec.reset_mount_target }
75
+
76
+ context 'defining mountings' do
77
+ it "should use Factory for defining mappings" do
78
+ Mapper::Factory.should_receive(:new).
79
+ with(:foo, :mapper_class_name => 'FooMapper').
80
+ and_call_original
81
+
82
+ expect{ MountingSpec::EmptyMapper.mount(:foo, :mapper_class_name => 'FooMapper') }.
83
+ to change{ MountingSpec::EmptyMapper.mountings.length }.from(0).to(1)
84
+ end
85
+ end
86
+
87
+ describe 'properties' do
88
+ it{ mapper.hosted?.should be_false }
89
+ it{ mounting.hosted?.should be_true }
90
+ it{ mounting.host.should == mapper }
91
+ end
92
+
93
+ it 'should be able to access mapping by name' do
94
+ mapper.mounting(:spec_mount).should be_a(FlatMap::Mapper)
95
+ mapper.mounting(:undefined_mount).should be_nil
96
+ end
97
+
98
+ it '#read should add mounted mappers to a result' do
99
+ mapper.read.should == {
100
+ :host_attr => 'attr',
101
+ :attr_a => 'a',
102
+ :mapped_attr_b => 'b'
103
+ }
104
+ end
105
+
106
+ it '#write should pass values to mounted mappers' do
107
+ target = MountingSpec.mount_target
108
+ mapper.write :attr_a => 'A', :mapped_attr_b => 'B'
109
+ target.attr_a.should == 'A'
110
+ target.attr_b.should == 'B'
111
+ end
112
+
113
+ it 'should delegate missing methods to mounted mappers' do
114
+ expect{ mapper.a_method.should == 'a value' }.not_to raise_error
115
+ end
116
+
117
+ specify '#before_save_mountings' do
118
+ mapper.before_save_mountings.should == [mapper.mounting(:spec_mount_before)]
119
+ end
120
+
121
+ specify '#after_save_mountings' do
122
+ mapper.after_save_mountings.should == [mapper.mounting(:spec_mount)]
123
+ end
124
+
125
+ context 'with suffix' do
126
+ let(:mapper){ MountingSuffixSpec::SpecMapper.new(OpenStruct.new) }
127
+ let(:mounting){ mapper.mounting(:mount_foo) }
128
+
129
+ it{ mapper.should_not be_suffixed }
130
+ it{ mounting.should be_suffixed }
131
+
132
+ it 'should cascade to nested mappings' do
133
+ mapper.attr_mount_foo = 'foo'
134
+ mapper.attr_nested_foo = 'bar'
135
+
136
+ mapper.read.should include({
137
+ :attr_mount_foo => 'foo',
138
+ :attr_nested_foo => 'bar' })
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module SkippingSpec
5
+ class SpecMapper < Mapper
6
+ trait :with_trait do
7
+ map :attr_a, :attr_b
8
+
9
+ set_callback :validate, :before, :set_attr_a, :prepend => true
10
+ set_callback :save, :before, :set_attr_b
11
+
12
+ validates_numericality_of :attr_a
13
+
14
+ def set_attr_a
15
+ self.attr_a = 'a'
16
+ end
17
+
18
+ def set_attr_b
19
+ self.attr_b = 'b'
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ describe 'Skipping' do
26
+ let(:mapper){ SkippingSpec::SpecMapper.new(OpenStruct.new, :with_trait) }
27
+
28
+ before{ mapper.trait(:with_trait).skip! }
29
+
30
+ it 'should completely ignore skipped mounting' do
31
+ mapper.should be_valid
32
+ mapper.save.should be_true
33
+ mapper.attr_a.should be_nil
34
+ mapper.attr_b.should be_nil
35
+ end
36
+
37
+ it '#use! should enable skipped mounting' do
38
+ mapper.trait(:with_trait).use!
39
+
40
+ mapper.should_not be_valid
41
+ mapper.attr_a.should == 'a'
42
+ mapper.errors[:attr_a].should be_present
43
+
44
+ mapper.attr_a = 5
45
+ mapper.save
46
+ mapper.attr_b.should == 'b'
47
+ end
48
+ end
49
+
50
+ describe 'Skipping ActiveRecord' do
51
+ let(:target){ OpenStruct.new }
52
+ let(:mapper){ SkippingSpec::SpecMapper.new(target, :with_trait) }
53
+
54
+ before{ target.stub(:is_a?).with(ActiveRecord::Base).and_return(true) }
55
+
56
+ context 'for new record' do
57
+ before do
58
+ target.stub(:new_record?).and_return(true)
59
+ mapper.trait(:with_trait).skip!
60
+ end
61
+
62
+ specify '#skip! should set ivar @destroyed to true' do
63
+ target.instance_variable_get('@destroyed').should be_true
64
+ end
65
+
66
+ specify '#use! should set ivar @destroyed to true' do
67
+ mapper.trait(:with_trait).use!
68
+ target.instance_variable_get('@destroyed').should be_false
69
+ end
70
+ end
71
+
72
+ context 'for persisted record' do
73
+ before do
74
+ target.stub(:new_record?).and_return(false)
75
+ end
76
+
77
+ specify '#skip! should reload persisted record' do
78
+ target.should_receive(:reload)
79
+ mapper.trait(:with_trait).skip!
80
+ end
81
+
82
+ specify '#use! should use all nested mountings' do
83
+ mapper.trait(:with_trait).skip!
84
+ mock = double('mounting')
85
+ mock.should_receive(:use!)
86
+ mapper.trait(:with_trait).stub(:all_nested_mountings).and_return([mock])
87
+ mapper.trait(:with_trait).use!
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module ModelMethodsSpec
5
+ class TargetClass < Struct.new(:attr_a, :attr_b)
6
+ end
7
+
8
+ class OtherTargetClass < Struct.new(:attr_c, :attr_d)
9
+ end
10
+
11
+ module ArbitraryModule
12
+ end
13
+
14
+ class TargetClassMapper < Mapper
15
+ include ArbitraryModule
16
+
17
+ map :attr_a
18
+ map :dob => :attr_b, :multiparam => Date
19
+ end
20
+
21
+ class InheritedClassMapper < TargetClassMapper
22
+ end
23
+
24
+ class ExplicitNameMapper < Mapper
25
+ self.target_class_name = 'FlatMap::ModelMethodsSpec::OtherTargetClass'
26
+ end
27
+ end
28
+
29
+ describe 'Working with Target' do
30
+ describe '#target_class' do
31
+ it 'should detect target_class from mapper class name' do
32
+ ModelMethodsSpec::TargetClassMapper.target_class.should == ModelMethodsSpec::TargetClass
33
+ end
34
+
35
+ it 'should detect target_class from nearest ancestor when inherited' do
36
+ ModelMethodsSpec::InheritedClassMapper.target_class.
37
+ should == ModelMethodsSpec::TargetClass
38
+ end
39
+
40
+ it 'should use explicit class name if specified' do
41
+ ModelMethodsSpec::ExplicitNameMapper.target_class.
42
+ should == ModelMethodsSpec::OtherTargetClass
43
+ end
44
+ end
45
+
46
+ describe '.build' do
47
+ it 'should use target class to build a new object for mapper' do
48
+ ModelMethodsSpec::TargetClassMapper.
49
+ should_receive(:new).
50
+ with(kind_of(ModelMethodsSpec::TargetClass), :used_trait)
51
+ ModelMethodsSpec::TargetClassMapper.build(:used_trait)
52
+ end
53
+ end
54
+
55
+ describe '.find' do
56
+ let(:target){ ModelMethodsSpec::TargetClass.new('a', 'b') }
57
+
58
+ it 'should delegate to target class to find object for mapper' do
59
+ ModelMethodsSpec::TargetClass.should_receive(:find).with(1).and_return(target)
60
+ ModelMethodsSpec::TargetClassMapper.should_receive(:new).with(target, :used_trait)
61
+ ModelMethodsSpec::TargetClassMapper.find(1, :used_trait)
62
+ end
63
+ end
64
+
65
+ describe 'behavior' do
66
+ let(:target){ ModelMethodsSpec::TargetClass.new('a', 'b') }
67
+ let(:mapper){ ModelMethodsSpec::TargetClassMapper.new(target){} }
68
+
69
+ specify '#model_name' do
70
+ mapper.model_name.should == 'mapper'
71
+ end
72
+
73
+ specify '#to_key should delegate to target' do
74
+ target.should_receive(:to_key).and_return(1)
75
+ mapper.to_key.should == 1
76
+ end
77
+
78
+ specify '#persisted? when target does not respond to :persised?' do
79
+ mapper.should_not be_persisted
80
+ end
81
+
82
+ specify '#persisted? when target responds to :persisted?' do
83
+ target.stub(:persisted?).and_return(true)
84
+ mapper.should be_persisted
85
+ end
86
+
87
+ specify '#id when target does not respond to :id' do
88
+ mapper.id.should be_nil
89
+ end
90
+
91
+ specify '#id when target responds to :id' do
92
+ target.stub(:id).and_return(1)
93
+ mapper.id.should == 1
94
+ end
95
+
96
+ describe '#write with multiparams' do
97
+ let(:params) {{
98
+ 'attr_a' => 'A',
99
+ 'dob(0i)' => '1999',
100
+ 'dob(1i)' => '01',
101
+ 'dob(2i)' => '02'
102
+ }}
103
+
104
+ it 'should assign values properly' do
105
+ mapper.write(params)
106
+ target.attr_a.should == 'A'
107
+ target.attr_b.should == Date.new(1999, 1, 2)
108
+ end
109
+ end
110
+
111
+ describe '#save_target' do
112
+ it 'should return true for owned mappers' do
113
+ mapper.extension.save_target.should be_true
114
+ end
115
+
116
+ it 'should return true if target does not respond to #save' do
117
+ mapper.save_target.should be_true
118
+ end
119
+
120
+ it 'should save with no validation if target responds to #save' do
121
+ target.should_receive(:save).with(:validate => false).and_return(true)
122
+ mapper.save_target.should be_true
123
+ end
124
+ end
125
+
126
+ describe '#apply' do
127
+ let(:params){{ :attr_a => 'A' }}
128
+
129
+ it 'should write params first' do
130
+ mapper.should_receive(:write).with(params)
131
+ ActiveRecord::Base.should_receive(:transaction).and_yield
132
+ mapper.apply(params)
133
+ end
134
+
135
+ it 'should not save if not valid' do
136
+ mapper.stub(:valid?).and_return(false)
137
+ mapper.should_not_receive(:save)
138
+ mapper.apply(params)
139
+ end
140
+
141
+ it 'should save if valid' do
142
+ mapper.stub(:valid?).and_return(true)
143
+ ActiveRecord::Base.should_receive(:transaction).and_yield
144
+ mapper.should_receive(:save)
145
+ mapper.apply(params)
146
+ end
147
+ end
148
+
149
+ specify '#shallow_save saves target in a save callbacks' do
150
+ mapper.should_receive(:run_callbacks).with(:save).and_yield
151
+ mapper.should_receive(:save_target)
152
+ mapper.shallow_save
153
+ end
154
+ end
155
+ end
156
+ end