active_data 0.3.0 → 1.0.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rspec +0 -1
  4. data/.rvmrc +1 -1
  5. data/.travis.yml +13 -6
  6. data/Appraisals +7 -0
  7. data/Gemfile +1 -5
  8. data/Guardfile +68 -15
  9. data/README.md +144 -2
  10. data/active_data.gemspec +19 -11
  11. data/gemfiles/rails.4.0.gemfile +14 -0
  12. data/gemfiles/rails.4.1.gemfile +14 -0
  13. data/gemfiles/rails.4.2.gemfile +14 -0
  14. data/gemfiles/rails.5.0.gemfile +14 -0
  15. data/lib/active_data.rb +120 -3
  16. data/lib/active_data/active_record/associations.rb +50 -0
  17. data/lib/active_data/active_record/nested_attributes.rb +24 -0
  18. data/lib/active_data/config.rb +40 -0
  19. data/lib/active_data/errors.rb +93 -0
  20. data/lib/active_data/extensions.rb +33 -0
  21. data/lib/active_data/model.rb +16 -74
  22. data/lib/active_data/model/associations.rb +84 -15
  23. data/lib/active_data/model/associations/base.rb +79 -0
  24. data/lib/active_data/model/associations/collection/embedded.rb +12 -0
  25. data/lib/active_data/model/associations/collection/proxy.rb +32 -0
  26. data/lib/active_data/model/associations/collection/referenced.rb +26 -0
  27. data/lib/active_data/model/associations/embeds_many.rb +124 -18
  28. data/lib/active_data/model/associations/embeds_one.rb +90 -15
  29. data/lib/active_data/model/associations/nested_attributes.rb +180 -0
  30. data/lib/active_data/model/associations/references_many.rb +96 -0
  31. data/lib/active_data/model/associations/references_one.rb +83 -0
  32. data/lib/active_data/model/associations/reflections/base.rb +100 -0
  33. data/lib/active_data/model/associations/reflections/embeds_many.rb +25 -0
  34. data/lib/active_data/model/associations/reflections/embeds_one.rb +49 -0
  35. data/lib/active_data/model/associations/reflections/reference_reflection.rb +45 -0
  36. data/lib/active_data/model/associations/reflections/references_many.rb +28 -0
  37. data/lib/active_data/model/associations/reflections/references_one.rb +28 -0
  38. data/lib/active_data/model/associations/validations.rb +63 -0
  39. data/lib/active_data/model/attributes.rb +247 -0
  40. data/lib/active_data/model/attributes/attribute.rb +73 -0
  41. data/lib/active_data/model/attributes/base.rb +116 -0
  42. data/lib/active_data/model/attributes/collection.rb +17 -0
  43. data/lib/active_data/model/attributes/dictionary.rb +26 -0
  44. data/lib/active_data/model/attributes/localized.rb +42 -0
  45. data/lib/active_data/model/attributes/reference_many.rb +21 -0
  46. data/lib/active_data/model/attributes/reference_one.rb +42 -0
  47. data/lib/active_data/model/attributes/reflections/attribute.rb +55 -0
  48. data/lib/active_data/model/attributes/reflections/base.rb +62 -0
  49. data/lib/active_data/model/attributes/reflections/collection.rb +10 -0
  50. data/lib/active_data/model/attributes/reflections/dictionary.rb +13 -0
  51. data/lib/active_data/model/attributes/reflections/localized.rb +43 -0
  52. data/lib/active_data/model/attributes/reflections/reference_many.rb +10 -0
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +58 -0
  54. data/lib/active_data/model/attributes/reflections/represents.rb +55 -0
  55. data/lib/active_data/model/attributes/represents.rb +64 -0
  56. data/lib/active_data/model/callbacks.rb +71 -0
  57. data/lib/active_data/model/conventions.rb +35 -0
  58. data/lib/active_data/model/dirty.rb +77 -0
  59. data/lib/active_data/model/lifecycle.rb +307 -0
  60. data/lib/active_data/model/localization.rb +21 -0
  61. data/lib/active_data/model/persistence.rb +57 -0
  62. data/lib/active_data/model/primary.rb +51 -0
  63. data/lib/active_data/model/scopes.rb +77 -0
  64. data/lib/active_data/model/validations.rb +27 -0
  65. data/lib/active_data/model/validations/associated.rb +19 -0
  66. data/lib/active_data/model/validations/nested.rb +39 -0
  67. data/lib/active_data/railtie.rb +7 -0
  68. data/lib/active_data/version.rb +1 -1
  69. data/spec/lib/active_data/active_record/associations_spec.rb +149 -0
  70. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +16 -0
  71. data/spec/lib/active_data/config_spec.rb +44 -0
  72. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +362 -52
  73. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +250 -31
  74. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +23 -0
  75. data/spec/lib/active_data/model/associations/references_many_spec.rb +196 -0
  76. data/spec/lib/active_data/model/associations/references_one_spec.rb +134 -0
  77. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +144 -0
  78. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +116 -0
  79. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +255 -0
  80. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +208 -0
  81. data/spec/lib/active_data/model/associations/validations_spec.rb +153 -0
  82. data/spec/lib/active_data/model/associations_spec.rb +189 -0
  83. data/spec/lib/active_data/model/attributes/attribute_spec.rb +144 -0
  84. data/spec/lib/active_data/model/attributes/base_spec.rb +82 -0
  85. data/spec/lib/active_data/model/attributes/collection_spec.rb +73 -0
  86. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +93 -0
  87. data/spec/lib/active_data/model/attributes/localized_spec.rb +88 -33
  88. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +72 -0
  89. data/spec/lib/active_data/model/attributes/reflections/base_spec.rb +56 -0
  90. data/spec/lib/active_data/model/attributes/reflections/collection_spec.rb +37 -0
  91. data/spec/lib/active_data/model/attributes/reflections/dictionary_spec.rb +43 -0
  92. data/spec/lib/active_data/model/attributes/reflections/localized_spec.rb +37 -0
  93. data/spec/lib/active_data/model/attributes/reflections/represents_spec.rb +70 -0
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +153 -0
  95. data/spec/lib/active_data/model/attributes_spec.rb +243 -0
  96. data/spec/lib/active_data/model/callbacks_spec.rb +338 -0
  97. data/spec/lib/active_data/model/conventions_spec.rb +12 -0
  98. data/spec/lib/active_data/model/dirty_spec.rb +75 -0
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +330 -0
  100. data/spec/lib/active_data/model/nested_attributes.rb +202 -0
  101. data/spec/lib/active_data/model/persistence_spec.rb +47 -0
  102. data/spec/lib/active_data/model/primary_spec.rb +84 -0
  103. data/spec/lib/active_data/model/scopes_spec.rb +88 -0
  104. data/spec/lib/active_data/model/typecasting_spec.rb +192 -0
  105. data/spec/lib/active_data/model/validations/associated_spec.rb +94 -0
  106. data/spec/lib/active_data/model/validations/nested_spec.rb +93 -0
  107. data/spec/lib/active_data/model/validations_spec.rb +31 -0
  108. data/spec/lib/active_data/model_spec.rb +1 -32
  109. data/spec/lib/active_data_spec.rb +12 -0
  110. data/spec/spec_helper.rb +39 -0
  111. data/spec/support/model_helpers.rb +10 -0
  112. metadata +246 -54
  113. data/gemfiles/Gemfile.rails-3 +0 -14
  114. data/lib/active_data/attributes/base.rb +0 -69
  115. data/lib/active_data/attributes/localized.rb +0 -42
  116. data/lib/active_data/model/associations/association.rb +0 -30
  117. data/lib/active_data/model/attributable.rb +0 -122
  118. data/lib/active_data/model/collectionizable.rb +0 -55
  119. data/lib/active_data/model/collectionizable/proxy.rb +0 -42
  120. data/lib/active_data/model/extensions.rb +0 -9
  121. data/lib/active_data/model/extensions/array.rb +0 -24
  122. data/lib/active_data/model/extensions/big_decimal.rb +0 -17
  123. data/lib/active_data/model/extensions/boolean.rb +0 -38
  124. data/lib/active_data/model/extensions/date.rb +0 -17
  125. data/lib/active_data/model/extensions/date_time.rb +0 -17
  126. data/lib/active_data/model/extensions/float.rb +0 -17
  127. data/lib/active_data/model/extensions/hash.rb +0 -22
  128. data/lib/active_data/model/extensions/integer.rb +0 -17
  129. data/lib/active_data/model/extensions/localized.rb +0 -22
  130. data/lib/active_data/model/extensions/object.rb +0 -17
  131. data/lib/active_data/model/extensions/string.rb +0 -17
  132. data/lib/active_data/model/extensions/time.rb +0 -17
  133. data/lib/active_data/model/localizable.rb +0 -31
  134. data/lib/active_data/model/nested_attributes.rb +0 -58
  135. data/lib/active_data/model/parameterizable.rb +0 -29
  136. data/lib/active_data/validations.rb +0 -7
  137. data/lib/active_data/validations/associated.rb +0 -17
  138. data/spec/lib/active_data/model/attributable_spec.rb +0 -191
  139. data/spec/lib/active_data/model/collectionizable_spec.rb +0 -60
  140. data/spec/lib/active_data/model/nested_attributes_spec.rb +0 -67
  141. data/spec/lib/active_data/model/type_cast_spec.rb +0 -116
  142. data/spec/lib/active_data/validations/associated_spec.rb +0 -88
