mongoid 7.0.6 → 7.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +1 -1
  5. data/Rakefile +14 -5
  6. data/lib/mongoid.rb +1 -1
  7. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  8. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  9. data/lib/mongoid/association/proxy.rb +1 -1
  10. data/lib/mongoid/atomic.rb +13 -3
  11. data/lib/mongoid/clients/sessions.rb +20 -4
  12. data/lib/mongoid/criteria.rb +7 -1
  13. data/lib/mongoid/criteria/modifiable.rb +2 -1
  14. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  15. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  16. data/lib/mongoid/criteria/queryable/extensions/time.rb +1 -1
  17. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  18. data/lib/mongoid/document.rb +3 -2
  19. data/lib/mongoid/extensions/hash.rb +4 -2
  20. data/lib/mongoid/extensions/regexp.rb +1 -1
  21. data/lib/mongoid/fields.rb +2 -1
  22. data/lib/mongoid/interceptable.rb +3 -1
  23. data/lib/mongoid/matchable/regexp.rb +2 -2
  24. data/lib/mongoid/persistable/pushable.rb +11 -2
  25. data/lib/mongoid/persistence_context.rb +6 -6
  26. data/lib/mongoid/query_cache.rb +61 -18
  27. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  28. data/lib/mongoid/version.rb +1 -1
  29. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  30. data/spec/app/models/customer.rb +11 -0
  31. data/spec/app/models/customer_address.rb +12 -0
  32. data/spec/app/models/delegating_patient.rb +16 -0
  33. data/spec/integration/app_spec.rb +192 -0
  34. data/spec/integration/associations/embedded_spec.rb +62 -0
  35. data/spec/integration/callbacks_models.rb +49 -0
  36. data/spec/integration/callbacks_spec.rb +216 -0
  37. data/spec/integration/criteria/date_field_spec.rb +41 -0
  38. data/spec/integration/document_spec.rb +22 -0
  39. data/spec/lite_spec_helper.rb +12 -4
  40. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  41. data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
  42. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  43. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  44. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  45. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +105 -0
  46. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  47. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  48. data/spec/mongoid/clients/options_spec.rb +4 -4
  49. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  50. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  51. data/spec/mongoid/clients_spec.rb +2 -2
  52. data/spec/mongoid/contextual/atomic_spec.rb +20 -10
  53. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  54. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  55. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  56. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  57. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  58. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  59. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  60. data/spec/mongoid/criteria_spec.rb +4 -2
  61. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  62. data/spec/mongoid/indexable_spec.rb +6 -4
  63. data/spec/mongoid/matchable/default_spec.rb +1 -1
  64. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  65. data/spec/mongoid/matchable_spec.rb +2 -2
  66. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  67. data/spec/mongoid/query_cache_spec.rb +77 -9
  68. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  69. data/spec/mongoid/scopable_spec.rb +2 -1
  70. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  71. data/spec/mongoid/tasks/database_spec.rb +1 -1
  72. data/spec/shared/LICENSE +20 -0
  73. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  74. data/spec/shared/lib/mrss/cluster_config.rb +211 -0
  75. data/spec/shared/lib/mrss/constraints.rb +330 -0
  76. data/spec/shared/lib/mrss/docker_runner.rb +262 -0
  77. data/spec/shared/lib/mrss/lite_constraints.rb +175 -0
  78. data/spec/shared/lib/mrss/server_version_registry.rb +69 -0
  79. data/spec/shared/lib/mrss/spec_organizer.rb +149 -0
  80. data/spec/shared/share/Dockerfile.erb +229 -0
  81. data/spec/shared/shlib/distro.sh +73 -0
  82. data/spec/shared/shlib/server.sh +270 -0
  83. data/spec/shared/shlib/set_env.sh +128 -0
  84. data/spec/spec_helper.rb +0 -31
  85. data/spec/support/child_process_helper.rb +76 -0
  86. data/spec/support/cluster_config.rb +3 -3
  87. data/spec/support/constraints.rb +201 -30
  88. data/spec/support/session_registry.rb +50 -0
  89. data/spec/support/spec_config.rb +12 -4
  90. metadata +510 -461
  91. metadata.gz.sig +2 -2
@@ -148,7 +148,7 @@ module Mongoid
148
148
  #
149
149
  # @since 2.3.0
150
150
  def filter(value)
