store_model 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81df7c27571266926547a2a5aad309128741860b15fe94876476744efa4932d9
4
- data.tar.gz: 33a62eb4baa468872e3b409e34755a8d057841f4b764e0ecac6f50cef4481286
3
+ metadata.gz: 637fbd26ab60269ee1f0e8c9d091c1c145af797edfb5924f2741b4175f63e550
4
+ data.tar.gz: fa6ccf49426de71facd6324f4d13da525b48f4136c6d21a6dc48bc1530cfcacc
5
5
  SHA512:
6
- metadata.gz: '019a485b12c9216f493198d6e02d03284e9f912c3eea96ee4c0fb1d2a4be3284c1febfc92e7045c28b6a5266bc8a7cd5eca663536fe4d928d27cf5dfdb31b688'
7
- data.tar.gz: b7aaea3584e1f59caea549b9f98e7ee310677746d1d7e58c8b741c01d899592697a6b831fe6a28615fcb6253d38fea9ce57bcb046bb97e6587601dc94ceea224
6
+ metadata.gz: ece230a89c62e1a5089ab15c184a1cc9fd7b18b3eb5652a3e21df6ae74a40e172fd8fbb8cd92df0e5b64586e4fe5f77f5f681a7092110ad7a11d458c6f3075cb
7
+ data.tar.gz: 21f9ba16ad9f8b361d02d7a0794c89af1ca3365ea31495dd5f8f1d9e059d9c91112c7d31beb3ae5e6b5371448f65784854cf18ec617cb275472dc2a2539b13ca
data/README.md CHANGED
@@ -88,6 +88,7 @@ product.save
88
88
  * [Nested models](./docs/nested_models.md)
89
89
  * [Unknown attributes](./docs/unknown_attributes.md)
90
90
  3. [Array of stored models](./docs/array_of_stored_models.md)
91
+ 4. [One of](./docs/one_of.md)
91
92
  4. [Alternatives](./docs/alternatives.md)
92
93
  5. [Defining custom types](./docs/defining_custom_types.md)
93
94
 
@@ -21,9 +21,9 @@ module ActiveModel
21
21
  end
22
22
 
23
23
  case record.type_for_attribute(attribute).type
24
- when :json
24
+ when :json, :polymorphic
25
25
  call_json_strategy(attribute, record.errors, value)
26
- when :array
26
+ when :array, :polymorphic_array
27
27
  call_array_strategy(attribute, record.errors, value)
28
28
  end
29
29
  end
@@ -10,5 +10,10 @@ module StoreModel # :nodoc:
10
10
  def config
11
11
  @config ||= Configuration.new
12
12
  end
13
+
14
+ # @return instance [Types::OneOf]
15
+ def one_of(&block)
16
+ Types::OneOf.new(&block)
17
+ end
13
18
  end
14
19
  end
@@ -14,9 +14,9 @@ module StoreModel
14
14
  def accepts_nested_attributes_for(*associations)
15
15
  associations.each do |association|
16
16
  case attribute_types[association.to_s]
17
- when Types::JsonType
17
+ when Types::One
18
18
  alias_method "#{association}_attributes=", "#{association}="
19
- when Types::ArrayType
19
+ when Types::Many
20
20
  define_method "#{association}_attributes=" do |attributes|
21
21
  assign_nested_attributes_for_collection_association(association, attributes)
22
22
  end
@@ -3,16 +3,16 @@
3
3
  module StoreModel
4
4
  # Contains methods for converting StoreModel::Model to ActiveModel::Type::Value.
5
5
  module TypeBuilders
6
- # Converts StoreModel::Model to Types::JsonType
7
- # @return [Types::JsonType]
6
+ # Converts StoreModel::Model to Types::One
7
+ # @return [Types::One]
8
8
  def to_type
9
- Types::JsonType.new(self)
9
+ Types::One.new(self)
10
10
  end
11
11
 
