store_model 4.4.0 → 4.6.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/README.md +46 -1
- data/lib/store_model/configuration.rb +15 -0
- data/lib/store_model/ext/active_admin_compatibility.rb +69 -0
- data/lib/store_model/ext/active_record/base.rb +16 -5
- data/lib/store_model/ext/parent_assignment.rb +4 -1
- data/lib/store_model/model.rb +9 -1
- data/lib/store_model/nested_attributes.rb +1 -1
- data/lib/store_model/railtie.rb +6 -0
- data/lib/store_model/types/one_of.rb +3 -2
- data/lib/store_model/types/one_polymorphic.rb +16 -2
- data/lib/store_model/version.rb +1 -1
- data/lib/store_model.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e69ab65c67b3cd46e0285be5860d8cd5b1d501b38e126f9833fbe01b3dda3ee8
|
|
4
|
+
data.tar.gz: 04b8d780feaaa6bfcb578465582b845d66960ba5f9df6e5d7aaa8d99f3d568bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8176e449148b7b7aa04596bf75b4932a156f737bac32395e79ae4ca5af9d6e186addb5c1de47b503f754b491f3a98a2ae081ce44fb8f3608c077f81fbcbe8d7
|
|
7
|
+
data.tar.gz: 9a771879567e9bbe266fa3825327469ac5b2e2008e7f90a7b608b72d28bbbb9704bd7f28e1498e4d65879c6014321a368557f54ec04fe23af0c7d4f8074d0613
|
data/README.md
CHANGED
|
@@ -22,7 +22,10 @@ class Product < ApplicationRecord
|
|
|
22
22
|
end
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
## Professional Support
|
|
26
|
+
|
|
27
|
+
Working on a complex Rails application and need an experienced eye?
|
|
28
|
+
I'm available for consulting — [get in touch](https://dmitrytsepelev.dev/consulting).
|
|
26
29
|
|
|
27
30
|
## Why should I wrap my JSON columns?
|
|
28
31
|
|
|
@@ -122,6 +125,48 @@ def supplier_params
|
|
|
122
125
|
end
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
### ActiveAdmin Integration
|
|
129
|
+
|
|
130
|
+
If you're using [ActiveAdmin](https://activeadmin.info/), enable compatibility mode to make the `has_many` form helper work with StoreModel attributes:
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
# config/initializers/store_model.rb
|
|
134
|
+
StoreModel.config.active_admin_compatibility = true
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Example usage:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
# app/models/supplier.rb
|
|
141
|
+
class Supplier
|
|
142
|
+
include StoreModel::Model
|
|
143
|
+
|
|
144
|
+
attribute :title, :string
|
|
145
|
+
attribute :address, :string
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# app/models/product.rb
|
|
149
|
+
class Product < ApplicationRecord
|
|
150
|
+
include StoreModel::NestedAttributes
|
|
151
|
+
|
|
152
|
+
attribute :suppliers, Supplier.to_array_type
|
|
153
|
+
accepts_nested_attributes_for :suppliers, allow_destroy: true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# app/admin/products.rb
|
|
157
|
+
ActiveAdmin.register Product do
|
|
158
|
+
permit_params suppliers_attributes: [:title, :address, :_destroy]
|
|
159
|
+
|
|
160
|
+
form do |f|
|
|
161
|
+
f.has_many :suppliers, allow_destroy: true do |s|
|
|
162
|
+
s.input :title
|
|
163
|
+
s.input :address
|
|
164
|
+
end
|
|
165
|
+
f.actions
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
125
170
|
## Documentation
|
|
126
171
|
|
|
127
172
|
1. [Installation](./docs/installation.md)
|
|
@@ -32,11 +32,26 @@ module StoreModel
|
|
|
32
32
|
# @return [Boolean]
|
|
33
33
|
attr_accessor :enable_parent_assignment
|
|
34
34
|
|
|
35
|
+
# Controls if ActiveAdmin compatibility patches are applied.
|
|
36
|
+
# When enabled, adds methods like `new_record?` and `reflect_on_association`
|
|
37
|
+
# that are expected by ActiveAdmin's form builders.
|
|
38
|
+
# Default: false
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
attr_accessor :active_admin_compatibility
|
|
41
|
+
|
|
42
|
+
# Controls whether nested attributes updates preserve existing model instances by default.
|
|
43
|
+
# When true, acts like `update_only: true` for all singular nested attributes unless overridden.
|
|
44
|
+
# Default: false
|
|
45
|
+
# @return [Boolean]
|
|
46
|
+
attr_accessor :nested_attributes_update_only
|
|
47
|
+
|
|
35
48
|
def initialize
|
|
36
49
|
@serialize_unknown_attributes = true
|
|
37
50
|
@enable_parent_assignment = true
|
|
38
51
|
@serialize_enums_using_as_json = true
|
|
39
52
|
@serialize_empty_attributes = true
|
|
53
|
+
@active_admin_compatibility = false
|
|
54
|
+
@nested_attributes_update_only = false
|
|
40
55
|
end
|
|
41
56
|
end
|
|
42
57
|
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StoreModel
|
|
4
|
+
# ActiveAdmin compatibility patches
|
|
5
|
+
#
|
|
6
|
+
# This module contains patches that make StoreModel compatible with ActiveAdmin's
|
|
7
|
+
# form builders, particularly the has_many helper which expects certain ActiveRecord-like
|
|
8
|
+
# methods to be present.
|
|
9
|
+
#
|
|
10
|
+
# To enable these patches, set:
|
|
11
|
+
# StoreModel.config.active_admin_compatibility = true
|
|
12
|
+
module ActiveAdminCompatibility
|
|
13
|
+
# Reflection class for StoreModel associations.
|
|
14
|
+
# This provides compatibility with form builders like ActiveAdmin's has_many
|
|
15
|
+
# that expect ActiveRecord-style reflection objects.
|
|
16
|
+
class Reflection
|
|
17
|
+
attr_reader :name, :klass
|
|
18
|
+
|
|
19
|
+
# @param name [Symbol] association name
|
|
20
|
+
# @param klass [Class] the StoreModel class
|
|
21
|
+
def initialize(name, klass)
|
|
22
|
+
@name = name
|
|
23
|
+
@klass = klass
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Patch for StoreModel::Model to add new_record? method
|
|
28
|
+
module NewRecordPatch
|
|
29
|
+
# Always returns true for StoreModel instances when ActiveAdmin compatibility is enabled.
|
|
30
|
+
# This is needed for compatibility with form builders like ActiveAdmin's has_many.
|
|
31
|
+
# For ActiveRecord models, delegates to the original implementation.
|
|
32
|
+
#
|
|
33
|
+
# @return [Boolean]
|
|
34
|
+
def new_record?
|
|
35
|
+
super
|
|
36
|
+
rescue NoMethodError
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Patch for StoreModel::NestedAttributes::ClassMethods to add reflection methods
|
|
42
|
+
module ReflectionMethods
|
|
43
|
+
# Returns reflection for the given association name.
|
|
44
|
+
# This provides compatibility with form builders like ActiveAdmin's has_many.
|
|
45
|
+
# First checks if the attribute is a StoreModel collection type, and if so,
|
|
46
|
+
# returns a reflection for it. Otherwise, delegates to the original implementation
|
|
47
|
+
# for ActiveRecord associations.
|
|
48
|
+
#
|
|
49
|
+
# @param name [Symbol, String] association name
|
|
50
|
+
# @return [StoreModel::ActiveAdminCompatibility::Reflection, nil]
|
|
51
|
+
def reflect_on_association(name)
|
|
52
|
+
# First check if this is a StoreModel attribute
|
|
53
|
+
# Use attribute_types directly to get the type for the given attribute
|
|
54
|
+
type = attribute_types[name.to_s]
|
|
55
|
+
|
|
56
|
+
if type.is_a?(StoreModel::Types::ManyBase) && type.respond_to?(:model_klass) && type.model_klass
|
|
57
|
+
# Return reflection for StoreModel collection attributes
|
|
58
|
+
return StoreModel::ActiveAdminCompatibility::Reflection.new(name.to_sym, type.model_klass)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Not a StoreModel attribute, try to call the original method for ActiveRecord associations
|
|
62
|
+
super
|
|
63
|
+
rescue NoMethodError
|
|
64
|
+
# No super method exists (pure StoreModel class), return nil
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -8,14 +8,25 @@ module StoreModel
|
|
|
8
8
|
include ParentAssignment
|
|
9
9
|
|
|
10
10
|
def _read_attribute(*)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
value = super
|
|
12
|
+
assign_parent_to_store_model_relation(value) if store_model_attribute?(value)
|
|
13
|
+
value
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def _write_attribute(*)
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
value = super
|
|
18
|
+
assign_parent_to_store_model_relation(value) if store_model_attribute?(value)
|
|
19
|
+
value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def store_model_attribute?(value)
|
|
25
|
+
case value
|
|
26
|
+
when StoreModel::Model then true
|
|
27
|
+
when Array then value.first.is_a?(StoreModel::Model)
|
|
28
|
+
when Hash then value.each_value.any? { |v| v.is_a?(StoreModel::Model) }
|
|
29
|
+
else false
|
|
19
30
|
end
|
|
20
31
|
end
|
|
21
32
|
end
|
data/lib/store_model/model.rb
CHANGED
|
@@ -15,6 +15,7 @@ module StoreModel
|
|
|
15
15
|
base.include ActiveRecord::AttributeMethods::BeforeTypeCast
|
|
16
16
|
base.include ActiveModel::AttributeMethods
|
|
17
17
|
base.include StoreModel::NestedAttributes
|
|
18
|
+
base.include ActiveModel::Validations::Callbacks
|
|
18
19
|
|
|
19
20
|
if ActiveModel::VERSION::MAJOR >= 8 && ActiveModel::VERSION::MINOR >= 1
|
|
20
21
|
base.include ActiveModel::Attributes::Normalization
|
|
@@ -215,7 +216,10 @@ module StoreModel
|
|
|
215
216
|
#
|
|
216
217
|
# @return [Hash]
|
|
217
218
|
def unknown_attributes
|
|
218
|
-
@unknown_attributes
|
|
219
|
+
return @unknown_attributes if defined?(@unknown_attributes)
|
|
220
|
+
return {}.freeze if frozen?
|
|
221
|
+
|
|
222
|
+
@unknown_attributes = {}
|
|
219
223
|
end
|
|
220
224
|
|
|
221
225
|
# Returns the value of the `@serialize_unknown_attributes` instance
|
|
@@ -313,6 +317,10 @@ module StoreModel
|
|
|
313
317
|
return unless Array(attr.value).all? { |value| value.is_a?(StoreModel::Model) }
|
|
314
318
|
|
|
315
319
|
Array(attr.value).each do |value|
|
|
320
|
+
# Skip frozen objects - they cannot have instance variables modified
|
|
321
|
+
# but will still serialize correctly with their existing settings
|
|
322
|
+
next if value.frozen?
|
|
323
|
+
|
|
316
324
|
value.serialize_unknown_attributes = serialize_unknown_attributes
|
|
317
325
|
value.serialize_enums_using_as_json = serialize_enums_using_as_json
|
|
318
326
|
value.serialize_empty_attributes = serialize_empty_attributes
|
|
@@ -50,7 +50,7 @@ module StoreModel
|
|
|
50
50
|
|
|
51
51
|
attributes.each do |attribute|
|
|
52
52
|
if nested_attribute_type(attribute).is_a?(Types::Base)
|
|
53
|
-
options.reverse_merge!(allow_destroy: false, update_only:
|
|
53
|
+
options.reverse_merge!(allow_destroy: false, update_only: StoreModel.config.nested_attributes_update_only)
|
|
54
54
|
define_store_model_attr_accessors(attribute, options)
|
|
55
55
|
else
|
|
56
56
|
super(*attribute, options)
|
data/lib/store_model/railtie.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "store_model/ext/active_model/attributes"
|
|
4
4
|
require "store_model/ext/active_record/base"
|
|
5
|
+
require "store_model/ext/active_admin_compatibility"
|
|
5
6
|
|
|
6
7
|
module StoreModel # :nodoc:
|
|
7
8
|
class Railtie < Rails::Railtie # :nodoc:
|
|
@@ -11,6 +12,11 @@ module StoreModel # :nodoc:
|
|
|
11
12
|
ActiveModel::Attributes.prepend(Attributes)
|
|
12
13
|
prepend(Base)
|
|
13
14
|
end
|
|
15
|
+
|
|
16
|
+
if StoreModel.config.active_admin_compatibility
|
|
17
|
+
StoreModel::Model.prepend(ActiveAdminCompatibility::NewRecordPatch)
|
|
18
|
+
StoreModel::NestedAttributes::ClassMethods.prepend(ActiveAdminCompatibility::ReflectionMethods)
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
21
|
end
|
|
16
22
|
end
|
|
@@ -7,12 +7,13 @@ module StoreModel
|
|
|
7
7
|
# Implements ActiveModel::Type::Value type for handling an array of
|
|
8
8
|
# StoreModel::Model
|
|
9
9
|
class OneOf
|
|
10
|
-
def initialize(&block)
|
|
10
|
+
def initialize(union: false, &block)
|
|
11
11
|
@block = block
|
|
12
|
+
@union = union
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def to_type
|
|
15
|
-
Types::OnePolymorphic.new(@block)
|
|
16
|
+
Types::OnePolymorphic.new(@block, union: @union)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def to_array_type
|
|
@@ -13,8 +13,9 @@ module StoreModel
|
|
|
13
13
|
# @param model_wrapper [Proc] class to handle
|
|
14
14
|
#
|
|
15
15
|
# @return [StoreModel::Types::OnePolymorphic ]
|
|
16
|
-
def initialize(model_wrapper)
|
|
16
|
+
def initialize(model_wrapper, union: false)
|
|
17
17
|
@model_wrapper = model_wrapper
|
|
18
|
+
@union = union
|
|
18
19
|
super()
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -30,8 +31,9 @@ module StoreModel
|
|
|
30
31
|
# @param value [Object] a value to cast
|
|
31
32
|
#
|
|
32
33
|
# @return StoreModel::Model
|
|
33
|
-
def cast_value(value) # rubocop:disable Metrics/MethodLength
|
|
34
|
+
def cast_value(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
34
35
|
return nil if value.nil?
|
|
36
|
+
return nil if @union && value.respond_to?(:empty?) && value.empty?
|
|
35
37
|
|
|
36
38
|
if value.is_a?(String)
|
|
37
39
|
decode_and_initialize(value)
|
|
@@ -87,6 +89,18 @@ module StoreModel
|
|
|
87
89
|
"Hash or instances which implement StoreModel::Model are allowed"
|
|
88
90
|
end
|
|
89
91
|
|
|
92
|
+
# rubocop:disable Style/RescueModifier
|
|
93
|
+
def decode_and_initialize(value)
|
|
94
|
+
decoded = ActiveSupport::JSON.decode(value) rescue nil
|
|
95
|
+
return nil if decoded.nil?
|
|
96
|
+
return nil if @union && decoded.respond_to?(:empty?) && decoded.empty?
|
|
97
|
+
|
|
98
|
+
model_instance(decoded)
|
|
99
|
+
rescue ActiveModel::UnknownAttributeError => e
|
|
100
|
+
handle_unknown_attribute(decoded, e)
|
|
101
|
+
end
|
|
102
|
+
# rubocop:enable Style/RescueModifier
|
|
103
|
+
|
|
90
104
|
def model_instance(value)
|
|
91
105
|
extract_model_klass(value).new(value)
|
|
92
106
|
end
|
data/lib/store_model/version.rb
CHANGED
data/lib/store_model.rb
CHANGED
|
@@ -58,7 +58,7 @@ module StoreModel # :nodoc:
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def union_one_of(discriminator, class_map)
|
|
61
|
-
Types::OneOf.new do |attributes|
|
|
61
|
+
Types::OneOf.new(union: true) do |attributes|
|
|
62
62
|
next nil unless attributes
|
|
63
63
|
|
|
64
64
|
discriminator_value = attributes.with_indifferent_access[discriminator]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: store_model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- DmitryTsepelev
|
|
@@ -84,6 +84,7 @@ files:
|
|
|
84
84
|
- lib/store_model/combine_errors_strategies/merge_hash_error_strategy.rb
|
|
85
85
|
- lib/store_model/configuration.rb
|
|
86
86
|
- lib/store_model/enum.rb
|
|
87
|
+
- lib/store_model/ext/active_admin_compatibility.rb
|
|
87
88
|
- lib/store_model/ext/active_model/attributes.rb
|
|
88
89
|
- lib/store_model/ext/active_record/base.rb
|
|
89
90
|
- lib/store_model/ext/parent_assignment.rb
|