activeentity 0.0.1.beta14 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +24 -7
- data/Rakefile +7 -7
- data/lib/active_entity.rb +30 -7
- data/lib/active_entity/aggregations.rb +2 -1
- data/lib/active_entity/associations.rb +46 -24
- data/lib/active_entity/associations/{embedded → embeds}/association.rb +2 -2
- data/lib/active_entity/associations/{embedded → embeds}/builder/association.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/builder/collection_association.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/builder/embedded_in.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/builder/embeds_many.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/builder/embeds_one.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/builder/singular_association.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/collection_association.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/collection_proxy.rb +2 -2
- data/lib/active_entity/associations/{embedded → embeds}/embedded_in_association.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/embeds_many_association.rb +1 -1
- data/lib/active_entity/associations/{embedded → embeds}/embeds_one_association.rb +2 -1
- data/lib/active_entity/associations/{embedded → embeds}/singular_association.rb +1 -1
- data/lib/active_entity/attribute_assignment.rb +10 -8
- data/lib/active_entity/attribute_methods.rb +52 -61
- data/lib/active_entity/attribute_methods/before_type_cast.rb +6 -6
- data/lib/active_entity/attribute_methods/dirty.rb +13 -0
- data/lib/active_entity/attribute_methods/primary_key.rb +6 -8
- data/lib/active_entity/attribute_methods/query.rb +11 -8
- data/lib/active_entity/attribute_methods/read.rb +10 -13
- data/lib/active_entity/attribute_methods/time_zone_conversion.rb +2 -0
- data/lib/active_entity/attribute_methods/write.rb +16 -25
- data/lib/active_entity/attributes.rb +76 -2
- data/lib/active_entity/base.rb +3 -14
- data/lib/active_entity/{define_callbacks.rb → callbacks.rb} +5 -1
- data/lib/active_entity/core.rb +97 -39
- data/lib/active_entity/enum.rb +30 -4
- data/lib/active_entity/errors.rb +0 -11
- data/lib/active_entity/gem_version.rb +4 -4
- data/lib/active_entity/inheritance.rb +4 -106
- data/lib/active_entity/integration.rb +1 -1
- data/lib/active_entity/model_schema.rb +0 -12
- data/lib/active_entity/nested_attributes.rb +5 -12
- data/lib/active_entity/railtie.rb +61 -1
- data/lib/active_entity/readonly_attributes.rb +9 -1
- data/lib/active_entity/reflection.rb +22 -19
- data/lib/active_entity/serialization.rb +9 -6
- data/lib/active_entity/store.rb +51 -2
- data/lib/active_entity/type.rb +8 -8
- data/lib/active_entity/type/registry.rb +5 -5
- data/lib/active_entity/{validate_embedded_association.rb → validate_embeds_association.rb} +6 -6
- data/lib/active_entity/validations.rb +2 -6
- data/lib/active_entity/validations/associated.rb +1 -1
- data/lib/active_entity/validations/{uniqueness_in_embedding.rb → uniqueness_in_embeds.rb} +1 -1
- data/lib/active_entity/validations/uniqueness_on_active_record.rb +42 -47
- metadata +33 -36
- data/lib/active_entity/type/decimal_without_scale.rb +0 -15
- data/lib/active_entity/type/hash_lookup_type_map.rb +0 -25
- data/lib/active_entity/type/type_map.rb +0 -62
- data/lib/tasks/active_entity_tasks.rake +0 -6
@@ -6,7 +6,7 @@ module ActiveEntity
|
|
6
6
|
# == Single table inheritance
|
7
7
|
#
|
8
8
|
# Active Entity allows inheritance by storing the name of the class in a column that by
|
9
|
-
# default is named "type" (can be changed by overwriting <tt>Base.
|
9
|
+
# default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
|
10
10
|
# This means that an inheritance looking like this:
|
11
11
|
#
|
12
12
|
# class Company < ActiveEntity::Base; end
|
@@ -37,12 +37,6 @@ module ActiveEntity
|
|
37
37
|
module Inheritance
|
38
38
|
extend ActiveSupport::Concern
|
39
39
|
|
40
|
-
included do
|
41
|
-
# Determines whether to store the full constant name including namespace when using STI.
|
42
|
-
# This is true, by default.
|
43
|
-
class_attribute :store_full_sti_class, instance_writer: false, default: true
|
44
|
-
end
|
45
|
-
|
46
40
|
module ClassMethods
|
47
41
|
# Determines if one of the attributes passed in is the inheritance column,
|
48
42
|
# and if the inheritance column is attr accessible, it initializes an
|
@@ -52,19 +46,7 @@ module ActiveEntity
|
|
52
46
|
raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
|
53
47
|
end
|
54
48
|
|
55
|
-
|
56
|
-
subclass = subclass_from_attributes(attributes)
|
57
|
-
|
58
|
-
if subclass.nil? && base_class?
|
59
|
-
subclass = subclass_from_attributes(_default_attributes)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
if subclass && subclass != self
|
64
|
-
subclass.new(attributes, &block)
|
65
|
-
else
|
66
|
-
super
|
67
|
-
end
|
49
|
+
super
|
68
50
|
end
|
69
51
|
|
70
52
|
# Returns +true+ if this does not need STI type condition. Returns
|
@@ -75,15 +57,10 @@ module ActiveEntity
|
|
75
57
|
elsif superclass.abstract_class?
|
76
58
|
superclass.descends_from_active_entity?
|
77
59
|
else
|
78
|
-
superclass == Base
|
60
|
+
superclass == Base
|
79
61
|
end
|
80
62
|
end
|
81
63
|
|
82
|
-
def finder_needs_type_condition? #:nodoc:
|
83
|
-
# This is like this because benchmarking justifies the strange :false stuff
|
84
|
-
:true == (@finder_needs_type_condition ||= descends_from_active_entity? ? :false : :true)
|
85
|
-
end
|
86
|
-
|
87
64
|
# Returns the class descending directly from ActiveEntity::Base, or
|
88
65
|
# an abstract class, if any, in the inheritance hierarchy.
|
89
66
|
#
|
@@ -94,7 +71,7 @@ module ActiveEntity
|
|
94
71
|
# and C.base_class would return B as the answer since A is an abstract_class.
|
95
72
|
def base_class
|
96
73
|
unless self < Base
|
97
|
-
raise ActiveEntityError, "#{name} doesn't belong in a hierarchy descending from
|
74
|
+
raise ActiveEntityError, "#{name} doesn't belong in a hierarchy descending from ActiveEntity"
|
98
75
|
end
|
99
76
|
|
100
77
|
if superclass == Base || superclass.abstract_class?
|
@@ -158,10 +135,6 @@ module ActiveEntity
|
|
158
135
|
defined?(@abstract_class) && @abstract_class == true
|
159
136
|
end
|
160
137
|
|
161
|
-
def sti_name
|
162
|
-
store_full_sti_class ? name : name.demodulize
|
163
|
-
end
|
164
|
-
|
165
138
|
def inherited(subclass)
|
166
139
|
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
|
167
140
|
super
|
@@ -198,81 +171,6 @@ module ActiveEntity
|
|
198
171
|
raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
|
199
172
|
end
|
200
173
|
end
|
201
|
-
|
202
|
-
private
|
203
|
-
|
204
|
-
# Called by +instantiate+ to decide which class to use for a new
|
205
|
-
# record instance. For single-table inheritance, we check the record
|
206
|
-
# for a +type+ column and return the corresponding class.
|
207
|
-
def discriminate_class_for_record(record)
|
208
|
-
if using_single_table_inheritance?(record)
|
209
|
-
find_sti_class(record[inheritance_attribute])
|
210
|
-
else
|
211
|
-
super
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def using_single_table_inheritance?(record)
|
216
|
-
record[inheritance_attribute].present? && has_attribute?(inheritance_attribute)
|
217
|
-
end
|
218
|
-
|
219
|
-
def find_sti_class(type_name)
|
220
|
-
type_name = base_class.type_for_attribute(inheritance_attribute).cast(type_name)
|
221
|
-
subclass = begin
|
222
|
-
if store_full_sti_class
|
223
|
-
ActiveSupport::Dependencies.constantize(type_name)
|
224
|
-
else
|
225
|
-
compute_type(type_name)
|
226
|
-
end
|
227
|
-
rescue NameError
|
228
|
-
raise SubclassNotFound,
|
229
|
-
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
|
230
|
-
"This error is raised because the attribute '#{inheritance_attribute}' is reserved for storing the class in case of inheritance. " \
|
231
|
-
"Please rename this attribute if you didn't intend it to be used for storing the inheritance class " \
|
232
|
-
"or overwrite #{name}.inheritance_attribute to use another attribute for that information."
|
233
|
-
end
|
234
|
-
unless subclass == self || descendants.include?(subclass)
|
235
|
-
raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
|
236
|
-
end
|
237
|
-
subclass
|
238
|
-
end
|
239
|
-
|
240
|
-
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
|
241
|
-
# is not self or a valid subclass, raises ActiveEntity::SubclassNotFound
|
242
|
-
def subclass_from_attributes(attrs)
|
243
|
-
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
|
244
|
-
if attrs.is_a?(Hash)
|
245
|
-
subclass_name = attrs[inheritance_attribute] || attrs[inheritance_attribute.to_sym]
|
246
|
-
|
247
|
-
if subclass_name.present?
|
248
|
-
find_sti_class(subclass_name)
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
174
|
end
|
253
|
-
|
254
|
-
def initialize_dup(other)
|
255
|
-
super
|
256
|
-
ensure_proper_type
|
257
|
-
end
|
258
|
-
|
259
|
-
private
|
260
|
-
|
261
|
-
def initialize_internals_callback
|
262
|
-
super
|
263
|
-
ensure_proper_type
|
264
|
-
end
|
265
|
-
|
266
|
-
# Sets the attribute used for single table inheritance to this class name if this is not the
|
267
|
-
# ActiveEntity::Base descendant.
|
268
|
-
# Considering the hierarchy Reply < Message < ActiveEntity::Base, this makes it possible to
|
269
|
-
# do Reply.new without having to set <tt>Reply[Reply.inheritance_attribute] = "Reply"</tt> yourself.
|
270
|
-
# No such attribute would be set for objects of the Message class in that example.
|
271
|
-
def ensure_proper_type
|
272
|
-
klass = self.class
|
273
|
-
if klass.finder_needs_type_condition?
|
274
|
-
_write_attribute(klass.inheritance_attribute, klass.sti_name)
|
275
|
-
end
|
276
|
-
end
|
277
175
|
end
|
278
176
|
end
|
@@ -30,7 +30,7 @@ module ActiveEntity
|
|
30
30
|
# user_path(user) # => "/users/Phusion"
|
31
31
|
def to_param
|
32
32
|
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
|
33
|
-
id
|
33
|
+
id&.to_s # Be sure to stringify the id for routes
|
34
34
|
end
|
35
35
|
|
36
36
|
module ClassMethods
|
@@ -9,22 +9,10 @@ module ActiveEntity
|
|
9
9
|
included do
|
10
10
|
delegate :type_for_attribute, to: :class
|
11
11
|
|
12
|
-
self.inheritance_attribute = "type"
|
13
|
-
|
14
12
|
initialize_load_schema_monitor
|
15
13
|
end
|
16
14
|
|
17
15
|
module ClassMethods
|
18
|
-
def inheritance_attribute
|
19
|
-
(@inheritance_attribute ||= nil) || superclass.inheritance_attribute
|
20
|
-
end
|
21
|
-
|
22
|
-
# Sets the value of inheritance_attribute
|
23
|
-
def inheritance_attribute=(value)
|
24
|
-
@inheritance_attribute = value.to_s
|
25
|
-
@explicit_inheritance_attribute = true
|
26
|
-
end
|
27
|
-
|
28
16
|
def attributes_builder # :nodoc:
|
29
17
|
unless defined?(@attributes_builder) && @attributes_builder
|
30
18
|
defaults = _default_attributes
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/hash/except"
|
4
4
|
require "active_support/core_ext/module/redefine_method"
|
5
|
-
require "active_support/core_ext/object/try"
|
6
5
|
require "active_support/core_ext/hash/indifferent_access"
|
7
6
|
|
8
7
|
module ActiveEntity
|
@@ -289,7 +288,7 @@ module ActiveEntity
|
|
289
288
|
# [:allow_destroy]
|
290
289
|
# If true, destroys any members from the attributes hash with a
|
291
290
|
# <tt>_destroy</tt> key and a value that evaluates to +true+
|
292
|
-
# (
|
291
|
+
# (e.g. 1, '1', true, or 'true'). This option is off by default.
|
293
292
|
# [:reject_if]
|
294
293
|
# Allows you to specify a Proc or a Symbol pointing to a method
|
295
294
|
# that checks whether a record should be built for a certain attribute
|
@@ -465,10 +464,10 @@ module ActiveEntity
|
|
465
464
|
if attributes_collection.is_a? Hash
|
466
465
|
keys = attributes_collection.keys
|
467
466
|
attributes_collection = if keys.include?("id") || keys.include?(:id)
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
467
|
+
[attributes_collection]
|
468
|
+
else
|
469
|
+
attributes_collection.values
|
470
|
+
end
|
472
471
|
end
|
473
472
|
|
474
473
|
association = association(association_name)
|
@@ -551,11 +550,5 @@ module ActiveEntity
|
|
551
550
|
def allow_destroy?(association_name)
|
552
551
|
nested_attributes_options[association_name][:allow_destroy]
|
553
552
|
end
|
554
|
-
|
555
|
-
def raise_nested_attributes_record_not_found!(association_name, record_id)
|
556
|
-
model = self.class._reflect_on_association(association_name).klass.name
|
557
|
-
raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
|
558
|
-
model, "id", record_id)
|
559
|
-
end
|
560
553
|
end
|
561
554
|
end
|
@@ -2,15 +2,75 @@
|
|
2
2
|
|
3
3
|
require "active_entity"
|
4
4
|
require "rails"
|
5
|
+
require "active_support/core_ext/object/try"
|
5
6
|
require "active_model/railtie"
|
6
7
|
|
8
|
+
# For now, action_controller must always be present with
|
9
|
+
# Rails, so let's make sure that it gets required before
|
10
|
+
# here. This is needed for correctly setting up the middleware.
|
11
|
+
# In the future, this might become an optional require.
|
12
|
+
require "action_controller/railtie"
|
13
|
+
|
7
14
|
module ActiveEntity
|
8
15
|
# = Active Entity Railtie
|
9
16
|
class Railtie < Rails::Railtie # :nodoc:
|
17
|
+
config.active_entity = ActiveSupport::OrderedOptions.new
|
18
|
+
|
10
19
|
config.eager_load_namespaces << ActiveEntity
|
11
20
|
|
21
|
+
# When loading console, force ActiveEntity::Base to be loaded
|
22
|
+
# to avoid cross references when loading a constant for the
|
23
|
+
# first time. Also, make it output to STDERR.
|
24
|
+
console do |_app|
|
25
|
+
require "active_entity/base"
|
26
|
+
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
|
27
|
+
console = ActiveSupport::Logger.new(STDERR)
|
28
|
+
Rails.logger.extend ActiveSupport::Logger.broadcast console
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
12
32
|
runner do
|
13
|
-
require "
|
33
|
+
require "active_entity/base"
|
34
|
+
end
|
35
|
+
|
36
|
+
initializer "active_entity.initialize_timezone" do
|
37
|
+
ActiveSupport.on_load(:active_entity) do
|
38
|
+
self.time_zone_aware_attributes = true
|
39
|
+
self.default_timezone = :utc
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
initializer "active_entity.logger" do
|
44
|
+
ActiveSupport.on_load(:active_entity) { self.logger ||= ::Rails.logger }
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
initializer "active_entity.define_attribute_methods" do |app|
|
49
|
+
config.after_initialize do
|
50
|
+
ActiveSupport.on_load(:active_entity) do
|
51
|
+
if app.config.eager_load
|
52
|
+
descendants.each do |model|
|
53
|
+
model.define_attribute_methods
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
initializer "active_entity.set_configs" do |app|
|
61
|
+
ActiveSupport.on_load(:active_entity) do
|
62
|
+
configs = app.config.active_entity
|
63
|
+
|
64
|
+
configs.each do |k, v|
|
65
|
+
send "#{k}=", v
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
initializer "active_entity.set_filter_attributes" do
|
71
|
+
ActiveSupport.on_load(:active_entity) do
|
72
|
+
self.filter_attributes += Rails.application.config.filter_parameters
|
73
|
+
end
|
14
74
|
end
|
15
75
|
end
|
16
76
|
end
|
@@ -16,7 +16,7 @@ module ActiveEntity
|
|
16
16
|
@_attr_readonly_enabled = true
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def without_attr_readonly
|
20
20
|
return unless block_given?
|
21
21
|
|
22
22
|
disable_attr_readonly!
|
@@ -31,6 +31,10 @@ module ActiveEntity
|
|
31
31
|
end
|
32
32
|
alias attr_readonly_enabled? _attr_readonly_enabled
|
33
33
|
|
34
|
+
def readonly_attribute?(name)
|
35
|
+
self.class.readonly_attribute?(name)
|
36
|
+
end
|
37
|
+
|
34
38
|
module ClassMethods
|
35
39
|
# Attributes listed as readonly will be used to create a new record but update operations will
|
36
40
|
# ignore these fields.
|
@@ -42,6 +46,10 @@ module ActiveEntity
|
|
42
46
|
def readonly_attributes
|
43
47
|
_attr_readonly
|
44
48
|
end
|
49
|
+
|
50
|
+
def readonly_attribute?(name) # :nodoc:
|
51
|
+
_attr_readonly.include?(name)
|
52
|
+
end
|
45
53
|
end
|
46
54
|
end
|
47
55
|
end
|
@@ -17,7 +17,7 @@ module ActiveEntity
|
|
17
17
|
def create(macro, name, scope, options, ar_or_ae)
|
18
18
|
reflection_class_for(macro).new(name, scope, options, ar_or_ae)
|
19
19
|
|
20
|
-
# TODO: Support bridge to Active
|
20
|
+
# TODO: Support bridge to Active Entity
|
21
21
|
# reflection = reflection_class_for(macro).new(name, scope, options, ar_or_ae)
|
22
22
|
# options[:through] ? ActiveRecord::ThroughReflection.new(reflection) : reflection
|
23
23
|
end
|
@@ -33,6 +33,7 @@ module ActiveEntity
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
+
|
36
37
|
def reflection_class_for(macro)
|
37
38
|
case macro
|
38
39
|
when :composed_of
|
@@ -77,21 +78,21 @@ module ActiveEntity
|
|
77
78
|
#
|
78
79
|
def reflections
|
79
80
|
@__reflections ||= begin
|
80
|
-
|
81
|
+
ref = {}
|
81
82
|
|
82
|
-
|
83
|
-
|
83
|
+
_reflections.each do |name, reflection|
|
84
|
+
parent_reflection = reflection.parent_reflection
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
86
|
+
if parent_reflection
|
87
|
+
parent_name = parent_reflection.name
|
88
|
+
ref[parent_name.to_s] = parent_reflection
|
89
|
+
else
|
90
|
+
ref[name] = reflection
|
91
|
+
end
|
92
|
+
end
|
92
93
|
|
93
|
-
|
94
|
-
|
94
|
+
ref
|
95
|
+
end
|
95
96
|
end
|
96
97
|
|
97
98
|
# Returns an array of AssociationReflection objects for all the
|
@@ -184,6 +185,7 @@ module ActiveEntity
|
|
184
185
|
end
|
185
186
|
|
186
187
|
protected
|
188
|
+
|
187
189
|
def actual_source_reflection # FIXME: this is a horrible name
|
188
190
|
self
|
189
191
|
end
|
@@ -242,12 +244,13 @@ module ActiveEntity
|
|
242
244
|
def ==(other_aggregation)
|
243
245
|
super ||
|
244
246
|
other_aggregation.kind_of?(self.class) &&
|
245
|
-
|
246
|
-
|
247
|
-
|
247
|
+
name == other_aggregation.name &&
|
248
|
+
!other_aggregation.options.nil? &&
|
249
|
+
active_entity == other_aggregation.active_entity
|
248
250
|
end
|
249
251
|
|
250
252
|
private
|
253
|
+
|
251
254
|
def derive_class_name
|
252
255
|
name.to_s.camelize
|
253
256
|
end
|
@@ -417,7 +420,7 @@ module ActiveEntity
|
|
417
420
|
def collection?; true; end
|
418
421
|
|
419
422
|
def association_class
|
420
|
-
Associations::
|
423
|
+
Associations::Embeds::EmbedsManyAssociation
|
421
424
|
end
|
422
425
|
end
|
423
426
|
|
@@ -427,7 +430,7 @@ module ActiveEntity
|
|
427
430
|
def embeds_one?; true; end
|
428
431
|
|
429
432
|
def association_class
|
430
|
-
Associations::
|
433
|
+
Associations::Embeds::EmbedsOneAssociation
|
431
434
|
end
|
432
435
|
end
|
433
436
|
|
@@ -437,7 +440,7 @@ module ActiveEntity
|
|
437
440
|
def embedded_in?; true; end
|
438
441
|
|
439
442
|
def association_class
|
440
|
-
Associations::
|
443
|
+
Associations::Embeds::EmbeddedInAssociation
|
441
444
|
end
|
442
445
|
end
|
443
446
|
end
|