@@ -0,0 +1,96 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ class ReferencesMany < Base
5
+ def apply_changes
6
+ present_keys = target.reject { |t| t.marked_for_destruction? }.map(&reflection.primary_key).compact
7
+ write_source(present_keys)
8
+ true
9
+ end
10
+
11
+ def target= object
12
+ loaded!
13
+ @target = object.to_a
14
+ end
15
+
16
+ def load_target
17
+ source = read_source
18
+ source.present? ? scope(source).to_a : default
19
+ end
20
+
21
+ def default
22
+ unless evar_loaded?
23
+ default = Array.wrap(reflection.default(owner))
24
+ if default.all? { |object| object.is_a?(reflection.klass) }
25
+ default
26
+ elsif default.all? { |object| object.is_a?(Hash) }
27
+ default.map { |attributes| reflection.klass.new(attributes) }
28
+ else
29
+ scope(default).to_a
30
+ end if default.present?
31
+ end || []
32
+ end
33
+
34
+ def read_source
35
+ attribute.read_before_type_cast
36
+ end
37
+
38
+ def write_source value
39
+ attribute.write_value value
40
+ end
41
+
42
+ def reader force_reload = false
43
+ reload if force_reload
44
+ @proxy ||= Collection::Referenced.new self
45
+ end
46
+
47
+ def replace objects
48
+ loaded!
49
+ transaction do
50
+ clear
51
+ append objects
52
+ end
53
+ end
54
+ alias_method :writer, :replace
55
+
56
+ def concat(*objects)
57
+ append objects.flatten
58
+ reader
59
+ end
60
+
61
+ def clear
62
+ attribute.pollute do
63
+ write_source([])
64
+ end
65
+ reload.empty?
66
+ end
67
+
68
+ def scope source = read_source
69
+ reflection.scope.where(reflection.primary_key => source)
70
+ end
71
+
72
+ def identify
73
+ target.map(&reflection.primary_key)
74
+ end
75
+
76
+ private
77
+
78
+ def append objects
79
+ attribute.pollute do
80
+ objects.each do |object|
81
+ next if target.include?(object)
82
+ raise AssociationTypeMismatch.new(reflection.klass, object.class) unless object.is_a?(reflection.klass)
83
+ target.push(object)
84
+ apply_changes!
85
+ end
86
+ end
87
+ target
88
+ end
89
+
90
+ def attribute
91
+ @attribute ||= owner.attribute(reflection.reference_key)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,83 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ class ReferencesOne < Base
5
+ def apply_changes
6
+ if target && !target.marked_for_destruction?
7
+ write_source identify
8
+ else
9
+ write_source nil
10
+ end
11
+ true
12
+ end
13
+
14
+ def target= object
15
+ loaded!
16
+ @target = object
17
+ end
18
+
19
+ def load_target
20
+ source = read_source
21
+ source ? scope(source).first : default
22
+ end
23
+
24
+ def default
25
+ unless evar_loaded?
26
+ default = reflection.default(owner)
27
+ case default
28
+ when reflection.klass
29
+ default
30
+ when Hash
31
+ reflection.klass.new(default)
32
+ else
33
+ scope(default).first
34
+ end if default
35
+ end
36
+ end
37
+
38
+ def read_source
39
+ attribute.read_before_type_cast
40
+ end
41
+
42
+ def write_source value
43
+ attribute.write_value value
44
+ end
45
+
46
+ def reader force_reload = false
47
+ reset if force_reload
48
+ target
49
+ end
50
+
51
+ def replace object
52
+ unless object.nil? || object.is_a?(reflection.klass)
53
+ raise AssociationTypeMismatch.new(reflection.klass, object.class)
54
+ end
55
+
56
+ transaction do
57
+ attribute.pollute do
58
+ self.target = object
59
+ apply_changes!
60
+ end
61
+ end
62
+
63
+ target
64
+ end
65
+ alias_method :writer, :replace
66
+
67
+ def scope source = read_source
68
+ reflection.scope.where(reflection.primary_key => source)
69
+ end
70
+
71
+ def identify
72
+ target.try(reflection.primary_key)
73
+ end
74
+
75
+ private
76
+
77
+ def attribute
78
+ @attribute ||= owner.attribute(reflection.reference_key)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,100 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module Reflections
5
+ class Base
6
+ READ = ->(reflection, object) { object.read_attribute reflection.name }
7
+ WRITE = ->(reflection, object, value) { object.write_attribute reflection.name, value }
8
+
9
+ attr_reader :name, :options
10
+ # AR compatibility
11
+ attr_accessor :parent_reflection
12
+ delegate :association_class, to: 'self.class'
13
+
14
+ def self.build target, generated_methods, name, options = {}, &block
15
+ if block
16
+ options[:class] = proc do |reflection|
17
+ superclass = reflection.options[:class_name].to_s.presence.try(:constantize)
18
+ klass = Class.new(superclass || Object) do
19
+ include ActiveData::Model
20
+ include ActiveData::Model::Associations
21
+ include ActiveData::Model::Lifecycle
22
+ include ActiveData::Model::Primary
23
+ end
24
+ target.const_set(name.to_s.classify, klass)
25
+ klass.class_eval(&block)
26
+ klass
27
+ end
28
+ end
29
+ generate_methods name, generated_methods
30
+ target.validates_nested name if options.delete(:validate) && target.respond_to?(:validates_nested)
31
+ new(name, options)
32
+ end
33
+
34
+ def self.generate_methods name, target
35
+ target.class_eval <<-RUBY, __FILE__, __LINE__ + 1
36
+ def #{name} force_reload = false
37
+ association(:#{name}).reader(force_reload)
38
+ end
39
+
40
+ def #{name}= value
41
+ association(:#{name}).writer(value)
42
+ end
43
+ RUBY
44
+ end
45
+
46
+ def self.association_class
47
+ @association_class ||= "ActiveData::Model::Associations::#{name.demodulize}".constantize
48
+ end
49
+
50
+ def initialize name, options = {}
51
+ @name, @options = name.to_sym, options
52
+ end
53
+
54
+ def macro
55
+ self.class.name.demodulize.underscore.to_sym
56
+ end
57
+
58
+ def klass
59
+ @klass ||= options[:class] ?
60
+ options[:class].call(self) :
61
+ (options[:class_name].presence || name.to_s.classify).to_s.constantize
62
+ end
63
+
64
+ def belongs_to?
65
+ false
66
+ end
67
+
68
+ def build_association object
69
+ self.class.association_class.new object, self
70
+ end
71
+
72
+ def read_source object
73
+ (options[:read] || READ).call(self, object)
74
+ end
75
+
76
+ def write_source object, value
77
+ (options[:write] || WRITE).call(self, object, value)
78
+ end
79
+
80
+ def default object
81
+ defaultizer = options[:default]
82
+ if defaultizer.is_a?(Proc)
83
+ if defaultizer.arity != 0
84
+ defaultizer.call(object)
85
+ else
86
+ object.instance_exec(&defaultizer)
87
+ end
88
+ else
89
+ defaultizer
90
+ end
91
+ end
92
+
93
+ def inspect
94
+ "#{self.class.name.demodulize}(#{klass})"
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module Reflections
5
+ class EmbedsMany < Base
6
+ def self.build target, generated_methods, name, options = {}, &block
7
+ if target < ActiveData::Model::Attributes
8
+ target.add_attribute(ActiveData::Model::Attributes::Reflections::Base, name)
9
+ end
10
+ options[:validate] = true unless options.key?(:validate)
11
+ super
12
+ end
13
+
14
+ def collection?
15
+ true
16
+ end
17
+
18
+ def embedded?
19
+ true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module Reflections
5
+ class EmbedsOne < Base
6
+ def self.build target, generated_methods, name, options = {}, &block
7
+ if target < ActiveData::Model::Attributes
8
+ target.add_attribute(ActiveData::Model::Attributes::Reflections::Base, name)
9
+ end
10
+ options[:validate] = true unless options.key?(:validate)
11
+ super
12
+ end
13
+
14
+ def self.generate_methods name, target
15
+ target.class_eval <<-RUBY, __FILE__, __LINE__ + 1
16
+ def #{name} force_reload = false
17
+ association(:#{name}).reader(force_reload)
18
+ end
19
+
20
+ def #{name}= value
21
+ association(:#{name}).writer(value)
22
+ end
23
+
24
+ def build_#{name} attributes = {}
25
+ association(:#{name}).build(attributes)
26
+ end
27
+
28
+ def create_#{name} attributes = {}
29
+ association(:#{name}).create(attributes)
30
+ end
31
+
32
+ def create_#{name}! attributes = {}
33
+ association(:#{name}).create!(attributes)
34
+ end
35
+ RUBY
36
+ end
37
+
38
+ def collection?
39
+ false
40
+ end
41
+
42
+ def embedded?
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,45 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module Reflections
5
+ class ReferenceReflection < Base
6
+ def self.build target, generated_methods, name, *args, &block
7
+ reflection = new(name, *args)
8
+ generate_methods name, generated_methods
9
+ reflection
10
+ end
11
+
12
+ def initialize name, *args
13
+ @options = args.extract_options!
14
+ @scope_proc = args.first
15
+ @name = name.to_sym
16
+ end
17
+
18
+ def read_source object
19
+ object.read_attribute(reference_key)
20
+ end
21
+
22
+ def write_source object, value
23
+ object.write_attribute(reference_key, value)
24
+ end
25
+
26
+ def primary_key
27
+ @primary_key ||= options[:primary_key].presence.try(:to_sym) || :id
28
+ end
29
+
30
+ def scope
31
+ @scope ||= begin
32
+ scope = klass.unscoped
33
+ scope = scope.instance_exec(&@scope_proc) if @scope_proc
34
+ scope
35
+ end
36
+ end
37
+
38
+ def embedded?
39
+ false
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module Reflections
5
+ class ReferencesMany < ReferenceReflection
6
+ def self.build target, generated_methods, name, *args, &block
7
+ reflection = super
8
+
9
+ target.add_attribute(
10
+ ActiveData::Model::Attributes::Reflections::ReferenceMany,
11
+ reflection.reference_key, association: name)
12
+
13
+ reflection
14
+ end
15
+
16
+ def collection?
17
+ true
18
+ end
19
+
20
+ def reference_key
21
+ @reference_key ||= options[:reference_key].presence.try(:to_sym) ||
22
+ :"#{name.to_s.singularize}_#{primary_key.to_s.pluralize}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module ActiveData
2
+ module Model
3
+ module Associations
4
+ module Reflections
5
+ class ReferencesOne < ReferenceReflection
6
+ def self.build target, generated_methods, name, *args, &block
7
+ reflection = super
8
+
9
+ target.add_attribute(
10
+ ActiveData::Model::Attributes::Reflections::ReferenceOne,
11
+ reflection.reference_key, association: name)
12
+
13
+ reflection
14
+ end
15
+
16
+ def collection?
17
+ false
18
+ end
19
+
20
+ def reference_key
21
+ @reference_key ||= options[:reference_key].presence.try(:to_sym) ||
22
+ :"#{name}_#{primary_key}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end