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,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.mappings.size.should == 4
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,152 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module PersistenceSpec
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::PersistenceSpec::OtherTargetClass'
26
+ end
27
+ end
28
+
29
+ describe Mapper::Persistence do
30
+ describe '#target_class' do
31
+ it 'should detect target_class from mapper class name' do
32
+ PersistenceSpec::TargetClassMapper.target_class.should == PersistenceSpec::TargetClass
33
+ end
34
+
35
+ it 'should detect target_class from nearest ancestor when inherited' do
36
+ PersistenceSpec::InheritedClassMapper.target_class.should == PersistenceSpec::TargetClass
37
+ end
38
+
39
+ it 'should use explicit class name if specified' do
40
+ PersistenceSpec::ExplicitNameMapper.target_class.should == PersistenceSpec::OtherTargetClass
41
+ end
42
+ end
43
+
44
+ describe '.build' do
45
+ it 'should use target class to build a new object for mapper' do
46
+ PersistenceSpec::TargetClassMapper.should_receive(:new).with(kind_of(PersistenceSpec::TargetClass), :used_trait)
47
+ PersistenceSpec::TargetClassMapper.build(:used_trait)
48
+ end
49
+ end
50
+
51
+ describe '.find' do
52
+ let(:target){ PersistenceSpec::TargetClass.new('a', 'b') }
53
+
54
+ it 'should delegate to target class to find object for mapper' do
55
+ PersistenceSpec::TargetClass.should_receive(:find).with(1).and_return(target)
56
+ PersistenceSpec::TargetClassMapper.should_receive(:new).with(target, :used_trait)
57
+ PersistenceSpec::TargetClassMapper.find(1, :used_trait)
58
+ end
59
+ end
60
+
61
+ describe 'behavior' do
62
+ let(:target){ PersistenceSpec::TargetClass.new('a', 'b') }
63
+ let(:mapper){ PersistenceSpec::TargetClassMapper.new(target){} }
64
+
65
+ specify '#model_name' do
66
+ mapper.model_name.should == 'mapper'
67
+ end
68
+
69
+ specify '#to_key should delegate to target' do
70
+ target.should_receive(:to_key).and_return(1)
71
+ mapper.to_key.should == 1
72
+ end
73
+
74
+ specify '#persisted? when target does not respond to :persised?' do
75
+ mapper.should_not be_persisted
76
+ end
77
+
78
+ specify '#persisted? when target responds to :persisted?' do
79
+ target.stub(:persisted?).and_return(true)
80
+ mapper.should be_persisted
81
+ end
82
+
83
+ specify '#id when target does not respond to :id' do
84
+ mapper.id.should be_nil
85
+ end
86
+
87
+ specify '#id when target responds to :id' do
88
+ target.stub(:id).and_return(1)
89
+ mapper.id.should == 1
90
+ end
91
+
92
+ describe '#write with multiparams' do
93
+ let(:params) {{
94
+ 'attr_a' => 'A',
95
+ 'dob(0i)' => '1999',
96
+ 'dob(1i)' => '01',
97
+ 'dob(2i)' => '02'
98
+ }}
99
+
100
+ it 'should assign values properly' do
101
+ mapper.write(params)
102
+ target.attr_a.should == 'A'
103
+ target.attr_b.should == Date.new(1999, 1, 2)
104
+ end
105
+ end
106
+
107
+ describe '#save_target' do
108
+ it 'should return true for owned mappers' do
109
+ mapper.extension.save_target.should be true
110
+ end
111
+
112
+ it 'should return true if target does not respond to #save' do
113
+ mapper.save_target.should be true
114
+ end
115
+
116
+ it 'should save with no validation if target responds to #save' do
117
+ target.should_receive(:save).with(:validate => false).and_return(true)
118
+ mapper.save_target.should be true
119
+ end
120
+ end
121
+
122
+ describe '#apply' do
123
+ let(:params){{ :attr_a => 'A' }}
124
+
125
+ it 'should write params first' do
126
+ mapper.should_receive(:write).with(params)
127
+ ActiveRecord::Base.should_receive(:transaction).and_yield
128
+ mapper.apply(params)
129
+ end
130
+
131
+ it 'should not save if not valid' do
132
+ mapper.stub(:valid?).and_return(false)
133
+ mapper.should_not_receive(:save)
134
+ mapper.apply(params)
135
+ end
136
+
137
+ it 'should save if valid' do
138
+ mapper.stub(:valid?).and_return(true)
139
+ ActiveRecord::Base.should_receive(:transaction).and_yield
140
+ mapper.should_receive(:save)
141
+ mapper.apply(params)
142
+ end
143
+ end
144
+
145
+ specify '#shallow_save saves target in a save callbacks' do
146
+ mapper.should_receive(:run_callbacks).with(:save).and_yield
147
+ mapper.should_receive(:save_target)
148
+ mapper.shallow_save
149
+ end
150
+ end
151
+ end
152
+ 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