nested_record 1.0.0 → 1.1.1

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
  SHA256:
3
- metadata.gz: 5710483535afba122d37dded3a1cf743254f6a2ea43e40ad126adf8a98bf8ed3
4
- data.tar.gz: 6219144c936fae21346f49380082b773e44c6e1bbed670de53f6b96a5d4200ec
3
+ metadata.gz: 0761ce9c739c3f58d9ee2b52552664015df2d6fb6aed4dab164dd33038fbc1fe
4
+ data.tar.gz: 2d63fb82c3be079c5de28599ca02d0d4ddeb20c5a82f554901601bbaa8b70011
5
5
  SHA512:
6
- metadata.gz: dbfb5c90d3a43c9a0671af39bc3969ca544deb1ba58809800d018621d4dccded78efdba763dfb425299236cb8fd631dfe6b30786fdab45d1fe6b712070afd0fd
7
- data.tar.gz: b18afab1feb7f6e29cac1cfec75a7b4176c3a35bf3de85a6b411d4a6501617f363c5e757d04cd009094002cba78c45682f189ad9235908598db04e65aac3e0da
6
+ metadata.gz: 8ff11ab93295ca0d08551e513f81b6f086c115c264c7c3ae7843fcb34dcae099b549f2501b9cad65425708593a018b76af00ab2e76339317dbd9ad57a759a9ef
7
+ data.tar.gz: d6cb9f2f8fcc4c7b958905e93690cb8f7e319fad664780e4e1abb311b15d66f7c6f28b756874a5f1035c31ecd2fcf03935ee35f711e66d411595a86ea66b6b65
@@ -0,0 +1,50 @@
1
+ name: RSpec
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - master
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby:
15
+ - "2.6"
16
+ - "2.7"
17
+ - "3.0"
18
+ - "3.1"
19
+ - "3.2"
20
+ gemfile:
21
+ - gemfiles/rails_5.2.gemfile
22
+ - gemfiles/rails_6.0.gemfile
23
+ - gemfiles/rails_6.1.gemfile
24
+ - gemfiles/rails_7.0.gemfile
25
+ - gemfiles/rails_7.1.gemfile
26
+ exclude:
27
+ - gemfile: gemfiles/rails_5.2.gemfile
28
+ ruby: "3.0"
29
+ - gemfile: gemfiles/rails_5.2.gemfile
30
+ ruby: "3.1"
31
+ - gemfile: gemfiles/rails_5.2.gemfile
32
+ ruby: "3.2"
33
+ - gemfile: gemfiles/rails_7.0.gemfile
34
+ ruby: "2.6"
35
+ - gemfile: gemfiles/rails_7.1.gemfile
36
+ ruby: "2.6"
37
+ env:
38
+ BUNDLE_GEMFILE: ${{ format('{0}/{1}', github.workspace, matrix.gemfile) }}
39
+ BUNDLE_PATH: ${{ format('{0}/vendor/bundle', github.workspace) }}
40
+ BUNDLE_DEPLOYMENT: true
41
+ steps:
42
+ - name: Checkout
43
+ uses: actions/checkout@v1
44
+ - name: Setup ruby
45
+ uses: ruby/setup-ruby@v1
46
+ with:
47
+ ruby-version: ${{ matrix.ruby }}
48
+ bundler-cache: true
49
+ - name: RSpec
50
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -6,8 +6,6 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
- /Gemfile.lock
10
- /gemfiles/*.lock
11
9
 
12
10
  # rspec failure tracking
13
11
  .rspec_status
data/Appraisals ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise "rails-5.2" do
4
+ gem 'activemodel', '~> 5.2.8.1'
5
+ end
6
+
7
+ appraise "rails-6.0" do
8
+ gem 'activemodel', '~> 6.0.6.1'
9
+ end
10
+
11
+ appraise "rails-6.1" do
12
+ gem 'activemodel', '~> 6.1.7.6'
13
+ end
14
+
15
+ appraise "rails-7.0" do
16
+ gem 'activemodel', '~> 7.0.8'
17
+ end
18
+
19
+ appraise "rails-7.1" do
20
+ gem 'activemodel', '~> 7.1.1'
21
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,67 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nested_record (1.1.1)
5
+ activemodel (> 5.2, < 7.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (7.1.1)
11
+ activesupport (= 7.1.1)
12
+ activesupport (7.1.1)
13
+ base64
14
+ bigdecimal
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ connection_pool (>= 2.2.5)
17
+ drb
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ mutex_m
21
+ tzinfo (~> 2.0)
22
+ appraisal (2.5.0)
23
+ bundler
24
+ rake
25
+ thor (>= 0.14.0)
26
+ base64 (0.1.1)
27
+ bigdecimal (3.1.4)
28
+ concurrent-ruby (1.2.2)
29
+ connection_pool (2.4.1)
30
+ diff-lcs (1.5.0)
31
+ drb (2.1.1)
32
+ ruby2_keywords
33
+ i18n (1.14.1)
34
+ concurrent-ruby (~> 1.0)
35
+ minitest (5.20.0)
36
+ mutex_m (0.1.2)
37
+ rake (13.0.6)
38
+ rspec (3.12.0)
39
+ rspec-core (~> 3.12.0)
40
+ rspec-expectations (~> 3.12.0)
41
+ rspec-mocks (~> 3.12.0)
42
+ rspec-core (3.12.2)
43
+ rspec-support (~> 3.12.0)
44
+ rspec-expectations (3.12.3)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.12.0)
47
+ rspec-mocks (3.12.6)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.12.0)
50
+ rspec-support (3.12.1)
51
+ ruby2_keywords (0.0.5)
52
+ thor (1.2.2)
53
+ tzinfo (2.0.6)
54
+ concurrent-ruby (~> 1.0)
55
+
56
+ PLATFORMS
57
+ ruby
58
+
59
+ DEPENDENCIES
60
+ appraisal
61
+ bundler (>= 2)
62
+ nested_record!
63
+ rake (~> 13.0)
64
+ rspec (~> 3.0)
65
+
66
+ BUNDLED WITH
67
+ 2.2.14
data/README.md CHANGED
@@ -105,6 +105,124 @@ user.profile_attributes = {
105
105
  }
106
106
  ```
107
107
 
108
+ ## Advanced usage
109
+
110
+ `nested_record` can do many different things for you!
111
+
112
+ ### Validations
113
+
114
+ Every `NestedRecord::Base` descendant in fact is an `ActiveModel::Model` so standard validations are also supported.
115
+
116
+ ```ruby
117
+ class Profile < NestedRecord::Base
118
+ attribute :age, :integer
119
+ attribute :active, :boolean
120
+
121
+ validates :age, presence: true
122
+ end
123
+ ```
124
+
125
+ ### Don't bother with defining record micro-classes
126
+
127
+ If you want so, you can rewrite the above example this way:
128
+
129
+ ```ruby
130
+ class User < ApplicationRecord
131
+ has_one_nested :profile do
132
+ attribute :age, :integer
133
+ attribute :active, :boolean
134
+ has_one_nested :contacts do
135
+ attribute :email, :string
136
+ attribute :phone, :string
137
+ has_many_nested :socials do
138
+ attribute :name
139
+ attribute :url
140
+ end
141
+ end
142
+ end
143
+ end
144
+ ```
145
+
146
+ Record classes then available under _local types_ namespace module e.g. `User::LocalTypes::Profile`.
147
+
148
+ ### Concerns
149
+
150
+ Common attributes, validations and other settings can be DRY-ed to modules called _concerns_.
151
+
152
+ ```ruby
153
+ module TitleAndDescription
154
+ extend NestedRecord::Concern
155
+
156
+ attribute :title
157
+ attribute :description
158
+
159
+ validates :title, presence: true
160
+ end
161
+
162
+ class Article < NestedRecord::Base
163
+ has_one_nested :foo do
164
+ include TitleAndDescription
165
+ end
166
+ end
167
+ ```
168
+
169
+ ### `:class_name` option
170
+
171
+ By default, class name of nested record is automatically inferred from the association name but of course it's all customizable. There's a `:class_name` option for this!
172
+
173
+ Depending on what form do you use — `has_* :foo` or `has_* :foo do ... end`, the `:class_name` option means different things.
174
+
175
+ #### `:class_name` option when referring an external model
176
+
177
+ In a non-`&block` form, the `:class_name` behaves similar to the option with same name in ActiveRecord's `has_one`/`has_many` associations.
178
+
179
+ ```ruby
180
+ class User < ApplicationRecord
181
+ has_one_nested :profile, class_name: 'SomeNamespace::Profile'
182
+ end
183
+
184
+ class SomeNamespace::Profile < NestedRecord::Base
185
+ attribute :age, :integer
186
+ attribute :active, :boolean
187
+ end
188
+ ```
189
+
190
+ #### `:class_name` option when using with an embedded _local types_
191
+
192
+ When record definition is embedded, `:class_name` option denotes the name of the class in _local types_ namespace module under which it's defined.
193
+
194
+ ```ruby
195
+ class User < ApplicationRecord
196
+ has_one_nested :profile, class_name: 'ProfileRecord' do
197
+ attribute :age, :integer
198
+ attribute :active, :boolean
199
+ end
200
+ end
201
+ ```
202
+
203
+ Then the profile model is available under `User::LocalTypes::ProfileRecord` name.
204
+
205
+ You can also disable the const naming at all, passing `class_name: false`. In this case, the _local type_ is anonymous so no constant in _local types_ namespace is set.
206
+
207
+ `class_name: true` (the default) means infer the class name from association name e.g. `User::LocalTypes::Profile` constant is set by default.
208
+
209
+ ### `nested_accessors`
210
+
211
+ This is the `store_accessor` on steroids! Unlike `store_accessor` it's support nesting, type coercions and all other things this library can do. Think of it as a `has_one_nested` association with accessors lifted up one level.
212
+
213
+ ```ruby
214
+ class User < ApplicationRecord
215
+ nested_accessors from: :profile do
216
+ attribute :age, :integer
217
+ attribute :active, :integer
218
+ end
219
+ end
220
+
221
+ user = User.new
222
+ user.age = 33
223
+ user.active = true
224
+ ```
225
+
108
226
  ## Development
109
227
 
110
228
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,5 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
- gem "rails", "~> 5.2.3"
5
+ gem "activemodel", "~> 5.2.8.1"
4
6
 
5
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ nested_record (1.1.1)
5
+ activemodel (> 5.2, < 7.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (5.2.8.1)
11
+ activesupport (= 5.2.8.1)
12
+ activesupport (5.2.8.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ appraisal (2.5.0)
18
+ bundler
19
+ rake
20
+ thor (>= 0.14.0)
21
+ concurrent-ruby (1.2.2)
22
+ diff-lcs (1.5.0)
23
+ i18n (1.14.1)
24
+ concurrent-ruby (~> 1.0)
25
+ minitest (5.20.0)
26
+ rake (13.0.6)
27
+ rspec (3.12.0)
28
+ rspec-core (~> 3.12.0)
29
+ rspec-expectations (~> 3.12.0)
30
+ rspec-mocks (~> 3.12.0)
31
+ rspec-core (3.12.2)
32
+ rspec-support (~> 3.12.0)
33
+ rspec-expectations (3.12.3)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.12.0)
36
+ rspec-mocks (3.12.6)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.12.0)
39
+ rspec-support (3.12.1)
40
+ thor (1.3.0)
41
+ thread_safe (0.3.6)
42
+ tzinfo (1.2.11)
43
+ thread_safe (~> 0.1)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ activemodel (~> 5.2.8.1)
50
+ appraisal
51
+ bundler (>= 2)
52
+ nested_record!
53
+ rake (~> 13.0)
54
+ rspec (~> 3.0)
55
+
56
+ BUNDLED WITH
57
+ 2.3.22
@@ -1,5 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
- gem "rails", "~> 6.0.0"
5
+ gem "activemodel", "~> 6.0.6.1"
4
6
 
5
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ nested_record (1.1.1)
5
+ activemodel (> 5.2, < 7.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (6.0.6.1)
11
+ activesupport (= 6.0.6.1)
12
+ activesupport (6.0.6.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ zeitwerk (~> 2.2, >= 2.2.2)
18
+ appraisal (2.5.0)
19
+ bundler
20
+ rake
21
+ thor (>= 0.14.0)
22
+ concurrent-ruby (1.2.2)
23
+ diff-lcs (1.5.0)
24
+ i18n (1.14.1)
25
+ concurrent-ruby (~> 1.0)
26
+ minitest (5.20.0)
27
+ rake (13.0.6)
28
+ rspec (3.12.0)
29
+ rspec-core (~> 3.12.0)
30
+ rspec-expectations (~> 3.12.0)
31
+ rspec-mocks (~> 3.12.0)
32
+ rspec-core (3.12.2)
33
+ rspec-support (~> 3.12.0)
34
+ rspec-expectations (3.12.3)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.12.0)
37
+ rspec-mocks (3.12.6)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.12.0)
40
+ rspec-support (3.12.1)
41
+ thor (1.3.0)
42
+ thread_safe (0.3.6)
43
+ tzinfo (1.2.11)
44
+ thread_safe (~> 0.1)
45
+ zeitwerk (2.6.12)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ activemodel (~> 6.0.6.1)
52
+ appraisal
53
+ bundler (>= 2)
54
+ nested_record!
55
+ rake (~> 13.0)
56
+ rspec (~> 3.0)
57
+
58
+ BUNDLED WITH
59
+ 2.4.20
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activemodel", "~> 6.1.7.6"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ nested_record (1.1.1)
5
+ activemodel (> 5.2, < 7.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (6.1.7.6)
11
+ activesupport (= 6.1.7.6)
12
+ activesupport (6.1.7.6)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 1.6, < 2)
15
+ minitest (>= 5.1)
16
+ tzinfo (~> 2.0)
17
+ zeitwerk (~> 2.3)
18
+ appraisal (2.5.0)
19
+ bundler
20
+ rake
21
+ thor (>= 0.14.0)
22
+ concurrent-ruby (1.2.2)
23
+ diff-lcs (1.5.0)
24
+ i18n (1.14.1)
25
+ concurrent-ruby (~> 1.0)
26
+ minitest (5.20.0)
27
+ rake (13.0.6)
28
+ rspec (3.12.0)
29
+ rspec-core (~> 3.12.0)
30
+ rspec-expectations (~> 3.12.0)
31
+ rspec-mocks (~> 3.12.0)
32
+ rspec-core (3.12.2)
33
+ rspec-support (~> 3.12.0)
34
+ rspec-expectations (3.12.3)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.12.0)
37
+ rspec-mocks (3.12.6)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.12.0)
40
+ rspec-support (3.12.1)
41
+ thor (1.3.0)
42
+ tzinfo (2.0.6)
43
+ concurrent-ruby (~> 1.0)
44
+ zeitwerk (2.6.12)
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ activemodel (~> 6.1.7.6)
51
+ appraisal
52
+ bundler (>= 2)
53
+ nested_record!
54
+ rake (~> 13.0)
55
+ rspec (~> 3.0)
56
+
57
+ BUNDLED WITH
58
+ 2.4.20
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activemodel", "~> 7.0.8"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ nested_record (1.1.1)
5
+ activemodel (> 5.2, < 7.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (7.0.8)
11
+ activesupport (= 7.0.8)
12
+ activesupport (7.0.8)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 1.6, < 2)
15
+ minitest (>= 5.1)
16
+ tzinfo (~> 2.0)
17
+ appraisal (2.5.0)
18
+ bundler
19
+ rake
20
+ thor (>= 0.14.0)
21
+ concurrent-ruby (1.2.2)
22
+ diff-lcs (1.5.0)
23
+ i18n (1.14.1)
24
+ concurrent-ruby (~> 1.0)
25
+ minitest (5.20.0)
26
+ rake (13.0.6)
27
+ rspec (3.12.0)
28
+ rspec-core (~> 3.12.0)
29
+ rspec-expectations (~> 3.12.0)
30
+ rspec-mocks (~> 3.12.0)
31
+ rspec-core (3.12.2)
32
+ rspec-support (~> 3.12.0)
33
+ rspec-expectations (3.12.3)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.12.0)
36
+ rspec-mocks (3.12.6)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.12.0)
39
+ rspec-support (3.12.1)
40
+ thor (1.3.0)
41
+ tzinfo (2.0.6)
42
+ concurrent-ruby (~> 1.0)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ activemodel (~> 7.0.8)
49
+ appraisal
50
+ bundler (>= 2)
51
+ nested_record!
52
+ rake (~> 13.0)
53
+ rspec (~> 3.0)
54
+
55
+ BUNDLED WITH
56
+ 2.4.20
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activemodel", "~> 7.1.1"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ nested_record (1.1.1)
5
+ activemodel (> 5.2, < 7.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (7.1.1)
11
+ activesupport (= 7.1.1)
12
+ activesupport (7.1.1)
13
+ base64
14
+ bigdecimal
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ connection_pool (>= 2.2.5)
17
+ drb
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ mutex_m
21
+ tzinfo (~> 2.0)
22
+ appraisal (2.5.0)
23
+ bundler
24
+ rake
25
+ thor (>= 0.14.0)
26
+ base64 (0.1.1)
27
+ bigdecimal (3.1.4)
28
+ concurrent-ruby (1.2.2)
29
+ connection_pool (2.4.1)
30
+ diff-lcs (1.5.0)
31
+ drb (2.1.1)
32
+ ruby2_keywords
33
+ i18n (1.14.1)
34
+ concurrent-ruby (~> 1.0)
35
+ minitest (5.20.0)
36
+ mutex_m (0.1.2)
37
+ rake (13.0.6)
38
+ rspec (3.12.0)
39
+ rspec-core (~> 3.12.0)
40
+ rspec-expectations (~> 3.12.0)
41
+ rspec-mocks (~> 3.12.0)
42
+ rspec-core (3.12.2)
43
+ rspec-support (~> 3.12.0)
44
+ rspec-expectations (3.12.3)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.12.0)
47
+ rspec-mocks (3.12.6)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.12.0)
50
+ rspec-support (3.12.1)
51
+ ruby2_keywords (0.0.5)
52
+ thor (1.3.0)
53
+ tzinfo (2.0.6)
54
+ concurrent-ruby (~> 1.0)
55
+
56
+ PLATFORMS
57
+ ruby
58
+
59
+ DEPENDENCIES
60
+ activemodel (~> 7.1.1)
61
+ appraisal
62
+ bundler (>= 2)
63
+ nested_record!
64
+ rake (~> 13.0)
65
+ rspec (~> 3.0)
66
+
67
+ BUNDLED WITH
68
+ 2.4.20
@@ -143,11 +143,11 @@ class NestedRecord::Base
143
143
  subclass ||=
144
144
  begin
145
145
  if type_name.start_with?('::')
146
- ActiveSupport::Dependencies.constantize(type_name)
146
+ NestedRecord.constantize(type_name)
147
147
  elsif subtypes_store_full?
148
- ActiveSupport::Dependencies.constantize(type_name)
148
+ NestedRecord.constantize(type_name)
149
149
  elsif subtypes_namespace
150
- ActiveSupport::Dependencies.safe_constantize("#{subtypes_namespace}::#{type_name}") || ActiveSupport::Dependencies.constantize(type_name)
150
+ NestedRecord.safe_constantize("#{subtypes_namespace}::#{type_name}") || NestedRecord.constantize(type_name)
151
151
  else
152
152
  NestedRecord.lookup_const(self, type_name)
153
153
  end
@@ -247,7 +247,9 @@ class NestedRecord::Base
247
247
  end
248
248
 
249
249
  def read_attribute(attr)
250
- attribute(attr)
250
+ name = attr.to_s
251
+ name = self.class.attribute_aliases[name] || name
252
+ @attributes.fetch_value(name)
251
253
  end
252
254
 
253
255
  def query_attribute(attr)
@@ -21,7 +21,7 @@ class NestedRecord::Collection
21
21
 
22
22
  def each
23
23
  return to_enum(:each) unless block_given?
24
- @ary.each(&proc)
24
+ @ary.each { |obj| yield obj }
25
25
  end
26
26
 
27
27
  def to_ary
@@ -70,19 +70,19 @@ class NestedRecord::Collection
70
70
 
71
71
  def select!
72
72
  return to_enum(:select!) unless block_given?
73
- @ary.select!(&proc)
73
+ @ary.select! { |obj| yield obj }
74
74
  self
75
75
  end
76
76
 
77
77
  def reject!
78
78
  return to_enum(:reject!) unless block_given?
79
- @ary.reject!(&proc)
79
+ @ary.reject! { |obj| yield obj }
80
80
  self
81
81
  end
82
82
 
83
83
  def sort_by!
84
84
  return to_enum(:sort_by!) unless block_given?
85
- @ary.sort_by!(&proc)
85
+ @ary.sort_by! { |obj| yield obj }
86
86
  self
87
87
  end
88
88
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class NestedRecord::CollectionProxy
2
4
  extend Forwardable
3
5
  include Enumerable
@@ -1,18 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NestedRecord
4
- # Derived from Rails source: https://github.com/rails/rails/blob/98a57aa5f610bc66af31af409c72173cdeeb3c9e/activerecord/lib/active_record/inheritance.rb#L181-L207
4
+ # TODO: Remove this when we drop support for Rails < 7.0
5
+ if ActiveSupport::VERSION::MAJOR < 7
6
+ def self.constantize(type_name)
7
+ ActiveSupport::Dependencies.constantize(type_name)
8
+ end
9
+
10
+ def self.safe_constantize(type_name)
11
+ ActiveSupport::Dependencies.safe_constantize(type_name)
12
+ end
13
+ else
14
+ def self.constantize(type_name)
15
+ type_name.constantize
16
+ end
5
17
 
18
+ def self.safe_constantize(type_name)
19
+ type_name.safe_constantize
20
+ end
21
+ end
22
+
23
+ # Derived from Rails source: https://github.com/rails/rails/blob/98a57aa5f610bc66af31af409c72173cdeeb3c9e/activerecord/lib/active_record/inheritance.rb#L181-L207
6
24
  def self.lookup_const(owner, type_name)
7
25
  if type_name.start_with?('::')
8
- ActiveSupport::Dependencies.constantize(type_name)
26
+ NestedRecord.constantize(type_name)
9
27
  else
10
28
  candidates = []
11
29
  owner.name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
12
30
  candidates << type_name
13
31
 
14
32
  candidates.each do |candidate|
15
- constant = ActiveSupport::Dependencies.safe_constantize(candidate)
33
+ constant = NestedRecord.safe_constantize(candidate)
16
34
  return constant if candidate == constant.to_s
17
35
  end
18
36
 
@@ -28,16 +28,20 @@ class NestedRecord::MacroRecorder
28
28
  after_initialize
29
29
  before_validation after_validation
30
30
  ].freeze.each do |meth|
31
- define_method(meth) do |*args, &block|
32
- @macros << [meth, args, block]
31
+ define_method(meth) do |*args, **kwargs, &block|
32
+ @macros << [meth, args, kwargs, block]
33
33
  end
34
34
  end
35
35
 
36
36
  def apply_to(mod_or_class)
37
37
  macros = @macros
38
38
  mod_or_class.module_eval do
39
- macros.each do |meth, args, block|
40
- public_send(meth, *args, &block)
39
+ macros.each do |meth, args, kwargs, block|
40
+ if kwargs.empty?
41
+ public_send(meth, *args, &block)
42
+ else
43
+ public_send(meth, *args, **kwargs, &block)
44
+ end
41
45
  end
42
46
  end
43
47
  end
@@ -95,24 +95,40 @@ class NestedRecord::Methods
95
95
 
96
96
  def validation_method_body
97
97
  name = @setup.name
98
- proc do
99
- collection = public_send(name)
100
- collection.map.with_index do |record, index|
101
- next true if record.valid?
102
- record.errors.each do |attribute, message|
103
- error_attribute = "#{name}[#{index}].#{attribute}"
104
- errors[error_attribute] << message
105
- errors[error_attribute].uniq!
106
- end
107
- record.errors.details.each_key do |attribute|
108
- error_attribute = "#{name}[#{index}].#{attribute}"
109
- record.errors.details[attribute].each do |error|
110
- errors.details[error_attribute] << error
111
- errors.details[error_attribute].uniq!
98
+ if ActiveModel::VERSION::MAJOR < 6 || (ActiveModel::VERSION::MAJOR == 6 && ActiveModel::VERSION::MINOR < 1)
99
+ proc do
100
+ collection = public_send(name)
101
+ collection.map.with_index do |record, index|
102
+ next true if record.valid?
103
+ record.errors.each do |attribute, message|
104
+ error_attribute = "#{name}[#{index}].#{attribute}"
105
+ errors[error_attribute] << message
106
+ errors[error_attribute].uniq!
112
107
  end
113
- end
114
- false
115
- end.all?
108
+ record.errors.details.each_key do |attribute|
109
+ error_attribute = "#{name}[#{index}].#{attribute}"
110
+ record.errors.details[attribute].each do |error|
111
+ errors.details[error_attribute] << error
112
+ errors.details[error_attribute].uniq!
113
+ end
114
+ end
115
+ false
116
+ end.all?
117
+ end
118
+ else
119
+ proc do
120
+ collection = public_send(name)
121
+ collection.map.with_index do |record, index|
122
+ next true if record.valid?
123
+ record.errors.group_by_attribute.each do |attribute, errors|
124
+ error_attribute = "#{name}[#{index}].#{attribute}"
125
+ errors.each do |error|
126
+ self.errors.import(error, attribute: error_attribute)
127
+ end
128
+ end
129
+ false
130
+ end.all?
131
+ end
116
132
  end
117
133
  end
118
134
  end
@@ -62,24 +62,40 @@ class NestedRecord::Methods
62
62
 
63
63
  def validation_method_body
64
64
  name = @setup.name
65
- proc do
66
- record = public_send(name)
67
- return true unless record
68
- return true if record.valid?
65
+ if ActiveModel::VERSION::MAJOR < 6 || (ActiveModel::VERSION::MAJOR == 6 && ActiveModel::VERSION::MINOR < 1)
66
+ proc do
67
+ record = public_send(name)
68
+ return true unless record
69
+ return true if record.valid?
69
70
 
70
- record.errors.each do |attribute, message|
71
- error_attribute = "#{name}.#{attribute}"
72
- errors[error_attribute] << message
73
- errors[error_attribute].uniq!
71
+ record.errors.each do |attribute, message|
72
+ error_attribute = "#{name}.#{attribute}"
73
+ errors[error_attribute] << message
74
+ errors[error_attribute].uniq!
75
+ end
76
+ record.errors.details.each_key do |attribute|
77
+ error_attribute = "#{name}.#{attribute}"
78
+ record.errors.details[attribute].each do |error|
79
+ errors.details[error_attribute] << error
80
+ errors.details[error_attribute].uniq!
81
+ end
82
+ end
83
+ false
74
84
  end
75
- record.errors.details.each_key do |attribute|
76
- error_attribute = "#{name}.#{attribute}"
77
- record.errors.details[attribute].each do |error|
78
- errors.details[error_attribute] << error
79
- errors.details[error_attribute].uniq!
85
+ else
86
+ proc do
87
+ record = public_send(name)
88
+ return true unless record
89
+ return true if record.valid?
90
+
91
+ record.errors.group_by_attribute.each do |attribute, errors|
92
+ error_attribute = "#{name}.#{attribute}"
93
+ errors.each do |error|
94
+ self.errors.import(error, attribute: error_attribute)
95
+ end
80
96
  end
97
+ false
81
98
  end
82
- false
83
99
  end
84
100
  end
85
101
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class NestedRecord::Type < ActiveRecord::Type::Json
3
+ class NestedRecord::Type < ActiveModel::Type::Value
4
4
  require 'nested_record/type/many'
5
5
  require 'nested_record/type/one'
6
6
 
@@ -13,11 +13,16 @@ class NestedRecord::Type < ActiveRecord::Type::Json
13
13
  end
14
14
 
15
15
  def deserialize(value)
16
- cast_value(super)
16
+ value = if value.is_a?(::String)
17
+ ActiveSupport::JSON.decode(value) rescue nil
18
+ else
19
+ value
20
+ end
21
+ cast_value(value)
17
22
  end
18
23
 
19
24
  def serialize(obj)
20
- super(obj&.as_json)
25
+ ActiveSupport::JSON.encode(obj.as_json) unless obj.nil?
21
26
  end
22
27
 
23
28
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NestedRecord
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.1'
5
5
  end
data/lib/nested_record.rb CHANGED
@@ -3,9 +3,11 @@ module NestedRecord
3
3
 
4
4
  require 'forwardable'
5
5
 
6
- require 'active_record'
6
+ require 'active_model'
7
7
  require 'active_support/dependencies'
8
+ require 'active_support/inflector'
8
9
  require 'active_support/concern'
10
+ require 'active_support/json'
9
11
 
10
12
  require 'nested_record/macro'
11
13
  require 'nested_record/base'
@@ -22,11 +22,10 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_development_dependency "bundler", "~> 2.0"
26
- spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "bundler", ">= 2"
26
+ spec.add_development_dependency "rake", "~> 13.0"
27
27
  spec.add_development_dependency "rspec", "~> 3.0"
28
- spec.add_development_dependency "pry"
29
- spec.add_development_dependency "pry-byebug"
28
+ spec.add_development_dependency "appraisal"
30
29
 
31
- spec.add_dependency "rails", ">= 5.2", "<= 6.0"
30
+ spec.add_dependency "activemodel", "> 5.2", "< 7.2"
32
31
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'bundler/setup'
2
2
  require 'nested_record'
3
- require 'pry'
4
3
 
5
4
  Dir[File.join(__dir__, 'support/**/*.rb')].each { |f| require f }
