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 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