activeentity 0.0.1.beta14 → 6.1.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +24 -7
  4. data/Rakefile +7 -7
  5. data/lib/active_entity.rb +30 -7
  6. data/lib/active_entity/aggregations.rb +2 -1
  7. data/lib/active_entity/associations.rb +46 -24
  8. data/lib/active_entity/associations/{embedded → embeds}/association.rb +2 -2
  9. data/lib/active_entity/associations/{embedded → embeds}/builder/association.rb +1 -1
  10. data/lib/active_entity/associations/{embedded → embeds}/builder/collection_association.rb +1 -1
  11. data/lib/active_entity/associations/{embedded → embeds}/builder/embedded_in.rb +1 -1
  12. data/lib/active_entity/associations/{embedded → embeds}/builder/embeds_many.rb +1 -1
  13. data/lib/active_entity/associations/{embedded → embeds}/builder/embeds_one.rb +1 -1
  14. data/lib/active_entity/associations/{embedded → embeds}/builder/singular_association.rb +1 -1
  15. data/lib/active_entity/associations/{embedded → embeds}/collection_association.rb +1 -1
  16. data/lib/active_entity/associations/{embedded → embeds}/collection_proxy.rb +2 -2
  17. data/lib/active_entity/associations/{embedded → embeds}/embedded_in_association.rb +1 -1
  18. data/lib/active_entity/associations/{embedded → embeds}/embeds_many_association.rb +1 -1
  19. data/lib/active_entity/associations/{embedded → embeds}/embeds_one_association.rb +2 -1
  20. data/lib/active_entity/associations/{embedded → embeds}/singular_association.rb +1 -1
  21. data/lib/active_entity/attribute_assignment.rb +10 -8
  22. data/lib/active_entity/attribute_methods.rb +52 -61
  23. data/lib/active_entity/attribute_methods/before_type_cast.rb +6 -6
  24. data/lib/active_entity/attribute_methods/dirty.rb +13 -0
  25. data/lib/active_entity/attribute_methods/primary_key.rb +6 -8
  26. data/lib/active_entity/attribute_methods/query.rb +11 -8
  27. data/lib/active_entity/attribute_methods/read.rb +10 -13
  28. data/lib/active_entity/attribute_methods/time_zone_conversion.rb +2 -0
  29. data/lib/active_entity/attribute_methods/write.rb +16 -25
  30. data/lib/active_entity/attributes.rb +76 -2
  31. data/lib/active_entity/base.rb +3 -14
  32. data/lib/active_entity/{define_callbacks.rb → callbacks.rb} +5 -1
  33. data/lib/active_entity/core.rb +97 -39
  34. data/lib/active_entity/enum.rb +30 -4
  35. data/lib/active_entity/errors.rb +0 -11
  36. data/lib/active_entity/gem_version.rb +4 -4
  37. data/lib/active_entity/inheritance.rb +4 -106
  38. data/lib/active_entity/integration.rb +1 -1
  39. data/lib/active_entity/model_schema.rb +0 -12
  40. data/lib/active_entity/nested_attributes.rb +5 -12
  41. data/lib/active_entity/railtie.rb +61 -1
  42. data/lib/active_entity/readonly_attributes.rb +9 -1
  43. data/lib/active_entity/reflection.rb +22 -19
  44. data/lib/active_entity/serialization.rb +9 -6
  45. data/lib/active_entity/store.rb +51 -2
  46. data/lib/active_entity/type.rb +8 -8
  47. data/lib/active_entity/type/registry.rb +5 -5
  48. data/lib/active_entity/{validate_embedded_association.rb → validate_embeds_association.rb} +6 -6
  49. data/lib/active_entity/validations.rb +2 -6
  50. data/lib/active_entity/validations/associated.rb +1 -1
  51. data/lib/active_entity/validations/{uniqueness_in_embedding.rb → uniqueness_in_embeds.rb} +1 -1
  52. data/lib/active_entity/validations/uniqueness_on_active_record.rb +42 -47
  53. metadata +33 -36
  54. data/lib/active_entity/type/decimal_without_scale.rb +0 -15
  55. data/lib/active_entity/type/hash_lookup_type_map.rb +0 -25
  56. data/lib/active_entity/type/type_map.rb +0 -62
  57. data/lib/tasks/active_entity_tasks.rake +0 -6
@@ -7,10 +7,10 @@ module ActiveEntity
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 0
11
- MINOR = 0
12
- TINY = 1
13
- PRE = "beta14"
10
+ MAJOR = 6
11
+ MINOR = 1
12
+ TINY = 0
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -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.inheritance_attribute</tt>).
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
- if has_attribute?(inheritance_attribute)
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 || !has_attribute?(inheritance_attribute)
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 Active Entity"
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 && id.to_s # Be sure to stringify the id for routes
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
- # (eg. 1, '1', true, or 'true'). This option is off by default.
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
- [attributes_collection]
469
- else
470
- attributes_collection.values
471
- end
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 "active_record/base"
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 enable_attr_readonly
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 Record
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
- ref = {}
81
+ ref = {}
81
82
 
82
- _reflections.each do |name, reflection|
83
- parent_reflection = reflection.parent_reflection
83
+ _reflections.each do |name, reflection|
84
+ parent_reflection = reflection.parent_reflection
84
85
 
85
- if parent_reflection
86
- parent_name = parent_reflection.name
87
- ref[parent_name.to_s] = parent_reflection
88
- else
89
- ref[name] = reflection
90
- end
91
- end
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
- ref
94
- end
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
- name == other_aggregation.name &&
246
- !other_aggregation.options.nil? &&
247
- active_entity == other_aggregation.active_entity
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::Embedded::EmbedsManyAssociation
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::Embedded::EmbedsOneAssociation
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::Embedded::EmbeddedInAssociation
443
+ Associations::Embeds::EmbeddedInAssociation
441
444
  end
442
445
  end
443
446
  end