6
5
 
@@ -53,7 +53,7 @@ module TestModel
53
53
  include NestedRecord::Macro
54
54
 
55
55
  def read_attribute(attr)
56
- attribute(attr)
56
+ attribute(attr.to_s)
57
57
  end
58
58
 
59
59
  class_eval(&block) if block
@@ -64,9 +64,19 @@ module TestModel
64
64
  module Erase
65
65
  def erase_test_consts
66
66
  Array(@test_consts).reverse_each do |klass|
67
- ActiveSupport::Dependencies.remove_constant(klass.name)
67
+ remove_constant(klass.name)
68
+ end
69
+ ActiveSupport::Dependencies.clear unless zeitwerk?
70
+ end
71
+
72
+ def remove_constant(klass_name)
73
+ if ActiveSupport::Dependencies.respond_to?(:remove_constant)
74
+ ActiveSupport::Dependencies.remove_constant(klass_name)
75
+ else
76
+ base, _, object = klass_name.to_s.rpartition('::')
77
+ base = base.empty? ? Object : NestedRecord.constantize(base)
78
+ base.send :remove_const, object
68
79
  end
69
- ActiveSupport::Dependencies.clear
70
80
  end
71
81
 
72
82
  def test_const_dig_name!(name)
@@ -85,5 +95,9 @@ module TestModel
85
95
  end
