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 +4 -4
- data/lib/duck_record.rb +9 -0
- data/lib/duck_record/attribute_decorators.rb +89 -0
- data/lib/duck_record/attribute_methods/serialization.rb +66 -0
- data/lib/duck_record/attributes.rb +6 -5
- data/lib/duck_record/base.rb +3 -8
- data/lib/duck_record/coders/json.rb +13 -0
- data/lib/duck_record/coders/yaml_column.rb +48 -0
- data/lib/duck_record/core.rb +2 -1
- data/lib/duck_record/enum.rb +139 -0
- data/lib/duck_record/errors.rb +4 -0
- data/lib/duck_record/model_schema.rb +15 -3
- data/lib/duck_record/persistence.rb +39 -0
- data/lib/duck_record/type.rb +4 -0
- data/lib/duck_record/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dab749c33860b97f56557d60be5eb235afe659f
|
4
|
+
data.tar.gz: fc755f0d4c244a248a9a030ae5354e3d2dc2645b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 :
|
10
|
-
self.
|
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.
|
198
|
-
|
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
|
-
|
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
|
data/lib/duck_record/base.rb
CHANGED
@@ -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,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
|
data/lib/duck_record/core.rb
CHANGED
@@ -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
|
data/lib/duck_record/errors.rb
CHANGED
@@ -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?(@
|
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
|
-
@
|
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
|
data/lib/duck_record/type.rb
CHANGED
data/lib/duck_record/version.rb
CHANGED
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.
|
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-
|
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.
|
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
|