mongoid 7.0.4 → 7.0.10
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -0
- data/README.md +3 -2
- data/Rakefile +26 -0
- data/lib/mongoid.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
- data/lib/mongoid/association/proxy.rb +1 -1
- data/lib/mongoid/atomic.rb +13 -3
- data/lib/mongoid/atomic/paths/embedded.rb +1 -1
- data/lib/mongoid/attributes.rb +28 -20
- data/lib/mongoid/attributes/dynamic.rb +15 -14
- data/lib/mongoid/clients/sessions.rb +20 -4
- data/lib/mongoid/config/environment.rb +21 -8
- data/lib/mongoid/criteria.rb +7 -1
- data/lib/mongoid/criteria/modifiable.rb +13 -2
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
- data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
- data/lib/mongoid/criteria/queryable/extensions/time.rb +1 -1
- data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
- data/lib/mongoid/criteria/queryable/key.rb +67 -8
- data/lib/mongoid/criteria/queryable/mergeable.rb +5 -4
- data/lib/mongoid/criteria/queryable/selectable.rb +3 -4
- data/lib/mongoid/criteria/queryable/selector.rb +9 -31
- data/lib/mongoid/extensions/hash.rb +4 -2
- data/lib/mongoid/extensions/regexp.rb +1 -1
- data/lib/mongoid/extensions/string.rb +2 -2
- data/lib/mongoid/fields.rb +2 -1
- data/lib/mongoid/matchable.rb +14 -15
- data/lib/mongoid/matchable/all.rb +4 -3
- data/lib/mongoid/matchable/default.rb +71 -24
- data/lib/mongoid/matchable/regexp.rb +2 -2
- data/lib/mongoid/persistable/pushable.rb +11 -2
- data/lib/mongoid/persistence_context.rb +6 -6
- data/lib/mongoid/positional.rb +1 -1
- data/lib/mongoid/query_cache.rb +24 -11
- data/lib/mongoid/validatable/macros.rb +1 -1
- data/lib/mongoid/validatable/uniqueness.rb +1 -1
- data/lib/mongoid/version.rb +2 -1
- data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
- data/spec/README.md +18 -0
- data/spec/app/models/delegating_patient.rb +16 -0
- data/spec/integration/app_spec.rb +192 -0
- data/spec/integration/associations/embedded_spec.rb +62 -0
- data/spec/integration/criteria/date_field_spec.rb +41 -0
- data/spec/integration/criteria/time_with_zone_spec.rb +32 -0
- data/spec/integration/document_spec.rb +22 -0
- data/spec/integration/matchable_spec.rb +680 -0
- data/spec/lite_spec_helper.rb +15 -5
- data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
- data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
- data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +105 -0
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
- data/spec/mongoid/attributes/dynamic_spec.rb +153 -0
- data/spec/mongoid/attributes_spec.rb +19 -7
- data/spec/mongoid/clients/factory_spec.rb +2 -2
- data/spec/mongoid/clients/options_spec.rb +4 -4
- data/spec/mongoid/clients/sessions_spec.rb +20 -7
- data/spec/mongoid/clients/transactions_spec.rb +36 -15
- data/spec/mongoid/clients_spec.rb +2 -2
- data/spec/mongoid/contextual/atomic_spec.rb +20 -10
- data/spec/mongoid/contextual/geo_near_spec.rb +12 -2
- data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
- data/spec/mongoid/contextual/mongo_spec.rb +76 -53
- data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
- data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +54 -0
- data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
- data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
- data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
- data/spec/mongoid/criteria/queryable/key_spec.rb +48 -6
- data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +762 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +5 -224
- data/spec/mongoid/criteria/queryable/selector_spec.rb +37 -0
- data/spec/mongoid/criteria_spec.rb +7 -2
- data/spec/mongoid/document_fields_spec.rb +88 -0
- data/spec/mongoid/document_persistence_context_spec.rb +33 -0
- data/spec/mongoid/indexable_spec.rb +6 -4
- data/spec/mongoid/matchable/default_spec.rb +10 -3
- data/spec/mongoid/matchable/regexp_spec.rb +2 -2
- data/spec/mongoid/matchable_spec.rb +2 -2
- data/spec/mongoid/persistable/pushable_spec.rb +55 -1
- data/spec/mongoid/query_cache_spec.rb +62 -8
- data/spec/mongoid/relations/proxy_spec.rb +1 -1
- data/spec/mongoid/scopable_spec.rb +2 -1
- data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
- data/spec/mongoid/tasks/database_spec.rb +1 -1
- data/spec/mongoid/validatable/uniqueness_spec.rb +33 -6
- data/spec/spec_helper.rb +4 -37
- data/spec/support/child_process_helper.rb +76 -0
- data/spec/support/cluster_config.rb +158 -0
- data/spec/support/constraints.rb +201 -30
- data/spec/support/expectations.rb +17 -3
- data/spec/support/spec_config.rb +12 -4
- metadata +490 -454
- metadata.gz.sig +0 -0
data/spec/lite_spec_helper.rb
CHANGED
|
@@ -4,7 +4,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
|
4
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
|
5
5
|
|
|
6
6
|
require "mongoid"
|
|
7
|
-
require "rspec"
|
|
8
7
|
|
|
9
8
|
# MRI 2.5 and JRuby 9.2 change visibility of Object#pp when 'pp' is required,
|
|
10
9
|
# which happens when RSpec reports anything. This creates an issue for tests
|
|
@@ -36,16 +35,27 @@ else
|
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
RSpec.configure do |config|
|
|
38
|
+
config.expect_with(:rspec) do |c|
|
|
39
|
+
c.syntax = [:should, :expect]
|
|
40
|
+
end
|
|
41
|
+
|
|
39
42
|
if SpecConfig.instance.ci?
|
|
40
43
|
config.add_formatter(RSpec::Core::Formatters::JsonFormatter, File.join(File.dirname(__FILE__), '../tmp/rspec.json'))
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
if SpecConfig.instance.ci?
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
timeout = if SpecConfig.instance.app_tests?
|
|
48
|
+
# Allow 5 minutes per test for the app tests, since they install
|
|
49
|
+
# gems for Rails applications which can take a long time.
|
|
50
|
+
300
|
|
51
|
+
else
|
|
52
|
+
# Allow a max of 30 seconds per test.
|
|
53
|
+
# Tests should take under 10 seconds ideally but it seems
|
|
54
|
+
# we have some that run for more than 10 seconds in CI.
|
|
55
|
+
30
|
|
56
|
+
end
|
|
47
57
|
config.around(:each) do |example|
|
|
48
|
-
TimeoutInterrupt.timeout(
|
|
58
|
+
TimeoutInterrupt.timeout(timeout) do
|
|
49
59
|
example.run
|
|
50
60
|
end
|
|
51
61
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
|
|
4
|
+
class EmmCongress
|
|
5
|
+
include Mongoid::Document
|
|
6
|
+
|
|
7
|
+
embeds_many :legislators, class_name: 'EmmLegislator'
|
|
8
|
+
|
|
9
|
+
field :name, type: String
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class EmmLegislator
|
|
13
|
+
include Mongoid::Document
|
|
14
|
+
|
|
15
|
+
embedded_in :congress, class_name: 'EmmCongress'
|
|
16
|
+
|
|
17
|
+
field :a, type: Integer, default: 0
|
|
18
|
+
field :b, type: Integer, default: 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Models with associations with :class_name as a :: prefixed string
|
|
22
|
+
|
|
23
|
+
class EmmCcCongress
|
|
24
|
+
include Mongoid::Document
|
|
25
|
+
|
|
26
|
+
embeds_many :legislators, class_name: '::EmmCcLegislator'
|
|
27
|
+
|
|
28
|
+
field :name, type: String
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class EmmCcLegislator
|
|
32
|
+
include Mongoid::Document
|
|
33
|
+
|
|
34
|
+
embedded_in :congress, class_name: '::EmmCcCongress'
|
|
35
|
+
|
|
36
|
+
field :a, type: Integer, default: 0
|
|
37
|
+
field :b, type: Integer, default: 0
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class EmmManufactory
|
|
41
|
+
include Mongoid::Document
|
|
42
|
+
|
|
43
|
+
embeds_many :products, order: :id.desc, class_name: 'EmmProduct'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class EmmProduct
|
|
48
|
+
include Mongoid::Document
|
|
49
|
+
|
|
50
|
+
embedded_in :manufactory, class_name: 'EmmManufactory'
|
|
51
|
+
|
|
52
|
+
field :name, type: String
|
|
53
|
+
end
|
|
@@ -646,6 +646,16 @@ describe Mongoid::Association::Embedded::EmbedsMany do
|
|
|
646
646
|
it 'returns the class name option' do
|
|
647
647
|
expect(association.klass).to eq(_class)
|
|
648
648
|
end
|
|
649
|
+
|
|
650
|
+
context 'when the class name is prefixed with ::' do
|
|
651
|
+
let(:options) do
|
|
652
|
+
{ class_name: '::OtherEmbeddedObject' }
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
it 'returns the class name option' do
|
|
656
|
+
expect(association.klass).to eq(_class)
|
|
657
|
+
end
|
|
658
|
+
end
|
|
649
659
|
end
|
|
650
660
|
|
|
651
661
|
context 'when the class_name option is not specified' do
|
|
@@ -1754,6 +1754,43 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
|
1754
1754
|
end
|
|
1755
1755
|
end
|
|
1756
1756
|
|
|
1757
|
+
describe "#any?" do
|
|
1758
|
+
|
|
1759
|
+
let(:person) do
|
|
1760
|
+
Person.create
|
|
1761
|
+
end
|
|
1762
|
+
|
|
1763
|
+
context "when nothing exists on the relation" do
|
|
1764
|
+
|
|
1765
|
+
context "when no document is added" do
|
|
1766
|
+
|
|
1767
|
+
let!(:sandwich) do
|
|
1768
|
+
Sandwich.create!
|
|
1769
|
+
end
|
|
1770
|
+
|
|
1771
|
+
it "returns false" do
|
|
1772
|
+
expect(sandwich.meats.any?).to be false
|
|
1773
|
+
end
|
|
1774
|
+
end
|
|
1775
|
+
|
|
1776
|
+
context "when the document is destroyed" do
|
|
1777
|
+
|
|
1778
|
+
before do
|
|
1779
|
+
Meat.create!
|
|
1780
|
+
end
|
|
1781
|
+
|
|
1782
|
+
let!(:sandwich) do
|
|
1783
|
+
Sandwich.create!
|
|
1784
|
+
end
|
|
1785
|
+
|
|
1786
|
+
it "returns false" do
|
|
1787
|
+
sandwich.destroy
|
|
1788
|
+
expect(sandwich.meats.any?).to be false
|
|
1789
|
+
end
|
|
1790
|
+
end
|
|
1791
|
+
end
|
|
1792
|
+
end
|
|
1793
|
+
|
|
1757
1794
|
context "when documents have been persisted" do
|
|
1758
1795
|
|
|
1759
1796
|
let!(:preference) do
|
|
@@ -1843,6 +1880,107 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
|
1843
1880
|
end
|
|
1844
1881
|
end
|
|
1845
1882
|
|
|
1883
|
+
describe "#any?" do
|
|
1884
|
+
|
|
1885
|
+
let(:sandwich) do
|
|
1886
|
+
Sandwich.create
|
|
1887
|
+
end
|
|
1888
|
+
|
|
1889
|
+
context "when nothing exists on the relation" do
|
|
1890
|
+
|
|
1891
|
+
context "when no document is added" do
|
|
1892
|
+
|
|
1893
|
+
let!(:sandwich) do
|
|
1894
|
+
Sandwich.create!
|
|
1895
|
+
end
|
|
1896
|
+
|
|
1897
|
+
it "returns false" do
|
|
1898
|
+
expect(sandwich.meats.any?).to be false
|
|
1899
|
+
end
|
|
1900
|
+
end
|
|
1901
|
+
|
|
1902
|
+
context "when the document is destroyed" do
|
|
1903
|
+
|
|
1904
|
+
before do
|
|
1905
|
+
Meat.create!
|
|
1906
|
+
end
|
|
1907
|
+
|
|
1908
|
+
let!(:sandwich) do
|
|
1909
|
+
Sandwich.create!
|
|
1910
|
+
end
|
|
1911
|
+
|
|
1912
|
+
it "returns false" do
|
|
1913
|
+
sandwich.destroy
|
|
1914
|
+
expect(sandwich.meats.any?).to be false
|
|
1915
|
+
end
|
|
1916
|
+
end
|
|
1917
|
+
end
|
|
1918
|
+
|
|
1919
|
+
context "when appending to a relation and _loaded/_unloaded are empty" do
|
|
1920
|
+
|
|
1921
|
+
let!(:sandwich) do
|
|
1922
|
+
Sandwich.create!
|
|
1923
|
+
end
|
|
1924
|
+
|
|
1925
|
+
before do
|
|
1926
|
+
sandwich.meats << Meat.new
|
|
1927
|
+
end
|
|
1928
|
+
|
|
1929
|
+
it "returns true" do
|
|
1930
|
+
expect(sandwich.meats.any?).to be true
|
|
1931
|
+
end
|
|
1932
|
+
end
|
|
1933
|
+
|
|
1934
|
+
context "when appending to a relation in a transaction" do
|
|
1935
|
+
require_transaction_support
|
|
1936
|
+
|
|
1937
|
+
let!(:sandwich) do
|
|
1938
|
+
Sandwich.create!
|
|
1939
|
+
end
|
|
1940
|
+
|
|
1941
|
+
it "returns true" do
|
|
1942
|
+
sandwich.with_session do |session|
|
|
1943
|
+
session.with_transaction do
|
|
1944
|
+
expect{ sandwich.meats << Meat.new }.to_not raise_error
|
|
1945
|
+
expect(sandwich.meats.any?).to be true
|
|
1946
|
+
end
|
|
1947
|
+
end
|
|
1948
|
+
end
|
|
1949
|
+
end
|
|
1950
|
+
|
|
1951
|
+
context "when documents have been persisted" do
|
|
1952
|
+
|
|
1953
|
+
let!(:meat) do
|
|
1954
|
+
sandwich.meats.create
|
|
1955
|
+
end
|
|
1956
|
+
|
|
1957
|
+
it "returns true" do
|
|
1958
|
+
expect(sandwich.meats.any?).to be true
|
|
1959
|
+
end
|
|
1960
|
+
end
|
|
1961
|
+
|
|
1962
|
+
context "when documents have not been persisted" do
|
|
1963
|
+
|
|
1964
|
+
let!(:meat) do
|
|
1965
|
+
sandwich.meats.build
|
|
1966
|
+
end
|
|
1967
|
+
|
|
1968
|
+
it "returns false" do
|
|
1969
|
+
expect(sandwich.meats.any?).to be true
|
|
1970
|
+
end
|
|
1971
|
+
end
|
|
1972
|
+
|
|
1973
|
+
context "when new documents exist in the database" do
|
|
1974
|
+
before do
|
|
1975
|
+
Meat.create(sandwiches: [sandwich])
|
|
1976
|
+
end
|
|
1977
|
+
|
|
1978
|
+
it "returns true" do
|
|
1979
|
+
expect(sandwich.meats.any?).to be true
|
|
1980
|
+
end
|
|
1981
|
+
end
|
|
1982
|
+
end
|
|
1983
|
+
|
|
1846
1984
|
[ :create, :create! ].each do |method|
|
|
1847
1985
|
|
|
1848
1986
|
describe "##{method}" do
|
|
@@ -2686,7 +2824,8 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
|
2686
2824
|
expect(preferences).to eq([ preference_one ])
|
|
2687
2825
|
end
|
|
2688
2826
|
|
|
2689
|
-
context 'when providing a collation'
|
|
2827
|
+
context 'when providing a collation' do
|
|
2828
|
+
min_server_version '3.4'
|
|
2690
2829
|
|
|
2691
2830
|
let(:preferences) do
|
|
2692
2831
|
person.preferences.where(name: "FIRST").collation(locale: 'en_US', strength: 2).to_a
|
|
@@ -253,6 +253,111 @@ describe Mongoid::Association::Referenced::HasMany::Targets::Enumerable do
|
|
|
253
253
|
end
|
|
254
254
|
end
|
|
255
255
|
end
|
|
256
|
+
|
|
257
|
+
context "when the documents have been loaded" do
|
|
258
|
+
let(:criteria) do
|
|
259
|
+
Post.where(person_id: person.id)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
let!(:enumerable) do
|
|
263
|
+
described_class.new(criteria)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
before do
|
|
267
|
+
enumerable.load_all!
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "is _loaded" do
|
|
271
|
+
expect(enumerable._loaded?).to be true
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
context "when a block is given" do
|
|
275
|
+
it "returns true when the predicate is true" do
|
|
276
|
+
expect(
|
|
277
|
+
enumerable.any? { |doc| true }
|
|
278
|
+
).to be true
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "returns false when the predicate is false" do
|
|
282
|
+
expect(
|
|
283
|
+
enumerable.any? { |doc| false }
|
|
284
|
+
).to be false
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
context "when an argument is given" do
|
|
289
|
+
ruby_version_gte '2.5'
|
|
290
|
+
|
|
291
|
+
it "returns true when the argument is true" do
|
|
292
|
+
expect(enumerable.any?(Post)).to be true
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "returns false when the argument is false" do
|
|
296
|
+
expect(enumerable.any?(Sandwich)).to be false
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
context "when both an argument and a block are given" do
|
|
301
|
+
ruby_version_gte '2.5'
|
|
302
|
+
|
|
303
|
+
it "gives precedence to the pattern" do
|
|
304
|
+
expect(
|
|
305
|
+
enumerable.any?(Post) { |doc| false }
|
|
306
|
+
).to be true
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
context "when the documents are not loaded" do
|
|
312
|
+
|
|
313
|
+
let(:criteria) do
|
|
314
|
+
Post.where(person_id: person.id)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
let!(:enumerable) do
|
|
318
|
+
described_class.new(criteria)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it "is not _loaded" do
|
|
322
|
+
expect(enumerable._loaded?).to be false
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
context "when a block is given" do
|
|
326
|
+
it "returns true when the predicate is true" do
|
|
327
|
+
expect(
|
|
328
|
+
enumerable.any? { |doc| true }
|
|
329
|
+
).to be true
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it "returns false when the predicate is false" do
|
|
333
|
+
expect(
|
|
334
|
+
enumerable.any? { |doc| false }
|
|
335
|
+
).to be false
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
context "when an argument is given" do
|
|
340
|
+
ruby_version_gte '2.5'
|
|
341
|
+
|
|
342
|
+
it "returns true when the argument is true" do
|
|
343
|
+
expect(enumerable.any?(Post)).to be true
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "returns false when the argument is false" do
|
|
347
|
+
expect(enumerable.any?(Sandwich)).to be false
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
context "when both an argument and a block are given" do
|
|
352
|
+
ruby_version_gte '2.5'
|
|
353
|
+
|
|
354
|
+
it "gives precedence to the pattern" do
|
|
355
|
+
expect(
|
|
356
|
+
enumerable.any?(Post) { |doc| false }
|
|
357
|
+
).to be true
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
256
361
|
end
|
|
257
362
|
|
|
258
363
|
describe "#clear" do
|
|
@@ -2904,7 +2904,8 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
|
|
|
2904
2904
|
expect(posts).to eq([ post_one ])
|
|
2905
2905
|
end
|
|
2906
2906
|
|
|
2907
|
-
context 'when providing a collation'
|
|
2907
|
+
context 'when providing a collation' do
|
|
2908
|
+
min_server_version '3.4'
|
|
2908
2909
|
|
|
2909
2910
|
let(:posts) do
|
|
2910
2911
|
person.posts.where(title: "FIRST").collation(locale: 'en_US', strength: 2)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe Mongoid::Attributes::Dynamic do
|
|
7
|
+
shared_examples_for 'dynamic field' do
|
|
8
|
+
let(:raw_attributes) do
|
|
9
|
+
{attr_name => 'foo bar'}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context 'when reading attributes' do
|
|
13
|
+
context 'an unsaved model' do
|
|
14
|
+
it 'can be read' do
|
|
15
|
+
bar = Bar.new(raw_attributes)
|
|
16
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'saved model' do
|
|
21
|
+
it 'can be read' do
|
|
22
|
+
bar = Bar.new(raw_attributes)
|
|
23
|
+
bar.save!
|
|
24
|
+
|
|
25
|
+
bar = Bar.find(bar.id)
|
|
26
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context 'when attribute is not set' do
|
|
31
|
+
it 'cannot be read' do
|
|
32
|
+
bar = Bar.new
|
|
33
|
+
expect do
|
|
34
|
+
bar.send(attr_name)
|
|
35
|
+
end.to raise_error(NoMethodError)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'reading via read_attribute' do
|
|
39
|
+
it 'returns nil' do
|
|
40
|
+
bar = Bar.new
|
|
41
|
+
expect(bar.read_attribute(:foo)).to be nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'reading via []' do
|
|
46
|
+
it 'returns nil' do
|
|
47
|
+
bar = Bar.new
|
|
48
|
+
expect(bar[:foo]).to be nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'when writing attributes via constructor' do
|
|
55
|
+
it 'can be written' do
|
|
56
|
+
bar = Bar.new(raw_attributes)
|
|
57
|
+
bar.save!
|
|
58
|
+
|
|
59
|
+
bar = Bar.find(bar.id)
|
|
60
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'when writing attributes via attributes=' do
|
|
65
|
+
it 'can be written' do
|
|
66
|
+
bar = Bar.new
|
|
67
|
+
bar.attributes = raw_attributes
|
|
68
|
+
bar.save!
|
|
69
|
+
|
|
70
|
+
bar = Bar.find(bar.id)
|
|
71
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context 'when writing attributes via write_attribute' do
|
|
76
|
+
it 'can be written' do
|
|
77
|
+
bar = Bar.new
|
|
78
|
+
bar.write_attribute(attr_name, 'foo bar')
|
|
79
|
+
bar.save!
|
|
80
|
+
|
|
81
|
+
bar = Bar.find(bar.id)
|
|
82
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'when writing attributes via []=' do
|
|
87
|
+
context 'string key' do
|
|
88
|
+
it 'can be written' do
|
|
89
|
+
bar = Bar.new
|
|
90
|
+
bar[attr_name.to_s] = 'foo bar'
|
|
91
|
+
bar.save!
|
|
92
|
+
|
|
93
|
+
bar = Bar.find(bar.id)
|
|
94
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'symbol key' do
|
|
99
|
+
it 'can be written' do
|
|
100
|
+
bar = Bar.new
|
|
101
|
+
bar[attr_name.to_sym] = 'foo bar'
|
|
102
|
+
bar.save!
|
|
103
|
+
|
|
104
|
+
bar = Bar.find(bar.id)
|
|
105
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'when writing attributes via #{attribute}=' do
|
|
111
|
+
context 'when attribute is not already set' do
|
|
112
|
+
let(:bar) { Bar.new }
|
|
113
|
+
|
|
114
|
+
it 'cannot be written' do
|
|
115
|
+
expect do
|
|
116
|
+
bar.send("#{attr_name}=", 'foo bar')
|
|
117
|
+
bar.save!
|
|
118
|
+
end.to raise_error(NoMethodError)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context 'when attribute is already set' do
|
|
123
|
+
let(:bar) { Bar.new(attr_name => 'foo bar') }
|
|
124
|
+
|
|
125
|
+
it 'can be written' do
|
|
126
|
+
bar.send("#{attr_name}=", 'new foo bar')
|
|
127
|
+
bar.save!
|
|
128
|
+
|
|
129
|
+
_bar = Bar.find(bar.id)
|
|
130
|
+
expect(_bar.send(attr_name)).to eq('new foo bar')
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context 'when attribute name is alphanumeric' do
|
|
137
|
+
let(:attr_name) { 'foo' }
|
|
138
|
+
|
|
139
|
+
it_behaves_like 'dynamic field'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context 'when attribute name contains spaces' do
|
|
143
|
+
let(:attr_name) { 'hello world' }
|
|
144
|
+
|
|
145
|
+
it_behaves_like 'dynamic field'
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
context 'when attribute name contains special characters' do
|
|
149
|
+
let(:attr_name) { 'hello%world' }
|
|
150
|
+
|
|
151
|
+
it_behaves_like 'dynamic field'
|
|
152
|
+
end
|
|
153
|
+
end
|