HornsAndHooves-flat_map 0.2.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.
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,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
@@ -0,0 +1,172 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ module TraitsSpec
5
+ TargetClass = Struct.new(:attr_a, :attr_b)
6
+ MountClass = Struct.new(:attr_c, :attr_d)
7
+
8
+ class HostMapper < Mapper
9
+ map :attr_a
10
+
11
+ trait :trait_one do
12
+ map :attr_b
13
+
14
+ mount :spec_mount,
15
+ :mapper_class_name => 'FlatMap::TraitsSpec::MountMapper',
16
+ :target => lambda{ |_| TraitsSpec.mount_target }
17
+
18
+ def method_one
19
+ 'one'
20
+ end
21
+
22
+ trait :trait_one_nested do
23
+ def method_one_nested
24
+ 'nested_one'
25
+ end
26
+ end
27
+ end
28
+
29
+ trait :trait_two do
30
+ def method_two
31
+ method_one
32
+ end
33
+ end
34
+ end
35
+
36
+ class MountMapper < Mapper
37
+ map :attr_c, :attr_d
38
+ end
39
+
40
+ class EmptyMapper < Mapper; end
41
+
42
+ def self.mount_target
43
+ @mount_target ||= MountClass.new('c', 'd')
44
+ end
45
+
46
+ def self.reset_mount_target
47
+ @mount_target = nil
48
+ end
49
+ end
50
+
51
+ describe 'Traits' do
52
+ describe 'trait definition' do
53
+ it "should add a traited mapper factory to a class" do
54
+ TraitsSpec::EmptyMapper.should_receive(:mount).
55
+ with(kind_of(Class), :trait_name => :a_trait).
56
+ and_call_original
57
+ expect{ TraitsSpec::EmptyMapper.trait(:a_trait) }.
58
+ to change{ TraitsSpec::EmptyMapper.mountings.length }.by(1)
59
+ trait_mapper_class =
60
+ TraitsSpec::EmptyMapper.mountings.first.instance_variable_get('@identifier')
61
+ trait_mapper_class.name.should == 'FlatMap::TraitsSpec::EmptyMapperATraitTrait'
62
+ end
63
+ end
64
+
65
+ describe 'trait usage' do
66
+ let(:target){ TraitsSpec::TargetClass.new('a', 'b') }
67
+ let(:mount_target){ TraitsSpec.mount_target }
68
+ let(:mapper){ TraitsSpec::HostMapper.new(target, :trait_one) }
69
+ let(:trait){ mapper.trait(:trait_one) }
70
+
71
+ after{ TraitsSpec.reset_mount_target }
72
+
73
+ describe 'trait properties' do
74
+ subject{ trait }
75
+
76
+ it{ should_not be_extension }
77
+ it{ should be_owned }
78
+ its(:owner){ should == mapper }
79
+ end
80
+
81
+ it 'should be able to access trait by name' do
82
+ mapper.trait(:trait_one).should be_a(Mapper)
83
+ mapper.trait(:undefined).should be_nil
84
+ end
85
+
86
+ it 'should not contain unused trait' do
87
+ mapper.trait(:trait_two).should be_nil
88
+ end
89
+
90
+ it 'should have mountings of a trait' do
91
+ mapper.mounting(:spec_mount).should be_present
92
+ end
93
+
94
+ it '#read should read values with respect to trait' do
95
+ mapper.read.should == {
96
+ :attr_a => 'a',
97
+ :attr_b => 'b',
98
+ :attr_c => 'c',
99
+ :attr_d => 'd'
100
+ }
101
+ end
102
+
103
+ it '#write should properly distribute values' do
104
+ mapper.write \
105
+ :attr_a => 'A',
106
+ :attr_b => 'B',
107
+ :attr_c => 'C',
108
+ :attr_d => 'D'
109
+ target.attr_a.should == 'A'
110
+ target.attr_b.should == 'B'
111
+ mount_target.attr_c.should == 'C'
112
+ mount_target.attr_d.should == 'D'
113
+ end
114
+
115
+ specify 'mapper should be avle to call methods of enabled traits' do
116
+ mapper = TraitsSpec::HostMapper.new(target, :trait_one)
117
+ mapper.method_one.should == 'one'
118
+ expect{ mapper.method_two }.to raise_error(NoMethodError)
119
+ end
120
+
121
+ specify 'traits should be able to call methods of each other' do
122
+ mapper = TraitsSpec::HostMapper.new(target, :trait_one, :trait_two)
123
+ mapper.trait(:trait_two).method_two.should == 'one'
124
+ end
125
+
126
+ describe 'trait nesting' do
127
+ let(:mapper){ TraitsSpec::HostMapper.new(target, :trait_one_nested) }
128
+
129
+ it 'should still be able to have top-level trait definitions' do
130
+ mapper.mounting(:spec_mount).should be_present
131
+ mapper.method_one.should == 'one'
132
+ end
133
+
134
+ it 'should have new definitions' do
135
+ mapper.method_one_nested.should == 'nested_one'
136
+ end
137
+ end
138
+
139
+ describe 'extension trait' do
140
+ let(:mapper) do
141
+ TraitsSpec::HostMapper.new(target) do
142
+ map :attr_b
143
+
144
+ def writing_error=(value)
145
+ raise ArgumentError, 'cannot be foo' if value == 'foo'
146
+ rescue ArgumentError => e
147
+ errors.preserve :writing_error, e.message
148
+ end
149
+ end
150
+ end
151
+
152
+ it 'should behave like a normal trait' do
153
+ mapper.trait(:extension).should be_present
154
+ mapper.read.should include :attr_b => 'b'
155
+ end
156
+
157
+ it 'should be accessible' do
158
+ mapper.extension.should be_present
159
+ end
160
+
161
+ it 'should be_extension' do
162
+ mapper.extension.should be_extension
163
+ end
164
+
165
+ it "should be able to handle save exception of traits" do
166
+ expect{ mapper.apply(:writing_error => 'foo') }.not_to raise_error
167
+ mapper.errors[:writing_error].should include 'cannot be foo'
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ class SpecFooValidator < ActiveModel::EachValidator
4
+ def validate_each(object, attribute, value)
5
+ object.errors.add(attribute, "can't be foo") if value == "foo"
6
+ end
7
+ end
8
+
9
+ module FlatMap
10
+ module ValidationsSpec
11
+ class TargetClass < Struct.new(:attr_a, :attr_b, :attr_c)
12
+ end
13
+
14
+ class MountTargetClass < Struct.new(:attr_d)
15
+ end
16
+
17
+ class MountMapper < Mapper
18
+ map :attr_d
19
+
20
+ validates :attr_d, :presence => true
21
+ end
22
+
23
+ class HostMapper < Mapper
24
+ map :attr_a
25
+
26
+ validates_presence_of :attr_a
27
+
28
+ trait :with_trait do
29
+ map :attr_b
30
+
31
+ validates :attr_b, :spec_foo => true
32
+
33
+ set_callback :validate, :before, :set_default_attr_b, :prepend => true
34
+
35
+ def set_default_attr_b
36
+ self.attr_b = 'foo' if attr_b.blank?
37
+ end
38
+ end
39
+
40
+ mount :mounted,
41
+ :target => lambda{ |_| ValidationsSpec::MountTargetClass.new },
42
+ :mapper_class_name => 'FlatMap::ValidationsSpec::MountMapper'
43
+ end
44
+ end
45
+
46
+ describe 'Validations' do
47
+ let(:mapper) do
48
+ ValidationsSpec::HostMapper.new(ValidationsSpec::TargetClass.new, :with_trait) do
49
+ map :attr_c
50
+ validates_presence_of :attr_c
51
+ end
52
+ end
53
+
54
+ it 'should not be valid' do
55
+ mapper.should_not be_valid
56
+ end
57
+
58
+ it 'should call callbacks' do
59
+ mapper.trait(:with_trait).should_receive(:set_default_attr_b).and_call_original
60
+ mapper.valid?
61
+ mapper.attr_b.should == 'foo'
62
+ end
63
+
64
+ it 'should have all the errors' do
65
+ mapper.valid?
66
+ mapper.errors[:attr_a].should == ["can't be blank"]
67
+ mapper.errors[:attr_b].should == ["can't be foo"]
68
+ mapper.errors[:attr_c].should == ["can't be blank"]
69
+ mapper.errors[:attr_d].should == ["can't be blank"]
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ describe Mapper do
5
+ # Place some complex behavior specs here.
6
+ # All specs responsible for basic modular behavior
7
+ # are located under spec/flat_map/mapper.
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Factory do
4
+ let(:factory){ described_class.new(:foo, :bar, :baz) }
5
+
6
+ specify('#create should delegate all initialization params to new mapping') do
7
+ mapper_stub = double('mapper')
8
+ FlatMap::Mapping.should_receive(:new).with(mapper_stub, :foo, :bar, :baz)
9
+
10
+ factory.create(mapper_stub)
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Reader::Basic do
4
+ let(:target){ double('target') }
5
+ let(:mapping){ double('mapping') }
6
+ let(:reader){ described_class.new(mapping) }
7
+
8
+ specify("#read should fetch value from mapping's target_attribute") do
9
+ mapping.should_receive(:target).and_return(target)
10
+ mapping.should_receive(:target_attribute).and_return(:foo)
11
+ target.should_receive(:foo).and_return(:bar)
12
+
13
+ reader.read.should == :bar
14
+ end
15
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Reader::Formatted do
4
+ let(:value){ 'fOoBaR' }
5
+ let(:target){ double('target', :attr => value) }
6
+ let(:mapping){ double('mapping', :target => target, :target_attribute => :attr) }
7
+
8
+ context 'generic behavior' do
9
+ let(:reader){ described_class.new(mapping, :spec_format).extend(spec_extension) }
10
+ let(:spec_extension) do
11
+ Module.new do
12
+ def spec_format(value, transformation = :upcase)
13
+ case transformation
14
+ when :upcase then value.upcase
15
+ when :downcase then value.downcase
16
+ else value
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ specify "#read should use formatting method for fetching a value" do
23
+ reader.read.should == 'FOOBAR'
24
+ reader.read(:downcase).should == 'foobar'
25
+ reader.read(:unknown).should == 'fOoBaR'
26
+ end
27
+ end
28
+
29
+ context 'default formats' do
30
+ describe "i18n_1" do
31
+ let(:reader){ described_class.new(mapping, :i18n_l) }
32
+
33
+ it "should use I18n_l to format value" do
34
+ formatted_value = "le FooBar"
35
+ I18n.should_receive(:l).with(value).and_return(formatted_value)
36
+
37
+ reader.read.should == formatted_value
38
+ end
39
+ end
40
+
41
+ describe "enum" do
42
+ unless defined? PowerEnum
43
+ module ::PowerEnum; end
44
+ load 'flat_map/mapping/reader/formatted/formats.rb'
45
+ end
46
+
47
+ let(:enum){ Object.new }
48
+ let(:target){ double('target', :attr => enum) }
49
+ let(:reader){ described_class.new(mapping, :enum) }
50
+
51
+ it "should use the name property of the target object for value" do
52
+ enum.should_receive(:name).and_return(value)
53
+ reader.read.should == value
54
+ end
55
+
56
+ it "should be able to use the desired method to get enum's property" do
57
+ enum.should_receive(:description).and_return(value)
58
+ reader.read(:description).should == value
59
+ end
60
+ end
61
+ end
62
+ end