duck_record 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- 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
|