86
96
  [namespace, name]
87
97
  end
98
+
99
+ def zeitwerk?
100
+ ActiveSupport::VERSION::MAJOR >= 7
101
+ end
88
102
  end
89
103
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nested_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Kochnev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-31 00:00:00.000000000 Z
11
+ date: 2023-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: pry
56
+ name: appraisal
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,57 +67,53 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry-byebug
70
+ name: activemodel
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rails
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
73
+ - - ">"
88
74
  - !ruby/object:Gem::Version
89
75
  version: '5.2'
90
- - - "<="
76
+ - - "<"
91
77
  - !ruby/object:Gem::Version
92
- version: '6.0'
78
+ version: '7.2'
93
79
  type: :runtime
94
80
  prerelease: false
95
81
  version_requirements: !ruby/object:Gem::Requirement
96
82
  requirements:
97
- - - ">="
83
+ - - ">"
98
84
  - !ruby/object:Gem::Version
99
85
  version: '5.2'
100
- - - "<="
86
+ - - "<"
101
87
  - !ruby/object:Gem::Version
102
- version: '6.0'
103
- description:
88
+ version: '7.2'
89
+ description:
104
90
  email:
105
91
  - hashtable@yandex.ru
106
92
  executables: []
107
93
  extensions: []
108
94
  extra_rdoc_files: []
