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,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
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Reader::Method do
4
+ let(:mapper){ double('mapper') }
5
+ let(:mapping){ double('mapping', :mapper => mapper) }
6
+ let(:reader){ described_class.new(mapping, :read_with_method) }
7
+
8
+ specify("#read delegates to mapper-defined method and passes mapping to it") do
9
+ mapper.should_receive(:read_with_method).with(mapping).and_return(:bar)
10
+
11
+ reader.read.should == :bar
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Reader::Proc do
4
+ let(:target){ double('target') }
5
+ let(:mapping){ double('mapping', :target => target) }
6
+ let(:reader){ described_class.new(mapping, lambda{ |t| t.foo }) }
7
+
8
+ specify("#read should pass target to Proc object to fetch value") do
9
+ target.should_receive(:foo).and_return(:bar)
10
+
11
+ reader.read.should == :bar
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Writer::Basic do
4
+ let(:target){ double('target') }
5
+ let(:mapping){ double('mapping') }
6
+ let(:writer){ described_class.new(mapping) }
7
+
8
+ specify("#write use target_attribute as writer to assign value to target") do
9
+ mapping.should_receive(:target).and_return(target)
10
+ mapping.should_receive(:target_attribute).and_return(:foo)
11
+ target.should_receive(:foo=).with(:bar)
12
+
13
+ writer.write(:bar)
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Writer::Method do
4
+ let(:mapper){ double('mapper') }
5
+ let(:mapping){ double('mapping', :mapper => mapper) }
6
+ let(:writer){ described_class.new(mapping, :write_with_method) }
7
+
8
+ specify("#write delegates to mapper-defined method and passes mapping and value to it") do
9
+ mapper.should_receive(:write_with_method).with(mapping, :foo)
10
+
11
+ writer.write(:foo)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe FlatMap::Mapping::Writer::Proc do
4
+ let(:target){ double('target') }
5
+ let(:mapping){ double('mapping', :target => target) }
6
+ let(:writer){ described_class.new(mapping, lambda{ |t, v| t.foo(v) }) }
7
+
8
+ specify("#write should pass target and value to Proc object for assignment") do
9
+ target.should_receive(:foo).with(:bar)
10
+
11
+ writer.write(:bar)
12
+ end
13
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ module FlatMap
4
+ describe Mapping do
5
+ let(:mapper){ double('mapper', :suffixed? => false) }
6
+ let(:mapping){ Mapping.new(mapper, :name, :attr) }
7
+
8
+ describe "initialization" do
9
+ it "should be initializable" do
10
+ expect{ mapping }.not_to raise_error
11
+ end
12
+
13
+ it 'should delegate #read to reader' do
14
+ mapping.reader.should_receive(:read)
15
+ mapping.read
16
+ end
17
+
18
+ it 'should delegate #write to writer' do
19
+ mapping.writer.should_receive(:write).with(:foo)
20
+ mapping.write(:foo)
21
+ end
22
+
23
+ context "properties" do
24
+ let(:mapper){ double('mapper', :suffixed? => true, :suffix => 'foo') }
25
+
26
+ subject{ Mapping.new(mapper, :name, :attr, :multiparam => Date) }
27
+
28
+ its(:mapper){ should == mapper }
29
+ its(:name){ should == :name }
30
+ its(:target_attribute){ should == :attr }
31
+ its(:full_name){ should == :name_foo }
32
+ its(:multiparam){ should == Date }
33
+ its(:multiparam?){ should be_true }
34
+ end
35
+
36
+ describe "#fetch_reader" do
37
+ context 'default' do
38
+ subject{ Mapping.new(mapper, :name, :attr) }
39
+
40
+ its(:reader){ should be_a(Mapping::Reader::Basic) }
41
+ end
42
+
43
+ context 'method' do
44
+ subject{ Mapping.new(mapper, :name, :attr, :reader => :method_name) }
45
+
46
+ its(:reader){ should be_a(Mapping::Reader::Method) }
47
+ end
48
+
49
+ context 'proc' do
50
+ subject{ Mapping.new(mapper, :name, :attr, :reader => lambda{ |t| t.foo }) }
51
+
52
+ its(:reader){ should be_a(Mapping::Reader::Proc) }
53
+ end
54
+
55
+ context 'formatted' do
56
+ subject{ Mapping.new(mapper, :name, :attr, :format => :i18n_l) }
57
+
58
+ its(:reader){ should be_a(Mapping::Reader::Formatted) }
59
+ end
60
+
61
+ context 'blank' do
62
+ subject{ Mapping.new(mapper, :name, :attr, :reader => false) }
63
+
64
+ its(:reader){ should be_nil }
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#fetch_writer" do
70
+ context 'default' do
71
+ subject{ Mapping.new(mapper, :name, :attr) }
72
+
73
+ its(:writer){ should be_a(Mapping::Writer::Basic) }
74
+ end
75
+
76
+ context 'method' do
77
+ subject{ Mapping.new(mapper, :name, :attr, :writer => :method_name) }
78
+
79
+ its(:writer){ should be_a(Mapping::Writer::Method) }
80
+ end
81
+
82
+ context 'proc' do
83
+ subject{ Mapping.new(mapper, :name, :attr, :writer => lambda{ |t| t.foo }) }
84
+
85
+ its(:writer){ should be_a(Mapping::Writer::Proc) }
86
+ end
87
+
88
+ context 'blank' do
89
+ subject{ Mapping.new(mapper, :name, :attr, :writer => false) }
90
+
91
+ its(:writer){ should be_nil }
92
+ end
93
+ end
94
+
95
+ describe "read_as_params" do
96
+ it "should return mapping as key value pair" do
97
+ stub_target
98
+ mapping.read_as_params.should == {:name => "target_foo"}
99
+ end
100
+ end
101
+
102
+ describe "write_from_params" do
103
+ it "should set the correct value on the target from params" do
104
+ stub_target('value')
105
+ mapping.write_from_params(:name => "value")
106
+ end
107
+
108
+ it "should set no value on the target from params" do
109
+ target = stub_target
110
+ target.should_not_receive(:attr=)
111
+ mapping.write_from_params(:not_mapping_name => "value")
112
+ target.attr.should == 'target_foo'
113
+ end
114
+ end
115
+
116
+ def stub_target(assignment = nil)
117
+ target = double('target', :attr => 'target_foo')
118
+ mapper.stub(:target).and_return(target)
119
+ target.should_receive(:attr=).with(assignment) if assignment.present?
120
+ target
121
+ end
122
+ end
123
+ end