12
- # Converts StoreModel::Model to Types::ArrayType
13
- # @return [Types::ArrayType]
12
+ # Converts StoreModel::Model to Types::Many
13
+ # @return [Types::Many]
14
14
  def to_array_type
15
- Types::ArrayType.new(self)
15
+ Types::Many.new(self)
16
16
  end
17
17
  end
18
18
  end
@@ -1,12 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "store_model/types/json_type"
4
- require "store_model/types/array_type"
3
+ require "store_model/types/polymorphic_helper"
4
+
5
+ require "store_model/types/one_base"
6
+ require "store_model/types/one"
7
+ require "store_model/types/one_polymorphic"
8
+
9
+ require "store_model/types/many_base"
10
+ require "store_model/types/many"
11
+ require "store_model/types/many_polymorphic"
12
+
5
13
  require "store_model/types/enum_type"
6
14
 
15
+ require "store_model/types/one_of"
16
+
7
17
  module StoreModel
8
18
  # Contains all custom types.
9
19
  module Types
10
20
  class CastError < StandardError; end
21
+ class ExpandWrapperError < StandardError; end
11
22
  end
12
23
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ module StoreModel
6
+ module Types
7
+ # Implements ActiveModel::Type::Value type for handling an array of
8
+ # StoreModel::Model
9
+ class Many < ManyBase
10
+ # Initializes type for model class
11
+ #
12
+ # @param model_klass [StoreModel::Model] model class to handle
13
+ #
14
+ # @return [StoreModel::Types::Many]
15
+ def initialize(model_klass)
16
+ @model_klass = model_klass
17
+ end
18
+
19
+ # Returns type
20
+ #
21
+ # @return [Symbol]
22
+ def type
23
+ :array
24
+ end
25
+
26
+ protected
27
+
28
+ def ensure_model_class(array)
29
+ array.map do |object|
30
+ object.is_a?(@model_klass) ? object : cast_model_type_value(object)
31
+ end
32
+ end
33
+
34
+ def cast_model_type_value(value)
35
+ model_klass_type.cast_value(value)
36
+ end
37
+
38
+ def model_klass_type
39
+ @model_klass_type ||= @model_klass.to_type
40
+ end
41
+
42
+ def raise_cast_error(value)
43
+ raise StoreModel::Types::CastError,
44
+ "failed casting #{value.inspect}, only String or Array instances are allowed"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -6,21 +6,12 @@ module StoreModel
6
6
  module Types
7
7
  # Implements ActiveModel::Type::Value type for handling an array of
8
8
  # StoreModel::Model
9
- class ArrayType < ActiveModel::Type::Value
10
- # Initializes type for model class
11
- #
12
- # @param model_klass [StoreModel::Model] model class to handle
13
- #
14
- # @return [StoreModel::Types::ArrayType]
15
- def initialize(model_klass)
16
- @model_klass = model_klass
17
- end
18
-
9
+ class ManyBase < ActiveModel::Type::Value
19
10
  # Returns type
20
11
  #
21
12
  # @return [Symbol]
22
13
  def type
23
- :array
14
+ raise NotImplementedError
24
15
  end
25
16
 
26
17
  # Casts +value+ from DB or user to StoreModel::Model instance
@@ -34,8 +25,7 @@ module StoreModel
34
25
  when Array then ensure_model_class(value)
35
26
  when nil then value
36
27
  else
37
- raise StoreModel::Types::CastError,
38
- "failed casting #{value.inspect}, only String or Array instances are allowed"
28
+ raise_cast_error(value)
39
29
  end
40
30
  end
41
31
 
@@ -64,28 +54,28 @@ module StoreModel
64
54
  cast_value(raw_old_value) != new_value
65
55
  end
66
56
 
67
- private
57
+ protected
68
58
 
