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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rspec +0 -1
- data/.rvmrc +1 -1
- data/.travis.yml +13 -6
- data/Appraisals +7 -0
- data/Gemfile +1 -5
- data/Guardfile +68 -15
- data/README.md +144 -2
- data/active_data.gemspec +19 -11
- data/gemfiles/rails.4.0.gemfile +14 -0
- data/gemfiles/rails.4.1.gemfile +14 -0
- data/gemfiles/rails.4.2.gemfile +14 -0
- data/gemfiles/rails.5.0.gemfile +14 -0
- data/lib/active_data.rb +120 -3
- data/lib/active_data/active_record/associations.rb +50 -0
- data/lib/active_data/active_record/nested_attributes.rb +24 -0
- data/lib/active_data/config.rb +40 -0
- data/lib/active_data/errors.rb +93 -0
- data/lib/active_data/extensions.rb +33 -0
- data/lib/active_data/model.rb +16 -74
- data/lib/active_data/model/associations.rb +84 -15
- data/lib/active_data/model/associations/base.rb +79 -0
- data/lib/active_data/model/associations/collection/embedded.rb +12 -0
- data/lib/active_data/model/associations/collection/proxy.rb +32 -0
- data/lib/active_data/model/associations/collection/referenced.rb +26 -0
- data/lib/active_data/model/associations/embeds_many.rb +124 -18
- data/lib/active_data/model/associations/embeds_one.rb +90 -15
- data/lib/active_data/model/associations/nested_attributes.rb +180 -0
- data/lib/active_data/model/associations/references_many.rb +96 -0
- data/lib/active_data/model/associations/references_one.rb +83 -0
- data/lib/active_data/model/associations/reflections/base.rb +100 -0
- data/lib/active_data/model/associations/reflections/embeds_many.rb +25 -0
- data/lib/active_data/model/associations/reflections/embeds_one.rb +49 -0
- data/lib/active_data/model/associations/reflections/reference_reflection.rb +45 -0
- data/lib/active_data/model/associations/reflections/references_many.rb +28 -0
- data/lib/active_data/model/associations/reflections/references_one.rb +28 -0
- data/lib/active_data/model/associations/validations.rb +63 -0
- data/lib/active_data/model/attributes.rb +247 -0
- data/lib/active_data/model/attributes/attribute.rb +73 -0
- data/lib/active_data/model/attributes/base.rb +116 -0
- data/lib/active_data/model/attributes/collection.rb +17 -0
- data/lib/active_data/model/attributes/dictionary.rb +26 -0
- data/lib/active_data/model/attributes/localized.rb +42 -0
- data/lib/active_data/model/attributes/reference_many.rb +21 -0
- data/lib/active_data/model/attributes/reference_one.rb +42 -0
- data/lib/active_data/model/attributes/reflections/attribute.rb +55 -0
- data/lib/active_data/model/attributes/reflections/base.rb +62 -0
- data/lib/active_data/model/attributes/reflections/collection.rb +10 -0
- data/lib/active_data/model/attributes/reflections/dictionary.rb +13 -0
- data/lib/active_data/model/attributes/reflections/localized.rb +43 -0
- data/lib/active_data/model/attributes/reflections/reference_many.rb +10 -0
- data/lib/active_data/model/attributes/reflections/reference_one.rb +58 -0
- data/lib/active_data/model/attributes/reflections/represents.rb +55 -0
- data/lib/active_data/model/attributes/represents.rb +64 -0
- data/lib/active_data/model/callbacks.rb +71 -0
- data/lib/active_data/model/conventions.rb +35 -0
- data/lib/active_data/model/dirty.rb +77 -0
- data/lib/active_data/model/lifecycle.rb +307 -0
- data/lib/active_data/model/localization.rb +21 -0
- data/lib/active_data/model/persistence.rb +57 -0
- data/lib/active_data/model/primary.rb +51 -0
- data/lib/active_data/model/scopes.rb +77 -0
- data/lib/active_data/model/validations.rb +27 -0
- data/lib/active_data/model/validations/associated.rb +19 -0
- data/lib/active_data/model/validations/nested.rb +39 -0
- data/lib/active_data/railtie.rb +7 -0
- data/lib/active_data/version.rb +1 -1
- data/spec/lib/active_data/active_record/associations_spec.rb +149 -0
- data/spec/lib/active_data/active_record/nested_attributes_spec.rb +16 -0
- data/spec/lib/active_data/config_spec.rb +44 -0
- data/spec/lib/active_data/model/associations/embeds_many_spec.rb +362 -52
- data/spec/lib/active_data/model/associations/embeds_one_spec.rb +250 -31
- data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +23 -0
- data/spec/lib/active_data/model/associations/references_many_spec.rb +196 -0
- data/spec/lib/active_data/model/associations/references_one_spec.rb +134 -0
- data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +144 -0
- data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +116 -0
- data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +255 -0
- data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +208 -0
- data/spec/lib/active_data/model/associations/validations_spec.rb +153 -0
- data/spec/lib/active_data/model/associations_spec.rb +189 -0
- data/spec/lib/active_data/model/attributes/attribute_spec.rb +144 -0
- data/spec/lib/active_data/model/attributes/base_spec.rb +82 -0
- data/spec/lib/active_data/model/attributes/collection_spec.rb +73 -0
- data/spec/lib/active_data/model/attributes/dictionary_spec.rb +93 -0
- data/spec/lib/active_data/model/attributes/localized_spec.rb +88 -33
- data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +72 -0
- data/spec/lib/active_data/model/attributes/reflections/base_spec.rb +56 -0
- data/spec/lib/active_data/model/attributes/reflections/collection_spec.rb +37 -0
- data/spec/lib/active_data/model/attributes/reflections/dictionary_spec.rb +43 -0
- data/spec/lib/active_data/model/attributes/reflections/localized_spec.rb +37 -0
- data/spec/lib/active_data/model/attributes/reflections/represents_spec.rb +70 -0
- data/spec/lib/active_data/model/attributes/represents_spec.rb +153 -0
- data/spec/lib/active_data/model/attributes_spec.rb +243 -0
- data/spec/lib/active_data/model/callbacks_spec.rb +338 -0
- data/spec/lib/active_data/model/conventions_spec.rb +12 -0
- data/spec/lib/active_data/model/dirty_spec.rb +75 -0
- data/spec/lib/active_data/model/lifecycle_spec.rb +330 -0
- data/spec/lib/active_data/model/nested_attributes.rb +202 -0
- data/spec/lib/active_data/model/persistence_spec.rb +47 -0
- data/spec/lib/active_data/model/primary_spec.rb +84 -0
- data/spec/lib/active_data/model/scopes_spec.rb +88 -0
- data/spec/lib/active_data/model/typecasting_spec.rb +192 -0
- data/spec/lib/active_data/model/validations/associated_spec.rb +94 -0
- data/spec/lib/active_data/model/validations/nested_spec.rb +93 -0
- data/spec/lib/active_data/model/validations_spec.rb +31 -0
- data/spec/lib/active_data/model_spec.rb +1 -32
- data/spec/lib/active_data_spec.rb +12 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/model_helpers.rb +10 -0
- metadata +246 -54
- data/gemfiles/Gemfile.rails-3 +0 -14
- data/lib/active_data/attributes/base.rb +0 -69
- data/lib/active_data/attributes/localized.rb +0 -42
- data/lib/active_data/model/associations/association.rb +0 -30
- data/lib/active_data/model/attributable.rb +0 -122
- data/lib/active_data/model/collectionizable.rb +0 -55
- data/lib/active_data/model/collectionizable/proxy.rb +0 -42
- data/lib/active_data/model/extensions.rb +0 -9
- data/lib/active_data/model/extensions/array.rb +0 -24
- data/lib/active_data/model/extensions/big_decimal.rb +0 -17
- data/lib/active_data/model/extensions/boolean.rb +0 -38
- data/lib/active_data/model/extensions/date.rb +0 -17
- data/lib/active_data/model/extensions/date_time.rb +0 -17
- data/lib/active_data/model/extensions/float.rb +0 -17
- data/lib/active_data/model/extensions/hash.rb +0 -22
- data/lib/active_data/model/extensions/integer.rb +0 -17
- data/lib/active_data/model/extensions/localized.rb +0 -22
- data/lib/active_data/model/extensions/object.rb +0 -17
- data/lib/active_data/model/extensions/string.rb +0 -17
- data/lib/active_data/model/extensions/time.rb +0 -17
- data/lib/active_data/model/localizable.rb +0 -31
- data/lib/active_data/model/nested_attributes.rb +0 -58
- data/lib/active_data/model/parameterizable.rb +0 -29
- data/lib/active_data/validations.rb +0 -7
- data/lib/active_data/validations/associated.rb +0 -17
- data/spec/lib/active_data/model/attributable_spec.rb +0 -191
- data/spec/lib/active_data/model/collectionizable_spec.rb +0 -60
- data/spec/lib/active_data/model/nested_attributes_spec.rb +0 -67
- data/spec/lib/active_data/model/type_cast_spec.rb +0 -116
- data/spec/lib/active_data/validations/associated_spec.rb +0 -88
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Localization
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def localized *args, &block
|
8
|
+
add_attribute(ActiveData::Model::Attributes::Reflections::Localized, *args, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def fallbacks locale
|
12
|
+
::I18n.respond_to?(:fallbacks) ? ::I18n.fallbacks[locale] : [locale]
|
13
|
+
end
|
14
|
+
|
15
|
+
def locale
|
16
|
+
I18n.locale
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Persistence
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def instantiate data
|
8
|
+
data = data.stringify_keys
|
9
|
+
instance = allocate
|
10
|
+
|
11
|
+
instance.instance_variable_set(:@initial_attributes, data.slice(*attribute_names))
|
12
|
+
instance.send(:mark_persisted!)
|
13
|
+
|
14
|
+
instance
|
15
|
+
end
|
16
|
+
|
17
|
+
def instantiate_collection data
|
18
|
+
collection = Array.wrap(data).map { |attrs| instantiate attrs }
|
19
|
+
collection = scope(collection, true) if respond_to?(:scope)
|
20
|
+
collection
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def persisted?
|
25
|
+
!!@persisted
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroyed?
|
29
|
+
!!@destroyed
|
30
|
+
end
|
31
|
+
|
32
|
+
def marked_for_destruction?
|
33
|
+
@marked_for_destruction
|
34
|
+
end
|
35
|
+
|
36
|
+
def mark_for_destruction
|
37
|
+
@marked_for_destruction = true
|
38
|
+
end
|
39
|
+
|
40
|
+
def _destroy
|
41
|
+
marked_for_destruction?
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def mark_persisted!
|
47
|
+
@persisted = true
|
48
|
+
@destroyed = false
|
49
|
+
end
|
50
|
+
|
51
|
+
def mark_destroyed!
|
52
|
+
@persisted = false
|
53
|
+
@destroyed = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Primary
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
DEFAULT_PRIMARY_ATTRIBUTE_OPTIONS = -> { {
|
6
|
+
type: ActiveData::UUID,
|
7
|
+
default: -> { ActiveData::UUID.random_create }
|
8
|
+
} }
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute :_primary_name, instance_writer: false
|
12
|
+
delegate :has_primary_attribute?, to: 'self.class'
|
13
|
+
|
14
|
+
prepend PrependMethods
|
15
|
+
alias_method :eql?, :==
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def primary *args
|
20
|
+
options = args.extract_options!
|
21
|
+
self._primary_name = (args.first.presence || ActiveData.primary_attribute).to_s
|
22
|
+
unless has_attribute?(_primary_name)
|
23
|
+
options.merge!(type: args.second) if args.second
|
24
|
+
attribute _primary_name, options.presence || DEFAULT_PRIMARY_ATTRIBUTE_OPTIONS.call
|
25
|
+
end
|
26
|
+
alias_attribute :primary_attribute, _primary_name
|
27
|
+
end
|
28
|
+
alias_method :primary_attribute, :primary
|
29
|
+
|
30
|
+
def has_primary_attribute?
|
31
|
+
has_attribute? _primary_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def primary_name
|
35
|
+
_primary_name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module PrependMethods
|
40
|
+
def == other
|
41
|
+
other.instance_of?(self.class) &&
|
42
|
+
has_primary_attribute? ?
|
43
|
+
primary_attribute ?
|
44
|
+
primary_attribute == other.primary_attribute :
|
45
|
+
object_id == other.object_id :
|
46
|
+
super(other)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Scopes
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :_scope_base
|
8
|
+
scopify
|
9
|
+
end
|
10
|
+
|
11
|
+
module ScopeProxy
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
def self.for(model)
|
15
|
+
klass = Class.new(model._scope_base) do
|
16
|
+
include ActiveData::Model::Scopes::ScopeProxy
|
17
|
+
end
|
18
|
+
klass.define_singleton_method(:_scope_model) { model }
|
19
|
+
model.const_set('ScopeProxy', klass)
|
20
|
+
end
|
21
|
+
|
22
|
+
included do
|
23
|
+
def initialize source = nil, trust = false
|
24
|
+
source ||= self.class.superclass.new
|
25
|
+
|
26
|
+
source.each do |entity|
|
27
|
+
raise AssociationTypeMismatch.new(self.class._scope_model, entity.class) unless entity.is_a?(self.class._scope_model)
|
28
|
+
end unless trust && source.is_a?(self.class)
|
29
|
+
|
30
|
+
super source
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_missing? method, _
|
35
|
+
super || self.class._scope_model.respond_to?(method)
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing method, *args, &block
|
39
|
+
with_scope { self.class._scope_model.public_send(method, *args, &block) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def with_scope
|
43
|
+
previous_scope, self.class._scope_model.current_scope = self.class._scope_model.current_scope, self
|
44
|
+
result = yield
|
45
|
+
self.class._scope_model.current_scope = previous_scope
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module ClassMethods
|
51
|
+
def scopify scope_base = Array
|
52
|
+
self._scope_base = scope_base
|
53
|
+
end
|
54
|
+
|
55
|
+
def scope_class
|
56
|
+
@scope_class ||= ActiveData::Model::Scopes::ScopeProxy.for(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def scope *args
|
60
|
+
if args.empty?
|
61
|
+
current_scope
|
62
|
+
else
|
63
|
+
scope_class.new *args
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def current_scope= value
|
68
|
+
@current_scope = value
|
69
|
+
end
|
70
|
+
|
71
|
+
def current_scope
|
72
|
+
@current_scope ||= scope_class.new
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Validations
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ActiveModel::Validations
|
6
|
+
|
7
|
+
included do
|
8
|
+
extend HelperMethods
|
9
|
+
include HelperMethods
|
10
|
+
|
11
|
+
alias_method :validate, :valid?
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate! context = nil
|
15
|
+
valid?(context) || raise_validation_error
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def raise_validation_error
|
21
|
+
raise ActiveData::ValidationError.new(self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Dir[File.dirname(__FILE__) + "/validations/*.rb"].each { |file| require file }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Validations
|
4
|
+
class AssociatedValidator < ActiveModel::EachValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
if Array.wrap(value).reject { |r| r.respond_to?(:valid?) && r.valid?(record.validation_context) }.any?
|
7
|
+
record.errors.add(attribute, :invalid, options.merge(value: value))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module HelperMethods
|
13
|
+
def validates_associated(*attr_names)
|
14
|
+
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ActiveData
|
2
|
+
module Model
|
3
|
+
module Validations
|
4
|
+
class NestedValidator < ActiveModel::EachValidator
|
5
|
+
def self.validate_nested(record, name, value)
|
6
|
+
if value.is_a?(Enumerable)
|
7
|
+
value.each.with_index do |object, i|
|
8
|
+
if yield(object)
|
9
|
+
object.errors.each do |key, message|
|
10
|
+
key = "#{name}.#{i}.#{key}"
|
11
|
+
record.errors[key] << message
|
12
|
+
record.errors[key].uniq!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
elsif value && yield(value)
|
17
|
+
value.errors.each do |key, message|
|
18
|
+
key = "#{name}.#{key}"
|
19
|
+
record.errors[key] << message
|
20
|
+
record.errors[key].uniq!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate_each(record, attribute, value)
|
26
|
+
self.class.validate_nested(record, attribute, value) do |object|
|
27
|
+
object.invalid?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module HelperMethods
|
33
|
+
def validates_nested(*attr_names)
|
34
|
+
validates_with NestedValidator, _merge_attributes(attr_names)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/active_data/version.rb
CHANGED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveData::ActiveRecord::Associations do
|
4
|
+
before do
|
5
|
+
stub_model(:project) do
|
6
|
+
include ActiveData::Model::Lifecycle
|
7
|
+
include ActiveData::Model::Associations
|
8
|
+
|
9
|
+
attribute :title, String
|
10
|
+
|
11
|
+
validates :title, presence: true
|
12
|
+
|
13
|
+
embeds_one :author do
|
14
|
+
attribute :name, String
|
15
|
+
|
16
|
+
validates :name, presence: true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
stub_model(:profile) do
|
21
|
+
include ActiveData::Model::Lifecycle
|
22
|
+
|
23
|
+
attribute :first_name, String
|
24
|
+
attribute :last_name, String
|
25
|
+
end
|
26
|
+
|
27
|
+
stub_class(:user, ActiveRecord::Base) do
|
28
|
+
embeds_many :projects
|
29
|
+
embeds_one :profile
|
30
|
+
|
31
|
+
validates :projects, associated: true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
its(:projects) { should = [] }
|
36
|
+
its(:profile) { should = nil }
|
37
|
+
|
38
|
+
context 'new owner' do
|
39
|
+
subject(:user) { User.new }
|
40
|
+
|
41
|
+
describe '#projects' do
|
42
|
+
specify { expect { user.projects << Project.new }
|
43
|
+
.not_to change { user.read_attribute(:projects) } }
|
44
|
+
specify { expect { user.projects << Project.new(title: 'First') }
|
45
|
+
.not_to change { user.read_attribute(:projects) } }
|
46
|
+
specify { expect { user.projects << Project.new(title: 'First') }
|
47
|
+
.not_to change { user.projects.reload.count } }
|
48
|
+
specify do
|
49
|
+
user.projects << Project.new(title: 'First')
|
50
|
+
user.save
|
51
|
+
expect(user.reload.projects.first.title).to eq('First')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#profile' do
|
56
|
+
specify { expect { user.profile = Profile.new(first_name: 'google.com') }
|
57
|
+
.not_to change { user.read_attribute(:profile) } }
|
58
|
+
specify { expect { user.profile = Profile.new(first_name: 'google.com') }
|
59
|
+
.to change { user.profile }.from(nil).to(an_instance_of(Profile)) }
|
60
|
+
specify do
|
61
|
+
user.profile = Profile.new(first_name: 'google.com')
|
62
|
+
user.save
|
63
|
+
expect(user.reload.profile.first_name).to eq('google.com')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'persisted owner' do
|
69
|
+
subject(:user) { User.create }
|
70
|
+
|
71
|
+
describe '#projects' do
|
72
|
+
specify { expect { user.projects << Project.new }
|
73
|
+
.not_to change { user.read_attribute(:projects) } }
|
74
|
+
specify { expect { user.projects << Project.new(title: 'First') }
|
75
|
+
.to change { user.projects.reload.count }.from(0).to(1) }
|
76
|
+
specify {
|
77
|
+
user.projects << Project.new(title: 'First')
|
78
|
+
user.save
|
79
|
+
expect(user.reload.projects.first.title).to eq('First') }
|
80
|
+
|
81
|
+
context do
|
82
|
+
let(:project) { Project.new(title: 'First') }
|
83
|
+
before { project.build_author(name: 'Author') }
|
84
|
+
|
85
|
+
specify { expect { user.projects << project }
|
86
|
+
.to change { user.attributes['projects'] }.from(nil)
|
87
|
+
.to([{title: 'First', author: {name: 'Author'}}].to_json) }
|
88
|
+
specify { expect { user.projects << project; user.save }
|
89
|
+
.to change { user.reload.attributes['projects'] }.from(nil)
|
90
|
+
.to([{title: 'First', author: {name: 'Author'}}].to_json) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#profile' do
|
95
|
+
specify { expect { user.profile = Profile.new(first_name: 'google.com') }
|
96
|
+
.to change { user.profile }.from(nil).to(an_instance_of(Profile)) }
|
97
|
+
specify {
|
98
|
+
user.profile = Profile.new(first_name: 'google.com')
|
99
|
+
user.save
|
100
|
+
expect(user.reload.profile.first_name).to eq('google.com') }
|
101
|
+
specify { expect { user.profile = Profile.new(first_name: 'google.com') }
|
102
|
+
.to change { user.attributes['profile'] }.from(nil)
|
103
|
+
.to({first_name: 'google.com', last_name: nil}.to_json) }
|
104
|
+
specify { expect { user.profile = Profile.new(first_name: 'google.com'); user.save }
|
105
|
+
.to change { user.reload.attributes['profile'] }.from(nil)
|
106
|
+
.to({first_name: 'google.com', last_name: nil}.to_json) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'class determine errors' do
|
111
|
+
specify do
|
112
|
+
expect { stub_class(:book, ActiveRecord::Base) do
|
113
|
+
embeds_one :author, class_name: 'Borogoves'
|
114
|
+
end.reflect_on_association(:author).klass }.to raise_error NameError
|
115
|
+
end
|
116
|
+
|
117
|
+
specify do
|
118
|
+
expect { stub_class(:user, ActiveRecord::Base) do
|
119
|
+
embeds_many :projects, class_name: 'Borogoves' do
|
120
|
+
attribute :title
|
121
|
+
end
|
122
|
+
end.reflect_on_association(:projects).klass }.to raise_error NameError
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'on the fly' do
|
127
|
+
before do
|
128
|
+
stub_class(:user, ActiveRecord::Base) do
|
129
|
+
embeds_many :projects do
|
130
|
+
attribute :title, String
|
131
|
+
end
|
132
|
+
embeds_one :profile, class_name: 'Profile' do
|
133
|
+
attribute :age, Integer
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
specify { expect(User.reflect_on_association(:projects).klass).to eq(User::Project) }
|
139
|
+
specify { expect(User.new.projects).to eq([]) }
|
140
|
+
specify { expect(User.new.tap { |u| u.projects.create(title: 'Project') }.projects).to be_a(ActiveData::Model::Associations::Collection::Embedded) }
|
141
|
+
specify { expect(User.new.tap { |u| u.projects.create(title: 'Project') }.read_attribute(:projects)).to eq([{title: 'Project'}].to_json) }
|
142
|
+
|
143
|
+
specify { expect(User.reflect_on_association(:profile).klass).to eq(User::Profile) }
|
144
|
+
specify { expect(User.reflect_on_association(:profile).klass).to be < Profile }
|
145
|
+
specify { expect(User.new.profile).to be_nil }
|
146
|
+
specify { expect(User.new.tap { |u| u.create_profile(first_name: 'Profile') }.profile).to be_a(User::Profile) }
|
147
|
+
specify { expect(User.new.tap { |u| u.create_profile(first_name: 'Profile') }.read_attribute(:profile)).to eq({first_name: 'Profile', last_name: nil, age: nil}.to_json) }
|
148
|
+
end
|
149
|
+
end
|