flatter 0.2.1 → 1.0.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +11 -3
- data/flatter.gemspec +2 -0
- data/lib/flatter/extensions/active_record.rb +160 -0
- data/lib/flatter/extensions/multiparam.rb +51 -0
- data/lib/flatter/extensions/order.rb +32 -0
- data/lib/flatter/extensions/skipping.rb +56 -0
- data/lib/flatter/extensions.rb +7 -0
- data/lib/flatter/mapper/validation.rb +27 -0
- data/lib/flatter/mapper.rb +2 -0
- data/lib/flatter/version.rb +1 -1
- data/lib/flatter.rb +2 -0
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 542b10f00cb2f869988def6cd621cf3c6588c50f
|
4
|
+
data.tar.gz: 482d1248aa1204fe7831898ac84e98214d961673
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b9a7a0eb25675f0402768233e990e854f2a73bb6473af393e575434134002041e3257a74b150f262290e5c38c4e9ba8a0b42f6ee291d6e02505402e089c11e8
|
7
|
+
data.tar.gz: 182d1f6281a6713f07271a7533db99c56b777f479ed527a1d719398f999992d92a29a9ffeff5ce99ca536b6ce0c5bc8f5eb7a180e6c9bcd3eb2c4caaf40749b8
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -163,6 +163,13 @@ Mappers include `ActiveModel::Validation` module and thus support `ActiveSupport
|
|
163
163
|
callbacks. Additionally, `:save` callbacks have been defined for `Flatter::Mapper`,
|
164
164
|
so you can do something like `set_callback :save, :after, :send_invitation`.
|
165
165
|
|
166
|
+
### Mapper and Target Validations
|
167
|
+
|
168
|
+
If mapper's target responds to `valid?` method, it will be called upon mapper's
|
169
|
+
validation. If target is invalid, mapper will receive `:target, :invalid` error.
|
170
|
+
Additionally, all target's errors on attributes that have declared mapping will
|
171
|
+
be consolidated with mapper's errors.
|
172
|
+
|
166
173
|
### Traits
|
167
174
|
|
168
175
|
Traits are another powerful mapper ability. Traits allow to encapsulate named sets
|
@@ -602,9 +609,10 @@ mapper.errors.messages # =>
|
|
602
609
|
|
603
610
|
### Extensions
|
604
611
|
|
605
|
-
Aside from core functionality and behavior
|
606
|
-
[
|
607
|
-
|
612
|
+
Aside from core functionality and behavior described above, there is also
|
613
|
+
number of handy [extensions](https://github.com/akuzko/flatter/wiki/Extensions)
|
614
|
+
(which originally were hosted in their own gem, but now are the part of the flatter)
|
615
|
+
that have aim to help you use mappers more efficiently. At this point there
|
608
616
|
are following extensions:
|
609
617
|
|
610
618
|
- `:multiparam` Allows you to define multiparam mappings by adding `:multiparam`
|
data/flatter.gemspec
CHANGED
@@ -26,6 +26,8 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency "activesupport", ">= 3.2"
|
27
27
|
spec.add_dependency "activemodel", ">= 3.0"
|
28
28
|
|
29
|
+
spec.add_development_dependency "activerecord", ">= 3.2"
|
30
|
+
spec.add_development_dependency "sqlite3"
|
29
31
|
spec.add_development_dependency "bundler", "~> 1.10"
|
30
32
|
spec.add_development_dependency "rake", "~> 10.0"
|
31
33
|
spec.add_development_dependency "rspec"
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Flatter::Extensions
|
2
|
+
module ActiveRecord
|
3
|
+
extend ::Flatter::Extension
|
4
|
+
|
5
|
+
register_as :active_record
|
6
|
+
|
7
|
+
hooked do
|
8
|
+
Flatter::Mapper::Collection::Concern.module_eval do
|
9
|
+
alias build_collection_item_without_ar build_collection_item
|
10
|
+
|
11
|
+
def build_collection_item
|
12
|
+
return build_collection_item_without_ar unless mounter!.try(:ar?)
|
13
|
+
|
14
|
+
mounter!.target.association(name.to_sym).try(:build) ||
|
15
|
+
build_collection_item_without_ar
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
factory.extend do
|
21
|
+
def default_target_from(mapper)
|
22
|
+
return super unless mapper.ar?
|
23
|
+
|
24
|
+
target_from_association(mapper.target) || super
|
25
|
+
end
|
26
|
+
private :default_target_from
|
27
|
+
|
28
|
+
def target_from_association(target)
|
29
|
+
reflection = reflection_from_target(target)
|
30
|
+
|
31
|
+
return unless reflection.present?
|
32
|
+
|
33
|
+
case reflection.macro
|
34
|
+
when :has_one, :belongs_to
|
35
|
+
target.public_send(name) || target.public_send("build_#{name}")
|
36
|
+
when :has_many
|
37
|
+
association = target.association(reflection.name)
|
38
|
+
collection? ? association.load_target : association.build
|
39
|
+
end
|
40
|
+
end
|
41
|
+
private :target_from_association
|
42
|
+
|
43
|
+
def reflection_from_target(target)
|
44
|
+
target_class = target.class
|
45
|
+
reflection = target_class.reflect_on_association(name.to_sym)
|
46
|
+
reflection || target_class.reflect_on_association(name.pluralize.to_sym)
|
47
|
+
end
|
48
|
+
private :reflection_from_target
|
49
|
+
end
|
50
|
+
|
51
|
+
mapper.add_options :foreign_key, :mounter_foreign_key do
|
52
|
+
extend ActiveSupport::Concern
|
53
|
+
attr_reader :ar_error
|
54
|
+
|
55
|
+
def set_target!(target)
|
56
|
+
super
|
57
|
+
add_skip_autosave_association_extension_to(target.class) if ar?
|
58
|
+
target
|
59
|
+
end
|
60
|
+
|
61
|
+
def apply(*)
|
62
|
+
return super unless ar?
|
63
|
+
|
64
|
+
::ActiveRecord::Base.transaction do
|
65
|
+
super or raise ::ActiveRecord::Rollback
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def save
|
70
|
+
::ActiveRecord::Base.transaction do
|
71
|
+
begin
|
72
|
+
@ar_error = nil
|
73
|
+
super
|
74
|
+
rescue ::ActiveRecord::StatementInvalid => e
|
75
|
+
@ar_error = e
|
76
|
+
raise ::ActiveRecord::Rollback
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_target_item(item)
|
82
|
+
item.destroy! if ar?(item)
|
83
|
+
super
|
84
|
+
end
|
85
|
+
|
86
|
+
def save_target
|
87
|
+
return super unless ar?
|
88
|
+
|
89
|
+
assign_foreign_keys_from_mountings
|
90
|
+
result = target.without_association_callbacks{ target.save }
|
91
|
+
assign_foreign_keys_for_mountings if result
|
92
|
+
|
93
|
+
result != false
|
94
|
+
end
|
95
|
+
protected :save_target
|
96
|
+
|
97
|
+
def target_valid?
|
98
|
+
return super unless ar?
|
99
|
+
target.without_association_callbacks{ super }
|
100
|
+
end
|
101
|
+
private :target_valid?
|
102
|
+
|
103
|
+
def assign_foreign_keys_from_mountings
|
104
|
+
associated_mountings(:mounter_foreign_key).each do |mounting|
|
105
|
+
target[mounting.mounter_foreign_key] ||= mounting.target.id
|
106
|
+
end
|
107
|
+
end
|
108
|
+
private :assign_foreign_keys_from_mountings
|
109
|
+
|
110
|
+
def assign_foreign_keys_for_mountings
|
111
|
+
associated_mountings(:foreign_key).each do |mounting|
|
112
|
+
mounting.target[mounting.foreign_key] ||= target.id
|
113
|
+
end
|
114
|
+
end
|
115
|
+
private :assign_foreign_keys_for_mountings
|
116
|
+
|
117
|
+
def associated_mountings(key)
|
118
|
+
root_mountings.select do |mounting|
|
119
|
+
mounter = mounting.mounter
|
120
|
+
mounter = mounter.mounter if mounter.trait?
|
121
|
+
mounting.options.key?(key) && mounter == self
|
122
|
+
end
|
123
|
+
end
|
124
|
+
private :associated_mountings
|
125
|
+
|
126
|
+
def add_skip_autosave_association_extension_to(klass)
|
127
|
+
return if klass.const_defined?('SkipAutosaveAssociationExtension')
|
128
|
+
|
129
|
+
klass.const_set('SkipAutosaveAssociationExtension', skip_autosave_association_extension_for(klass))
|
130
|
+
klass.send(:prepend, klass::SkipAutosaveAssociationExtension)
|
131
|
+
end
|
132
|
+
private :add_skip_autosave_association_extension_to
|
133
|
+
|
134
|
+
def skip_autosave_association_extension_for(klass)
|
135
|
+
association_autosave_methods = klass.instance_methods.grep(/autosave_associated_records_for_/)
|
136
|
+
association_validation_methods = klass.instance_methods.grep(/validate_associated_records_for_/)
|
137
|
+
|
138
|
+
Module.new do
|
139
|
+
(association_autosave_methods + association_validation_methods).each do |name|
|
140
|
+
define_method(name) do
|
141
|
+
@skip_association_callbacks || super()
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def without_association_callbacks
|
146
|
+
@skip_association_callbacks = true
|
147
|
+
yield
|
148
|
+
ensure
|
149
|
+
remove_instance_variable('@skip_association_callbacks')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
private :skip_autosave_association_extension_for
|
154
|
+
|
155
|
+
def ar?(object = target)
|
156
|
+
object.class < ::ActiveRecord::Base
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Flatter::Extensions
|
2
|
+
module Multiparam
|
3
|
+
extend ::Flatter::Extension
|
4
|
+
|
5
|
+
register_as :multiparam
|
6
|
+
|
7
|
+
mapping.add_option :multiparam do
|
8
|
+
def write(value)
|
9
|
+
return super unless multiparam?
|
10
|
+
|
11
|
+
write!(multiparam.new(*value))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
mapper.extend do
|
16
|
+
def write(params)
|
17
|
+
extract_multiparams!(params)
|
18
|
+
|
19
|
+
super(params)
|
20
|
+
end
|
21
|
+
|
22
|
+
def extract_multiparams!(params)
|
23
|
+
return super if collection?
|
24
|
+
|
25
|
+
local_mappings.each do |mapping|
|
26
|
+
next unless mapping.multiparam?
|
27
|
+
|
28
|
+
param_keys = params.keys.
|
29
|
+
select{ |key| key.to_s =~ /#{mapping.name}\(\d+[if]\)/ }.
|
30
|
+
sort_by{ |key| key.to_s[/\((\d+).*\)/, 1].to_i }
|
31
|
+
|
32
|
+
next if param_keys.empty?
|
33
|
+
|
34
|
+
args = param_keys.each_with_object([]) do |key, values|
|
35
|
+
value = params.delete key
|
36
|
+
type = key[/\(\d+([if]*)\)/, 1]
|
37
|
+
|
38
|
+
value = if value.present?
|
39
|
+
type.blank? ? value : value.send("to_#{type}")
|
40
|
+
end
|
41
|
+
|
42
|
+
values.push value
|
43
|
+
end
|
44
|
+
|
45
|
+
params[mapping.name] = args
|
46
|
+
end
|
47
|
+
end
|
48
|
+
private :extract_multiparams!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Flatter::Extensions
|
2
|
+
module Order
|
3
|
+
extend ::Flatter::Extension
|
4
|
+
|
5
|
+
register_as :order
|
6
|
+
|
7
|
+
mapping.add_option :index do
|
8
|
+
def index
|
9
|
+
options[:index] || 0
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
mapper.add_option :index do
|
14
|
+
def index
|
15
|
+
options[:index] || 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def local_mappings
|
19
|
+
@_local_mappings ||= super.sort_by(&:index)
|
20
|
+
end
|
21
|
+
protected :local_mappings
|
22
|
+
|
23
|
+
def mappers_chain(context)
|
24
|
+
super.sort_by do |mapper|
|
25
|
+
index = mapper.index
|
26
|
+
index.is_a?(Hash) ? (index[context] || 0) : index
|
27
|
+
end
|
28
|
+
end
|
29
|
+
private :mappers_chain
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Flatter::Extensions
|
2
|
+
module Skipping
|
3
|
+
extend ::Flatter::Extension
|
4
|
+
|
5
|
+
register_as :skipping
|
6
|
+
|
7
|
+
hooked do
|
8
|
+
::Flatter::Mapper::Collection.module_eval do
|
9
|
+
alias extract_data_without_reject extract_data
|
10
|
+
|
11
|
+
def extract_data(params)
|
12
|
+
extract_data_without_reject(params).tap do |data|
|
13
|
+
data.reject!{ |params| reject_if[params] } if reject_if?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
mapper.add_options :skip_if, :reject_if do
|
20
|
+
extend ActiveSupport::Concern
|
21
|
+
|
22
|
+
included do
|
23
|
+
set_callback :validate, :before, :ignore_skipped_mountings
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_validations!
|
27
|
+
if skipped?
|
28
|
+
errors.clear
|
29
|
+
true
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_save!
|
36
|
+
skipped? ? true : super
|
37
|
+
end
|
38
|
+
|
39
|
+
def skip!
|
40
|
+
collection.each(&:skip!) if collection?
|
41
|
+
@skipped = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def skipped?
|
45
|
+
!!@skipped
|
46
|
+
end
|
47
|
+
|
48
|
+
def ignore_skipped_mountings
|
49
|
+
local_mountings.each do |mapper|
|
50
|
+
mapper.skip! if mapper.skip_if? && instance_exec(&mapper.skip_if)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
private :ignore_skipped_mountings
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Flatter
|
2
|
+
module Mapper::Validation
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
validate :target_validation
|
7
|
+
end
|
8
|
+
|
9
|
+
def target_validation
|
10
|
+
return if target_valid?
|
11
|
+
|
12
|
+
errors.add(:target, :invalid)
|
13
|
+
|
14
|
+
local_mappings.each do |mapping|
|
15
|
+
target.errors[mapping.target_attribute].each do |message|
|
16
|
+
errors.add(mapping.name, message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
private :target_validation
|
21
|
+
|
22
|
+
def target_valid?
|
23
|
+
!target.respond_to?(:valid?) || target.valid?
|
24
|
+
end
|
25
|
+
private :target_valid?
|
26
|
+
end
|
27
|
+
end
|
data/lib/flatter/mapper.rb
CHANGED
@@ -9,6 +9,7 @@ module Flatter
|
|
9
9
|
autoload :Traits
|
10
10
|
autoload :Target
|
11
11
|
autoload :AttributeMethods
|
12
|
+
autoload :Validation
|
12
13
|
autoload :Persistence
|
13
14
|
autoload :ModelName
|
14
15
|
autoload :Collection
|
@@ -21,6 +22,7 @@ module Flatter
|
|
21
22
|
include Target
|
22
23
|
include AttributeMethods
|
23
24
|
include ActiveModel::Validations
|
25
|
+
include Validation
|
24
26
|
include Persistence
|
25
27
|
prepend ModelName
|
26
28
|
prepend Collection
|
data/lib/flatter/version.rb
CHANGED
data/lib/flatter.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flatter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Kuzko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activerecord
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: bundler
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,6 +193,11 @@ files:
|
|
165
193
|
- lib/flatter/extension/mapper.rb
|
166
194
|
- lib/flatter/extension/mapping.rb
|
167
195
|
- lib/flatter/extension/registrar.rb
|
196
|
+
- lib/flatter/extensions.rb
|
197
|
+
- lib/flatter/extensions/active_record.rb
|
198
|
+
- lib/flatter/extensions/multiparam.rb
|
199
|
+
- lib/flatter/extensions/order.rb
|
200
|
+
- lib/flatter/extensions/skipping.rb
|
168
201
|
- lib/flatter/mapper.rb
|
169
202
|
- lib/flatter/mapper/attribute_methods.rb
|
170
203
|
- lib/flatter/mapper/collection.rb
|
@@ -176,6 +209,7 @@ files:
|
|
176
209
|
- lib/flatter/mapper/persistence.rb
|
177
210
|
- lib/flatter/mapper/target.rb
|
178
211
|
- lib/flatter/mapper/traits.rb
|
212
|
+
- lib/flatter/mapper/validation.rb
|
179
213
|
- lib/flatter/mapper/write_with_indifferent_access.rb
|
180
214
|
- lib/flatter/mapping.rb
|
181
215
|
- lib/flatter/mapping/factory.rb
|