duck_record 0.0.13 → 0.0.14

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
  SHA1:
3
- metadata.gz: a40d7265442d33cb9b0523abc991f2425c3d87ff
4
- data.tar.gz: 266ba229695849261a975fd2924cc9837a095671
3
+ metadata.gz: 1dab749c33860b97f56557d60be5eb235afe659f
4
+ data.tar.gz: fc755f0d4c244a248a9a030ae5354e3d2dc2645b
5
5
  SHA512:
6
- metadata.gz: 363119060b6e6191f6fe401a121c6548b2ca3ae28f5875a95db30aaffbb29d952c8baa0a6a37f3deb4085a24389af87c4b698eb950bcf9b9a46c1761a1a82721
7
- data.tar.gz: bc7e1560401aa6afebc76066457085b1b0898e04b69feff43cf7d4ae72693bfe164022c604575752463941360b4ca8bed5ff868d90ad4e7bf0764bc92570c1bd
6
+ metadata.gz: 79d0c5715448f98ff7702253d5b26f6338fb5f51faec2b36e8a2c7151f5f13989919518b887e1dd94712f59ca4e742dd975ac96a5dfa170f3d17dc15a09b0e01
7
+ data.tar.gz: 57b4d1113ea3accb7672f3fdd834f23d5e5934fc4b4e21400e640fe7b5fa458ff300dbb84068fad889fbe90bc235ee8166ec6c52e86449847153bc428e39cba9
data/lib/duck_record.rb CHANGED
@@ -11,10 +11,13 @@ module DuckRecord
11
11
  extend ActiveSupport::Autoload
12
12
 
13
13
  autoload :Attribute
14
+ autoload :AttributeDecorators
14
15
  autoload :Base
15
16
  autoload :Callbacks
16
17
  autoload :Core
18
+ autoload :Enum
17
19
  autoload :Inheritance
20
+ autoload :Persistence
18
21
  autoload :ModelSchema
19
22
  autoload :NestedAttributes
20
23
  autoload :ReadonlyAttributes
@@ -32,6 +35,11 @@ module DuckRecord
32
35
  autoload :NestedValidateAssociation
33
36
  end
34
37
 
38
+ module Coders
39
+ autoload :YAMLColumn, "duck_record/coders/yaml_column"
40
+ autoload :JSON, "duck_record/coders/json"
41
+ end
42
+
35
43
  module AttributeMethods
36
44
  extend ActiveSupport::Autoload
37
45
 
@@ -39,6 +47,7 @@ module DuckRecord
39
47
  autoload :BeforeTypeCast
40
48
  autoload :Dirty
41
49
  autoload :Read
50
+ autoload :Serialization
42
51
  autoload :Write
43
52
  end
44
53
  end