151
- !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}$/i : value
151
+ !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}\z/i : value
152
152
  end
153
153
 
154
154
  # Scope the criteria to the scope options provided.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Mongoid
4
- VERSION = "7.0.6"
4
+ VERSION = "7.0.12"
5
5
  end
@@ -13,7 +13,7 @@ class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent]
13
13
  field :<%= attribute.name %>, type: <%= attribute.type_class %>
14
14
  <% end -%>
15
15
  <% attributes.select{|attr| attr.reference? }.each do |attribute| -%>
16
- embedded_in :<%= attribute.name%>
16
+ belongs_to :<%= attribute.name%>
17
17
  <% end -%>
18
18
  end
19
19
  <% end -%>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ class Customer
5
+ include Mongoid::Document
6
+
7
+ field :name
8
+
9
+ embeds_one :home_address, class_name: 'CustomerAddress', as: :addressable
10
+ embeds_one :work_address, class_name: 'CustomerAddress', as: :addressable
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ class CustomerAddress
5
+ include Mongoid::Document
6
+
7
+ field :street, type: String
8
+ field :city, type: String
9
+ field :state, type: String
10
+
11
+ embedded_in :addressable, polymorphic: true
12
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ class DelegatingPatient
5
+ include Mongoid::Document
6
+
7
+ embeds_one :email
8
+
9
+ # Instance level delegation
10
+ delegate :address, to: :email
11
+
12
+ class << self
13
+ # Class level delegation
14
+ delegate :default_client, to: ::Mongoid
15
+ end
16
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ BASE = File.join(File.dirname(__FILE__), '../..')
7
+ TMP_BASE = File.join(BASE, 'tmp')
8
+
9
+ describe 'Mongoid application tests' do
10
+ before(:all) do
11
+ unless SpecConfig.instance.app_tests?
12
+ skip 'Set APP_TESTS=1 in environment to run application tests'
13
+ end
14
+
15
+ require 'fileutils'
16
+ require 'support/child_process_helper'
17
+ require 'open-uri'
18
+
19
+ FileUtils.mkdir_p(TMP_BASE)
20
+ end
21
+
22
+ context 'demo application - sinatra' do
23
+ it 'runs' do
24
+ clone_application(
25
+ 'https://github.com/mongoid/mongoid-demo',
26
+ subdir: 'sinatra-minimal',
27
+ ) do
28
+
29
+ process = ChildProcess.build(*%w(bundle exec ruby app.rb))
30
+ process.environment.update(clean_env)
31
+ process.io.inherit!
32
+ process.start
33
+
34
+ begin
35
+ # JRuby needs a long timeout
36
+ wait_for_port(4567, 20)
37
+ sleep 1
38
+
39
+ uri = URI.parse('http://localhost:4567/posts')
40
+ resp = JSON.parse(uri.open.read)
41
+ ensure
42
+ Process.kill('TERM', process.pid)
43
+ status = process.wait
44
+ end
45
+
46
+ resp.should == []
47
+
48
+ status.should == 0
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'demo application - rails-api' do
54
+ ['~> 6.0.0'].each do |rails_version|
55
+ context "with rails #{rails_version}" do
56
+ it 'runs' do
57
+ clone_application(
58
+ 'https://github.com/mongoid/mongoid-demo',
59
+ subdir: 'rails-api',
60
+ rails_version: rails_version,
61
+ ) do
62
+
63
+ process = ChildProcess.build(*%w(bundle exec rails s))
64
+ process.environment.update(clean_env)
65
+ process.io.inherit!
66
+ process.start
67
+
68
+ begin
69
+ # JRuby needs a long timeout
70
+ wait_for_port(3000, 30)
71
+ sleep 1
72
+
73
+ uri = URI.parse('http://localhost:3000/posts')
74
+ resp = JSON.parse(uri.open.read)
75
+ ensure
76
+ Process.kill('TERM', process.pid)
77
+ status = process.wait
78
+ end
79
+
80
+ resp.should == []
81
+
82
+ # 143 = 128 + 15
83
+ [0, 15, 143].should include(status)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'new application - rails' do
91
+ ['~> 5.1.0', '~> 5.2.0', '~> 6.0.0'].each do |rails_version|
92
+ context "with rails #{rails_version}" do
93
+ it 'creates' do
94
+ ChildProcessHelper.check_call(%w(gem uni rails -a))
95
+ ChildProcessHelper.check_call(%w(gem install rails --no-document -v) + [rails_version])
96
+
97
+ Dir.chdir(TMP_BASE) do
98
+ FileUtils.rm_rf('mongoid-test')
99
+ ChildProcessHelper.check_call(%w(rails new mongoid-test --skip-spring), env: clean_env)
100
+
101
+ Dir.chdir('mongoid-test') do
102
+ adjust_app_gemfile
103
+ ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
104
+
105
+ ChildProcessHelper.check_call(%w(rails g model post), env: clean_env)
106
+ ChildProcessHelper.check_call(%w(rails g model comment post:belongs_to), env: clean_env)
107
+
108
+ # https://jira.mongodb.org/browse/MONGOID-4885
109
+ comment_text = File.read('app/models/comment.rb')
110
+ comment_text.should =~ /belongs_to :post/
111
+ comment_text.should_not =~ /embedded_in :post/
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def clone_application(repo_url, subdir: nil, rails_version: nil)
120
+ Dir.chdir(TMP_BASE) do
121
+ FileUtils.rm_rf(File.basename(repo_url))
122
+ ChildProcessHelper.check_call(%w(git clone) + [repo_url])
123
+ Dir.chdir(File.join(*[File.basename(repo_url), subdir].compact)) do
124
+ adjust_app_gemfile(rails_version: rails_version)
125
+ ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
126
+ puts `git diff`
127
+
128
+ config = {'development' => {'clients' => {'default' => {'uri' => SpecConfig.instance.uri_str}}}}
129
+ File.open('config/mongoid.yml', 'w') do |f|
130
+ f << YAML.dump(config)
131
+ end
132
+
133
+ yield
134
+ end
135
+ end
136
+ end
137
+
138
+ def adjust_app_gemfile(rails_version: nil)
139
+ lock_lines = IO.readlines('Gemfile.lock')
140
+ # Get rid of the bundled with line so that whatever bundler is installed
141
+ # on the system is usable with the application.
142
+ if i = lock_lines.index("BUNDLED WITH\n")
143
+ lock_lines.slice!(i, 2)
144
+ File.open('Gemfile.lock', 'w') do |f|
145
+ f << lock_lines.join
146
+ end
147
+ end
148
+
149
+ gemfile_lines = IO.readlines('Gemfile')
150
+ gemfile_lines.delete_if do |line|
151
+ line =~ /mongoid/
152
+ end
153
+ gemfile_lines << "gem 'mongoid', path: '#{File.expand_path(BASE)}'\n"
154
+ if rails_version
155
+ gemfile_lines.delete_if do |line|
156
+ line =~ /rails/
157
+ end
158
+ gemfile_lines << "gem 'rails', '#{rails_version}'\n"
159
+ end
160
+ File.open('Gemfile', 'w') do |f|
161
+ f << gemfile_lines.join
162
+ end
163
+ end
164
+
165
+ def remove_spring
166
+ # Spring produces this error in Evergreen:
167
+ # /data/mci/280eb2ecf4fd69208e2106cd3af526f1/src/rubies/ruby-2.7.0/lib/ruby/gems/2.7.0/gems/spring-2.1.0/lib/spring/client/run.rb:26:
168
+ # in `initialize': too long unix socket path (126bytes given but 108bytes max) (ArgumentError)
169
+ # Is it trying to create unix sockets in current directory?
170
+ # https://stackoverflow.com/questions/30302021/rails-runner-without-spring
171
+ ChildProcessHelper.check_call(%w(bin/spring binstub --remove --all), env: clean_env)
172
+ end
173
+
174
+ def clean_env
175
+ @clean_env ||= Hash[ENV.keys.grep(/BUNDLE|RUBYOPT/).map { |k| [k, nil ] }]
176
+ end
177
+
178
+ def wait_for_port(port, timeout)
179
+ deadline = Time.now + timeout
180
+ loop do
181
+ begin
182
+ Socket.tcp('localhost', port, nil, nil, connect_timeout: 0.5) do |socket|
183
+ return
184
+ end
185
+ rescue IOError, SystemCallError
186
+ if Time.now > deadline
187
+ raise
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+ require_relative '../../mongoid/association/embedded/embeds_many_models'
6
+ require_relative '../../mongoid/association/embedded/embeds_one_models'
7
+
8
+ describe 'embedded associations' do
9
+
10
+ describe 'parent association' do
11
+ let(:parent) do
12
+ parent_cls.new
13
+ end
14
+
15
+ context 'embeds_one' do
16
+
17
+ shared_examples 'is set' do
18
+ it 'is set' do
19
+ parent.child = child_cls.new
20
+ parent.child.parent.should == parent
21
+ end
22
+ end
23
+
24
+ context 'class_name set without leading ::' do
25
+ let(:parent_cls) { EomParent }
26
+ let(:child_cls) { EomChild }
27
+
28
+ it_behaves_like 'is set'
29
+ end
30
+
31
+ context 'class_name set with leading ::' do
32
+ let(:parent_cls) { EomCcParent }
33
+ let(:child_cls) { EomCcChild }
34
+
35
+ it_behaves_like 'is set'
36
+ end
37
+ end
38
+
39
+ context 'embeds_many' do
40
+
41
+ let(:child) { parent.legislators.new }
42
+
43
+ shared_examples 'is set' do
44
+ it 'is set' do
45
+ child.congress.should == parent
46
+ end
47
+ end
48
+
49
+ context 'class_name set without leading ::' do
50
+ let(:parent_cls) { EmmCongress }
51
+
52
+ it_behaves_like 'is set'
53
+ end
54
+
55
+ context 'class_name set with leading ::' do
56
+ let(:parent_cls) { EmmCcCongress }
57
+
58
+ it_behaves_like 'is set'
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,49 @@
1
+ class Galaxy
2
+ include Mongoid::Document
3
+
4
+ field :age, type: Integer
5
+
6
+ before_validation :set_age
7
+
8
+ embeds_many :stars
9
+
10
+ private
11
+
12
+ def set_age
13
+ self.age ||= 100_000
14
+ end
15
+ end
16
+
17
+ class Star
18
+ include Mongoid::Document
19
+
20
+ embedded_in :galaxy
21
+
22
+ field :age, type: Integer
23
+
24
+ before_validation :set_age
25
+
26
+ embeds_many :planets
27
+
28
+ private
29
+
30
+ def set_age
31
+ self.age ||= 42_000
32
+ end
33
+ end
34
+
35
+ class Planet
36
+ include Mongoid::Document
37
+
38
+ embedded_in :star
39
+
40
+ field :age, type: Integer
41
+
42
+ before_validation :set_age
43
+
44
+ private
45
+
46
+ def set_age
47
+ self.age ||= 2_000
48
+ end
49
+ end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+ require_relative './callbacks_models'
6
+
7
+ describe 'callbacks integration tests' do
8
+ context 'when modifying attributes in a callback' do
9
+
10
+ context 'when creating top-level document' do
11
+ context 'top level document' do
12
+ let(:instance) do
13
+ Galaxy.create!
14
+ end
15
+
16
+ it 'writes the attribute value into the model' do
17
+ instance.age.should == 100_000
18
+ end
19
+
20
+ it 'persists the attribute value' do
21
+ Galaxy.find(instance.id).age.should == 100_000
22
+ end
23
+ end
24
+
25
+ context 'embedded document' do
26
+ shared_examples 'persists the attribute value' do
27
+ it 'writes the attribute value into the model' do
28
+ instance.stars.first.age.should == 42_000
29
+ end
30
+
31
+ it 'persists the attribute value' do
32
+ Galaxy.find(instance.id).stars.first.age.should == 42_000
33
+ end
34
+ end
35
+
36
+ context 'set as a document instance' do
37
+ let(:instance) do
38
+ Galaxy.create!(stars: [Star.new])
39
+ end
40
+
41
+ include_examples 'persists the attribute value'
42
+ end
43
+
44
+ context 'set as attributes on parent' do
45
+ let(:instance) do
46
+ Galaxy.create!(stars: [{}])
47
+ end
48
+
49
+ include_examples 'persists the attribute value'
50
+ end
51
+ end
52
+
53
+ context 'nested embedded document' do
54
+ shared_examples 'persists the attribute value' do
55
+ it 'writes the attribute value into the model' do
56
+ instance.stars.first.planets.first.age.should == 2_000
57
+ end
58
+
59
+ it 'persists the attribute value' do
60
+ Galaxy.find(instance.id).stars.first.planets.first.age.should == 2_000
61
+ end
62
+ end
63
+
64
+ context 'set as a document instance' do
65
+ let(:instance) do
66
+ Galaxy.create!(stars: [Star.new(
67
+ planets: [Planet.new],
68
+ )])
69
+ end
70
+
71
+ include_examples 'persists the attribute value'
72
+ end
73
+
74
+ context 'set as attributes on parent' do
75
+ let(:instance) do
76
+ Galaxy.create!(stars: [
77
+ planets: [{}],
78
+ ])
79
+ end
80
+
81
+ include_examples 'persists the attribute value'
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'when updating top-level document via #save' do
87
+ let!(:instance) do
88
+ Galaxy.create!
89
+ end
90
+
91
+ context 'embedded document' do
92
+ shared_examples 'persists the attribute value' do
93
+ it 'writes the attribute value into the model' do
94
+ instance.stars.first.age.should == 42_000
95
+ end
96
+
97
+ it 'persists the attribute value' do
98
+ Galaxy.find(instance.id).stars.first.age.should == 42_000
99
+ end
100
+ end
101
+
102
+ context 'set as a document instance' do
103
+ before do
104
+ instance.stars = [Star.new]
105
+ instance.save!
106
+ end
107
+
108
+ include_examples 'persists the attribute value'
109
+ end
110
+
111
+ context 'set as attributes on parent' do
112
+ before do
113
+ instance.stars = [{}]
114
+ instance.save!
115
+ end
116
+
117
+ include_examples 'persists the attribute value'
118
+ end
119
+ end
120
+
121
+ context 'nested embedded document' do
122
+ shared_examples 'persists the attribute value' do
123
+ it 'writes the attribute value into the model' do
124
+ instance.stars.first.planets.first.age.should == 2_000
125
+ end
126
+
127
+ it 'persists the attribute value' do
128
+ Galaxy.find(instance.id).stars.first.planets.first.age.should == 2_000
129
+ end
130
+ end
131
+
132
+ context 'set as a document instance' do
133
+ before do
134
+ instance.stars = [Star.new(planets: [Planet.new])]
135
+ instance.save!
136
+ end
137
+
138
+ include_examples 'persists the attribute value'
139
+ end
140
+
141
+ context 'set as attributes on parent' do
142
+ before do
143
+ instance.stars = [planets: [{}]]
144
+ instance.save!
145
+ end
146
+
147
+ include_examples 'persists the attribute value'
148
+ end
149
+ end
150
+ end
151
+
152
+ context 'when updating top-level document via #update_attributes' do
153
+ let!(:instance) do
154
+ Galaxy.create!
155
+ end
156
+
157
+ context 'embedded document' do
158
+ shared_examples 'persists the attribute value' do
159
+ it 'writes the attribute value into the model' do
160
+ instance.stars.first.age.should == 42_000
161
+ end
162
+
163
+ it 'persists the attribute value' do
164
+ Galaxy.find(instance.id).stars.first.age.should == 42_000
165
+ end
166
+ end
167
+
168
+ context 'set as a document instance' do
169
+ before do
170
+ instance.update_attributes(stars: [Star.new])
171
+ end
172
+
173
+ include_examples 'persists the attribute value'
174
+ end
175
+
176
+ context 'set as attributes on parent' do
177
+ before do
178
+ instance.update_attributes(stars: [{}])
179
+ end
180
+
181
+ include_examples 'persists the attribute value'
182
+ end
183
+ end
184
+
185
+ context 'nested embedded document' do
186
+ shared_examples 'persists the attribute value' do
187
+ it 'writes the attribute value into the model' do
188
+ instance.stars.first.planets.first.age.should == 2_000
189
+ end
190
+
191
+ it 'persists the attribute value' do
192
+ pending 'MONGOID-4476'
193
+
194
+ Galaxy.find(instance.id).stars.first.planets.first.age.should == 2_000
195
+ end
196
+ end
197
+
198
+ context 'set as a document instance' do
199
+ before do
200
+ instance.update_attributes(stars: [Star.new(planets: [Planet.new])])
201
+ end
202
+
203
+ include_examples 'persists the attribute value'
204
+ end
205
+
206
+ context 'set as attributes on parent' do
207
+ before do
208
+ instance.update_attributes(stars: [planets: [{}]])
209
+ end
210
+
211
+ include_examples 'persists the attribute value'
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end