69
- # rubocop:disable Style/RescueModifier
70
- def decode_and_initialize(array_value)
71
- decoded = ActiveSupport::JSON.decode(array_value) rescue []
72
- decoded.map { |attributes| cast_model_type_value(attributes) }
59
+ def ensure_model_class(_array)
60
+ raise NotImplementedError
73
61
  end
74
- # rubocop:enable Style/RescueModifier
75
62
 
76
- def ensure_model_class(array)
77
- array.map do |object|
78
- object.is_a?(@model_klass) ? object : cast_model_type_value(object)
79
- end
63
+ def cast_model_type_value(_value)
64
+ raise NotImplementedError
80
65
  end
81
66
 
82
- def cast_model_type_value(value)
83
- model_klass_type.cast_value(value)
67
+ def raise_cast_error(_value)
68
+ raise NotImplementedError
84
69
  end
85
70
 
86
- def model_klass_type
87
- @model_klass_type ||= @model_klass.to_type
71
+ private
72
+
73
+ # rubocop:disable Style/RescueModifier
74
+ def decode_and_initialize(array_value)
75
+ decoded = ActiveSupport::JSON.decode(array_value) rescue []
76
+ decoded.map { |attributes| cast_model_type_value(attributes) }
88
77
  end
78
+ # rubocop:enable Style/RescueModifier
89
79
  end
90
80
  end
91
81
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ module StoreModel
6
+ module Types
7
+ # Implements ActiveModel::Type::Value type for handling an array of
8
+ # StoreModel::Model
9
+ class ManyPolymorphic < ManyBase
10
+ include PolymorphicHelper
11
+
12
+ # Initializes type for model class
13
+ #
14
+ # @param model_wrapper [Proc] class to handle
15
+ #
16
+ # @return [StoreModel::Types::PolymorphicArrayType ]
17
+ def initialize(model_wrapper)
18
+ @model_wrapper = model_wrapper
19
+ end
20
+
21
+ # Returns type
22
+ #
23
+ # @return [Symbol]
24
+ def type
25
+ :polymorphic_array
26
+ end
27
+
28
+ private
29
+
30
+ def ensure_model_class(array)
31
+ array.map do |object|
32
+ next object if implements_model?(object.class)
33
+
34
+ cast_model_type_value(object)
35
+ end
36
+ end
37
+
38
+ def cast_model_type_value(value)
39
+ model_klass = @model_wrapper.call(value)
40
+
41
+ raise_extract_wrapper_error(model_klass) unless implements_model?(model_klass)
42
+
43
+ model_klass.to_type.cast_value(value)
44
+ end
45
+
46
+ def raise_cast_error(value)
47
+ raise StoreModel::Types::CastError,
48
+ "failed casting #{value.inspect}, only String, " \
49
+ "Hash or instances which implement StoreModel::Model are allowed"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,12 +5,12 @@ require "active_model"
5
5
  module StoreModel
6
6
  module Types
7
7
  # Implements ActiveModel::Type::Value type for handling an instance of StoreModel::Model
8
- class JsonType < ActiveModel::Type::Value
8
+ class One < OneBase
9
9
  # Initializes type for model class
10
10
  #
11
11
  # @param model_klass [StoreModel::Model] model class to handle
12
12
  #
13
- # @return [StoreModel::Types::JsonType]
13
+ # @return [StoreModel::Types::One]
14
14
  def initialize(model_klass)
15
15
  @model_klass = model_klass
16
16
  end
@@ -30,7 +30,7 @@ module StoreModel
30
30
  def cast_value(value)
31
31
  case value
32
32
  when String then decode_and_initialize(value)
33
- when Hash then @model_klass.new(value)
33
+ when Hash then model_instance(value)
34
34
  when @model_klass, nil then value
35
35
  else raise_cast_error(value)
36
36
  end
@@ -53,40 +53,16 @@ module StoreModel
53
53
  end
54
54
  end
55
55
 
