HornsAndHooves-flat_map 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.metrics +17 -0
- data/.rspec +4 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +20 -0
- data/HornsAndHooves-flat_map.gemspec +29 -0
- data/LICENSE +21 -0
- data/README.markdown +214 -0
- data/Rakefile +15 -0
- data/lib/flat_map.rb +14 -0
- data/lib/flat_map/errors.rb +57 -0
- data/lib/flat_map/mapping.rb +124 -0
- data/lib/flat_map/mapping/factory.rb +21 -0
- data/lib/flat_map/mapping/reader.rb +12 -0
- data/lib/flat_map/mapping/reader/basic.rb +28 -0
- data/lib/flat_map/mapping/reader/formatted.rb +45 -0
- data/lib/flat_map/mapping/reader/formatted/formats.rb +28 -0
- data/lib/flat_map/mapping/reader/method.rb +25 -0
- data/lib/flat_map/mapping/reader/proc.rb +15 -0
- data/lib/flat_map/mapping/writer.rb +11 -0
- data/lib/flat_map/mapping/writer/basic.rb +25 -0
- data/lib/flat_map/mapping/writer/method.rb +28 -0
- data/lib/flat_map/mapping/writer/proc.rb +18 -0
- data/lib/flat_map/model_mapper.rb +195 -0
- data/lib/flat_map/model_mapper/persistence.rb +108 -0
- data/lib/flat_map/model_mapper/skipping.rb +45 -0
- data/lib/flat_map/open_mapper.rb +113 -0
- data/lib/flat_map/open_mapper/attribute_methods.rb +55 -0
- data/lib/flat_map/open_mapper/factory.rb +244 -0
- data/lib/flat_map/open_mapper/mapping.rb +123 -0
- data/lib/flat_map/open_mapper/mounting.rb +168 -0
- data/lib/flat_map/open_mapper/persistence.rb +178 -0
- data/lib/flat_map/open_mapper/skipping.rb +66 -0
- data/lib/flat_map/open_mapper/traits.rb +95 -0
- data/lib/flat_map/version.rb +3 -0
- data/spec/flat_map/errors_spec.rb +23 -0
- data/spec/flat_map/mapper/attribute_methods_spec.rb +36 -0
- data/spec/flat_map/mapper/callbacks_spec.rb +76 -0
- data/spec/flat_map/mapper/factory_spec.rb +285 -0
- data/spec/flat_map/mapper/mapping_spec.rb +98 -0
- data/spec/flat_map/mapper/mounting_spec.rb +142 -0
- data/spec/flat_map/mapper/persistence_spec.rb +152 -0
- data/spec/flat_map/mapper/skipping_spec.rb +91 -0
- data/spec/flat_map/mapper/targeting_spec.rb +156 -0
- data/spec/flat_map/mapper/traits_spec.rb +172 -0
- data/spec/flat_map/mapper/validations_spec.rb +72 -0
- data/spec/flat_map/mapper_spec.rb +9 -0
- data/spec/flat_map/mapping/factory_spec.rb +12 -0
- data/spec/flat_map/mapping/reader/basic_spec.rb +15 -0
- data/spec/flat_map/mapping/reader/formatted_spec.rb +62 -0
- data/spec/flat_map/mapping/reader/method_spec.rb +13 -0
- data/spec/flat_map/mapping/reader/proc_spec.rb +13 -0
- data/spec/flat_map/mapping/writer/basic_spec.rb +15 -0
- data/spec/flat_map/mapping/writer/method_spec.rb +13 -0
- data/spec/flat_map/mapping/writer/proc_spec.rb +13 -0
- data/spec/flat_map/mapping_spec.rb +123 -0
- data/spec/flat_map/open_mapper_spec.rb +19 -0
- data/spec/spec_helper.rb +7 -0
- data/tmp/metric_fu/_data/20131218.yml +6902 -0
- data/tmp/metric_fu/_data/20131219.yml +6726 -0
- metadata +220 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e476b0c2831807b9734932bcec75f3d798a09d08
|
4
|
+
data.tar.gz: 88d957bf1d9467c8abcf2f1e75e655c28630a51c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6d721973e3a24a1f9e42599f7daaa3ce4c8ca34b7de8cbdcd8cec93249a72ef4adbe29025ddf481a52de4622309dd04aa2d1cd8f89028cb426001cfa5f449b9f
|
7
|
+
data.tar.gz: f8b5c8efcdff94145f7a329abc3f124797754bcc50baf3ecd828d70cd9bb37973f8a247e4fb4d99b7fcc1b4ea14a61da4d808b30c923d40b032ad5641bee688a
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
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
|
+
# IDE
|
16
|
+
.idea
|
17
|
+
|
18
|
+
# jeweler generated
|
19
|
+
pkg
|
20
|
+
|
21
|
+
*.gem
|
22
|
+
Gemfile.lock
|
23
|
+
|
24
|
+
# For MacOS:
|
25
|
+
.DS_Store
|
26
|
+
|
27
|
+
# exclude everything in tmp
|
28
|
+
tmp/*
|
29
|
+
# except the metric_fu directory
|
30
|
+
!tmp/metric_fu/
|
31
|
+
# but exclude everything *in* the metric_fu directory
|
32
|
+
tmp/metric_fu/*
|
33
|
+
# except for the _data directory to track metrical outputs
|
34
|
+
!tmp/metric_fu/_data/
|
data/.metrics
ADDED
@@ -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
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
flat_map
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.2
|
data/.travis.yml
ADDED
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
|
@@ -0,0 +1,29 @@
|
|
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 = "HornsAndHooves-flat_map"
|
7
|
+
s.version = FlatMap::VERSION
|
8
|
+
s.authors = ["Artem Kuzko", "Zachary Belzer", "Sergey Potapov"]
|
9
|
+
s.email = ["a.kuzko@gmail.com", "zbelzer@gmail.com", "blake131313@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/HornsAndHooves/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.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_dependency(%q<activesupport>, ["~> 4.0.5"])
|
23
|
+
s.add_dependency(%q<activerecord>, ["~> 4.0.5"])
|
24
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
25
|
+
|
26
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
s.add_development_dependency "rspec-its"
|
28
|
+
s.add_development_dependency "rake"
|
29
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 HornsAndHooves
|
4
|
+
Copyright (c) 2013 TMXCredit, authors Artem Kuzko, Zachary Belzer, Sergey Potapov
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
7
|
+
this software and associated documentation files (the "Software"), to deal in
|
8
|
+
the Software without restriction, including without limitation the rights to
|
9
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
10
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
11
|
+
subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
18
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
19
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
20
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
# FlatMap
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/HornsAndHooves/flat_map.png)](http://travis-ci.org/HornsAndHooves/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
|
+
* `:mapper_class_name` Alternate string form of class name instead of mapper_class.
|
90
|
+
* `:target` Allows to manually specify target for the new mapper. May be oject or lambda
|
91
|
+
with arity of one that accepts host mapper target as argument. Comes in handy
|
92
|
+
when target cannot be obviously detected or requires additional setup:
|
93
|
+
`mount :title, :target => lambda{ |customer| customer.title_customers.build.build_title }`
|
94
|
+
* `:traits` Specifies list of traits to be used by mounted mapper
|
95
|
+
* `:suffix` Specifies the suffix that will be appended to all mappings and mountings of mapper,
|
96
|
+
as well as mapper name itself.
|
97
|
+
|
98
|
+
### Traits
|
99
|
+
|
100
|
+
Traits allow mappers to encapsulate named sets of additional definitions, and use them optionally
|
101
|
+
on mapper initialization. Everything that can be defined within the mapper may be defined within
|
102
|
+
the trait. In fact, from the implementation perspective traits are mappers themselves that are
|
103
|
+
mounted on the host mapper.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
class CustomerAccountMapper < FlatMap::Mapper
|
107
|
+
map :brand, :format => :enum
|
108
|
+
trait :with_email do
|
109
|
+
map :source, :format => :enum
|
110
|
+
mount :email_address
|
111
|
+
trait :with_email_phones_residence do
|
112
|
+
mount :customer, :traits => [:with_phone_numbers, :with_residence]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
CustomerAccountMapper.find(1).read # => {:brand => 'TLP'}
|
117
|
+
CustomerAccountMapper.find(1, :with_email).read # => {:brand => 'TLP', :source => nil, :email_address => 'j.smith@gmail.com'}
|
118
|
+
CustomerAccountMapper.find(1, :with_email_phone_residence).read # => :brand, :source, :email_address, phone numbers,
|
119
|
+
#:residence attributes - all will be available for reading and writing in plain hash
|
120
|
+
```
|
121
|
+
|
122
|
+
### Extensions
|
123
|
+
|
124
|
+
When mounting a mapper, one can pass an optional block. This block is used as an extension for a mounted
|
125
|
+
mapper and acts as an anonymous trait. For example:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class CustomerAccountMapper < FlatMap::Mapper
|
129
|
+
mount :customer do
|
130
|
+
map :dob => :date_of_birth, :format => :i18n_l
|
131
|
+
validates_presence_of :dob
|
132
|
+
|
133
|
+
mount :unique_identifier
|
134
|
+
|
135
|
+
validates_acceptance_of :mandatory_agreement, :message => "You must check this box to continue"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
### Validation
|
141
|
+
|
142
|
+
`FlatMap::Mapper` includes `ActiveModel::Validations` module, allowing each model to
|
143
|
+
perform its own validation routines before trying to save its target (which is usually AR model). Mapper
|
144
|
+
validation is very handy when mappers are used with Rails forms, since there no need to lookup for a
|
145
|
+
deeply nested errors hash of the AR models to extract error messages. Mapper validations will attach
|
146
|
+
messages to mapping names.
|
147
|
+
Mapper validations become even more useful when used within traits, providing way of very flexible validation sets.
|
148
|
+
|
149
|
+
### Callbacks
|
150
|
+
|
151
|
+
Since mappers include `ActiveModel::Validation`, they already support ActiveSupport's callbacks.
|
152
|
+
Additionally, `:save` callbacks have been defined (i.e. there have been define_callbacks `:save`
|
153
|
+
call for `FlatMap::Mapper`). This allows you to control flow of mapper saving:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
set_callback :save, :before, :set_model_validation
|
157
|
+
def set_model_validation
|
158
|
+
target.use_validation :some_themis_validation
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
### Skipping
|
163
|
+
|
164
|
+
In some cases, it is required to omit mapper processing after it has been created within mounting chain. If
|
165
|
+
`skip!` method is called on mapper, it will return `true` for `valid?` and `save`
|
166
|
+
method calls without performing any other operations. For example:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
class CustomerMapper < FlatMap::Mapper
|
170
|
+
# some definitions
|
171
|
+
|
172
|
+
trait :product_selection do
|
173
|
+
attr_reader :selected_product_id
|
174
|
+
|
175
|
+
mount :product
|
176
|
+
|
177
|
+
set_callback :validate, :before, :ignore_new_product
|
178
|
+
|
179
|
+
def ignore_new_product
|
180
|
+
mounting(:product).skip! if product_selected?
|
181
|
+
end
|
182
|
+
|
183
|
+
# some more definitions
|
184
|
+
end
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
### Attribute Methods
|
189
|
+
|
190
|
+
All mappers have the ability to read and write values via method calls:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
mapper.read[:first_name] # => John
|
194
|
+
mapper.first_name # => 'John'
|
195
|
+
mapper.last_name = 'Smith'
|
196
|
+
```
|
197
|
+
|
198
|
+
## Run tests
|
199
|
+
|
200
|
+
```sh
|
201
|
+
rake spec
|
202
|
+
```
|
203
|
+
|
204
|
+
## Credits
|
205
|
+
|
206
|
+
* [Artem Kuzko](https://github.com/akuzko)
|
207
|
+
* [Zachary Belzer](https://github.com/zbelzer)
|
208
|
+
* [Potapov Sergey](https://github.com/greyblake)
|
209
|
+
|
210
|
+
## Copyright
|
211
|
+
|
212
|
+
Copyright (c) 2014 HornsAndHooves.
|
213
|
+
|
214
|
+
Copyright (c) 2013 TMX Credit.
|
data/Rakefile
ADDED
@@ -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
|
data/lib/flat_map.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
require "flat_map/version"
|
6
|
+
require 'flat_map/mapping'
|
7
|
+
require 'flat_map/errors'
|
8
|
+
require 'flat_map/open_mapper'
|
9
|
+
require 'flat_map/model_mapper'
|
10
|
+
|
11
|
+
module FlatMap
|
12
|
+
# for backwards compatability
|
13
|
+
Mapper = ModelMapper
|
14
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module FlatMap
|
2
|
+
# Inherited from ActiveModel::Errors to slightly ease work when writing
|
3
|
+
# attributes in a way that can possibly result in an exception. If we'd
|
4
|
+
# want to add errors on that point and see them in the resulting object,
|
5
|
+
# we have to preserve them before owner's <tt>run_validations!</tt> method
|
6
|
+
# call, since it will clear all the errors.
|
7
|
+
#
|
8
|
+
# After validation complete, preserved errors are added to the list of
|
9
|
+
# the original ones.
|
10
|
+
#
|
11
|
+
# Usecase scenario:
|
12
|
+
#
|
13
|
+
# class MyMapper < FlatMap::Mapper
|
14
|
+
# def custom_attr=(value)
|
15
|
+
# raise MyException, 'cannot be foo' if value == 'foo'
|
16
|
+
# rescue MyException => e
|
17
|
+
# errors.preserve :custom_attr, e.message
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# mapper = MyMapper.new(MyObject.new)
|
22
|
+
# mapper.apply(:custom_attr => 'foo') # => false
|
23
|
+
# mapper.errors[:custom_attr] # => ['cannot be foo']
|
24
|
+
class Errors < ActiveModel::Errors
|
25
|
+
# Add <tt>@preserved_errors</tt> to object.
|
26
|
+
def initialize(*)
|
27
|
+
@preserved_errors = {}
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
# Postpone error. It will be added to <tt>@messages</tt> later,
|
32
|
+
# on <tt>empty?</tt> method call.
|
33
|
+
#
|
34
|
+
# @param [String, Symbol] key
|
35
|
+
# @param [String] message
|
36
|
+
def preserve(key, message)
|
37
|
+
@preserved_errors[key] = message
|
38
|
+
end
|
39
|
+
|
40
|
+
# Overloaded to add <tt>@preserved_errors</tt> to the list of
|
41
|
+
# original <tt>@messages</tt>. <tt>@preserved_errors</tt> are
|
42
|
+
# cleared after this method call.
|
43
|
+
def empty?
|
44
|
+
unless @preserved_errors.empty?
|
45
|
+
@preserved_errors.each{ |key, value| add(key, value) }
|
46
|
+
@preserved_errors.clear
|
47
|
+
end
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
# Overridden to add suffixing support for mappings of mappers with name suffix
|
52
|
+
def add(attr, *args)
|
53
|
+
attr = :"#{attr}_#{@base.suffix}" if attr != :base && @base.suffixed?
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|