@@ -0,0 +1,89 @@
1
+ module DuckRecord
2
+ module AttributeDecorators # :nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :attribute_type_decorations, instance_accessor: false # :internal:
7
+ self.attribute_type_decorations = TypeDecorator.new
8
+ end
9
+
10
+ module ClassMethods # :nodoc:
11
+ # This method is an internal API used to create class macros such as
12
+ # +serialize+, and features like time zone aware attributes.
13
+ #
14
+ # Used to wrap the type of an attribute in a new type.
15
+ # When the schema for a model is loaded, attributes with the same name as
16
+ # +column_name+ will have their type yielded to the given block. The
17
+ # return value of that block will be used instead.
18
+ #
19
+ # Subsequent calls where +column_name+ and +decorator_name+ are the same
20
+ # will override the previous decorator, not decorate twice. This can be
21
+ # used to create idempotent class macros like +serialize+
22
+ def decorate_attribute_type(column_name, decorator_name, &block)
23
+ matcher = ->(name, _) { name == column_name.to_s }
24
+ key = "_#{column_name}_#{decorator_name}"
25
+ decorate_matching_attribute_types(matcher, key, &block)
26
+ end
27
+
28
+ # This method is an internal API used to create higher level features like
29
+ # time zone aware attributes.
30
+ #
31
+ # When the schema for a model is loaded, +matcher+ will be called for each
32
+ # attribute with its name and type. If the matcher returns a truthy value,
33
+ # the type will then be yielded to the given block, and the return value
34
+ # of that block will replace the type.
35
+ #
36
+ # Subsequent calls to this method with the same value for +decorator_name+
37
+ # will replace the previous decorator, not decorate twice. This can be
38
+ # used to ensure that class macros are idempotent.
39
+ def decorate_matching_attribute_types(matcher, decorator_name, &block)
40
+ reload_schema_from_cache
41
+ decorator_name = decorator_name.to_s
42
+
43
+ # Create new hashes so we don't modify parent classes
44
+ self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
45
+ end
46
+
47
+ private
48
+
49
+ def load_schema!
50
+ super
51
+ attribute_types.each do |name, type|
52
+ decorated_type = attribute_type_decorations.apply(name, type)
53
+ define_attribute(name, decorated_type)
54
+ end
55
+ end
56
+ end
57
+
58
+ class TypeDecorator # :nodoc:
59
+ delegate :clear, to: :@decorations
60
+
61
+ def initialize(decorations = {})
62
+ @decorations = decorations
63
+ end
64
+
65
+ def merge(*args)
66
+ TypeDecorator.new(@decorations.merge(*args))
67
+ end
68
+
69
+ def apply(name, type)
70
+ decorations = decorators_for(name, type)
71
+ decorations.inject(type) do |new_type, block|
72
+ block.call(new_type)
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def decorators_for(name, type)
79
+ matching(name, type).map(&:last)
80
+ end
81
+
82
+ def matching(name, type)
83
+ @decorations.values.select do |(matcher, _)|
84
+ matcher.call(name, type)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,66 @@
1
+ module DuckRecord
2
+ module AttributeMethods
3
+ module Serialization
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # If you have an attribute that needs to be saved to the database as an
8
+ # object, and retrieved as the same object, then specify the name of that
9
+ # attribute using this method and it will be handled automatically. The
10
+ # serialization is done through YAML. If +class_name+ is specified, the
11
+ # serialized object must be of that class on assignment and retrieval.
12
+ # Otherwise SerializationTypeMismatch will be raised.
13
+ #
14
+ # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
15
+ # +Array+, will always be persisted as null.
16
+ #
17
+ # Keep in mind that database adapters handle certain serialization tasks
18
+ # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
19
+ # converted between JSON object/array syntax and Ruby +Hash+ or +Array+
20
+ # objects transparently. There is no need to use #serialize in this
21
+ # case.
22
+ #
23
+ # For more complex cases, such as conversion to or from your application
24
+ # domain objects, consider using the ActiveRecord::Attributes API.
25
+ #
26
+ # ==== Parameters
27
+ #
28
+ # * +attr_name+ - The field name that should be serialized.
29
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
30
+ # or a class name that the object type should be equal to.
31
+ #
32
+ # ==== Example
33
+ #
34
+ # # Serialize a preferences attribute.
35
+ # class User < ActiveRecord::Base
36
+ # serialize :preferences
37
+ # end
38
+ #
39
+ # # Serialize preferences using JSON as coder.
40
+ # class User < ActiveRecord::Base
41
+ # serialize :preferences, JSON
42
+ # end
43
+ #
44
+ # # Serialize preferences as Hash using YAML coder.
45
+ # class User < ActiveRecord::Base
46
+ # serialize :preferences, Hash
47
+ # end
48
+ def serialize(attr_name, class_name_or_coder = Object)
49
+ # When ::JSON is used, force it to go through the Active Support JSON encoder
50
+ # to ensure special objects (e.g. Active Record models) are dumped correctly
51
+ # using the #as_json hook.
52
+ coder = if class_name_or_coder == ::JSON
53
+ Coders::JSON
54
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
55
+ class_name_or_coder
56
+ else
57
+ Coders::YAMLColumn.new(attr_name, class_name_or_coder)
58
+ end
59
+ decorate_attribute_type(attr_name, :serialize) do |type|
60
+ Type::Serialized.new(type, coder)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -6,8 +6,8 @@ module DuckRecord
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- class_attribute :attributes_to_define, instance_accessor: false # :internal:
10
- self.attributes_to_define = {}
9
+ class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false # :internal:
10
+ self.attributes_to_define_after_schema_loads = {}
11
11
  end
12
12
 
13
13
  module ClassMethods
@@ -193,9 +193,10 @@ module DuckRecord
193
193
  # methods in ActiveModel::Type::Value for more details.
194
194
  def attribute(name, cast_type = Type::Value.new, **options)
195
195
  name = name.to_s
196
+ reload_schema_from_cache
196
197
 
