couchbase-orm 1.1.1 → 2.0.2
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/.github/workflows/test.yml +45 -0
- data/.gitignore +2 -0
- data/.travis.yml +3 -2
- data/CODEOWNERS +1 -0
- data/Gemfile +5 -3
- data/LICENSE +201 -24
- data/README.md +248 -35
- data/ci/run_couchbase.sh +22 -0
- data/couchbase-orm.gemspec +26 -20
- data/lib/couchbase-orm/active_record_compat.rb +92 -0
- data/lib/couchbase-orm/associations.rb +119 -0
- data/lib/couchbase-orm/base.rb +143 -166
- data/lib/couchbase-orm/changeable.rb +512 -0
- data/lib/couchbase-orm/connection.rb +28 -8
- data/lib/couchbase-orm/encrypt.rb +48 -0
- data/lib/couchbase-orm/error.rb +17 -2
- data/lib/couchbase-orm/inspectable.rb +37 -0
- data/lib/couchbase-orm/json_schema/json_validation_error.rb +13 -0
- data/lib/couchbase-orm/json_schema/loader.rb +47 -0
- data/lib/couchbase-orm/json_schema/validation.rb +18 -0
- data/lib/couchbase-orm/json_schema/validator.rb +45 -0
- data/lib/couchbase-orm/json_schema.rb +9 -0
- data/lib/couchbase-orm/json_transcoder.rb +27 -0
- data/lib/couchbase-orm/locale/en.yml +5 -0
- data/lib/couchbase-orm/n1ql.rb +133 -0
- data/lib/couchbase-orm/persistence.rb +61 -52
- data/lib/couchbase-orm/proxies/bucket_proxy.rb +36 -0
- data/lib/couchbase-orm/proxies/collection_proxy.rb +52 -0
- data/lib/couchbase-orm/proxies/n1ql_proxy.rb +40 -0
- data/lib/couchbase-orm/proxies/results_proxy.rb +23 -0
- data/lib/couchbase-orm/railtie.rb +6 -17
- data/lib/couchbase-orm/relation.rb +249 -0
- data/lib/couchbase-orm/strict_loading.rb +21 -0
- data/lib/couchbase-orm/timestamps/created.rb +20 -0
- data/lib/couchbase-orm/timestamps/updated.rb +21 -0
- data/lib/couchbase-orm/timestamps.rb +15 -0
- data/lib/couchbase-orm/types/array.rb +32 -0
- data/lib/couchbase-orm/types/date.rb +9 -0
- data/lib/couchbase-orm/types/date_time.rb +14 -0
- data/lib/couchbase-orm/types/encrypted.rb +17 -0
- data/lib/couchbase-orm/types/nested.rb +43 -0
- data/lib/couchbase-orm/types/timestamp.rb +18 -0
- data/lib/couchbase-orm/types.rb +20 -0
- data/lib/couchbase-orm/utilities/enum.rb +13 -1
- data/lib/couchbase-orm/utilities/has_many.rb +72 -36
- data/lib/couchbase-orm/utilities/ignored_properties.rb +15 -0
- data/lib/couchbase-orm/utilities/index.rb +18 -20
- data/lib/couchbase-orm/utilities/properties_always_exists_in_document.rb +16 -0
- data/lib/couchbase-orm/utilities/query_helper.rb +148 -0
- data/lib/couchbase-orm/utils.rb +25 -0
- data/lib/couchbase-orm/version.rb +1 -1
- data/lib/couchbase-orm/views.rb +38 -41
- data/lib/couchbase-orm.rb +44 -9
- data/lib/ext/query_n1ql.rb +124 -0
- data/lib/rails/generators/couchbase_orm/config/templates/couchbase.yml +3 -2
- data/spec/associations_spec.rb +219 -50
- data/spec/base_spec.rb +296 -14
- data/spec/collection_proxy_spec.rb +29 -0
- data/spec/connection_spec.rb +27 -0
- data/spec/couchbase-orm/active_record_compat_spec.rb +24 -0
- data/spec/couchbase-orm/changeable_spec.rb +16 -0
- data/spec/couchbase-orm/json_schema/validation_spec.rb +23 -0
- data/spec/couchbase-orm/json_schema/validator_spec.rb +13 -0
- data/spec/couchbase-orm/timestamps_spec.rb +85 -0
- data/spec/couchbase-orm/timestamps_spec_models.rb +36 -0
- data/spec/empty-json-schema/.gitkeep +0 -0
- data/spec/enum_spec.rb +34 -0
- data/spec/has_many_spec.rb +101 -54
- data/spec/index_spec.rb +13 -9
- data/spec/json-schema/JsonSchemaBaseTest.json +19 -0
- data/spec/json-schema/entity_snakecase.json +20 -0
- data/spec/json-schema/loader_spec.rb +42 -0
- data/spec/json-schema/specific_path.json +20 -0
- data/spec/json_schema_spec.rb +178 -0
- data/spec/n1ql_spec.rb +193 -0
- data/spec/persistence_spec.rb +49 -9
- data/spec/relation_nested_spec.rb +88 -0
- data/spec/relation_spec.rb +430 -0
- data/spec/support.rb +16 -8
- data/spec/type_array_spec.rb +52 -0
- data/spec/type_encrypted_spec.rb +114 -0
- data/spec/type_nested_spec.rb +191 -0
- data/spec/type_spec.rb +317 -0
- data/spec/utilities/ignored_properties_spec.rb +20 -0
- data/spec/utilities/properties_always_exists_in_document_spec.rb +24 -0
- data/spec/views_spec.rb +32 -11
- metadata +193 -30
@@ -0,0 +1,191 @@
|
|
1
|
+
require File.expand_path("../support", __FILE__)
|
2
|
+
|
3
|
+
require "active_model"
|
4
|
+
|
5
|
+
class SubTypeTest < CouchbaseOrm::NestedDocument
|
6
|
+
attribute :name, :string
|
7
|
+
attribute :tags, :array, type: :string
|
8
|
+
attribute :milestones, :array, type: :date
|
9
|
+
attribute :flags, :array, type: :boolean
|
10
|
+
attribute :things
|
11
|
+
attribute :child, :nested, type: SubTypeTest
|
12
|
+
end
|
13
|
+
|
14
|
+
class TypeNestedTest < CouchbaseOrm::Base
|
15
|
+
attribute :main, :nested, type: SubTypeTest
|
16
|
+
attribute :others, :array, type: SubTypeTest
|
17
|
+
attribute :flags, :array, type: :boolean
|
18
|
+
end
|
19
|
+
|
20
|
+
describe CouchbaseOrm::Types::Nested do
|
21
|
+
it "should be able to store and retrieve a nested object" do
|
22
|
+
obj = TypeNestedTest.new
|
23
|
+
obj.main = SubTypeTest.new
|
24
|
+
obj.main.name = "foo"
|
25
|
+
obj.main.tags = ["foo", "bar"]
|
26
|
+
obj.main.child = SubTypeTest.new(name: "bar")
|
27
|
+
obj.save!
|
28
|
+
|
29
|
+
obj = TypeNestedTest.find(obj.id)
|
30
|
+
expect(obj.main.name).to eq "foo"
|
31
|
+
expect(obj.main.tags).to eq ["foo", "bar"]
|
32
|
+
expect(obj.main.child.name).to eq "bar"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be able to store and retrieve an array of nested objects" do
|
36
|
+
obj = TypeNestedTest.new
|
37
|
+
obj.others = [SubTypeTest.new, SubTypeTest.new]
|
38
|
+
obj.others[0].name = "foo"
|
39
|
+
obj.others[0].tags = ["foo", "bar"]
|
40
|
+
obj.others[1].name = "bar"
|
41
|
+
obj.others[1].tags = ["bar", "baz"]
|
42
|
+
obj.others[1].child = SubTypeTest.new(name: "baz")
|
43
|
+
obj.save!
|
44
|
+
|
45
|
+
obj = TypeNestedTest.find(obj.id)
|
46
|
+
expect(obj.others[0].name).to eq "foo"
|
47
|
+
expect(obj.others[0].tags).to eq ["foo", "bar"]
|
48
|
+
expect(obj.others[1].name).to eq "bar"
|
49
|
+
expect(obj.others[1].tags).to eq ["bar", "baz"]
|
50
|
+
expect(obj.others[1].child.name).to eq "baz"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should serialize to JSON" do
|
54
|
+
obj = TypeNestedTest.new
|
55
|
+
obj.others = [SubTypeTest.new, SubTypeTest.new]
|
56
|
+
obj.others[0].name = "foo"
|
57
|
+
obj.others[0].tags = ["foo", "bar"]
|
58
|
+
obj.others[1].name = "bar"
|
59
|
+
obj.others[1].tags = ["bar", "baz"]
|
60
|
+
obj.others[1].child = SubTypeTest.new(name: "baz")
|
61
|
+
obj.save!
|
62
|
+
|
63
|
+
obj = TypeNestedTest.find(obj.id)
|
64
|
+
expect(obj.send(:serialized_attributes)).to eq ({
|
65
|
+
"id" => obj.id,
|
66
|
+
"main" => nil,
|
67
|
+
"flags" => [],
|
68
|
+
"others" => [
|
69
|
+
{
|
70
|
+
"name" => "foo",
|
71
|
+
"tags" => ["foo", "bar"],
|
72
|
+
"milestones" => [],
|
73
|
+
"flags" => [],
|
74
|
+
"things" => nil,
|
75
|
+
"child" => nil
|
76
|
+
},
|
77
|
+
{
|
78
|
+
"name" => "bar",
|
79
|
+
"tags" => ["bar", "baz"],
|
80
|
+
"milestones" => [],
|
81
|
+
"flags" => [],
|
82
|
+
"things" => nil,
|
83
|
+
"child" => {
|
84
|
+
"name" => "baz",
|
85
|
+
"tags" => [],
|
86
|
+
"milestones" => [],
|
87
|
+
"flags" => [],
|
88
|
+
"things" => nil,
|
89
|
+
"child" => nil
|
90
|
+
}
|
91
|
+
}
|
92
|
+
]
|
93
|
+
})
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not have a save method" do
|
97
|
+
expect(SubTypeTest.new).to_not respond_to(:save)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should not cast a list" do
|
101
|
+
expect{CouchbaseOrm::Types::Nested.new(type: SubTypeTest).cast([1,2,3])}.to raise_error(ArgumentError)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should not serialize a list" do
|
105
|
+
expect{CouchbaseOrm::Types::Nested.new(type: SubTypeTest).serialize([1,2,3])}.to raise_error(ArgumentError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should save a object with nested changes" do
|
109
|
+
obj = TypeNestedTest.new
|
110
|
+
obj.main = SubTypeTest.new(name: "foo")
|
111
|
+
obj.others = [SubTypeTest.new(name: "foo"), SubTypeTest.new(name: "bar")]
|
112
|
+
obj.flags = [false, true]
|
113
|
+
obj.save!
|
114
|
+
obj.main.name = "bar"
|
115
|
+
obj.others[0].name = "bar"
|
116
|
+
obj.others[1].name = "baz"
|
117
|
+
obj.flags[0] = true
|
118
|
+
|
119
|
+
obj.save!
|
120
|
+
obj = TypeNestedTest.find(obj.id)
|
121
|
+
expect(obj.main.name).to eq "bar"
|
122
|
+
expect(obj.others[0].name).to eq "bar"
|
123
|
+
expect(obj.others[1].name).to eq "baz"
|
124
|
+
expect(obj.flags).to eq [true, true]
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "Validations" do
|
128
|
+
class SubWithValidation < CouchbaseOrm::NestedDocument
|
129
|
+
attribute :id, :string
|
130
|
+
attribute :name
|
131
|
+
attribute :label
|
132
|
+
attribute :child, :nested, type: SubWithValidation
|
133
|
+
validates :name, presence: true
|
134
|
+
validates :child, nested: true
|
135
|
+
end
|
136
|
+
|
137
|
+
class WithValidationParent < CouchbaseOrm::Base
|
138
|
+
attribute :child, :nested, type: SubWithValidation
|
139
|
+
attribute :children, :array, type: SubWithValidation
|
140
|
+
validates :child, :children, nested: true
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should generate an id" do
|
144
|
+
expect(SubWithValidation.new.id).to be_present
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should not regenerate the id after reloading parent" do
|
148
|
+
obj = WithValidationParent.new
|
149
|
+
obj.child = SubWithValidation.new(name: "foo")
|
150
|
+
obj.save!
|
151
|
+
expect(obj.child.id).to be_present
|
152
|
+
old_id = obj.child.id
|
153
|
+
obj.reload
|
154
|
+
expect(obj.child.id).to eq(old_id)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should not override the param id" do
|
158
|
+
expect(SubWithValidation.new(id: "foo").id).to eq "foo"
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should validate the nested object" do
|
162
|
+
obj = WithValidationParent.new
|
163
|
+
obj.child = SubWithValidation.new
|
164
|
+
expect(obj).to_not be_valid
|
165
|
+
expect(obj.errors[:child]).to eq ["is invalid"]
|
166
|
+
expect(obj.child.errors[:name]).to eq ["can't be blank"]
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should validate the nested objects in an array" do
|
170
|
+
obj = WithValidationParent.new
|
171
|
+
obj.children = [SubWithValidation.new(name: "foo"), SubWithValidation.new]
|
172
|
+
expect(obj).to_not be_valid
|
173
|
+
expect(obj.errors[:children]).to eq ["is invalid"]
|
174
|
+
expect(obj.children[1].errors[:name]).to eq ["can't be blank"]
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should validate the nested in the nested object" do
|
178
|
+
obj = WithValidationParent.new
|
179
|
+
obj.child = SubWithValidation.new name: "foo", label: "parent"
|
180
|
+
obj.child.child = SubWithValidation.new label: "child"
|
181
|
+
|
182
|
+
expect(obj).to_not be_valid
|
183
|
+
expect(obj.child).to_not be_valid
|
184
|
+
expect(obj.child.child).to_not be_valid
|
185
|
+
|
186
|
+
expect(obj.errors[:child]).to eq ["is invalid"]
|
187
|
+
expect(obj.child.errors[:child]).to eq ["is invalid"]
|
188
|
+
expect(obj.child.child.errors[:name]).to eq ["can't be blank"]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
data/spec/type_spec.rb
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
require File.expand_path("../support", __FILE__)
|
2
|
+
require "timecop"
|
3
|
+
require "active_model"
|
4
|
+
require "couchbase-orm/types"
|
5
|
+
|
6
|
+
class DateTimeWith3Decimal < CouchbaseOrm::Types::DateTime
|
7
|
+
def serialize(value)
|
8
|
+
value&.iso8601(3)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
ActiveModel::Type.register(:datetime3decimal, DateTimeWith3Decimal)
|
13
|
+
|
14
|
+
class TypeTest < CouchbaseOrm::Base
|
15
|
+
attribute :name, :string
|
16
|
+
attribute :age, :integer
|
17
|
+
attribute :size, :float
|
18
|
+
attribute :renewal_date, :date
|
19
|
+
attribute :subscribed_at, :datetime
|
20
|
+
attribute :some_time, :timestamp
|
21
|
+
attribute :precision3_time, :datetime3decimal
|
22
|
+
attribute :precision6_time, :datetime, precision: 6
|
23
|
+
|
24
|
+
attribute :created_at, :datetime, precision: 6
|
25
|
+
attribute :updated_at, :datetime, precision: 6
|
26
|
+
|
27
|
+
attribute :active, :boolean
|
28
|
+
|
29
|
+
index :age, presence: false
|
30
|
+
index :renewal_date, presence: false
|
31
|
+
index :some_time, presence: false
|
32
|
+
index :precision3_time, presence: false
|
33
|
+
end
|
34
|
+
|
35
|
+
class N1qlTypeTest < CouchbaseOrm::Base
|
36
|
+
attribute :name, :string
|
37
|
+
attribute :age, :integer
|
38
|
+
attribute :size, :float
|
39
|
+
attribute :renewal_date, :date
|
40
|
+
attribute :subscribed_at, :datetime
|
41
|
+
attribute :some_time, :timestamp
|
42
|
+
attribute :precision3_time, :datetime3decimal
|
43
|
+
attribute :active, :boolean
|
44
|
+
|
45
|
+
index_n1ql :name, validate: false
|
46
|
+
index_n1ql :age, validate: false
|
47
|
+
index_n1ql :size, validate: false
|
48
|
+
index_n1ql :active, validate: false
|
49
|
+
index_n1ql :renewal_date, validate: false
|
50
|
+
index_n1ql :some_time, validate: false
|
51
|
+
index_n1ql :subscribed_at, validate: false
|
52
|
+
index_n1ql :precision3_time, validate: false
|
53
|
+
n1ql :by_both_dates, emit_key: [:renewal_date, :subscribed_at], presence: false
|
54
|
+
end
|
55
|
+
|
56
|
+
TypeTest.ensure_design_document!
|
57
|
+
N1qlTypeTest.ensure_design_document!
|
58
|
+
|
59
|
+
describe CouchbaseOrm::Types::Timestamp do
|
60
|
+
it "should cast an integer to time" do
|
61
|
+
t = Time.at(Time.now.to_i)
|
62
|
+
expect(CouchbaseOrm::Types::Timestamp.new.cast(t.to_i)).to eq(t)
|
63
|
+
end
|
64
|
+
it "should cast an integer string to time" do
|
65
|
+
t = Time.at(Time.now.to_i)
|
66
|
+
expect(CouchbaseOrm::Types::Timestamp.new.cast(t.to_s)).to eq(t)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe CouchbaseOrm::Types::Date do
|
71
|
+
it "should cast an string to date" do
|
72
|
+
d = Date.today
|
73
|
+
expect(CouchbaseOrm::Types::Date.new.cast(d.to_s)).to eq(d)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should serialize date to string" do
|
77
|
+
d = Date.today
|
78
|
+
expect(CouchbaseOrm::Types::Date.new.serialize(d)).to eq(d.to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should get the type from the registry" do
|
82
|
+
expect(ActiveModel::Type.lookup(:date)).to eq(CouchbaseOrm::Types::Date.new)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe CouchbaseOrm::Base do
|
87
|
+
before(:each) do
|
88
|
+
TypeTest.delete_all
|
89
|
+
N1qlTypeTest.delete_all
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be typed" do
|
93
|
+
expect(N1qlTypeTest.attribute_types["name"]).to be_a(ActiveModel::Type::String)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be createable" do
|
97
|
+
t = TypeTest.create!
|
98
|
+
expect(t).to be_a(TypeTest)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should be able to set attributes" do
|
102
|
+
t = TypeTest.new
|
103
|
+
t.name = "joe"
|
104
|
+
t.age = 20
|
105
|
+
t.size = 1.5
|
106
|
+
t.renewal_date = Date.today
|
107
|
+
t.subscribed_at = Time.now
|
108
|
+
t.active = true
|
109
|
+
t.save!
|
110
|
+
|
111
|
+
expect(t.name).to eq("joe")
|
112
|
+
expect(t.age).to eq(20)
|
113
|
+
expect(t.size).to eq(1.5)
|
114
|
+
expect(t.renewal_date).to eq(Date.today)
|
115
|
+
expect(t.subscribed_at).to be_a(Time)
|
116
|
+
expect(t.active).to eq(true)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should be able to set attributes with a hash" do
|
120
|
+
t = TypeTest.new(name: "joe", age: 20, size: 1.5, renewal_date: Date.today, subscribed_at: Time.now, active: true)
|
121
|
+
t.save!
|
122
|
+
|
123
|
+
expect(t.name).to eq("joe")
|
124
|
+
expect(t.age).to eq(20)
|
125
|
+
expect(t.size).to eq(1.5)
|
126
|
+
expect(t.renewal_date).to eq(Date.today)
|
127
|
+
expect(t.subscribed_at).to be_a(Time)
|
128
|
+
expect(t.active).to eq(true)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should be able to be stored and retrieved" do
|
132
|
+
now = Time.now
|
133
|
+
t = TypeTest.create!(name: "joe", age: 20, size: 1.5, renewal_date: Date.today, subscribed_at: now, active: true)
|
134
|
+
t2 = TypeTest.find(t.id)
|
135
|
+
|
136
|
+
expect(t2.name).to eq("joe")
|
137
|
+
expect(t2.age).to eq(20)
|
138
|
+
expect(t2.size).to eq(1.5)
|
139
|
+
expect(t2.renewal_date).to eq(Date.today)
|
140
|
+
expect(t2.subscribed_at).to eq(now.utc.change(usec: 0))
|
141
|
+
expect(t2.active).to eq(true)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should be able to query by age" do
|
145
|
+
t = TypeTest.create!(age: 20)
|
146
|
+
_t2 = TypeTest.create!(age: 40)
|
147
|
+
expect(TypeTest.find_by_age(20)).to eq t
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should be able to query by age and type cast" do
|
151
|
+
t = TypeTest.create!(age: "20")
|
152
|
+
expect(TypeTest.find_by_age(20)).to eq t
|
153
|
+
expect(TypeTest.find_by_age("20")).to eq t
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should be able to query by date" do
|
157
|
+
t = TypeTest.create!(renewal_date: Date.today)
|
158
|
+
_t2 = TypeTest.create!(renewal_date: Date.today + 1)
|
159
|
+
expect(TypeTest.find_by_renewal_date(Date.today)).to eq t
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should be able to query by date and type cast" do
|
163
|
+
t = TypeTest.create!(renewal_date: Date.today.to_s)
|
164
|
+
expect(TypeTest.find_by_renewal_date(Date.today)).to eq t
|
165
|
+
expect(TypeTest.find_by_renewal_date(Date.today.to_s)).to eq t
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be able to query by time" do
|
169
|
+
now = Time.now
|
170
|
+
t = TypeTest.create!(name: "t", some_time: now)
|
171
|
+
_t2 = TypeTest.create!(name: "t2", some_time: now + 1)
|
172
|
+
expect(TypeTest.find_by_some_time(now)).to eq t
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should be able to query by time and type cast" do
|
176
|
+
now = Time.now
|
177
|
+
now_s = now.to_i.to_s
|
178
|
+
t = TypeTest.create!(some_time: now_s)
|
179
|
+
expect(TypeTest.find_by_some_time(now)).to eq t
|
180
|
+
expect(TypeTest.find_by_some_time(now_s)).to eq t
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should be able to query by custom type" do
|
184
|
+
now = Time.now
|
185
|
+
t = TypeTest.create!(precision3_time: now)
|
186
|
+
_t2 = TypeTest.create!(precision3_time: now + 1)
|
187
|
+
expect(TypeTest.find_by_precision3_time(now)).to eq t
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should be able to query by custom type and type cast" do
|
191
|
+
now = Time.now
|
192
|
+
now_s = now.utc.iso8601(3)
|
193
|
+
t = TypeTest.create!(precision3_time: now_s)
|
194
|
+
expect(TypeTest.find_by_precision3_time(now)).to eq t
|
195
|
+
expect(TypeTest.find_by_precision3_time(now_s)).to eq t
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should be able to set attributes with a hash with indifferent access" do
|
199
|
+
t = TypeTest.new(ActiveSupport::HashWithIndifferentAccess.new(name: "joe", age: 20, size: 1.5, renewal_date: Date.today, subscribed_at: Time.now, active: true))
|
200
|
+
t.save!
|
201
|
+
|
202
|
+
expect(t.name).to eq("joe")
|
203
|
+
expect(t.age).to eq(20)
|
204
|
+
expect(t.size).to eq(1.5)
|
205
|
+
expect(t.renewal_date).to eq(Date.today)
|
206
|
+
expect(t.subscribed_at).to be_a(Time)
|
207
|
+
expect(t.active).to eq(true)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should be able to type cast attributes" do
|
211
|
+
t = TypeTest.new(name: "joe", age: "20", size: "1.5", renewal_date: Date.today.to_s, subscribed_at: Time.now.to_s, active: "true")
|
212
|
+
t.save!
|
213
|
+
|
214
|
+
expect(t.name).to eq("joe")
|
215
|
+
expect(t.age).to eq(20)
|
216
|
+
expect(t.size).to eq(1.5)
|
217
|
+
expect(t.renewal_date).to eq(Date.today)
|
218
|
+
expect(t.subscribed_at).to be_a(Time)
|
219
|
+
expect(t.active).to eq(true)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should be consistent with active record on failed cast" do
|
223
|
+
t = TypeTest.new(name: "joe", age: "joe", size: "joe", renewal_date: "joe", subscribed_at: "joe", active: "true")
|
224
|
+
t.save!
|
225
|
+
|
226
|
+
expect(t.age).to eq 0
|
227
|
+
expect(t.size).to eq 0.0
|
228
|
+
expect(t.renewal_date).to eq nil
|
229
|
+
expect(t.subscribed_at).to eq nil
|
230
|
+
expect(t.active).to eq true
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should be able to query by name" do
|
234
|
+
t = N1qlTypeTest.create!(name: "joe")
|
235
|
+
_t2 = N1qlTypeTest.create!(name: "john")
|
236
|
+
expect(N1qlTypeTest.find_by_name("joe").to_a).to eq [t]
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should be able to query by nil value" do
|
240
|
+
t = N1qlTypeTest.create!()
|
241
|
+
_t2 = N1qlTypeTest.create!(name: "john")
|
242
|
+
expect(N1qlTypeTest.find_by_name(nil).to_a).to eq [t]
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should be able to query by array value" do
|
246
|
+
t = N1qlTypeTest.create!(name: "laura")
|
247
|
+
t2 = N1qlTypeTest.create!(name: "joe")
|
248
|
+
_t3 = N1qlTypeTest.create!(name: "john")
|
249
|
+
expect(N1qlTypeTest.find_by_name(["laura", "joe"]).to_a).to match_array [t, t2]
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should be able to query by integer" do
|
253
|
+
t = N1qlTypeTest.create!(age: 20)
|
254
|
+
t2 = N1qlTypeTest.create!(age: 20)
|
255
|
+
_t3 = N1qlTypeTest.create!(age: 40)
|
256
|
+
expect(N1qlTypeTest.find_by_age(20).to_a).to match_array [t, t2]
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should be able to query by integer and type cast" do
|
260
|
+
t = N1qlTypeTest.create!(age: "20")
|
261
|
+
expect(N1qlTypeTest.find_by_age(20).to_a).to eq [t]
|
262
|
+
expect(N1qlTypeTest.find_by_age("20").to_a).to eq [t]
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should be able to query by date" do
|
266
|
+
t = N1qlTypeTest.create!(renewal_date: Date.today)
|
267
|
+
_t2 = N1qlTypeTest.create!(renewal_date: Date.today + 1)
|
268
|
+
expect(N1qlTypeTest.find_by_renewal_date(Date.today).to_a).to eq [t]
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should be able to query by datetime" do
|
272
|
+
now = Time.now
|
273
|
+
t = N1qlTypeTest.create!(subscribed_at: now)
|
274
|
+
_t2 = N1qlTypeTest.create!(subscribed_at: now + 1)
|
275
|
+
expect(N1qlTypeTest.find_by_subscribed_at(now).to_a).to eq [t]
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should be able to query by timestamp" do
|
279
|
+
now = Time.now
|
280
|
+
t = N1qlTypeTest.create!(some_time: now)
|
281
|
+
_t2 = N1qlTypeTest.create!(some_time: now + 1)
|
282
|
+
expect(N1qlTypeTest.find_by_some_time(now).to_a).to eq [t]
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should be able to query by custom type" do
|
286
|
+
now = Time.now
|
287
|
+
t = N1qlTypeTest.create!(precision3_time: now)
|
288
|
+
_t2 = N1qlTypeTest.create!(precision3_time: now + 1)
|
289
|
+
expect(N1qlTypeTest.find_by_precision3_time(now).to_a).to eq [t]
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should be able to query by boolean" do
|
293
|
+
t = N1qlTypeTest.create!(active: true)
|
294
|
+
_t2 = N1qlTypeTest.create!(active: false)
|
295
|
+
expect(N1qlTypeTest.find_by_active(true).to_a).to eq [t]
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should be able to query by float" do
|
299
|
+
t = N1qlTypeTest.create!(size: 1.5)
|
300
|
+
_t2 = N1qlTypeTest.create!(size: 2.5)
|
301
|
+
expect(N1qlTypeTest.find_by_size(1.5).to_a).to eq [t]
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should set datetime with precision" do
|
305
|
+
time = Time.at(1667499592.5170466123)
|
306
|
+
Timecop.freeze(time) do
|
307
|
+
test = TypeTest.create!(precision3_time: 1667499592.5170466123, some_time: 1667499592.5170466123, precision6_time: Time.now)
|
308
|
+
|
309
|
+
expect(test.created_at).to eq(time.floor(6))
|
310
|
+
expect(test.updated_at).to eq(time.floor(6))
|
311
|
+
|
312
|
+
expect(test.some_time).to eq(time.floor)
|
313
|
+
expect(test.precision3_time).to eq(time.floor(3))
|
314
|
+
expect(test.precision6_time).to eq(time.floor(6))
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'couchbase-orm/utilities/ignored_properties'
|
2
|
+
|
3
|
+
class DummyClass
|
4
|
+
extend CouchbaseOrm::IgnoredProperties
|
5
|
+
end
|
6
|
+
|
7
|
+
class DummyClass2
|
8
|
+
extend CouchbaseOrm::IgnoredProperties
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.describe CouchbaseOrm::IgnoredProperties do
|
12
|
+
|
13
|
+
describe '#ignored_properties=' do
|
14
|
+
it 'does not mixup ignored properties between classes' do
|
15
|
+
DummyClass.ignored_properties = [:property1, :property2]
|
16
|
+
expect(DummyClass.ignored_properties).to eq(['property1', 'property2'])
|
17
|
+
expect(DummyClass2.ignored_properties).to be_empty
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'couchbase-orm/utilities/properties_always_exists_in_document'
|
2
|
+
|
3
|
+
class DummyClass
|
4
|
+
extend CouchbaseOrm::PropertiesAlwaysExistsInDocument
|
5
|
+
end
|
6
|
+
|
7
|
+
class DummyClass2
|
8
|
+
extend CouchbaseOrm::PropertiesAlwaysExistsInDocument
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.describe CouchbaseOrm::PropertiesAlwaysExistsInDocument do
|
12
|
+
|
13
|
+
describe '#properties_always_exists_in_document=' do
|
14
|
+
it 'Checks properties_always_exists_in_document value when initialize or not' do
|
15
|
+
DummyClass.properties_always_exists_in_document = true
|
16
|
+
expect(DummyClass.properties_always_exists_in_document).to be(true)
|
17
|
+
expect(DummyClass2.properties_always_exists_in_document).to be(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises error when a non boolean value is passed' do
|
21
|
+
expect { DummyClass.properties_always_exists_in_document = 'toto' }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/views_spec.rb
CHANGED
@@ -7,21 +7,41 @@ class ViewTest < CouchbaseOrm::Base
|
|
7
7
|
attribute :name, type: String
|
8
8
|
enum rating: [:awesome, :good, :okay, :bad], default: :okay
|
9
9
|
|
10
|
-
view :
|
11
|
-
view :by_rating, emit_key: :rating
|
10
|
+
view :vall
|
12
11
|
|
13
12
|
# This generates both:
|
14
|
-
# view :by_rating, emit_key: :rating
|
13
|
+
# view :by_rating, emit_key: :rating
|
15
14
|
# def self.find_by_rating(rating); end # also provide this helper function
|
16
15
|
index_view :rating
|
17
16
|
end
|
18
17
|
|
19
18
|
|
20
19
|
describe CouchbaseOrm::Views do
|
20
|
+
before(:each) do
|
21
|
+
ViewTest.delete_all
|
22
|
+
rescue Couchbase::Error::DesignDocumentNotFound
|
23
|
+
# ignore (FIXME: check before merge) mainly because if there is nothing in all we should not have an error
|
24
|
+
end
|
25
|
+
|
26
|
+
after(:each) do
|
27
|
+
ViewTest.delete_all
|
28
|
+
rescue Couchbase::Error::InternalServerFailure
|
29
|
+
# ignore (FIXME: check before merge)
|
30
|
+
rescue Couchbase::Error::DesignDocumentNotFound
|
31
|
+
# ignore (FIXME: check before merge) (7.1)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not allow n1ql to override existing methods" do
|
35
|
+
expect { ViewTest.view :all }.to raise_error(ArgumentError)
|
36
|
+
end
|
37
|
+
|
21
38
|
it "should save a new design document" do
|
22
39
|
begin
|
23
|
-
ViewTest.bucket.
|
24
|
-
rescue
|
40
|
+
ViewTest.bucket.view_indexes.drop_design_document(ViewTest.design_document, :production)
|
41
|
+
rescue Couchbase::Error::InternalServerFailure
|
42
|
+
# ignore if design document does not exist
|
43
|
+
rescue Couchbase::Error::DesignDocumentNotFound
|
44
|
+
# ignore if design document does not exist (7.1)
|
25
45
|
end
|
26
46
|
expect(ViewTest.ensure_design_document!).to be(true)
|
27
47
|
end
|
@@ -29,12 +49,16 @@ describe CouchbaseOrm::Views do
|
|
29
49
|
it "should not re-save a design doc if nothing has changed" do
|
30
50
|
expect(ViewTest.ensure_design_document!).to be(false)
|
31
51
|
end
|
52
|
+
|
53
|
+
it "should return an empty array when there is no objects" do
|
54
|
+
expect(ViewTest.vall).to eq([])
|
55
|
+
end
|
32
56
|
|
33
57
|
it "should perform a map-reduce and return the view" do
|
34
58
|
ViewTest.ensure_design_document!
|
35
|
-
|
59
|
+
ViewTest.create! name: :bob, rating: :good
|
36
60
|
|
37
|
-
docs = ViewTest.
|
61
|
+
docs = ViewTest.vall.collect { |ob|
|
38
62
|
ob.destroy
|
39
63
|
ob.name
|
40
64
|
}
|
@@ -47,7 +71,7 @@ describe CouchbaseOrm::Views do
|
|
47
71
|
ViewTest.create! name: :jane, rating: :awesome
|
48
72
|
ViewTest.create! name: :greg, rating: :bad
|
49
73
|
|
50
|
-
docs = ViewTest.by_rating(
|
74
|
+
docs = ViewTest.by_rating(order: :descending).collect { |ob|
|
51
75
|
ob.destroy
|
52
76
|
ob.name
|
53
77
|
}
|
@@ -64,9 +88,6 @@ describe CouchbaseOrm::Views do
|
|
64
88
|
docs = ViewTest.find_by_rating(1).collect { |ob|
|
65
89
|
ob.name
|
66
90
|
}
|
67
|
-
ViewTest.all.stream { |ob|
|
68
|
-
ob.destroy
|
69
|
-
}
|
70
91
|
|
71
92
|
expect(Set.new(docs)).to eq(Set.new(['bob', 'jane']))
|
72
93
|
end
|