granite-form 0.2.0 → 0.4.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/.github/CODEOWNERS +1 -2
- data/.github/workflows/{ci.yml → ruby.yml} +22 -4
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +3 -3
- data/Appraisals +1 -2
- data/CHANGELOG.md +15 -0
- data/README.md +6 -15
- data/docker-compose.yml +14 -0
- data/gemfiles/rails.5.0.gemfile +0 -1
- data/gemfiles/rails.5.1.gemfile +0 -1
- data/gemfiles/rails.5.2.gemfile +0 -1
- data/granite-form.gemspec +15 -15
- data/lib/granite/form/active_record/associations.rb +1 -1
- data/lib/granite/form/base.rb +1 -2
- data/lib/granite/form/errors.rb +0 -15
- data/lib/granite/form/model/associations/base.rb +0 -4
- data/lib/granite/form/model/associations/collection/embedded.rb +2 -1
- data/lib/granite/form/model/associations/collection/proxy.rb +1 -1
- data/lib/granite/form/model/associations/embeds_any.rb +7 -0
- data/lib/granite/form/model/associations/embeds_many.rb +9 -58
- data/lib/granite/form/model/associations/embeds_one.rb +7 -36
- data/lib/granite/form/model/associations/nested_attributes.rb +7 -7
- data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +0 -4
- data/lib/granite/form/model/associations/persistence_adapters/base.rb +0 -4
- data/lib/granite/form/model/associations/references_many.rb +0 -32
- data/lib/granite/form/model/associations/references_one.rb +0 -28
- data/lib/granite/form/model/associations/reflections/embeds_any.rb +1 -1
- data/lib/granite/form/model/associations/reflections/embeds_many.rb +1 -1
- data/lib/granite/form/model/associations/reflections/embeds_one.rb +11 -1
- data/lib/granite/form/model/associations/reflections/references_any.rb +0 -4
- data/lib/granite/form/model/associations/reflections/singular.rb +0 -22
- data/lib/granite/form/model/associations.rb +0 -6
- data/lib/granite/form/model/attributes/attribute.rb +3 -21
- data/lib/granite/form/model/attributes/base.rb +5 -23
- data/lib/granite/form/model/attributes/reference_many.rb +1 -1
- data/lib/granite/form/model/attributes/reference_one.rb +1 -1
- data/lib/granite/form/model/attributes/reflections/attribute.rb +2 -8
- data/lib/granite/form/model/attributes/reflections/base/build_type_definition.rb +38 -0
- data/lib/granite/form/model/attributes/reflections/base.rb +20 -17
- data/lib/granite/form/model/attributes/reflections/collection/build_type_definition.rb +19 -0
- data/lib/granite/form/model/attributes/reflections/dictionary/build_type_definition.rb +19 -0
- data/lib/granite/form/model/attributes/reflections/dictionary.rb +0 -3
- data/lib/granite/form/model/attributes/reflections/reference_one.rb +0 -6
- data/lib/granite/form/model/attributes/reflections/represents/build_type_definition.rb +73 -0
- data/lib/granite/form/model/attributes/reflections/represents.rb +10 -2
- data/lib/granite/form/model/attributes/represents.rb +22 -37
- data/lib/granite/form/model/attributes.rb +10 -2
- data/lib/granite/form/model/persistence.rb +1 -19
- data/lib/granite/form/model/representation.rb +1 -0
- data/lib/granite/form/model/validations.rb +6 -0
- data/lib/granite/form/model.rb +1 -3
- data/lib/granite/form/types/active_support/time_zone.rb +2 -0
- data/lib/granite/form/types/array.rb +2 -0
- data/lib/granite/form/types/big_decimal.rb +2 -0
- data/lib/granite/form/types/boolean.rb +2 -0
- data/lib/granite/form/types/collection.rb +11 -0
- data/lib/granite/form/types/date.rb +2 -0
- data/lib/granite/form/types/date_time.rb +2 -0
- data/lib/granite/form/types/dictionary.rb +23 -0
- data/lib/granite/form/types/float.rb +2 -0
- data/lib/granite/form/types/has_subtype.rb +18 -0
- data/lib/granite/form/types/hash_with_action_controller_parameters.rb +2 -0
- data/lib/granite/form/types/integer.rb +2 -0
- data/lib/granite/form/types/object.rb +28 -0
- data/lib/granite/form/types/string.rb +2 -0
- data/lib/granite/form/types/time.rb +2 -0
- data/lib/granite/form/types/uuid.rb +2 -0
- data/lib/granite/form/types.rb +3 -0
- data/lib/granite/form/util.rb +55 -0
- data/lib/granite/form/version.rb +1 -1
- data/lib/granite/form.rb +1 -0
- data/spec/granite/form/active_record/associations_spec.rb +16 -18
- data/spec/granite/form/model/associations/embeds_many_spec.rb +29 -305
- data/spec/granite/form/model/associations/embeds_one_spec.rb +27 -212
- data/spec/granite/form/model/associations/nested_attributes_spec.rb +0 -95
- data/spec/granite/form/model/associations/references_many_spec.rb +5 -326
- data/spec/granite/form/model/associations/references_one_spec.rb +7 -279
- data/spec/granite/form/model/associations/reflections/embeds_any_spec.rb +1 -2
- data/spec/granite/form/model/associations/reflections/embeds_many_spec.rb +18 -26
- data/spec/granite/form/model/associations/reflections/embeds_one_spec.rb +16 -23
- data/spec/granite/form/model/associations/reflections/references_many_spec.rb +1 -1
- data/spec/granite/form/model/associations/reflections/references_one_spec.rb +1 -22
- data/spec/granite/form/model/associations/validations_spec.rb +0 -3
- data/spec/granite/form/model/associations_spec.rb +3 -24
- data/spec/granite/form/model/attributes/attribute_spec.rb +0 -29
- data/spec/granite/form/model/attributes/reflections/attribute_spec.rb +0 -9
- data/spec/granite/form/model/attributes/reflections/base/build_type_definition_spec.rb +27 -0
- data/spec/granite/form/model/attributes/reflections/base_spec.rb +16 -10
- data/spec/granite/form/model/attributes/reflections/collection/build_type_definition_spec.rb +24 -0
- data/spec/granite/form/model/attributes/reflections/dictionary/build_type_definition_spec.rb +24 -0
- data/spec/granite/form/model/attributes/reflections/dictionary_spec.rb +0 -6
- data/spec/granite/form/model/attributes/reflections/represents/build_type_definition_spec.rb +129 -0
- data/spec/granite/form/model/attributes/reflections/represents_spec.rb +43 -20
- data/spec/granite/form/model/attributes/represents_spec.rb +78 -55
- data/spec/granite/form/model/attributes_spec.rb +84 -23
- data/spec/granite/form/model/dirty_spec.rb +1 -7
- data/spec/granite/form/model/persistence_spec.rb +0 -2
- data/spec/granite/form/model/representation_spec.rb +4 -7
- data/spec/granite/form/model/validations/associated_spec.rb +2 -4
- data/spec/granite/form/model/validations/nested_spec.rb +2 -4
- data/spec/granite/form/model/validations_spec.rb +28 -1
- data/spec/granite/form/types/collection_spec.rb +22 -0
- data/spec/granite/form/types/dictionary_spec.rb +32 -0
- data/spec/granite/form/types/has_subtype_spec.rb +20 -0
- data/spec/granite/form/types/object_spec.rb +50 -4
- data/spec/granite/form/util_spec.rb +108 -0
- data/spec/spec_helper.rb +0 -15
- data/spec/support/active_record.rb +23 -0
- data/spec/support/shared/nested_attribute_examples.rb +3 -21
- metadata +56 -51
- data/.github/workflows/main.yml +0 -29
- data/gemfiles/rails.4.2.gemfile +0 -15
- data/lib/granite/form/model/attributes/collection.rb +0 -19
- data/lib/granite/form/model/attributes/dictionary.rb +0 -28
- data/lib/granite/form/model/attributes/localized.rb +0 -44
- data/lib/granite/form/model/attributes/reflections/localized.rb +0 -45
- data/lib/granite/form/model/callbacks.rb +0 -72
- data/lib/granite/form/model/lifecycle.rb +0 -309
- data/lib/granite/form/model/localization.rb +0 -26
- data/spec/granite/form/model/attributes/collection_spec.rb +0 -72
- data/spec/granite/form/model/attributes/dictionary_spec.rb +0 -100
- data/spec/granite/form/model/attributes/localized_spec.rb +0 -103
- data/spec/granite/form/model/attributes/reflections/localized_spec.rb +0 -37
- data/spec/granite/form/model/callbacks_spec.rb +0 -337
- data/spec/granite/form/model/lifecycle_spec.rb +0 -356
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 908865a58fa79739294c85bb506d878596a28782c02a9a2825ca1fb7fd128972
|
|
4
|
+
data.tar.gz: ff9b6cbed34774e3dedb0b33c28b75694b1575051adb989248ca5695254e9586
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92d055b121d30a9644521227782908a0b84ef0847c16ce60fafec47cb17c5c2988e8a071e199ed5506dd0783bba335b6a399a519c095867658e310680dd96f10
|
|
7
|
+
data.tar.gz: 4b4cf5f23530ad12408f498df6eed11e53438dd0a6f110ae04c10568934f041dbb64b946ee42352b7bfec83119a5827b652c8fddd62e745fa0f2835448674ce6
|
data/.github/CODEOWNERS
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
.github/workflows/main.yml @toptal/coresmiths-team
|
|
1
|
+
* @toptal/portals-experience-be
|
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
name:
|
|
2
|
-
on:
|
|
1
|
+
name: Ruby
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ master ]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [ master ]
|
|
3
7
|
jobs:
|
|
4
8
|
rspec:
|
|
5
9
|
strategy:
|
|
6
10
|
fail-fast: false
|
|
7
11
|
matrix:
|
|
8
12
|
include:
|
|
9
|
-
- { ruby: '2.3', rails: '4.2' }
|
|
10
13
|
- { ruby: '2.4', rails: '5.0' }
|
|
11
14
|
- { ruby: '2.5', rails: '5.1' }
|
|
12
15
|
- { ruby: '2.6', rails: '5.2' }
|
|
13
16
|
- { ruby: '2.7', rails: '6.0' }
|
|
14
17
|
- { ruby: '3.0', rails: '6.1' }
|
|
15
|
-
- { ruby: '3.
|
|
18
|
+
- { ruby: '3.1', rails: '7.0' }
|
|
16
19
|
runs-on: ubuntu-latest
|
|
17
20
|
env:
|
|
18
21
|
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails.${{ matrix.rails }}.gemfile
|
|
22
|
+
services:
|
|
23
|
+
postgres:
|
|
24
|
+
image: postgres
|
|
25
|
+
env:
|
|
26
|
+
POSTGRES_USER: granite
|
|
27
|
+
POSTGRES_PASSWORD: granite
|
|
28
|
+
# Set health checks to wait until postgres has started
|
|
29
|
+
options: >-
|
|
30
|
+
--health-cmd pg_isready
|
|
31
|
+
--health-interval 10s
|
|
32
|
+
--health-timeout 5s
|
|
33
|
+
--health-retries 5
|
|
34
|
+
ports:
|
|
35
|
+
# Maps tcp port 5432 on service container to the host
|
|
36
|
+
- 5432:5432
|
|
19
37
|
steps:
|
|
20
38
|
- uses: actions/checkout@v2
|
|
21
39
|
- uses: ruby/setup-ruby@v1
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
# Offense count: 19
|
|
10
10
|
Metrics/AbcSize:
|
|
11
|
-
Max:
|
|
11
|
+
Max: 59
|
|
12
12
|
|
|
13
13
|
# Offense count: 2
|
|
14
14
|
# Configuration parameters: CountComments.
|
|
@@ -17,7 +17,7 @@ Metrics/ClassLength:
|
|
|
17
17
|
|
|
18
18
|
# Offense count: 4
|
|
19
19
|
Metrics/CyclomaticComplexity:
|
|
20
|
-
Max:
|
|
20
|
+
Max: 15
|
|
21
21
|
|
|
22
22
|
# Offense count: 904
|
|
23
23
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives.
|
|
@@ -41,7 +41,7 @@ Metrics/BlockLength:
|
|
|
41
41
|
|
|
42
42
|
# Offense count: 4
|
|
43
43
|
Metrics/PerceivedComplexity:
|
|
44
|
-
Max:
|
|
44
|
+
Max: 18
|
|
45
45
|
|
|
46
46
|
# Offense count: 75
|
|
47
47
|
Style/Documentation:
|
data/Appraisals
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
%w[
|
|
1
|
+
%w[5.0 5.1 5.2 6.0 6.1 7.0].each do |version|
|
|
2
2
|
appraise "rails.#{version}" do
|
|
3
3
|
gem 'activesupport', "~> #{version}.0"
|
|
4
4
|
gem 'activemodel', "~> #{version}.0"
|
|
5
5
|
gem 'activerecord', "~> #{version}.0"
|
|
6
|
-
gem 'sqlite3', '~> 1.3.6' if version < '6.0'
|
|
7
6
|
end
|
|
8
7
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
# master
|
|
2
2
|
|
|
3
3
|
## Next
|
|
4
|
+
## v0.4.0
|
|
5
|
+
|
|
6
|
+
* [BREAKING] Drop support for taking `model` as first argument in default/readonly/enum/normalize. This means that `default: -> (model) { model.other_field}` is no longer supported and should be replaced with `default: -> { other_field }`.
|
|
7
|
+
* Add support for evaluating `Symbol` for readonly/enum/normalize. If symbol is passed in one of those options, method with that name will be called when evaluating the value.
|
|
8
|
+
* [BREAKING] Remove `localized` attribute type.
|
|
9
|
+
* [BREAKING] Change the behavior of `default` and `normalize` for `collection` & `dictionary`. Instead of acting per element they will now act on the attribute as a whole.
|
|
10
|
+
* E.g. `collection :numbers, default: [1, 2, 3]` will not set the default for the whole collection of `numbers` rather than each element in `numbers`.
|
|
11
|
+
|
|
12
|
+
## v0.3.0
|
|
13
|
+
|
|
14
|
+
- [BREAKING] Stop automatically saving `references_one`/`references_many` when applying changes.
|
|
15
|
+
- [BREAKING] Removed Lifecycle module. `embeds_many`/`embeds_one` objects can no longer be created/saved/updated/destroyed.
|
|
16
|
+
- [BREAKING] Due to changes above `accepts_nested_attributes_for` for `embeds_many`/`embeds_one` associations no longer marks objects for destruction but simply removes them, making changes instantly.
|
|
17
|
+
- Drop support for ruby 2.3 and rails 4.2
|
|
18
|
+
|
|
4
19
|
## v0.2.0
|
|
5
20
|
|
|
6
21
|
- Replace typecasters with proper type definitions.
|
data/README.md
CHANGED
|
@@ -172,9 +172,10 @@ It is possible to provide default values for attributes and they will act in the
|
|
|
172
172
|
|
|
173
173
|
```ruby
|
|
174
174
|
attribute :check, Boolean, default: false # Simply false by default
|
|
175
|
-
attribute :
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
attribute :wday, Integer, default: ->{ today.wday } # Default evaluated in instance context
|
|
176
|
+
def calculate_today
|
|
177
|
+
Time.zone.now.today
|
|
178
|
+
end
|
|
178
179
|
```
|
|
179
180
|
|
|
180
181
|
##### Enums
|
|
@@ -201,8 +202,8 @@ attribute :title, String, normalizers: [->(value) { value.strip }, trim: {length
|
|
|
201
202
|
|
|
202
203
|
```ruby
|
|
203
204
|
attribute :name, String, readonly: true # Readonly forever
|
|
204
|
-
attribute :name, String, readonly:
|
|
205
|
-
attribute :name, String, readonly: ->
|
|
205
|
+
attribute :name, String, readonly: :name_changed? # Conditional with calling method
|
|
206
|
+
attribute :name, String, readonly: -> { subject.present? } # Conditional with lambda
|
|
206
207
|
```
|
|
207
208
|
|
|
208
209
|
#### Collection
|
|
@@ -251,14 +252,6 @@ end
|
|
|
251
252
|
|
|
252
253
|
The keys list might be restricted with the `:keys` option. Default and enum modifiers are applied on each value, normalizers are applied on the hash.
|
|
253
254
|
|
|
254
|
-
#### Localized
|
|
255
|
-
|
|
256
|
-
`localized` is similar to how `Globalize 3` attributes work.
|
|
257
|
-
|
|
258
|
-
```ruby
|
|
259
|
-
localized :title, String
|
|
260
|
-
```
|
|
261
|
-
|
|
262
255
|
#### Represents
|
|
263
256
|
|
|
264
257
|
`represents` provides an easy way to expose model attributes through an interface.
|
|
@@ -384,8 +377,6 @@ Options:
|
|
|
384
377
|
* `:validate` - true or false
|
|
385
378
|
* `:default` - default value for association: reference collection or objects themselves
|
|
386
379
|
|
|
387
|
-
#### Interacting with ActiveRecord
|
|
388
|
-
|
|
389
380
|
### Persistence Adapters
|
|
390
381
|
|
|
391
382
|
Adapter definition syntax:
|
data/docker-compose.yml
ADDED
data/gemfiles/rails.5.0.gemfile
CHANGED
data/gemfiles/rails.5.1.gemfile
CHANGED
data/gemfiles/rails.5.2.gemfile
CHANGED
data/granite-form.gemspec
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
require File.expand_path('../lib/granite/form/version', __FILE__)
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |gem|
|
|
4
|
-
gem.authors
|
|
5
|
-
gem.
|
|
6
|
-
gem.
|
|
7
|
-
gem.
|
|
8
|
-
gem.homepage = ''
|
|
4
|
+
gem.authors = ['Toptal Engineering']
|
|
5
|
+
gem.description = 'Making object from any hash or hash array'
|
|
6
|
+
gem.summary = 'Working with hashes in AR style'
|
|
7
|
+
gem.homepage = 'https://github.com/toptal/granite-form'
|
|
9
8
|
|
|
10
|
-
gem.files
|
|
11
|
-
gem.executables
|
|
12
|
-
gem.test_files
|
|
13
|
-
gem.name
|
|
9
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
|
10
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
11
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
12
|
+
gem.name = 'granite-form'
|
|
14
13
|
gem.require_paths = ['lib']
|
|
15
|
-
gem.
|
|
14
|
+
gem.required_ruby_version = '>= 2.4.0'
|
|
15
|
+
gem.version = Granite::Form::VERSION
|
|
16
16
|
|
|
17
|
-
gem.add_development_dependency 'actionpack', '>=
|
|
18
|
-
gem.add_development_dependency 'activerecord', '>=
|
|
17
|
+
gem.add_development_dependency 'actionpack', '>= 5.0'
|
|
18
|
+
gem.add_development_dependency 'activerecord', '>= 5.0'
|
|
19
19
|
gem.add_development_dependency 'appraisal'
|
|
20
20
|
gem.add_development_dependency 'bump'
|
|
21
21
|
gem.add_development_dependency 'database_cleaner'
|
|
22
|
+
gem.add_development_dependency 'pg'
|
|
22
23
|
gem.add_development_dependency 'rake'
|
|
23
24
|
gem.add_development_dependency 'rspec', '~> 3.7.0'
|
|
24
25
|
gem.add_development_dependency 'rspec-its'
|
|
25
26
|
gem.add_development_dependency 'rubocop', '0.52.1'
|
|
26
|
-
gem.add_development_dependency 'sqlite3'
|
|
27
27
|
gem.add_development_dependency 'uuidtools'
|
|
28
28
|
|
|
29
|
-
gem.add_runtime_dependency 'activemodel', '>=
|
|
30
|
-
gem.add_runtime_dependency 'activesupport', '>=
|
|
29
|
+
gem.add_runtime_dependency 'activemodel', '>= 5.0'
|
|
30
|
+
gem.add_runtime_dependency 'activesupport', '>= 5.0'
|
|
31
31
|
gem.add_runtime_dependency 'tzinfo'
|
|
32
32
|
end
|
data/lib/granite/form/base.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'granite/form/model'
|
|
2
2
|
require 'granite/form/model/primary'
|
|
3
|
-
require 'granite/form/model/lifecycle'
|
|
4
3
|
require 'granite/form/model/associations'
|
|
5
4
|
|
|
6
5
|
module Granite
|
|
@@ -8,7 +7,7 @@ module Granite
|
|
|
8
7
|
class Base
|
|
9
8
|
include Granite::Form::Model
|
|
10
9
|
include Granite::Form::Model::Primary
|
|
11
|
-
include Granite::Form::Model::
|
|
10
|
+
include Granite::Form::Model::Persistence
|
|
12
11
|
include Granite::Form::Model::Associations
|
|
13
12
|
end
|
|
14
13
|
end
|
data/lib/granite/form/errors.rb
CHANGED
|
@@ -17,21 +17,6 @@ module Granite
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
class UnsavableObject < Error
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
class UndestroyableObject < Error
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
class ObjectNotSaved < Error
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
class ObjectNotDestroyed < Error
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
class AssociationChangesNotApplied < Error
|
|
33
|
-
end
|
|
34
|
-
|
|
35
20
|
class AssociationTypeMismatch < Error
|
|
36
21
|
def initialize(expected, got)
|
|
37
22
|
super "Expected `#{expected}` (##{expected.object_id}), but got `#{got}` (##{got.object_id})"
|
|
@@ -6,7 +6,7 @@ module Granite
|
|
|
6
6
|
class Proxy
|
|
7
7
|
include Enumerable
|
|
8
8
|
|
|
9
|
-
delegate :target, :
|
|
9
|
+
delegate :target, :loaded?, :reload, :clear, :concat, to: :@association
|
|
10
10
|
delegate :each, :size, :length, :first, :last, :empty?, :many?, :==, :dup, to: :target
|
|
11
11
|
alias_method :<<, :concat
|
|
12
12
|
alias_method :push, :concat
|
|
@@ -12,6 +12,13 @@ module Granite
|
|
|
12
12
|
def embed_object(object)
|
|
13
13
|
object.instance_variable_set(:@embedder, owner)
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
def model_data(model)
|
|
17
|
+
return unless model
|
|
18
|
+
|
|
19
|
+
model.association_names.each { |assoc_name| model.association(assoc_name).sync }
|
|
20
|
+
model.attributes
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
24
|
end
|
|
@@ -7,27 +7,6 @@ module Granite
|
|
|
7
7
|
push_object(build_object(attributes))
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def create(attributes = {})
|
|
11
|
-
build(attributes).tap(&:save)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def create!(attributes = {})
|
|
15
|
-
build(attributes).tap(&:save!)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def destroyed
|
|
19
|
-
@destroyed ||= []
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def apply_changes
|
|
23
|
-
result = target.map do |object|
|
|
24
|
-
object.destroyed? || object.marked_for_destruction? ? object.destroy : object.save
|
|
25
|
-
end.all?
|
|
26
|
-
@destroyed = target.select(&:destroyed?)
|
|
27
|
-
target.delete_if(&:destroyed?)
|
|
28
|
-
result
|
|
29
|
-
end
|
|
30
|
-
|
|
31
10
|
def target=(objects)
|
|
32
11
|
objects.each { |object| setup_performers! object }
|
|
33
12
|
loaded!
|
|
@@ -63,13 +42,14 @@ module Granite
|
|
|
63
42
|
@target = []
|
|
64
43
|
end
|
|
65
44
|
|
|
45
|
+
def sync
|
|
46
|
+
write_source(target.map { |model| model_data(model) })
|
|
47
|
+
end
|
|
48
|
+
|
|
66
49
|
def clear
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
nil
|
|
71
|
-
end
|
|
72
|
-
reload.empty?
|
|
50
|
+
target
|
|
51
|
+
@target = []
|
|
52
|
+
true
|
|
73
53
|
end
|
|
74
54
|
|
|
75
55
|
def reader(force_reload = false)
|
|
@@ -80,7 +60,7 @@ module Granite
|
|
|
80
60
|
def replace(objects)
|
|
81
61
|
transaction do
|
|
82
62
|
clear
|
|
83
|
-
append(objects)
|
|
63
|
+
append(objects)
|
|
84
64
|
end
|
|
85
65
|
end
|
|
86
66
|
|
|
@@ -101,8 +81,7 @@ module Granite
|
|
|
101
81
|
raise AssociationTypeMismatch.new(reflection.klass, object.class) unless object && object.is_a?(reflection.klass)
|
|
102
82
|
push_object object
|
|
103
83
|
end
|
|
104
|
-
|
|
105
|
-
result && target
|
|
84
|
+
target
|
|
106
85
|
end
|
|
107
86
|
|
|
108
87
|
def push_object(object)
|
|
@@ -115,34 +94,6 @@ module Granite
|
|
|
115
94
|
embed_object(object)
|
|
116
95
|
callback(:before_add, object)
|
|
117
96
|
|
|
118
|
-
association = self
|
|
119
|
-
|
|
120
|
-
object.define_create do
|
|
121
|
-
source = association.send(:read_source)
|
|
122
|
-
index = association.target
|
|
123
|
-
.select { |one| one.persisted? || one.equal?(self) }
|
|
124
|
-
.index { |one| one.equal?(self) }
|
|
125
|
-
|
|
126
|
-
source.insert(index, attributes)
|
|
127
|
-
association.send(:write_source, source)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
object.define_update do
|
|
131
|
-
source = association.send(:read_source)
|
|
132
|
-
index = association.target.select(&:persisted?).index { |one| one.equal?(self) }
|
|
133
|
-
|
|
134
|
-
source[index] = attributes
|
|
135
|
-
association.send(:write_source, source)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
object.define_destroy do
|
|
139
|
-
source = association.send(:read_source)
|
|
140
|
-
index = association.target.select(&:persisted?).index { |one| one.equal?(self) }
|
|
141
|
-
|
|
142
|
-
source.delete_at(index) if index
|
|
143
|
-
association.send(:write_source, source)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
97
|
callback(:after_add, object)
|
|
147
98
|
end
|
|
148
99
|
end
|
|
@@ -3,33 +3,10 @@ module Granite
|
|
|
3
3
|
module Model
|
|
4
4
|
module Associations
|
|
5
5
|
class EmbedsOne < EmbedsAny
|
|
6
|
-
attr_reader :destroyed
|
|
7
|
-
|
|
8
6
|
def build(attributes = {})
|
|
9
7
|
self.target = build_object(attributes)
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
def create(attributes = {})
|
|
13
|
-
build(attributes).tap(&:save)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def create!(attributes = {})
|
|
17
|
-
build(attributes).tap(&:save!)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def apply_changes
|
|
21
|
-
if target
|
|
22
|
-
if target.destroyed? || target.marked_for_destruction?
|
|
23
|
-
@destroyed = target
|
|
24
|
-
clear
|
|
25
|
-
else
|
|
26
|
-
target.save
|
|
27
|
-
end
|
|
28
|
-
else
|
|
29
|
-
true
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
10
|
def target=(object)
|
|
34
11
|
if object
|
|
35
12
|
callback(:before_add, object)
|
|
@@ -63,9 +40,14 @@ module Granite
|
|
|
63
40
|
object
|
|
64
41
|
end
|
|
65
42
|
|
|
43
|
+
def sync
|
|
44
|
+
write_source(model_data(target))
|
|
45
|
+
end
|
|
46
|
+
|
|
66
47
|
def clear
|
|
67
|
-
target
|
|
68
|
-
|
|
48
|
+
target
|
|
49
|
+
@target = nil
|
|
50
|
+
true
|
|
69
51
|
end
|
|
70
52
|
|
|
71
53
|
def reader(force_reload = false)
|
|
@@ -79,7 +61,6 @@ module Granite
|
|
|
79
61
|
transaction do
|
|
80
62
|
clear
|
|
81
63
|
self.target = object
|
|
82
|
-
apply_changes! if owner.persisted?
|
|
83
64
|
end
|
|
84
65
|
else
|
|
85
66
|
clear
|
|
@@ -94,16 +75,6 @@ module Granite
|
|
|
94
75
|
|
|
95
76
|
def setup_performers!(object)
|
|
96
77
|
embed_object(object)
|
|
97
|
-
association = self
|
|
98
|
-
|
|
99
|
-
object.define_save do
|
|
100
|
-
association.send(:write_source, attributes)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
object.define_destroy do
|
|
104
|
-
association.send(:write_source, nil)
|
|
105
|
-
true
|
|
106
|
-
end
|
|
107
78
|
end
|
|
108
79
|
end
|
|
109
80
|
end
|
|
@@ -74,11 +74,12 @@ module Granite
|
|
|
74
74
|
primary_attribute_name = primary_name_for(association.reflection.klass)
|
|
75
75
|
if existing_record
|
|
76
76
|
primary_attribute = existing_record.attribute(primary_attribute_name)
|
|
77
|
-
primary_attribute_value = primary_attribute.type_definition.
|
|
77
|
+
primary_attribute_value = primary_attribute.type_definition.prepare(attributes[primary_attribute_name]) if primary_attribute
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
if existing_record && (!primary_attribute || options[:update_only] || existing_record.primary_attribute == primary_attribute_value)
|
|
81
|
-
|
|
81
|
+
assign_to(existing_record, attributes) unless call_reject_if(object, association_name, attributes)
|
|
82
|
+
association.clear if destroy_flag?(attributes) && options[:allow_destroy]
|
|
82
83
|
elsif attributes[primary_attribute_name].present?
|
|
83
84
|
raise Granite::Form::ObjectNotFound.new(object, association_name, attributes[primary_attribute_name])
|
|
84
85
|
elsif !reject_new_object?(object, association_name, attributes, options)
|
|
@@ -123,11 +124,12 @@ module Granite
|
|
|
123
124
|
else
|
|
124
125
|
existing_record = association.target.detect do |record|
|
|
125
126
|
primary_attribute_value = record.attribute(primary_attribute_name)
|
|
126
|
-
.type_definition.
|
|
127
|
+
.type_definition.prepare(attributes[primary_attribute_name])
|
|
127
128
|
record.primary_attribute == primary_attribute_value
|
|
128
129
|
end
|
|
129
130
|
if existing_record
|
|
130
|
-
|
|
131
|
+
assign_to(existing_record, attributes) unless call_reject_if(object, association_name, attributes)
|
|
132
|
+
association.target.delete(existing_record) if destroy_flag?(attributes) && options[:allow_destroy]
|
|
131
133
|
elsif association.reflection.embedded?
|
|
132
134
|
unless reject_new_object?(object, association_name, attributes, options)
|
|
133
135
|
association.reflection.klass.with_sanitize(false) do
|
|
@@ -156,9 +158,8 @@ module Granite
|
|
|
156
158
|
raise Granite::Form::TooManyObjects.new(limit, attributes_collection.size)
|
|
157
159
|
end
|
|
158
160
|
|
|
159
|
-
def self.
|
|
161
|
+
def self.assign_to(object, attributes)
|
|
160
162
|
object.assign_attributes(attributes.except(*unassignable_keys(object)))
|
|
161
|
-
object.mark_for_destruction if destroy_flag?(attributes) && allow_destroy
|
|
162
163
|
end
|
|
163
164
|
|
|
164
165
|
def self.destroy_flag?(hash)
|
|
@@ -170,7 +171,6 @@ module Granite
|
|
|
170
171
|
end
|
|
171
172
|
|
|
172
173
|
def self.call_reject_if(object, association_name, attributes)
|
|
173
|
-
return false if destroy_flag?(attributes)
|
|
174
174
|
case callback = object.nested_attributes_options[association_name][:reject_if]
|
|
175
175
|
when Symbol
|
|
176
176
|
method(callback).arity.zero? ? send(callback) : send(callback, attributes)
|
|
@@ -16,10 +16,6 @@ module Granite
|
|
|
16
16
|
raise NotImplementedError, 'Should be implemented in inhereted adapter. Build new instance of data object by attributes'
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def persist(_object, *)
|
|
20
|
-
raise NotImplementedError, 'Should be implemented in inhereted adapter. Build new instance of data object by attributes'
|
|
21
|
-
end
|
|
22
|
-
|
|
23
19
|
def scope(_owner, _source)
|
|
24
20
|
raise NotImplementedError, 'Should be implemented in inhereted adapter. Better to be Enumerable'
|
|
25
21
|
end
|
|
@@ -3,38 +3,6 @@ module Granite
|
|
|
3
3
|
module Model
|
|
4
4
|
module Associations
|
|
5
5
|
class ReferencesMany < ReferencesAny
|
|
6
|
-
def build(attributes = {})
|
|
7
|
-
append([build_object(attributes)]).last
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def create(attributes = {})
|
|
11
|
-
object = build(attributes)
|
|
12
|
-
persist_object(object)
|
|
13
|
-
object
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def create!(attributes = {})
|
|
17
|
-
object = build(attributes)
|
|
18
|
-
persist_object(object, raise_error: true)
|
|
19
|
-
object
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def apply_changes
|
|
23
|
-
target.all? do |object|
|
|
24
|
-
if object
|
|
25
|
-
if object.marked_for_destruction? && reflection.autosave?
|
|
26
|
-
object.destroy
|
|
27
|
-
elsif object.new_record? || (reflection.autosave? && object.changed?)
|
|
28
|
-
persist_object(object)
|
|
29
|
-
else
|
|
30
|
-
true
|
|
31
|
-
end
|
|
32
|
-
else
|
|
33
|
-
true
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
6
|
def target=(object)
|
|
39
7
|
loaded!
|
|
40
8
|
@target = object.to_a
|