197
- self.attributes_to_define =
198
- attributes_to_define.merge(
198
+ self.attributes_to_define_after_schema_loads =
199
+ attributes_to_define_after_schema_loads.merge(
199
200
  name => [cast_type, options]
200
201
  )
201
202
  end
@@ -229,7 +230,7 @@ module DuckRecord
229
230
 
230
231
  def load_schema! # :nodoc:
231
232
  super
232
- attributes_to_define.each do |name, (type, options)|
233
+ attributes_to_define_after_schema_loads.each do |name, (type, options)|
233
234
  if type.is_a?(Symbol)
234
235
  type = DuckRecord::Type.lookup(type, **options.except(:default))
235
236
  end
@@ -274,8 +274,10 @@ module DuckRecord #:nodoc:
274
274
  extend ActiveSupport::DescendantsTracker
275
275
 
276
276
  extend Translation
277
+ extend Enum
277
278
 
278
279
  include Core
280
+ include Persistence
279
281
  include ReadonlyAttributes
280
282
  include ModelSchema
281
283
  include Inheritance
@@ -283,6 +285,7 @@ module DuckRecord #:nodoc:
283
285
  include ActiveModel::Conversion
284
286
  include Validations
285
287
  include Attributes
288
+ include AttributeDecorators
286
289
  include DefineCallbacks
287
290
  include AttributeMethods
288
291
  include Callbacks
@@ -292,14 +295,6 @@ module DuckRecord #:nodoc:
292
295
  include Reflection
293
296
  include Serialization
294
297
 
295
- def persisted?
296
- false
297
- end
298
-
299
- def new_record?
300
- true
301
- end
302
-
303
298
  def to_h(include_empty: true)
304
299
  hash = serializable_hash
305
300
 
@@ -0,0 +1,13 @@
1
+ module DuckRecord
2
+ module Coders # :nodoc:
3
+ class JSON # :nodoc:
4
+ def self.dump(obj)
5
+ ActiveSupport::JSON.encode(obj)
6
+ end
7
+
8
+ def self.load(json)
9
+ ActiveSupport::JSON.decode(json) unless json.blank?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ require "yaml"
2
+
3
+ module DuckRecord
4
+ module Coders # :nodoc:
5
+ class YAMLColumn # :nodoc:
6
+ attr_accessor :object_class
7
+
8
+ def initialize(attr_name, object_class = Object)
9
+ @attr_name = attr_name
10
+ @object_class = object_class
11
+ check_arity_of_constructor
12
+ end
13
+
14
+ def dump(obj)
15
+ return if obj.nil?
16
+
17
+ assert_valid_value(obj, action: "dump")
18
+ YAML.dump obj
19
+ end
20
+
21
+ def load(yaml)
22
+ return object_class.new if object_class != Object && yaml.nil?
23
+ return yaml unless yaml.is_a?(String) && /^---/.match?(yaml)
24
+ obj = YAML.load(yaml)
25
+
26
+ assert_valid_value(obj, action: "load")
27
+ obj ||= object_class.new if object_class != Object
28
+
29
+ obj
30
+ end
31
+
32
+ def assert_valid_value(obj, action:)
33
+ unless obj.nil? || obj.is_a?(object_class)
34
+ raise SerializationTypeMismatch,
35
+ "can't #{action} `#{@attr_name}`: was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def check_arity_of_constructor
42
+ load(nil)
43
+ rescue ArgumentError
44
+ raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
45
+ end
46
+ end
47
+ end
48
+ end
@@ -45,7 +45,8 @@ module DuckRecord
45
45
  if abstract_class?
46
46
  "#{super}(abstract)"
47
47
  else
48
- super
48
+ attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
49
+ "#{super}(#{attr_list})"
49
50
  end
50
51
  end
51
52
  end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/deep_dup"
4
+
5
+ module DuckRecord
6
+ module Enum
7
+ def self.extended(base) # :nodoc:
8
+ base.class_attribute(:defined_enums, instance_writer: false)
9
+ base.defined_enums = {}
10
+ end
11
+
12
+ def inherited(base) # :nodoc:
13
+ base.defined_enums = defined_enums.deep_dup
14
+ super
15
+ end
16
+
17
+ class EnumType < ActiveModel::Type::Value # :nodoc:
18
+ delegate :type, to: :subtype
19
+
20
+ def initialize(name, mapping, subtype)
21
+ @name = name
22
+ @mapping = mapping
23
+ @subtype = subtype
24
+ end
25
+
26
+ def cast(value)
27
+ return if value.blank?
28
+
29
+ if mapping.has_key?(value)
30
+ value.to_s
31
+ elsif mapping.has_value?(value)
32
+ mapping.key(value)
33
+ else
34
+ assert_valid_value(value)
35
+ end
36
+ end
37
+
38
+ def deserialize(value)
39
+ return if value.nil?
40
+ mapping.key(subtype.deserialize(value))
41
+ end
42
+
43
+ def serialize(value)
44
+ mapping.fetch(value, value)
45
+ end
46
+
47
+ def assert_valid_value(value)
48
+ unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
49
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :name, :mapping, :subtype
56
+ end
57
+
58
+ def enum(definitions)
59
+ klass = self
60
+ enum_prefix = definitions.delete(:_prefix)
61
+ enum_suffix = definitions.delete(:_suffix)
62
+ definitions.each do |name, values|
63
+ # statuses = { }
64
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
65
+ name = name.to_sym
66
+
67
+ # def self.statuses() statuses end
68
+ detect_enum_conflict!(name, name.to_s.pluralize, true)
69
+ klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
70
+
71
+ detect_enum_conflict!(name, name)
72
+ detect_enum_conflict!(name, "#{name}=")
73
+
74
+ attr = attribute_alias?(name) ? attribute_alias(name) : name
75
+ decorate_attribute_type(attr, :enum) do |subtype|
76
+ EnumType.new(attr, enum_values, subtype)
77
+ end
78
+
79
+ _enum_methods_module.module_eval do
80
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
81
+ pairs.each do |value, i|
82
+ if enum_prefix == true
83
+ prefix = "#{name}_"
84
+ elsif enum_prefix
85
+ prefix = "#{enum_prefix}_"
86
+ end
87
+ if enum_suffix == true
88
+ suffix = "_#{name}"
89
+ elsif enum_suffix
90
+ suffix = "_#{enum_suffix}"
91
+ end
92
+
93
+ value_method_name = "#{prefix}#{value}#{suffix}"
94
+ enum_values[value] = i
95
+
96
+ # def active?() status == 0 end
97
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
98
+ define_method("#{value_method_name}?") { self[attr] == value.to_s }
99
+ end
100
+ end
101
+ defined_enums[name.to_s] = enum_values
102
+ end
103
+ end
104
+
105
+ private
106
+ def _enum_methods_module
107
+ @_enum_methods_module ||= begin
108
+ mod = Module.new
109
+ include mod
110
+ mod
111
+ end
112
+ end
113
+
114
+ ENUM_CONFLICT_MESSAGE = \
115
+ "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
116
+ "this will generate a %{type} method \"%{method}\", which is already defined " \
117
+ "by %{source}."
118
+
119
+ def detect_enum_conflict!(enum_name, method_name, klass_method = false)
120
+ if klass_method && dangerous_class_method?(method_name)
121
+ raise_conflict_error(enum_name, method_name, type: "class")
122
+ elsif !klass_method && dangerous_attribute_method?(method_name)
123
+ raise_conflict_error(enum_name, method_name)
124
+ elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
125
+ raise_conflict_error(enum_name, method_name, source: "another enum")
126
+ end
127
+ end
128
+
129
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
130
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
131
+ enum: enum_name,
132
+ klass: name,
133
+ type: type,
134
+ method: method_name,
135
+ source: source
136
+ }
137
+ end
138
+ end
139
+ end
@@ -53,6 +53,10 @@ module DuckRecord
53
53
  end