109
95
  files:
96
+ - ".github/workflows/rspec.yml"
110
97
  - ".gitignore"
111
98
  - ".rspec"
112
- - ".travis.yml"
99
+ - Appraisals
113
100
  - Gemfile
101
+ - Gemfile.lock
114
102
  - LICENSE.txt
115
103
  - README.md
116
104
  - Rakefile
117
105
  - bin/console
118
106
  - bin/setup
119
107
  - gemfiles/rails_5.2.gemfile
108
+ - gemfiles/rails_5.2.gemfile.lock
120
109
  - gemfiles/rails_6.0.gemfile
110
+ - gemfiles/rails_6.0.gemfile.lock
111
+ - gemfiles/rails_6.1.gemfile
112
+ - gemfiles/rails_6.1.gemfile.lock
113
+ - gemfiles/rails_7.0.gemfile
114
+ - gemfiles/rails_7.0.gemfile.lock
115
+ - gemfiles/rails_7.1.gemfile
116
+ - gemfiles/rails_7.1.gemfile.lock
121
117
  - lib/nested_record.rb
122
118
  - lib/nested_record/base.rb
123
119
  - lib/nested_record/collection.rb
@@ -150,7 +146,7 @@ homepage: https://github.com/marshall-lee/nested_record
150
146
  licenses:
151
147
  - MIT
152
148
  metadata: {}
153
- post_install_message:
149
+ post_install_message:
154
150
  rdoc_options: []
155
151
  require_paths:
156
152
  - lib
@@ -165,9 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
161
  - !ruby/object:Gem::Version
166
162
  version: '0'
167
163
  requirements: []
168
- rubyforge_project:
169
- rubygems_version: 2.7.6
170
- signing_key:
164
+ rubygems_version: 3.4.20
165
+ signing_key:
171
166
  specification_version: 4
172
167
  summary: ActiveModel mapper for JSON fields
173
168
  test_files: []
data/.travis.yml DELETED
@@ -1,11 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.5
7
- - 2.6
8
- gemfile:
9
- - gemfiles/rails_5.2.gemfile
10
- - gemfiles/rails_6.0.gemfile
11
- before_install: gem install bundler -v 2.0.1