active_typed_store 0.1.0 → 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/.rubocop.yml +8 -2
- data/Gemfile +7 -8
- data/Gemfile.lock +104 -82
- data/README.md +73 -15
- data/Rakefile +3 -3
- data/lib/active_typed_store/attrs.rb +68 -0
- data/lib/active_typed_store/store.rb +5 -51
- data/lib/active_typed_store/version.rb +1 -3
- data/lib/active_typed_store.rb +5 -4
- metadata +22 -28
- data/.idea/.gitignore +0 -8
- data/.idea/active_typed_store.iml +0 -60
- data/.idea/misc.xml +0 -4
- data/.idea/modules.xml +0 -8
- data/.idea/vcs.xml +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9df5fb7b19e2a2003785e1275e39e66125c565b2566b0bddb217a04c725d7829
|
|
4
|
+
data.tar.gz: 139af5bd480cb19bf91bfa734004150025b53215a917f540786b708071856780
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3acca100a52acbf90ff6ae3e8e3283186d524361c5918f6d7a51a073124e846808fcc8b1860294a9e5d27e1eb5dad232dcf3fe3a00f51e86a4df97ff4251b381
|
|
7
|
+
data.tar.gz: 9f7e5c83a2f4a36e3e6d9b7cd912075be7a9707adfac1f581cffe5ab8b6851402e2b864894c3892ad0fbf5f0156d989424867c7470b9b9a776c8f5183c34ab05
|
data/.rubocop.yml
CHANGED
|
@@ -3,7 +3,7 @@ inherit_gem:
|
|
|
3
3
|
- .rubocop.yml
|
|
4
4
|
|
|
5
5
|
AllCops:
|
|
6
|
-
TargetRubyVersion:
|
|
6
|
+
TargetRubyVersion: 3.1
|
|
7
7
|
|
|
8
8
|
Lint/ConstantDefinitionInBlock:
|
|
9
9
|
Enabled: false
|
|
@@ -15,4 +15,10 @@ Rails/ApplicationRecord:
|
|
|
15
15
|
Enabled: false
|
|
16
16
|
|
|
17
17
|
Rails/TimeZone:
|
|
18
|
-
Enabled: false
|
|
18
|
+
Enabled: false
|
|
19
|
+
|
|
20
|
+
Layout/EmptyLinesAroundModuleBody:
|
|
21
|
+
EnforcedStyle: no_empty_lines
|
|
22
|
+
|
|
23
|
+
Layout/EmptyLinesAroundClassBody:
|
|
24
|
+
EnforcedStyle: no_empty_lines
|
data/Gemfile
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
source
|
|
3
|
+
source "https://rubygems.org"
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
gem
|
|
8
|
-
gem
|
|
9
|
-
gem
|
|
10
|
-
gem
|
|
11
|
-
gem
|
|
12
|
-
gem
|
|
13
|
-
gem 'sqlite3', '~> 1.4'
|
|
7
|
+
gem "dry-types"
|
|
8
|
+
gem "priscilla", github: "corp-gp/priscilla"
|
|
9
|
+
gem "rake", "~> 13.0"
|
|
10
|
+
gem "rspec", "~> 3.9"
|
|
11
|
+
gem "rubocop-gp", github: "corp-gp/rubocop-gp"
|
|
12
|
+
gem "sqlite3", "~> 1.4"
|
data/Gemfile.lock
CHANGED
|
@@ -8,128 +8,150 @@ GIT
|
|
|
8
8
|
|
|
9
9
|
GIT
|
|
10
10
|
remote: https://github.com/corp-gp/rubocop-gp.git
|
|
11
|
-
revision:
|
|
11
|
+
revision: 61d6ca243e1a5f7d209e9619417c8fc1d303d181
|
|
12
12
|
specs:
|
|
13
|
-
rubocop-gp (0.0.
|
|
13
|
+
rubocop-gp (0.0.4)
|
|
14
14
|
rubocop
|
|
15
|
+
rubocop-capybara
|
|
16
|
+
rubocop-factory_bot
|
|
15
17
|
rubocop-performance
|
|
16
18
|
rubocop-rails
|
|
17
19
|
rubocop-rspec
|
|
20
|
+
rubocop-rspec_rails
|
|
18
21
|
|
|
19
22
|
PATH
|
|
20
23
|
remote: .
|
|
21
24
|
specs:
|
|
22
25
|
active_typed_store (0.1.0)
|
|
23
|
-
activemodel
|
|
24
|
-
activerecord
|
|
25
|
-
activesupport
|
|
26
|
+
activemodel
|
|
27
|
+
activerecord
|
|
28
|
+
activesupport
|
|
26
29
|
|
|
27
30
|
GEM
|
|
28
31
|
remote: https://rubygems.org/
|
|
29
32
|
specs:
|
|
30
|
-
activemodel (
|
|
31
|
-
activesupport (=
|
|
32
|
-
activerecord (
|
|
33
|
-
activemodel (=
|
|
34
|
-
activesupport (=
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
activemodel (7.2.1)
|
|
34
|
+
activesupport (= 7.2.1)
|
|
35
|
+
activerecord (7.2.1)
|
|
36
|
+
activemodel (= 7.2.1)
|
|
37
|
+
activesupport (= 7.2.1)
|
|
38
|
+
timeout (>= 0.4.0)
|
|
39
|
+
activesupport (7.2.1)
|
|
40
|
+
base64
|
|
41
|
+
bigdecimal
|
|
42
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
43
|
+
connection_pool (>= 2.2.5)
|
|
44
|
+
drb
|
|
45
|
+
i18n (>= 1.6, < 2)
|
|
46
|
+
logger (>= 1.4.2)
|
|
47
|
+
minitest (>= 5.1)
|
|
48
|
+
securerandom (>= 0.3)
|
|
49
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
41
50
|
ast (2.4.2)
|
|
42
|
-
|
|
51
|
+
base64 (0.2.0)
|
|
52
|
+
bigdecimal (3.1.8)
|
|
43
53
|
colorize (0.8.1)
|
|
44
|
-
concurrent-ruby (1.
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
concurrent-ruby (1.3.4)
|
|
55
|
+
connection_pool (2.4.1)
|
|
56
|
+
diff-lcs (1.5.1)
|
|
57
|
+
drb (2.2.1)
|
|
58
|
+
dry-core (1.0.1)
|
|
47
59
|
concurrent-ruby (~> 1.0)
|
|
48
|
-
|
|
49
|
-
dry-
|
|
60
|
+
zeitwerk (~> 2.6)
|
|
61
|
+
dry-inflector (1.1.0)
|
|
62
|
+
dry-logic (1.5.0)
|
|
50
63
|
concurrent-ruby (~> 1.0)
|
|
51
|
-
dry-
|
|
52
|
-
|
|
64
|
+
dry-core (~> 1.0, < 2)
|
|
65
|
+
zeitwerk (~> 2.6)
|
|
66
|
+
dry-types (1.7.2)
|
|
67
|
+
bigdecimal (~> 3.0)
|
|
53
68
|
concurrent-ruby (~> 1.0)
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
dry-core (~> 1.0)
|
|
70
|
+
dry-inflector (~> 1.0)
|
|
71
|
+
dry-logic (~> 1.4)
|
|
72
|
+
zeitwerk (~> 2.6)
|
|
73
|
+
i18n (1.14.5)
|
|
56
74
|
concurrent-ruby (~> 1.0)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
dry-logic (~> 1.0, >= 1.0.2)
|
|
64
|
-
i18n (1.8.11)
|
|
65
|
-
concurrent-ruby (~> 1.0)
|
|
66
|
-
method_source (1.0.0)
|
|
67
|
-
minitest (5.15.0)
|
|
68
|
-
parallel (1.21.0)
|
|
69
|
-
parser (3.0.3.2)
|
|
75
|
+
json (2.7.2)
|
|
76
|
+
language_server-protocol (3.17.0.3)
|
|
77
|
+
logger (1.6.1)
|
|
78
|
+
minitest (5.25.1)
|
|
79
|
+
parallel (1.26.3)
|
|
80
|
+
parser (3.3.5.0)
|
|
70
81
|
ast (~> 2.4.1)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
rspec-
|
|
81
|
-
|
|
82
|
-
rspec-
|
|
83
|
-
rspec-
|
|
84
|
-
rspec-support (~> 3.9.3)
|
|
85
|
-
rspec-expectations (3.9.4)
|
|
82
|
+
racc
|
|
83
|
+
racc (1.8.1)
|
|
84
|
+
rack (3.1.7)
|
|
85
|
+
rainbow (3.1.1)
|
|
86
|
+
rake (13.2.1)
|
|
87
|
+
regexp_parser (2.9.2)
|
|
88
|
+
rspec (3.13.0)
|
|
89
|
+
rspec-core (~> 3.13.0)
|
|
90
|
+
rspec-expectations (~> 3.13.0)
|
|
91
|
+
rspec-mocks (~> 3.13.0)
|
|
92
|
+
rspec-core (3.13.1)
|
|
93
|
+
rspec-support (~> 3.13.0)
|
|
94
|
+
rspec-expectations (3.13.3)
|
|
86
95
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
87
|
-
rspec-support (~> 3.
|
|
88
|
-
rspec-mocks (3.
|
|
96
|
+
rspec-support (~> 3.13.0)
|
|
97
|
+
rspec-mocks (3.13.1)
|
|
89
98
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
90
|
-
rspec-support (~> 3.
|
|
91
|
-
rspec-support (3.
|
|
92
|
-
rubocop (1.
|
|
99
|
+
rspec-support (~> 3.13.0)
|
|
100
|
+
rspec-support (3.13.1)
|
|
101
|
+
rubocop (1.66.1)
|
|
102
|
+
json (~> 2.3)
|
|
103
|
+
language_server-protocol (>= 3.17.0)
|
|
93
104
|
parallel (~> 1.10)
|
|
94
|
-
parser (>= 3.
|
|
105
|
+
parser (>= 3.3.0.2)
|
|
95
106
|
rainbow (>= 2.2.2, < 4.0)
|
|
96
|
-
regexp_parser (>=
|
|
97
|
-
|
|
98
|
-
rubocop-ast (>= 1.12.0, < 2.0)
|
|
107
|
+
regexp_parser (>= 2.4, < 3.0)
|
|
108
|
+
rubocop-ast (>= 1.32.2, < 2.0)
|
|
99
109
|
ruby-progressbar (~> 1.7)
|
|
100
|
-
unicode-display_width (>=
|
|
101
|
-
rubocop-ast (1.
|
|
102
|
-
parser (>= 3.
|
|
103
|
-
rubocop-
|
|
104
|
-
rubocop (
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
111
|
+
rubocop-ast (1.32.3)
|
|
112
|
+
parser (>= 3.3.1.0)
|
|
113
|
+
rubocop-capybara (2.21.0)
|
|
114
|
+
rubocop (~> 1.41)
|
|
115
|
+
rubocop-factory_bot (2.26.1)
|
|
116
|
+
rubocop (~> 1.61)
|
|
117
|
+
rubocop-performance (1.21.1)
|
|
118
|
+
rubocop (>= 1.48.1, < 2.0)
|
|
119
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
|
120
|
+
rubocop-rails (2.26.1)
|
|
107
121
|
activesupport (>= 4.2.0)
|
|
108
122
|
rack (>= 1.1)
|
|
109
|
-
rubocop (>= 1.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
rubocop (>= 1.52.0, < 2.0)
|
|
124
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
|
125
|
+
rubocop-rspec (3.0.5)
|
|
126
|
+
rubocop (~> 1.61)
|
|
127
|
+
rubocop-rspec_rails (2.30.0)
|
|
128
|
+
rubocop (~> 1.61)
|
|
129
|
+
rubocop-rspec (~> 3, >= 3.0.1)
|
|
130
|
+
ruby-progressbar (1.13.0)
|
|
113
131
|
rumoji (0.5.0)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
securerandom (0.3.1)
|
|
133
|
+
sqlite3 (1.7.3-arm64-darwin)
|
|
134
|
+
sqlite3 (1.7.3-x86_64-darwin)
|
|
135
|
+
sqlite3 (1.7.3-x86_64-linux)
|
|
136
|
+
timeout (0.4.1)
|
|
137
|
+
tzinfo (2.0.6)
|
|
138
|
+
concurrent-ruby (~> 1.0)
|
|
139
|
+
unicode-display_width (2.6.0)
|
|
140
|
+
zeitwerk (2.6.18)
|
|
120
141
|
|
|
121
142
|
PLATFORMS
|
|
143
|
+
arm64-darwin-21
|
|
144
|
+
x86_64-darwin-21
|
|
122
145
|
x86_64-linux
|
|
123
146
|
|
|
124
147
|
DEPENDENCIES
|
|
125
148
|
active_typed_store!
|
|
126
149
|
dry-types
|
|
127
150
|
priscilla!
|
|
128
|
-
pry (~> 0.12)
|
|
129
151
|
rake (~> 13.0)
|
|
130
152
|
rspec (~> 3.9)
|
|
131
153
|
rubocop-gp!
|
|
132
154
|
sqlite3 (~> 1.4)
|
|
133
155
|
|
|
134
156
|
BUNDLED WITH
|
|
135
|
-
2.
|
|
157
|
+
2.5.7
|
data/README.md
CHANGED
|
@@ -1,38 +1,96 @@
|
|
|
1
1
|
# ActiveTypedStore
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`active_typed_store` is a lightweight (__65 lines of code__) and highly performant gem (see [benchmarks](#benchmarks))
|
|
4
|
+
designed to help you store and manage typed data in JSON format within database.
|
|
5
|
+
This gem provides a simple, yet powerful way to ensure that your JSON data cast
|
|
6
|
+
to specific types, enabling more structured and reliable use of JSON fields in your Rails models.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
You can use `ActiveRecord Types` for simplicity or `Dry Types` for more advanced features such as
|
|
9
|
+
constraints and type composition. You can also combine both approaches
|
|
10
|
+
in the same model to get the best of both worlds.
|
|
6
11
|
|
|
7
12
|
## Installation
|
|
8
13
|
|
|
9
14
|
Add this line to your application's Gemfile:
|
|
10
15
|
|
|
11
16
|
```ruby
|
|
12
|
-
gem
|
|
17
|
+
gem "active_typed_store"
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
$ bundle install
|
|
20
|
+
## Usage
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
### Using [ActiveRecord Types](https://api.rubyonrails.org/classes/ActiveRecord/Type.html)
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
```ruby
|
|
25
|
+
class Model < ActiveRecord::Base
|
|
26
|
+
typed_store(:params) do # params - the name of the store
|
|
27
|
+
attr :task_id, :integer
|
|
28
|
+
attr :name, :string
|
|
29
|
+
attr :notify_at, :datetime
|
|
30
|
+
attr :asap, :boolean, default: false
|
|
31
|
+
attr :settings, :json
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
m = Model.first
|
|
36
|
+
m.task_id = "123" # string
|
|
37
|
+
m.task_id # => 123, int
|
|
38
|
+
m.task_id? # => true, value.present? under the hood
|
|
39
|
+
m.asap? # => false
|
|
40
|
+
m.asap = "yes"
|
|
41
|
+
m.asap # => true
|
|
42
|
+
m.asap? # => true
|
|
43
|
+
```
|
|
22
44
|
|
|
23
|
-
|
|
45
|
+
`attr(name, type, options)`
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
- `name` the name of the accessor to the store
|
|
48
|
+
- `type` a symbol such as `:string` or `:integer`, or a type object to be used for the accessor
|
|
49
|
+
- `options` (optional), a hash of cast type options such as:
|
|
50
|
+
- `precision`, `limit`, `scale`
|
|
51
|
+
- `default` the default value to use when no value is provided. Otherwise, the default will be nil
|
|
52
|
+
- `array` specifies that the type should be an array
|
|
26
53
|
|
|
27
|
-
## Development
|
|
28
54
|
|
|
29
|
-
|
|
55
|
+
### Using [Dry Types](https://dry-rb.org/gems/dry-types/1.7/built-in-types/)
|
|
56
|
+
```ruby
|
|
57
|
+
class Model < ActiveRecord::Base
|
|
58
|
+
typed_store(:params) do
|
|
59
|
+
attr :task_id, Types::Params::Integer
|
|
60
|
+
attr :name, Types::Params::String
|
|
61
|
+
attr :notify_at, Types::Params::Time
|
|
62
|
+
attr :asap, Types::Params::Bool.default(false)
|
|
63
|
+
attr :email, Types::String.constrained(format: /@/)
|
|
64
|
+
attr :settings, Types::Params::Hash
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
30
68
|
|
|
31
|
-
|
|
69
|
+
### Combine ActiveRecord and Dry Types
|
|
32
70
|
|
|
33
|
-
|
|
71
|
+
```ruby
|
|
72
|
+
class Model < ActiveRecord::Base
|
|
73
|
+
typed_store(:params) do
|
|
74
|
+
attr :price, :decimal, scale: 2
|
|
75
|
+
attr :active, :immutable_string
|
|
76
|
+
attr :email, Types::String.constrained(format: /@/)
|
|
77
|
+
attr :state, Types::String.enum('draft', 'published', 'archived')
|
|
78
|
+
attr :tariff_id, Types::Array.of(Types::Params::Integer)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
```
|
|
34
82
|
|
|
35
|
-
|
|
83
|
+
### Benchmarks
|
|
84
|
+
compare `active_typed_store` with other gems
|
|
85
|
+
```ruby
|
|
86
|
+
# gem getter i/s setter i/s Lines of code
|
|
87
|
+
# rails (without types): 27930.8 660 170
|
|
88
|
+
# active_typed_store: 24318.5 - 1.15x slower 656 65
|
|
89
|
+
# store_attribute: 23748.3 - 1.18x slower 639 276
|
|
90
|
+
# store_model: 23324.4 - 1.20x slower 595 857
|
|
91
|
+
# attr_json: 15541.4 - 1.80x slower 577 - 1.14x slower 1195
|
|
92
|
+
# jsonb_accessor: 15000.1 - 1.86x slower 626 324
|
|
93
|
+
```
|
|
36
94
|
|
|
37
95
|
## License
|
|
38
96
|
|
data/Rakefile
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
5
|
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
|
8
|
-
require
|
|
8
|
+
require "rubocop/rake_task"
|
|
9
9
|
|
|
10
10
|
RuboCop::RakeTask.new
|
|
11
11
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveTypedStore
|
|
4
|
+
class Attrs
|
|
5
|
+
attr_reader :fields, :store_module, :store_attribute
|
|
6
|
+
|
|
7
|
+
def initialize(store_attribute)
|
|
8
|
+
@store_attribute = store_attribute
|
|
9
|
+
@fields = []
|
|
10
|
+
@store_module = Module.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def attr(field, type, default: nil, **options)
|
|
14
|
+
@fields << field
|
|
15
|
+
field = field.name
|
|
16
|
+
attr_name = store_attribute
|
|
17
|
+
|
|
18
|
+
store_module.define_method(:"#{field}?") do
|
|
19
|
+
read_store_attribute(attr_name, field).present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if type.is_a?(Symbol)
|
|
23
|
+
value_caster = ActiveRecord::Type.lookup(type, **options)
|
|
24
|
+
writer(attr_name, field, value_caster)
|
|
25
|
+
reader(attr_name, field, value_caster, default)
|
|
26
|
+
elsif type.class.name.start_with?("Dry::Types")
|
|
27
|
+
writer(attr_name, field, type)
|
|
28
|
+
# dry_type[nil] для optional типов возвращает не default-значение, а nil
|
|
29
|
+
reader(attr_name, field, type, (type.value if type.default?))
|
|
30
|
+
else
|
|
31
|
+
raise "type <#{type}> for field '#{field}' not supported"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private def writer(store_attribute, field, type)
|
|
36
|
+
store_module.define_method(:"#{field}=") do |value|
|
|
37
|
+
v = (type.respond_to?(:cast) ? type.cast(value) : type[value]) unless value.nil?
|
|
38
|
+
write_store_attribute(store_attribute, field, v)
|
|
39
|
+
self[store_attribute].delete(field) if v.nil?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private def reader(store_attribute, field, type, default)
|
|
44
|
+
ivar_prev = :"@__ts_prev_#{field}"
|
|
45
|
+
ivar_cache = :"@__ts_cache_#{field}"
|
|
46
|
+
store_module.define_method(field) do
|
|
47
|
+
val = read_store_attribute(store_attribute, field)
|
|
48
|
+
return instance_variable_get(ivar_cache) if instance_variable_get(ivar_prev) == val && !val.nil?
|
|
49
|
+
|
|
50
|
+
cache_val =
|
|
51
|
+
if val.nil? && !default.nil?
|
|
52
|
+
is_changed = attribute_changed?(store_attribute)
|
|
53
|
+
stored_value = self[store_attribute][field] = default.dup
|
|
54
|
+
clear_attribute_change(store_attribute) unless is_changed
|
|
55
|
+
stored_value
|
|
56
|
+
elsif type.respond_to?(:cast)
|
|
57
|
+
casted = type.cast(val)
|
|
58
|
+
casted.eql?(val) ? val : casted
|
|
59
|
+
else
|
|
60
|
+
type[val]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
instance_variable_set(ivar_prev, val)
|
|
64
|
+
instance_variable_set(ivar_cache, cache_val)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -2,58 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveTypedStore
|
|
4
4
|
module Store
|
|
5
|
+
def typed_store(store_attribute, &)
|
|
6
|
+
attrs = Attrs.new(store_attribute)
|
|
7
|
+
attrs.instance_eval(&)
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
ActiveModel::Type::DateTime,
|
|
9
|
-
ActiveModel::Type::Time,
|
|
10
|
-
].freeze
|
|
11
|
-
|
|
12
|
-
def typed_store(store_attribute, attrs)
|
|
13
|
-
store_accessor store_attribute, attrs.keys
|
|
14
|
-
|
|
15
|
-
attrs.each do |key, value_klass|
|
|
16
|
-
key = key.to_s
|
|
17
|
-
define_method("#{key}?") do
|
|
18
|
-
read_store_attribute(store_attribute, key).present?
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
_setter_for_typed_store(store_attribute, key, value_klass)
|
|
22
|
-
_getter_for_typed_store(key, value_klass)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def _setter_for_typed_store(store_attribute, key, value_klass)
|
|
27
|
-
if value_klass.name.start_with?('ActiveModel::Type::')
|
|
28
|
-
define_method("#{key}=") do |value|
|
|
29
|
-
v = value_klass.new.cast(value)
|
|
30
|
-
write_store_attribute(store_attribute, key, v)
|
|
31
|
-
self[store_attribute].delete(key) if v.nil?
|
|
32
|
-
end
|
|
33
|
-
else
|
|
34
|
-
define_method("#{key}=") do |value|
|
|
35
|
-
v = value.nil? ? nil : value_klass[value]
|
|
36
|
-
write_store_attribute(store_attribute, key, v)
|
|
37
|
-
self[store_attribute].delete(key) if v.nil?
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def _getter_for_typed_store(key, value_klass)
|
|
43
|
-
if JSON_NOT_SERIALIZED_TYPES.include?(value_klass)
|
|
44
|
-
define_method(key) do
|
|
45
|
-
val = super()
|
|
46
|
-
value_klass.new.cast(val) unless val.nil?
|
|
47
|
-
end
|
|
48
|
-
elsif value_klass.is_a?(Dry::Types::Default)
|
|
49
|
-
define_method(key) do
|
|
50
|
-
val = super()
|
|
51
|
-
# value_klass.value возвращает default
|
|
52
|
-
# value_klass[nil] для optional типов возвращает не default-значение, а nil
|
|
53
|
-
val.nil? ? value_klass.value : val
|
|
54
|
-
end
|
|
55
|
-
end
|
|
9
|
+
store_accessor store_attribute, attrs.fields
|
|
10
|
+
include attrs.store_module
|
|
56
11
|
end
|
|
57
|
-
|
|
58
12
|
end
|
|
59
13
|
end
|
data/lib/active_typed_store.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require
|
|
3
|
+
require_relative "active_typed_store/version"
|
|
4
|
+
require "active_support"
|
|
5
5
|
|
|
6
6
|
ActiveSupport.on_load(:active_record) do
|
|
7
|
-
require_relative
|
|
8
|
-
|
|
7
|
+
require_relative "active_typed_store/store"
|
|
8
|
+
require_relative "active_typed_store/attrs"
|
|
9
|
+
ActiveSupport.on_load(:active_record) { extend ActiveTypedStore::Store }
|
|
9
10
|
end
|
metadata
CHANGED
|
@@ -1,69 +1,64 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_typed_store
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ermolaev Andrey
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-09-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activemodel
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '0'
|
|
20
20
|
type: :runtime
|
|
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: '
|
|
26
|
+
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: activerecord
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '0'
|
|
34
34
|
type: :runtime
|
|
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: '
|
|
40
|
+
version: '0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: activesupport
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '0'
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
55
|
-
description:
|
|
54
|
+
version: '0'
|
|
55
|
+
description:
|
|
56
56
|
email:
|
|
57
57
|
- andruhafirst@yandex.ru
|
|
58
58
|
executables: []
|
|
59
59
|
extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
|
61
61
|
files:
|
|
62
|
-
- ".idea/.gitignore"
|
|
63
|
-
- ".idea/active_typed_store.iml"
|
|
64
|
-
- ".idea/misc.xml"
|
|
65
|
-
- ".idea/modules.xml"
|
|
66
|
-
- ".idea/vcs.xml"
|
|
67
62
|
- ".rspec"
|
|
68
63
|
- ".rubocop.yml"
|
|
69
64
|
- Gemfile
|
|
@@ -74,16 +69,15 @@ files:
|
|
|
74
69
|
- bin/console
|
|
75
70
|
- bin/setup
|
|
76
71
|
- lib/active_typed_store.rb
|
|
72
|
+
- lib/active_typed_store/attrs.rb
|
|
77
73
|
- lib/active_typed_store/store.rb
|
|
78
74
|
- lib/active_typed_store/version.rb
|
|
79
75
|
homepage: https://github.com/corp-gp/active_typed_store
|
|
80
76
|
licenses:
|
|
81
77
|
- MIT
|
|
82
78
|
metadata:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
source_code_uri: https://github.com/corp-gp/active_typed_store
|
|
86
|
-
post_install_message:
|
|
79
|
+
rubygems_mfa_required: 'true'
|
|
80
|
+
post_install_message:
|
|
87
81
|
rdoc_options: []
|
|
88
82
|
require_paths:
|
|
89
83
|
- lib
|
|
@@ -91,15 +85,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
91
85
|
requirements:
|
|
92
86
|
- - ">="
|
|
93
87
|
- !ruby/object:Gem::Version
|
|
94
|
-
version:
|
|
88
|
+
version: 3.1.0
|
|
95
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
90
|
requirements:
|
|
97
91
|
- - ">="
|
|
98
92
|
- !ruby/object:Gem::Version
|
|
99
93
|
version: '0'
|
|
100
94
|
requirements: []
|
|
101
|
-
rubygems_version: 3.
|
|
102
|
-
signing_key:
|
|
95
|
+
rubygems_version: 3.3.17
|
|
96
|
+
signing_key:
|
|
103
97
|
specification_version: 4
|
|
104
98
|
summary: Typed store for active records json/hstore columns
|
|
105
99
|
test_files: []
|
data/.idea/.gitignore
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="RUBY_MODULE" version="4">
|
|
3
|
-
<component name="ModuleRunConfigurationManager">
|
|
4
|
-
<shared />
|
|
5
|
-
</component>
|
|
6
|
-
<component name="NewModuleRootManager">
|
|
7
|
-
<content url="file://$MODULE_DIR$">
|
|
8
|
-
<sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
|
|
9
|
-
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
|
10
|
-
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
|
11
|
-
</content>
|
|
12
|
-
<orderEntry type="inheritedJdk" />
|
|
13
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
14
|
-
<orderEntry type="library" scope="PROVIDED" name="activemodel (v6.0.4.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
15
|
-
<orderEntry type="library" scope="PROVIDED" name="activerecord (v6.0.4.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
16
|
-
<orderEntry type="library" scope="PROVIDED" name="activesupport (v6.0.4.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
17
|
-
<orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, rbenv: 2.7.4) [gem]" level="application" />
|
|
18
|
-
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.2.29, rbenv: 2.7.4) [gem]" level="application" />
|
|
19
|
-
<orderEntry type="library" scope="PROVIDED" name="coderay (v1.1.3, rbenv: 2.7.4) [gem]" level="application" />
|
|
20
|
-
<orderEntry type="library" scope="PROVIDED" name="colorize (v0.8.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
21
|
-
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.9, rbenv: 2.7.4) [gem]" level="application" />
|
|
22
|
-
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.4.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
23
|
-
<orderEntry type="library" scope="PROVIDED" name="dry-configurable (v0.13.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
24
|
-
<orderEntry type="library" scope="PROVIDED" name="dry-container (v0.9.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
25
|
-
<orderEntry type="library" scope="PROVIDED" name="dry-core (v0.7.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
26
|
-
<orderEntry type="library" scope="PROVIDED" name="dry-inflector (v0.2.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
27
|
-
<orderEntry type="library" scope="PROVIDED" name="dry-logic (v1.2.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
28
|
-
<orderEntry type="library" scope="PROVIDED" name="dry-types (v1.5.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
29
|
-
<orderEntry type="library" scope="PROVIDED" name="i18n (v1.8.11, rbenv: 2.7.4) [gem]" level="application" />
|
|
30
|
-
<orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
31
|
-
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.15.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
32
|
-
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.21.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
33
|
-
<orderEntry type="library" scope="PROVIDED" name="parser (v3.0.3.2, rbenv: 2.7.4) [gem]" level="application" />
|
|
34
|
-
<orderEntry type="library" scope="PROVIDED" name="priscilla (v1.0.3@46ba74, rbenv: 2.7.4) [gem]" level="application" />
|
|
35
|
-
<orderEntry type="library" scope="PROVIDED" name="pry (v0.14.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
36
|
-
<orderEntry type="library" scope="PROVIDED" name="rack (v2.2.3, rbenv: 2.7.4) [gem]" level="application" />
|
|
37
|
-
<orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
38
|
-
<orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, rbenv: 2.7.4) [gem]" level="application" />
|
|
39
|
-
<orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.2.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
40
|
-
<orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.5, rbenv: 2.7.4) [gem]" level="application" />
|
|
41
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec (v3.9.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
42
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.9.3, rbenv: 2.7.4) [gem]" level="application" />
|
|
43
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.9.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
44
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.9.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
45
|
-
<orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.9.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
46
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop (v1.23.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
47
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.15.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
48
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-gp (v0.0.2@4d390b, rbenv: 2.7.4) [gem]" level="application" />
|
|
49
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-performance (v1.12.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
50
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-rails (v2.12.4, rbenv: 2.7.4) [gem]" level="application" />
|
|
51
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-rspec (v2.6.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
52
|
-
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
53
|
-
<orderEntry type="library" scope="PROVIDED" name="rumoji (v0.5.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
54
|
-
<orderEntry type="library" scope="PROVIDED" name="sqlite3 (v1.4.2, rbenv: 2.7.4) [gem]" level="application" />
|
|
55
|
-
<orderEntry type="library" scope="PROVIDED" name="thread_safe (v0.3.6, rbenv: 2.7.4) [gem]" level="application" />
|
|
56
|
-
<orderEntry type="library" scope="PROVIDED" name="tzinfo (v1.2.9, rbenv: 2.7.4) [gem]" level="application" />
|
|
57
|
-
<orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.1.0, rbenv: 2.7.4) [gem]" level="application" />
|
|
58
|
-
<orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.5.1, rbenv: 2.7.4) [gem]" level="application" />
|
|
59
|
-
</component>
|
|
60
|
-
</module>
|
data/.idea/misc.xml
DELETED
data/.idea/modules.xml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ProjectModuleManager">
|
|
4
|
-
<modules>
|
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/active_typed_store.iml" filepath="$PROJECT_DIR$/.idea/active_typed_store.iml" />
|
|
6
|
-
</modules>
|
|
7
|
-
</component>
|
|
8
|
-
</project>
|