54
54
  end
55
55
 
56
+ # Raised when unserialized object's type mismatches one specified for serializable field.
57
+ class SerializationTypeMismatch < DuckRecordError
58
+ end
59
+
56
60
  # Raised when there are multiple errors while doing a mass assignment through the
57
61
  # {DuckRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
58
62
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
@@ -9,7 +9,7 @@ module DuckRecord
9
9
  module ClassMethods
10
10
  def attribute_types # :nodoc:
11
11
  load_schema
12
- @attribute_types ||= Hash.new
12
+ @attribute_types ||= Hash.new(Type.default_value)
13
13
  end
14
14
 
15
15
  def yaml_encoder # :nodoc:
@@ -42,7 +42,7 @@ module DuckRecord
42
42
  private
43
43
 
44
44
  def schema_loaded?
45
- defined?(@loaded) && @loaded
45
+ defined?(@schema_loaded) && @schema_loaded
46
46
  end
47
47
 
48
48
  def load_schema
@@ -52,7 +52,19 @@ module DuckRecord
52
52
  end
53
53
 
54
54
  def load_schema!
55
- @loaded = true
55
+ @schema_loaded = true
56
+ end
57
+
58
+ def reload_schema_from_cache
59
+ @attribute_types = nil
60
+ @default_attributes = nil
61
+ @attributes_builder = nil
62
+ @schema_loaded = false
63
+ @attribute_names = nil
64
+ @yaml_encoder = nil
65
+ direct_descendants.each do |descendant|
66
+ descendant.send(:reload_schema_from_cache)
67
+ end
56
68
  end