56
- # Determines whether the mutable value has been modified since it was read
57
- #
58
- # @param raw_old_value [Object] old value
59
- # @param new_value [Object] new value
60
- #
61
- # @return [Boolean]
62
- def changed_in_place?(raw_old_value, new_value)
63
- cast_value(raw_old_value) != new_value
64
- end
65
-
66
56
  private
67
57
 
68
- # rubocop:disable Style/RescueModifier
69
- def decode_and_initialize(value)
70
- decoded = ActiveSupport::JSON.decode(value) rescue nil
71
- @model_klass.new(decoded) unless decoded.nil?
72
- rescue ActiveModel::UnknownAttributeError => e
73
- handle_unknown_attribute(decoded, e)
74
- end
75
- # rubocop:enable Style/RescueModifier
76
-
77
58
  def raise_cast_error(value)
78
59
  raise StoreModel::Types::CastError,
79
60
  "failed casting #{value.inspect}, only String, " \
80
61
  "Hash or #{@model_klass.name} instances are allowed"
81
62
  end
82
63
 
83
- def handle_unknown_attribute(value, exception)
84
- attribute = exception.attribute.to_sym
85
- value_symbolized = value.symbolize_keys
86
-
87
- cast_value(value_symbolized.except(attribute)).tap do |configuration|
88
- configuration.unknown_attributes[attribute.to_s] = value_symbolized[attribute]
89
- end
64
+ def model_instance(value)
65
+ @model_klass.new(value)
90
66
  end
91
67
  end
