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.
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