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,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjNmMzU5MGI0NjIwZGQyZmY4YzBlM2RhYzFmYTMzNGJmM2ZiMzIzMA==
5
+ data.tar.gz: !binary |-
6
+ NjAxZjRjYThiNWMxN2QwN2NkMWNkODFhZDg0NjEzZDQ0MGE5YWM5ZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ N2M2NmQzZmFkMjZjMjg3NThmZjhjN2I0YmI2NGMyMWQxZTcyNTYwYTFmNzJj
10
+ NmM0Yzc1NDg4YWM1MmM1ZGE3ZTA3MTUzZTNmNjgwZGZkZDYxN2ViMzliNDVl
11
+ NWQ4Yjg3ZDI5NTg2MmZiOTQ2ZjQ5ODk1YTUwODQyN2JmNzIxNDQ=
12
+ data.tar.gz: !binary |-
13
+ ZTg1ZmI2NzY1NjA3ZGYyYzQ1NTAwYzFlMDUxODY2Y2M5YmNlMWI5YTNkNWYx
14
+ MTI5NGQwNDA5YmQwOTNjOTJiYmRhZDdjYWY0YTkwMzU4MzQxM2FlYjU5Nzgy
15
+ YzlkYmEzNmNlM2ExNTA5M2E5Y2E1OGFlNDViYmU1MGVmYzg5M2U=
@@ -0,0 +1,31 @@
1
+ # SimpleCov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ *.gem
19
+ Gemfile.lock
20
+
21
+ # For MacOS:
22
+ .DS_Store
23
+
24
+ # exclude everything in tmp
25
+ tmp/*
26
+ # except the metric_fu directory
27
+ !tmp/metric_fu/
28
+ # but exclude everything *in* the metric_fu directory
29
+ tmp/metric_fu/*
30
+ # except for the _data directory to track metrical outputs
31
+ !tmp/metric_fu/_data/
@@ -0,0 +1,17 @@
1
+ MetricFu::Configuration.run do |config|
2
+ [ :rcov,
3
+ :rails_best_practices
4
+ ].each do |metric|
5
+ config.configure_metric(metric) do |m|
6
+ m.enabled = false
7
+ end
8
+ end
9
+
10
+ config.configure_metric(:cane) do |cane|
11
+ cane.line_length = 100
12
+ end
13
+
14
+ config.configure_metric(:flay) do |flay|
15
+ flay.minimum_score = 10
16
+ end
17
+ end
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format nested
3
+ --order rand
4
+ --profile
@@ -0,0 +1 @@
1
+ flat_map
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p448
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ script: "bundle exec rake spec"
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+ notifications:
8
+ email:
9
+ - a.kuzko@gmail.com
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in flat_map.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'redcarpet'
8
+ gem 'yard'
9
+ gem 'pry'
10
+ end
11
+
12
+ group :development, :test do
13
+ # code metrics:
14
+ gem "metric_fu"
15
+ end
16
+
17
+ group :test do
18
+ gem 'simplecov' , :require => false
19
+ gem 'simplecov-rcov-text', :require => false
20
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 TMXCredit, authors Artem Kuzko, Zachary Belzer, Sergey Potapov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,211 @@
1
+ # FlatMap
2
+
3
+ [![Build Status](https://secure.travis-ci.org/TMXCredit/flat_map.png)](http://travis-ci.org/TMXCredit/flat_map)
4
+
5
+ FlatMap is a flexible tool for mapping a complex, deeply nested object graph
6
+ into a mapper object with all mapped attributes accessible in a plain way.
7
+
8
+
9
+ ## Usage
10
+
11
+ ### Mapper
12
+
13
+ FlatMap mappers are designed to provide complex set of data, distributed over
14
+ associated AR models, in the simple form of a plain hash. They accept a plain
15
+ hash of the same format and distribute its values over deeply nested AR models.
16
+ To achieve this goal, Mapper uses three major concepts: Mappings, Mountings and
17
+ Traits.
18
+
19
+ ### Mappings
20
+
21
+ Mappings are defined view `Mapper.map` method. They represent a simple one-to-one
22
+ relation between target attribute and a mapper, extended by additional features
23
+ for convenience. The best way to show how they work is by example:
24
+
25
+ ```ruby
26
+ class CustomerMapper < FlatMap::Mapper
27
+ # When there is no need to rename attributes, they can be passed as array:
28
+ map :first_name, :last_name
29
+ # When hash is used, it will map field name to attribute name:
30
+ map :dob => :date_of_birth
31
+ # Also, additional options can be used:
32
+ map :name_suffix, :format => :enum
33
+ map :password, :reader => false, :writer => :assign_password
34
+ # Or you can combine all definitions together if they all are common:
35
+ map :first_name, :last_name,
36
+ :dob => :date_of_birth,
37
+ :suffix => :name_suffix,
38
+ :reader => :my_custom_reader
39
+ end
40
+ ```
41
+ When mappings are defined, one can read and write values using them:
42
+
43
+ ```ruby
44
+ mapper = CustomerMapper.find(1)
45
+ mapper.read # => {:first_name => 'John', :last_name => 'Smith', :dob => '02/01/1970'}
46
+ mapper.write(params) # will assign same-looking hash of arguments
47
+ ```
48
+
49
+ Following options may be used when defining mappings:
50
+
51
+ * `:format` Allows to additionally process output value on reading it. All formats are
52
+ defined within `FlatMap::Mapping::Reader::Formatted::Formats` and
53
+ specify the actual output of the mapping
54
+ * `:reader` Allows you to manually control reader value of a mapping, or a group of
55
+ mappings listed on definition. When String or Symbol is used, will call
56
+ a method, defined by mapper class, and pass mapping object to it. When
57
+ lambda is used, mapper's target (the model) will be passed to it.
58
+ * `:writer` Just like with the :reader option, allows to control how value is assigned
59
+ (written). Works the same way as :reader does, but additionally value is
60
+ sent to both mapper method and lambda.
61
+ * `:multiparam` If used, multiparam attributes will be extracted from params, when
62
+ those are passed for writing. Class should be passed as a value for
63
+ this option. Object of this class will be initialized with the arguments
64
+ extracted from params hash.
65
+
66
+ ### Mountings
67
+
68
+ Mappers may be mounted on top of each other. This ability allows host mapper to gain all the
69
+ mappings of the mounted mapper, thus providing more information for external usage (both reading
70
+ and writing). Usually, target for mounted mapper may be obtained from association of target of
71
+ the host mapper itself, but may be defined manually.
72
+
73
+ ```ruby
74
+ class CustomerMapper < FlatMap::Mapper
75
+ map :first_name, :last_name
76
+ end
77
+ class CustomerAccountMapper < FlatMap::Mapper
78
+ map :source, :brand, :format => :enum
79
+ mount :customer
80
+ end
81
+ mapper = CustomerAccountMapper.find(1)
82
+ mapper.read # => {:first_name => 'John', :last_name => 'Smith', :source => nil, :brand => 'FTW'}
83
+ mapper.write(params) # Will assign params for both CustomerAccount and Customer records
84
+ ```
85
+
86
+ The following options may be used when mounting a mapper:
87
+
88
+ * `:mapper_class` Specifies mapper class if it cannot be determined from mounting itself
89
+ * `:target` Allows to manually specify target for the new mapper. May be oject or lambda
90
+ with arity of one that accepts host mapper target as argument. Comes in handy
91
+ when target cannot be obviously detected or requires additional setup:
92
+ `mount :title, :target => lambda{ |customer| customer.title_customers.build.build_title }`
93
+ * `:traits` Specifies list of traits to be used by mounted mapper
94
+ * `:suffix` Specifies the suffix that will be appended to all mappings and mountings of mapper,
95
+ as well as mapper name itself.
96
+
97
+ ### Traits
98
+
99
+ Traits allow mappers to encapsulate named sets of additional definitions, and use them optionally
100
+ on mapper initialization. Everything that can be defined within the mapper may be defined within
101
+ the trait. In fact, from the implementation perspective traits are mappers themselves that are
102
+ mounted on the host mapper.
103
+
104
+ ```ruby
105
+ class CustomerAccountMapper < FlatMap::Mapper
106
+ map :brand, :format => :enum
107
+ trait :with_email do
108
+ map :source, :format => :enum
109
+ mount :email_address
110
+ trait :with_email_phones_residence do
111
+ mount :customer, :traits => [:with_phone_numbers, :with_residence]
112
+ end
113
+ end
114
+ end
115
+ CustomerAccountMapper.find(1).read # => {:brand => 'TLP'}
116
+ CustomerAccountMapper.find(1, :with_email).read # => {:brand => 'TLP', :source => nil, :email_address => 'j.smith@gmail.com'}
117
+ CustomerAccountMapper.find(1, :with_email_phone_residence).read # => :brand, :source, :email_address, phone numbers,
118
+ #:residence attributes - all will be available for reading and writing in plain hash
119
+ ```
120
+
121
+ ### Extensions
122
+
123
+ When mounting a mapper, one can pass an optional block. This block is used as an extension for a mounted
124
+ mapper and acts as an anonymous trait. For example:
125
+
126
+ ```ruby
127
+ class CustomerAccountMapper < FlatMap::Mapper
128
+ mount :customer do
129
+ map :dob => :date_of_birth, :format => :i18n_l
130
+ validates_presence_of :dob
131
+
132
+ mount :unique_identifier
133
+
134
+ validates_acceptance_of :mandatory_agreement, :message => "You must check this box to continue"
135
+ end
136
+ end
137
+ ```
138
+
139
+ ### Validation
140
+
141
+ `FlatMap::Mapper` includes `ActiveModel::Validations` module, allowing each model to
142
+ perform its own validation routines before trying to save its target (which is usually AR model). Mapper
143
+ validation is very handy when mappers are used with Rails forms, since there no need to lookup for a
144
+ deeply nested errors hash of the AR models to extract error messages. Mapper validations will attach
145
+ messages to mapping names.
146
+ Mapper validations become even more useful when used within traits, providing way of very flexible validation sets.
147
+
148
+ ### Callbacks
149
+
150
+ Since mappers include `ActiveModel::Validation`, they already support ActiveSupport's callbacks.
151
+ Additionally, `:save` callbacks have been defined (i.e. there have been define_callbacks `:save`
152
+ call for `FlatMap::Mapper`). This allows you to control flow of mapper saving:
153
+
154
+ ```ruby
155
+ set_callback :save, :before, :set_model_validation
156
+ def set_model_validation
157
+ target.use_validation :some_themis_validation
158
+ end
159
+ ```
160
+
161
+ ### Skipping
162
+
163
+ In some cases, it is required to omit mapper processing after it has been created within mounting chain. If
164
+ `skip!` method is called on mapper, it will return `true` for `valid?` and `save`
165
+ method calls without performing any other operations. For example:
166
+
167
+ ```ruby
168
+ class CustomerMapper < FlatMap::Mapper
169
+ # some definitions
170
+
171
+ trait :product_selection do
172
+ attr_reader :selected_product_id
173
+
174
+ mount :product
175
+
176
+ set_callback :validate, :before, :ignore_new_product
177
+
178
+ def ignore_new_product
179
+ mounting(:product).skip! if product_selected?
180
+ end
181
+
182
+ # some more definitions
183
+ end
184
+ end
185
+ ```
186
+
187
+ ### Attribute Methods
188
+
189
+ All mappers have the ability to read and write values via method calls:
190
+
191
+ ```ruby
192
+ mapper.read[:first_name] # => John
193
+ mapper.first_name # => 'John'
194
+ mapper.last_name = 'Smith'
195
+ ```
196
+
197
+ ## Run tests
198
+
199
+ ```sh
200
+ rake spec
201
+ ```
202
+
203
+ ## Credits
204
+
205
+ * [Artem Kuzko](https://github.com/akuzko)
206
+ * [Zachary Belzer](https://github.com/zbelzer)
207
+ * [Potapov Sergey](https://github.com/greyblake)
208
+
209
+ ## Copyright
210
+
211
+ Copyright (c) 2013 TMX Credit.
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ task :default => :spec
5
+ RSpec::Core::RakeTask.new
6
+
7
+ require 'rdoc/task'
8
+ Rake::RDocTask.new do |rdoc|
9
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
10
+
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = "flat_map #{version}"
13
+ rdoc.rdoc_files.include('README*')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "flat_map/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "flat_map"
7
+ s.version = FlatMap::VERSION
8
+ s.authors = ["TMX Credit", "Artem Kuzko", "Zachary Belzer", "Sergey Potapov"]
9
+ s.email = ["rubygems@tmxcredit.com", "a.kuzko@gmail.com", "zbelzer@gmail.com", "blake131313@gmail.com"]
10
+ s.homepage = "https://github.com/TMXCredit/flat_map"
11
+ s.licenses = ["LICENSE"]
12
+ s.summary = %q{Deep object graph to a plain properties mapper}
13
+ s.description = %q{This library allows to map accessors and properties of deeply
14
+ nested object graph to a plain mapper object with flexible behavior}
15
+
16
+ s.rubyforge_project = "flat_map"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ # specify any dependencies here; for example:
24
+ s.add_dependency(%q<activesupport>, ["~> 3.2"])
25
+ s.add_dependency(%q<activerecord>, ["~> 3.2"])
26
+ s.add_dependency(%q<yard>, [">= 0"])
27
+
28
+ s.add_development_dependency "rspec"
29
+ s.add_development_dependency "rake"
30
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_support/core_ext'
2
+ require 'active_record'
3
+
4
+ require "flat_map/version"
5
+ require 'flat_map/mapping'
6
+ require 'flat_map/errors'
7
+ require 'flat_map/base_mapper'
8
+ require 'flat_map/mapper'
9
+ require 'flat_map/empty_mapper'
@@ -0,0 +1,95 @@
1
+ module FlatMap
2
+ # +BaseMapper+ is an abstract class that hosts overwhelming majority
3
+ # of common functionality of {EmptyMapper EmptyMappers} and {Mapper Mappers}.
4
+ #
5
+ # For more detailed information on what mappers are, refer to {Mapper} documentation.
6
+ class BaseMapper
7
+ extend ActiveSupport::Autoload
8
+
9
+ autoload :Mapping
10
+ autoload :Mounting
11
+ autoload :Traits
12
+ autoload :Factory
13
+ autoload :AttributeMethods
14
+ autoload :Persistence
15
+ autoload :Skipping
16
+
17
+ include Mapping
18
+ include Mounting
19
+ include Traits
20
+ include AttributeMethods
21
+ include ActiveModel::Validations
22
+ include Persistence
23
+ include Skipping
24
+
25
+ attr_reader :traits
26
+ attr_writer :host, :suffix
27
+ attr_accessor :owner, :name
28
+
29
+ # Callback to dup mappings and mountings on inheritance.
30
+ # The values are cloned from actual mappers (i.e. something
31
+ # like CustomerAccountMapper, since it is useless to clone
32
+ # empty values of FlatMap::Mapper).
33
+ #
34
+ # Note: those class attributes are defined in {Mapping}
35
+ # and {Mounting} modules.
36
+ def self.inherited(subclass)
37
+ subclass.mappings = mappings.dup
38
+ subclass.mountings = mountings.dup
39
+ end
40
+
41
+ # Raise exception on trying to initialize an instance.
42
+ #
43
+ # @raise [RuntimeError]
44
+ def initialize
45
+ raise 'BaseMapper is abstract class and cannot be initialized'
46
+ end
47
+
48
+ # Return a simple string representation of +mapper+. Done so to
49
+ # avoid really long inspection of internal objects (target -
50
+ # usually AR model, mountings and mappings)
51
+ # @return [String]
52
+ def inspect
53
+ to_s
54
+ end
55
+
56
+ # Return +true+ if +mapper+ is owned. This means that current
57
+ # mapper is actually a trait. Thus, it is a part of an owner
58
+ # mapper.
59
+ #
60
+ # @return [Boolean]
61
+ def owned?
62
+ owner.present?
63
+ end
64
+
65
+ # If mapper was mounted by another mapper, host is the one who
66
+ # mounted +self+.
67
+ #
68
+ # @return [FlatMap::Mapper]
69
+ def host
70
+ owned? ? owner.host : @host
71
+ end
72
+
73
+ # Return +true+ if mapper is hosted, i.e. it is mounted by another
74
+ # mapper.
75
+ #
76
+ # @return [Boolean]
77
+ def hosted?
78
+ host.present?
79
+ end
80
+
81
+ # +suffix+ reader. Delegated to owner for owned mappers.
82
+ #
83
+ # @return [String, nil]
84
+ def suffix
85
+ owned? ? owner.suffix : @suffix
86
+ end
87
+
88
+ # Return +true+ if +suffix+ is present.
89
+ #
90
+ # @return [Boolean]
91
+ def suffixed?
92
+ suffix.present?
93
+ end
94
+ end
95
+ end