57
69
  end
58
70
  end
@@ -0,0 +1,39 @@
1
+ module DuckRecord
2
+ # = DuckRecord \Persistence
3
+ module Persistence
4
+ extend ActiveSupport::Concern
5
+
6
+ def persisted?
7
+ false
8
+ end
9
+
10
+ def destroyed?
11
+ false
12
+ end
13
+
14
+ def new_record?
15
+ true
16
+ end
17
+
18
+ # Returns an instance of the specified +klass+ with the attributes of the
19
+ # current record. This is mostly useful in relation to single-table
20
+ # inheritance structures where you want a subclass to appear as the
21
+ # superclass. This can be used along with record identification in
22
+ # Action Pack to allow, say, <tt>Client < Company</tt> to do something
23
+ # like render <tt>partial: @client.becomes(Company)</tt> to render that
24
+ # instance using the companies/company partial instead of clients/client.
25
+ #
26
+ # Note: The new instance will share a link to the same attributes as the original class.
27
+ # Therefore the sti column value will still be the same.
28
+ # Any change to the attributes on either instance will affect both instances.
29
+ # If you want to change the sti column as well, use #becomes! instead.
30
+ def becomes(klass)
31
+ became = klass.new
32
+ became.instance_variable_set("@attributes", @attributes)
33
+ became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker)
34
+ became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
35
+ became.errors.copy!(errors)
36
+ became
37
+ end
38
+ end
39
+ end
@@ -41,6 +41,10 @@ module DuckRecord
41
41
  def lookup(*args, **kwargs) # :nodoc:
42
42
  registry.lookup(*args, **kwargs)
43
43
  end
44
+
45
+ def default_value # :nodoc:
46
+ @default_value ||= Value.new
47
+ end
44
48
  end
45
49
 
46
50
  Helpers = ActiveModel::Type::Helpers
@@ -1,3 +1,3 @@
1
1
  module DuckRecord
2
- VERSION = "0.0.13"
2
+ VERSION = "0.0.14"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duck_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - jasl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-04 00:00:00.000000000 Z
11
+ date: 2017-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -97,10 +97,12 @@ files:
97
97
  - lib/duck_record/attribute.rb
98
98
  - lib/duck_record/attribute/user_provided_default.rb
99
99
  - lib/duck_record/attribute_assignment.rb
100
+ - lib/duck_record/attribute_decorators.rb
100
101
  - lib/duck_record/attribute_methods.rb
101
102
  - lib/duck_record/attribute_methods/before_type_cast.rb
102
103
  - lib/duck_record/attribute_methods/dirty.rb
103
104
  - lib/duck_record/attribute_methods/read.rb
105
+ - lib/duck_record/attribute_methods/serialization.rb
104
106
  - lib/duck_record/attribute_methods/write.rb
105
107
  - lib/duck_record/attribute_mutation_tracker.rb
106
108
  - lib/duck_record/attribute_set.rb
@@ -108,14 +110,18 @@ files:
108
110
  - lib/duck_record/attributes.rb
109
111
  - lib/duck_record/base.rb
110
112
  - lib/duck_record/callbacks.rb
113
+ - lib/duck_record/coders/json.rb
114
+ - lib/duck_record/coders/yaml_column.rb
111
115
  - lib/duck_record/core.rb
112
116
  - lib/duck_record/define_callbacks.rb
117
+ - lib/duck_record/enum.rb
113
118
  - lib/duck_record/errors.rb
114
119
  - lib/duck_record/inheritance.rb
115
120
  - lib/duck_record/locale/en.yml
116
121
  - lib/duck_record/model_schema.rb
117
122
  - lib/duck_record/nested_attributes.rb
118
123
  - lib/duck_record/nested_validate_association.rb
124
+ - lib/duck_record/persistence.rb
119
125
  - lib/duck_record/readonly_attributes.rb
120
126
  - lib/duck_record/reflection.rb
121
127
  - lib/duck_record/serialization.rb
@@ -158,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
164
  version: '0'
159
165
  requirements: []
160
166
  rubyforge_project:
161
- rubygems_version: 2.6.12
167
+ rubygems_version: 2.6.13
162
168
  signing_key:
163
169
  specification_version: 4
164
170
  summary: Used for creating virtual models like ActiveType or ModelAttribute does