92
68
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ module StoreModel
6
+ module Types
7
+ # Implements ActiveModel::Type::Value type for handling an instance of StoreModel::Model
8
+ class OneBase < ActiveModel::Type::Value
9
+ # Returns type
10
+ #
11
+ # @return [Symbol]
12
+ def type
13
+ raise NotImplementedError
14
+ end
15
+
16
+ # Casts +value+ from DB or user to StoreModel::Model instance
17
+ #
18
+ # @param value [Object] a value to cast
19
+ #
20
+ # @return StoreModel::Model
21
+ def cast_value(_value)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ # Determines whether the mutable value has been modified since it was read
26
+ #
27
+ # @param raw_old_value [Object] old value
28
+ # @param new_value [Object] new value
29
+ #
30
+ # @return [Boolean]
31
+ def changed_in_place?(raw_old_value, new_value)
32
+ cast_value(raw_old_value) != new_value
33
+ end
34
+
35
+ protected
36
+
37
+ def raise_cast_error(_value)
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def model_instance(_value)
42
+ raise NotImplementedError
43
+ end
44
+
45
+ private
46
+
47
+ # rubocop:disable Style/RescueModifier
48
+ def decode_and_initialize(value)
49
+ decoded = ActiveSupport::JSON.decode(value) rescue nil
50
+ model_instance(decoded) unless decoded.nil?
51
+ rescue ActiveModel::UnknownAttributeError => e
52
+ handle_unknown_attribute(decoded, e)
53
+ end
54
+ # rubocop:enable Style/RescueModifier
55
+
56
+ def handle_unknown_attribute(value, exception)
57
+ attribute = exception.attribute.to_sym
58
+ value_symbolized = value.symbolize_keys
59
+
60
+ cast_value(value_symbolized.except(attribute)).tap do |configuration|
61
+ configuration.unknown_attributes[attribute.to_s] = value_symbolized[attribute]
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ module StoreModel
6
+ module Types
7
+ # Implements ActiveModel::Type::Value type for handling an array of
8
+ # StoreModel::Model
9
+ class OneOf
10
+ def initialize(&block)
11
+ @block = block
12
+ end
13
+
14
+ def to_type
15
+ Types::OnePolymorphic.new(@block)
16
+ end
17
+
18
+ def to_array_type
19
+ Types::ManyPolymorphic.new(@block)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ module StoreModel
6
+ module Types
7
+ # Implements ActiveModel::Type::Value type for handling an instance of StoreModel::Model
8
+ class OnePolymorphic < OneBase
9
+ include PolymorphicHelper
10
+
11
+ # Initializes type for model class
12
+ #
13
+ # @param model_wrapper [Proc] class to handle
14
+ #
15
+ # @return [StoreModel::Types::OnePolymorphic ]
16
+ def initialize(model_wrapper)
17
+ @model_wrapper = model_wrapper
18
+ end
19
+
20
+ # Returns type
21
+ #
22
+ # @return [Symbol]
23
+ def type
24
+ :polymorphic
25
+ end
26
+
27
+ # Casts +value+ from DB or user to StoreModel::Model instance
28
+ #
29
+ # @param value [Object] a value to cast
30
+ #
31
+ # @return StoreModel::Model
32
+ def cast_value(value)
33
+ case value
34
+ when String then decode_and_initialize(value)
35
+ when Hash then extract_model_klass(value).new(value)
36
+ when nil then value
37
+ else
38
+ raise_cast_error(value) unless value.class.ancestors.include?(StoreModel::Model)
39
+
40
+ value
41
+ end
42
+ rescue ActiveModel::UnknownAttributeError => e
43
+ handle_unknown_attribute(value, e)
44
+ end
45
+
46
+ # Casts a value from the ruby type to a type that the database knows how
47
+ # to understand.
48
+ #
49
+ # @param value [Object] value to serialize
50
+ #
51
+ # @return [String] serialized value
52
+ def serialize(value)
53
+ case value
54
+ when Hash
55
+ ActiveSupport::JSON.encode(value)
56
+ else
57
+ return ActiveSupport::JSON.encode(value) if implements_model?(value.class)
58
+
59
+ super
60
+ end
61
+ end
62
+
63
+ protected
64
+
65
+ # Check if block returns an appropriate class and raise cast error if not
66
+ #
67
+ # @param value [Object] raw data
68
+ #
69
+ # @return [Class] which implements StoreModel::Model
70
+ def extract_model_klass(value)
71
+ model_klass = @model_wrapper.call(value)
72
+
73
+ raise_extract_wrapper_error(model_klass) unless implements_model?(model_klass)
74
+
75
+ model_klass
76
+ end
77
+
78
+ def raise_cast_error(value)
79
+ raise StoreModel::Types::CastError,
80
+ "failed casting #{value.inspect}, only String, " \
81
+ "Hash or instances which implement StoreModel::Model are allowed"
82
+ end
83
+
84
+ def model_instance(value)
85
+ extract_model_klass(value).new(value)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Shared methods for polymorphic classes
4
+ module PolymorphicHelper
5
+ def raise_extract_wrapper_error(invalid_klass)
6
+ raise StoreModel::Types::ExpandWrapperError,
7
+ "#{invalid_klass.inspect} is an invalid model klass"
8
+ end
9
+
10
+ def implements_model?(klass)
11
+ klass&.ancestors&.include?(StoreModel::Model)
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreModel # :nodoc:
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: store_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-19 00:00:00.000000000 Z
11
+ date: 2020-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -106,9 +106,15 @@ files:
106
106
  - lib/store_model/railtie.rb
107
107
  - lib/store_model/type_builders.rb
108
108
  - lib/store_model/types.rb
109
- - lib/store_model/types/array_type.rb
110
109
  - lib/store_model/types/enum_type.rb
111
- - lib/store_model/types/json_type.rb
110
+ - lib/store_model/types/many.rb
111
+ - lib/store_model/types/many_base.rb
112
+ - lib/store_model/types/many_polymorphic.rb
113
+ - lib/store_model/types/one.rb
114
+ - lib/store_model/types/one_base.rb
115
+ - lib/store_model/types/one_of.rb
116
+ - lib/store_model/types/one_polymorphic.rb
117
+ - lib/store_model/types/polymorphic_helper.rb
112
118
  - lib/store_model/version.rb
113
119
  homepage: https://github.com/DmitryTsepelev/store_model
114
120
  licenses: