flatter 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8c9d3f37b6dfdb2b990df2a47c1682af45a526b
4
- data.tar.gz: 83e78ea63b40fcbceea19a6f940f97747a00bd2f
3
+ metadata.gz: 542b10f00cb2f869988def6cd621cf3c6588c50f
4
+ data.tar.gz: 482d1248aa1204fe7831898ac84e98214d961673
5
5
  SHA512:
6
- metadata.gz: bed580ee036c3d9a21ce0c24e293e4472f0664ae6d4f8acbd93cb8a2cbdf8018cf5e5a3e4b5a873e1a7c8cd799ae2a3c100c5bd865792e8141e44c37f063a830
7
- data.tar.gz: 8463c12d806db30ad17305ceaaf3b9493e65b9074fff674e21e5b2a3fcfc22f3427d6bfc8f7f73991dbe81fa3cfc96e65806d6b387a3156dbcfd0ef6b86b46f9
6
+ metadata.gz: 3b9a7a0eb25675f0402768233e990e854f2a73bb6473af393e575434134002041e3257a74b150f262290e5c38c4e9ba8a0b42f6ee291d6e02505402e089c11e8
7
+ data.tar.gz: 182d1f6281a6713f07271a7533db99c56b777f479ed527a1d719398f999992d92a29a9ffeff5ce99ca536b6ce0c5bc8f5eb7a180e6c9bcd3eb2c4caaf40749b8
data/.travis.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0.0
3
+ - 2.2.2
4
4
  before_install: gem install bundler -v 1.10.6
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 defined in this gem, there is also
606
- [flatter-extensions](https://github.com/akuzko/flatter-extensions) gem that
607
- provides extensions to help you use mappers more efficiently. At this point there
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,7 @@
1
+ require 'flatter/extensions/multiparam'
2
+ require 'flatter/extensions/order'
3
+ require 'flatter/extensions/skipping'
4
+
5
+ if defined? ActiveRecord
6
+ require 'flatter/extensions/active_record'
7
+ 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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Flatter
2
- VERSION = "0.2.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/flatter.rb CHANGED
@@ -28,3 +28,5 @@ module Flatter
28
28
  modules.each{ |mod| klass.send(:include, mod) }
29
29
  end
30
30
  end
31
+
32
+ require 'flatter/extensions'
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.2.1
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: 2016-05-05 00:00:00.000000000 Z
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