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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +45 -0
  3. data/.gitignore +2 -0
  4. data/.travis.yml +3 -2
  5. data/CODEOWNERS +1 -0
  6. data/Gemfile +5 -3
  7. data/LICENSE +201 -24
  8. data/README.md +248 -35
  9. data/ci/run_couchbase.sh +22 -0
  10. data/couchbase-orm.gemspec +26 -20
  11. data/lib/couchbase-orm/active_record_compat.rb +92 -0
  12. data/lib/couchbase-orm/associations.rb +119 -0
  13. data/lib/couchbase-orm/base.rb +143 -166
  14. data/lib/couchbase-orm/changeable.rb +512 -0
  15. data/lib/couchbase-orm/connection.rb +28 -8
  16. data/lib/couchbase-orm/encrypt.rb +48 -0
  17. data/lib/couchbase-orm/error.rb +17 -2
  18. data/lib/couchbase-orm/inspectable.rb +37 -0
  19. data/lib/couchbase-orm/json_schema/json_validation_error.rb +13 -0
  20. data/lib/couchbase-orm/json_schema/loader.rb +47 -0
  21. data/lib/couchbase-orm/json_schema/validation.rb +18 -0
  22. data/lib/couchbase-orm/json_schema/validator.rb +45 -0
  23. data/lib/couchbase-orm/json_schema.rb +9 -0
  24. data/lib/couchbase-orm/json_transcoder.rb +27 -0
  25. data/lib/couchbase-orm/locale/en.yml +5 -0
  26. data/lib/couchbase-orm/n1ql.rb +133 -0
  27. data/lib/couchbase-orm/persistence.rb +61 -52
  28. data/lib/couchbase-orm/proxies/bucket_proxy.rb +36 -0
  29. data/lib/couchbase-orm/proxies/collection_proxy.rb +52 -0
  30. data/lib/couchbase-orm/proxies/n1ql_proxy.rb +40 -0
  31. data/lib/couchbase-orm/proxies/results_proxy.rb +23 -0
  32. data/lib/couchbase-orm/railtie.rb +6 -17
  33. data/lib/couchbase-orm/relation.rb +249 -0
  34. data/lib/couchbase-orm/strict_loading.rb +21 -0
  35. data/lib/couchbase-orm/timestamps/created.rb +20 -0
  36. data/lib/couchbase-orm/timestamps/updated.rb +21 -0
  37. data/lib/couchbase-orm/timestamps.rb +15 -0
  38. data/lib/couchbase-orm/types/array.rb +32 -0
  39. data/lib/couchbase-orm/types/date.rb +9 -0
  40. data/lib/couchbase-orm/types/date_time.rb +14 -0
  41. data/lib/couchbase-orm/types/encrypted.rb +17 -0
  42. data/lib/couchbase-orm/types/nested.rb +43 -0
  43. data/lib/couchbase-orm/types/timestamp.rb +18 -0
  44. data/lib/couchbase-orm/types.rb +20 -0
  45. data/lib/couchbase-orm/utilities/enum.rb +13 -1
  46. data/lib/couchbase-orm/utilities/has_many.rb +72 -36
  47. data/lib/couchbase-orm/utilities/ignored_properties.rb +15 -0
  48. data/lib/couchbase-orm/utilities/index.rb +18 -20
  49. data/lib/couchbase-orm/utilities/properties_always_exists_in_document.rb +16 -0
  50. data/lib/couchbase-orm/utilities/query_helper.rb +148 -0
  51. data/lib/couchbase-orm/utils.rb +25 -0
  52. data/lib/couchbase-orm/version.rb +1 -1
  53. data/lib/couchbase-orm/views.rb +38 -41
  54. data/lib/couchbase-orm.rb +44 -9
  55. data/lib/ext/query_n1ql.rb +124 -0
  56. data/lib/rails/generators/couchbase_orm/config/templates/couchbase.yml +3 -2
  57. data/spec/associations_spec.rb +219 -50
  58. data/spec/base_spec.rb +296 -14
  59. data/spec/collection_proxy_spec.rb +29 -0
  60. data/spec/connection_spec.rb +27 -0
  61. data/spec/couchbase-orm/active_record_compat_spec.rb +24 -0
  62. data/spec/couchbase-orm/changeable_spec.rb +16 -0
  63. data/spec/couchbase-orm/json_schema/validation_spec.rb +23 -0
  64. data/spec/couchbase-orm/json_schema/validator_spec.rb +13 -0
  65. data/spec/couchbase-orm/timestamps_spec.rb +85 -0
  66. data/spec/couchbase-orm/timestamps_spec_models.rb +36 -0
  67. data/spec/empty-json-schema/.gitkeep +0 -0
  68. data/spec/enum_spec.rb +34 -0
  69. data/spec/has_many_spec.rb +101 -54
  70. data/spec/index_spec.rb +13 -9
  71. data/spec/json-schema/JsonSchemaBaseTest.json +19 -0
  72. data/spec/json-schema/entity_snakecase.json +20 -0
  73. data/spec/json-schema/loader_spec.rb +42 -0
  74. data/spec/json-schema/specific_path.json +20 -0
  75. data/spec/json_schema_spec.rb +178 -0
  76. data/spec/n1ql_spec.rb +193 -0
  77. data/spec/persistence_spec.rb +49 -9
  78. data/spec/relation_nested_spec.rb +88 -0
  79. data/spec/relation_spec.rb +430 -0
  80. data/spec/support.rb +16 -8
  81. data/spec/type_array_spec.rb +52 -0
  82. data/spec/type_encrypted_spec.rb +114 -0
  83. data/spec/type_nested_spec.rb +191 -0
  84. data/spec/type_spec.rb +317 -0
  85. data/spec/utilities/ignored_properties_spec.rb +20 -0
  86. data/spec/utilities/properties_always_exists_in_document_spec.rb +24 -0
  87. data/spec/views_spec.rb +32 -11
  88. metadata +193 -30
data/README.md CHANGED
@@ -1,18 +1,24 @@
1
1
  # Couchbase ORM for Rails
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/acaprojects/couchbase-orm.svg)](http://travis-ci.org/acaprojects/couchbase-orm)
3
+ ## Documentation
4
+ https://couchbase-ruby-orm.com
4
5
 
5
- ## Rails integration
6
+ ## Installation
7
+ Add this line to your application's Gemfile:
8
+ ```ruby
9
+ gem 'couchbase-orm', git: 'https://github.com/Couchbase-Ecosystem/couchbase-ruby-orm'
10
+ ```
11
+ And then execute:
6
12
 
7
- To generate config you can use `rails generate couchbase_orm:config`:
13
+ $ bundle install
8
14
 
9
- $ rails generate couchbase_orm:config dev_bucket dev_user dev_password
10
- => create config/couchbase.yml
15
+ ## Rails integration
11
16
 
12
- It will generate this `config/couchbase.yml` for you:
17
+ Create a couchbase-orm config file `config/couchbase.yml`
13
18
 
19
+ ```yaml
14
20
  common: &common
15
- hosts: localhost
21
+ connection_string: couchbase://localhost
16
22
  username: dev_user
17
23
  password: dev_password
18
24
 
@@ -26,10 +32,25 @@ It will generate this `config/couchbase.yml` for you:
26
32
 
27
33
  # set these environment variables on your production server
28
34
  production:
29
- hosts: <%= ENV['COUCHBASE_HOST'] || ENV['COUCHBASE_HOSTS'] %>
30
- bucket: <%= ENV['COUCHBASE_BUCKET'] %>
31
- username: <%= ENV['COUCHBASE_USER'] %>
32
- password: <%= ENV['COUCHBASE_PASSWORD'] %>
35
+ connection_string: <%= ENV['COUCHBASE_CONNECTION_STRING'] %>
36
+ bucket: <%= ENV['COUCHBASE_BUCKET'] %>
37
+ username: <%= ENV['COUCHBASE_USER'] %>
38
+ password: <%= ENV['COUCHBASE_PASSWORD'] %>
39
+ ```
40
+
41
+ ## Setup without Rails
42
+
43
+ If you are not using Rails, you can configure couchbase-orm with an initializer:
44
+
45
+ ```ruby
46
+ # config/initializers/couchbase_orm.rb
47
+ CouchbaseOrm::Connection.config = {
48
+ connection_string: "couchbase://localhost"
49
+ username: "dev_user"
50
+ password: "dev_password"
51
+ bucket: "dev_bucket"
52
+ }
53
+ ```
33
54
 
34
55
  Views are generated on application load if they don't exist or mismatch.
35
56
  This works fine in production however by default in development models are lazy loaded.
@@ -37,16 +58,15 @@ This works fine in production however by default in development models are lazy
37
58
  # config/environments/development.rb
38
59
  config.eager_load = true
39
60
 
40
-
41
61
  ## Examples
42
62
 
43
63
  ```ruby
44
64
  require 'couchbase-orm'
45
65
 
46
66
  class Post < CouchbaseOrm::Base
47
- attribute :title, type: String
48
- attribute :body, type: String
49
- attribute :draft, type: Boolean
67
+ attribute :title, :string
68
+ attribute :body, :string
69
+ attribute :draft, :boolean
50
70
  end
51
71
 
52
72
  p = Post.new(id: 'hello-world',
@@ -69,16 +89,45 @@ You can also let the library generate the unique identifier for you:
69
89
  p.id #=> "post-abcDE34"
70
90
  ```
71
91
 
72
- You can define connection options on per model basis:
92
+ <!-- You can define connection options on per model basis:
73
93
 
74
94
  ```ruby
75
95
  class Post < CouchbaseOrm::Base
76
- attribute :title, type: String
77
- attribute :body, type: String
78
- attribute :draft, type: Boolean
96
+ attribute :title, :string
97
+ attribute :body, :string
98
+ attribute :draft, :boolean
79
99
 
80
100
  connect bucket: 'blog', password: ENV['BLOG_BUCKET_PASSWORD']
81
101
  end
102
+ ``` -->
103
+
104
+ ## Typing
105
+
106
+ The following types have been tested :
107
+
108
+ - :string
109
+ - :integer
110
+ - :float
111
+ - :boolean
112
+ - :date
113
+ - :datetime (stored as iso8601, use precision: n to store more decimal precision)
114
+ - :timestamp (stored as integer)
115
+ - :encrypted
116
+ - see <https://docs.couchbase.com/couchbase-lite/current/c/field-level-encryption.html>
117
+ - You must store a string that can be encoded in json (not binary data), use base64 if needed
118
+ - :array (see below)
119
+ - :nested (see below)
120
+
121
+ You can register other types in ActiveModel registry :
122
+
123
+ ```ruby
124
+ class DateTimeWith3Decimal < CouchbaseOrm::Types::DateTime
125
+ def serialize(value)
126
+ value&.iso8601(3)
127
+ end
128
+ end
129
+
130
+ ActiveModel::Type.register(:datetime3decimal, DateTimeWith3Decimal)
82
131
  ```
83
132
 
84
133
  ## Validations
@@ -89,7 +138,8 @@ context of rails application. You can also enforce types using ruby
89
138
 
90
139
  ```ruby
91
140
  class Comment < Couchbase::Model
92
- attribute :author, :body, type: String
141
+ attribute :author, :string
142
+ attribute :body, :string
93
143
 
94
144
  validates_presence_of :author, :body
95
145
  end
@@ -102,7 +152,8 @@ can then be used for filtering results or ordering.
102
152
 
103
153
  ```ruby
104
154
  class Comment < CouchbaseOrm::Base
105
- attribute :author, :body, type: String
155
+ attribute :author :string
156
+ attribute :body, :string
106
157
  view :all # => emits :id and will return all comments
107
158
  view :by_author, emit_key: :author
108
159
 
@@ -110,15 +161,12 @@ can then be used for filtering results or ordering.
110
161
  # * the by_author view above
111
162
  # * def find_by_author(author); end
112
163
  index_view :author
113
-
164
+
114
165
  # You can make compound keys by passing an array to :emit_key
115
166
  # this allow to query by read/unread comments
116
167
  view :by_read, emit_key: [:user_id, :read]
117
168
  # this allow to query by view_count
118
169
  view :by_view_count, emit_key: [:user_id, :view_count]
119
-      
120
-
121
-      
122
170
 
123
171
  validates_presence_of :author, :body
124
172
  end
@@ -128,20 +176,129 @@ You can use `Comment.find_by_author('name')` to obtain all the comments by
128
176
  a particular author. The same thing, using the view directly would be:
129
177
  `Comment.by_author(key: 'name')`
130
178
 
131
- When using a compound key, the usage is the same, you just give the full key :
179
+ When using a compound key, the usage is the same, you just give the full key :
132
180
 
133
181
  ```ruby
134
182
  Comment.by_read(key: '["'+user_id+'",false]') # gives all unread comments for one particular user
135
-
183
+
136
184
  # or even a range !
185
+
186
+ Comment.by_view_count(startkey: '["'+user_id+'",10]', endkey: '["'+user_id+'",20]')
137
187
 
138
- Comment.by_view_count(startkey: '["'+user_id+'",10]', endkey: '["'+user_id+'",20]') # gives all comments that have been seen more than 10 times but less than 20
188
+ # gives all comments that have been seen more than 10 times but less than 20
139
189
  ```
140
-
141
- Check this couchbase help page to learn more on what's possible with compound keys : https://developer.couchbase.com/documentation/server/3.x/admin/Views/views-translateSQL.html
190
+ Check this couchbase help page to learn more on what's possible with compound keys : <https://developer.couchbase.com/documentation/server/3.x/admin/Views/views-translateSQL.html>
142
191
 
143
192
  Ex : Compound keys allows to decide the order of the results, and you can reverse it by passing `descending: true`
144
193
 
194
+ ```ruby
195
+ class Comment < CouchbaseOrm::Base19
196
+ self.ignored_properties = [:old_name] # ignore old_name property in the model
197
+ self.properties_always_exists_in_document = true # use is null for nil value instead of not valued for performance purpose, only possible if all properties always exists in document
198
+ end
199
+ ```
200
+ You can specify `properties_always_exists_in_document` to true if all properties always exists in document, this will allow to use `is null` instead of `not valued` for nil value, this will improve performance.
201
+
202
+ WARNING: If a document exists without a property, the query will failed! So you must be sure that all documents have all properties.
203
+
204
+
205
+ ## N1ql
206
+
207
+ Like views, it's possible to use N1QL to process some requests used for filtering results or ordering.
208
+
209
+ ```ruby
210
+ class Comment < CouchbaseOrm::Base
211
+ attribute :author, :string
212
+ attribute :body, :string
213
+ n1ql :by_author, emit_key: :author
214
+
215
+ # Generates two functions:
216
+ # * the by_author view above
217
+ # * def find_by_author(author); end
218
+ index_n1ql :author
219
+
220
+ # You can make compound keys by passing an array to :emit_key
221
+ # this allow to query by read/unread comments
222
+ n1ql :by_read, emit_key: [:user_id, :read]
223
+ # this allow to query by view_count
224
+ n1ql :by_view_count, emit_key: [:user_id, :view_count]
225
+
226
+ validates_presence_of :author, :body
227
+ end
228
+ ```
229
+
230
+ ## Basic Active Record like query engine
231
+
232
+ ```ruby
233
+ class Comment < CouchbaseOrm::Base
234
+ attribute :title, :string
235
+ attribute :author, :string
236
+ attribute :category, :string
237
+ attribute :ratings, :number
238
+ end
239
+
240
+ Comment.where(author: "Anne McCaffrey", category: ['S-F', 'Fantasy']).not(ratings: 0).order(:title).limit(10)
241
+
242
+ # Relation can be composed as in AR:
243
+
244
+ amc_comments = Comment.where(author: "Anne McCaffrey")
245
+
246
+ amc_comments.count
247
+
248
+ amc_sf_comments = amc_comments.where(category: 'S-F')
249
+
250
+ # pluck is available, but will query all object fields first
251
+
252
+ Comment.pluck(:title, :ratings)
253
+
254
+ # To load the ids without loading the models
255
+
256
+ Comment.where(author: "David Eddings").ids
257
+
258
+ # To delete all the models of a relation
259
+
260
+ Comment.where(ratings: 0).delete_all
261
+ ```
262
+
263
+ ## scopes
264
+
265
+ Scopes can be written as class method, scope method is not implemented yet.
266
+ They can be chained as in AR or mixed with relation methods.
267
+
268
+ ```ruby
269
+ class Comment < CouchbaseOrm::Base
270
+ attribute :title, :string
271
+ attribute :author, :string
272
+ attribute :category, :string
273
+ attribute :ratings, :number
274
+
275
+ def self.by_author(author)
276
+ where(author: author)
277
+ end
278
+ end
279
+
280
+ Comment.by_author("Anne McCaffrey").where(category: 'S-F').not(ratings: 0).order(:title).limit(10)
281
+ ```
282
+
283
+ ## Operators
284
+
285
+ Several operators are available to filter numerical results : \_gt, \_lt, \_gte, \_lte, \_ne
286
+
287
+ ```ruby
288
+ Comment.where(ratings: {_gt: 3})
289
+ ```
290
+
291
+ ## Range in the where
292
+
293
+ You can specify a Range of date or intger in the where clause
294
+
295
+ ```ruby
296
+ Person.where(birth_date: DateTime.new(1980, 1, 1)..DateTime.new(1990, 1, 1))
297
+ Person.where(age: 10..20)
298
+
299
+ Person.where(age: 10...20) # to exclude the upper bound
300
+ ```
301
+
145
302
  ## Associations and Indexes
146
303
 
147
304
  There are common active record helpers available for use `belongs_to` and `has_many`
@@ -155,21 +312,77 @@ There are common active record helpers available for use `belongs_to` and `has_m
155
312
  has_many :comments, dependent: :destroy
156
313
 
157
314
  # You can ensure an attribute is unique for this model
158
- attribute :email, type: String
315
+ attribute :email, :string
159
316
  ensure_unique :email
160
317
  end
161
318
  ```
162
319
 
320
+ By default, `has_many` uses a view for association,
321
+ but you can define a `type` option to specify an association using N1QL instead:
322
+
323
+ ```ruby
324
+ class Comment < CouchbaseOrm::Base
325
+ belongs_to :author
326
+ end
327
+
328
+ class Author < CouchbaseOrm::Base
329
+ has_many :comments, type: :n1ql, dependent: :destroy
330
+ end
331
+ ```
332
+
333
+ ## Nested
334
+
335
+ Attributes can be of type nested, they must specify a type of NestedDocument.
336
+ The NestedValidation triggers nested validation on parent validation.
337
+
338
+ ```ruby
339
+ class Address < CouchbaseOrm::NestedDocument
340
+ attribute :road, :string
341
+ attribute :city, :string
342
+ validates :road, :city, presence: true
343
+ end
344
+
345
+ class Author < CouchbaseOrm::Base
346
+ attribute :address, :nested, type: Address
347
+ validates :address, nested: true
348
+ end
349
+ ```
350
+
351
+ Model can be queried using the nested attributes
352
+
353
+ ```ruby
354
+ Author.where(address: {road: '1 rue de la paix', city: 'Paris'})
355
+ ```
356
+
357
+ ## Array
358
+
359
+ Attributes can be of type array, they must contain something that can be serialized and deserialized to/from JSON.
360
+ You can enforce the type of array elements. The type can be a NestedDocument
361
+
362
+ ```ruby
363
+ class Book < CouchbaseOrm::NestedDocument
364
+ attribute :name, :string
365
+ validates :name, presence: true
366
+ end
367
+
368
+ class Author < CouchbaseOrm::Base
369
+ attribute things, :array
370
+ attribute flags, :array, type: :string
371
+ attribute books, :array, type: Book
372
+
373
+ validates :books, nested: true
374
+ end
375
+ ```
163
376
 
164
377
  ## Performance Comparison with Couchbase-Ruby-Model
165
378
 
166
379
  Basically we migrated an application from [Couchbase Ruby Model](https://github.com/couchbase/couchbase-ruby-model)
167
380
  to [Couchbase-ORM](https://github.com/acaprojects/couchbase-orm) (this project)
168
381
 
169
- * Rails 5 production
170
- * Puma as the webserver
171
- * Running on a 2015 Macbook Pro
172
- * Performance test: `siege -c250 -r10 http://localhost:3000/auth/authority`
382
+ - Rails 5 production
383
+ - Puma as the webserver
384
+ - Running on a 2015 Macbook Pro
385
+ - Performance test: `siege -c250 -r10 http://localhost:3000/auth/authority`
173
386
 
174
387
  The request above pulls the same database document each time and returns it. A simple O(1) operation.
175
388
 
@@ -0,0 +1,22 @@
1
+ set -x
2
+ set -e
3
+
4
+ VERSION=$1
5
+ BUCKET=$2
6
+ USER=$3
7
+ PASSWORD=$4
8
+
9
+
10
+ wget https://packages.couchbase.com/releases/$VERSION/couchbase-server-enterprise_$VERSION-ubuntu20.04_amd64.deb
11
+ dpkg -i couchbase-server-enterprise_$VERSION-ubuntu20.04_amd64.deb
12
+ sleep 8
13
+ sudo service couchbase-server status
14
+ /opt/couchbase/bin/couchbase-cli cluster-init -c 127.0.0.1:8091 --cluster-username=admin --cluster-password=password --cluster-ramsize=320 --cluster-index-ramsize=256 --cluster-fts-ramsize=256 --services=data,index,query,fts
15
+ sleep 5
16
+ /opt/couchbase/bin/couchbase-cli server-info -c 127.0.0.1:8091 -u admin -p password
17
+ /opt/couchbase/bin/couchbase-cli bucket-create -c 127.0.0.1:8091 -u admin -p password --bucket=$BUCKET --bucket-type=couchbase --bucket-ramsize=160 --bucket-replica=0 --wait
18
+ sleep 1
19
+ /opt/couchbase/bin/couchbase-cli user-manage -c 127.0.0.1:8091 -u admin -p password --set --rbac-username $USER --rbac-password $PASSWORD --rbac-name "Auto Tester" --roles admin --auth-domain local
20
+ curl http://admin:password@localhost:8093/query/service -d "statement=CREATE INDEX \`default_type\` ON \`$BUCKET\`(\`type\`)"
21
+ curl http://admin:password@localhost:8093/query/service -d "statement=CREATE INDEX \`default_rating\` ON \`$BUCKET\`(\`rating\`)"
22
+ curl http://admin:password@localhost:8093/query/service -d "statement=CREATE INDEX \`default_name\` ON \`$BUCKET\`(\`name\`)"
@@ -1,27 +1,33 @@
1
- require File.expand_path("../lib/couchbase-orm/version", __FILE__)
1
+ require File.expand_path('../lib/couchbase-orm/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |gem|
4
- gem.name = "couchbase-orm"
5
- gem.version = CouchbaseOrm::VERSION
6
- gem.license = 'MIT'
7
- gem.authors = ["Stephen von Takach"]
8
- gem.email = ["steve@cotag.me"]
9
- gem.homepage = "https://github.com/cotag/couchbase-orm"
10
- gem.summary = "Couchbase ORM for Rails"
11
- gem.description = "A Couchbase ORM for Rails"
4
+ gem.name = 'couchbase-orm'
5
+ gem.version = CouchbaseOrm::VERSION
6
+ gem.license = 'Apache-2.0'
7
+ gem.authors = ['Stephen von Takach', 'Gauthier Monserand', 'Pierre Merlin', 'Julien Burnet-Fauche']
8
+ gem.homepage = 'https://github.com/Couchbase-Ecosystem/couchbase-ruby-orm'
9
+ gem.summary = 'Couchbase ORM for Rails'
10
+ gem.description = 'A Couchbase ORM for Rails'
12
11
 
13
- gem.required_ruby_version = '>= 2.1.0'
14
- gem.require_paths = ["lib"]
12
+ gem.required_ruby_version = '>= 2.7.0'
13
+ gem.require_paths = ['lib']
15
14
 
16
- gem.add_runtime_dependency 'libcouchbase', '~> 1.2'
17
- gem.add_runtime_dependency 'activemodel', '>= 4.0', '< 6.0'
18
- gem.add_runtime_dependency 'radix', '~> 2.2' # converting numbers to and from any base
15
+ gem.add_runtime_dependency 'activemodel', ENV['ACTIVE_MODEL_VERSION'] || '>= 5.2'
19
16
 
20
- gem.add_development_dependency 'rake', '~> 12.2'
21
- gem.add_development_dependency 'rspec', '~> 3.7'
22
- gem.add_development_dependency 'yard', '~> 0.9'
23
- gem.add_development_dependency 'minitest', '~> 5.10'
17
+ gem.add_runtime_dependency 'couchbase', '>= 3.4.2'
18
+ gem.add_runtime_dependency 'radix', '~> 2.2' # converting numbers to and from any base
19
+ gem.add_runtime_dependency 'json-schema', '>= 3' # validating JSON against a schema
24
20
 
25
- gem.files = `git ls-files`.split("\n")
26
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ gem.add_development_dependency 'rake', '~> 12.2'
22
+ gem.add_development_dependency 'rspec', '~> 3.7'
23
+ gem.add_development_dependency 'yard', '~> 0.9'
24
+ gem.add_development_dependency 'pry'
25
+ gem.add_development_dependency 'pry-stack_explorer'
26
+ gem.add_development_dependency 'simplecov'
27
+ gem.add_development_dependency 'actionpack'
28
+ gem.add_development_dependency 'timecop'
29
+ gem.add_development_dependency 'base64'
30
+
31
+ gem.files = `git ls-files`.split("\n")
32
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
33
  end
@@ -0,0 +1,92 @@
1
+ require 'active_support/concern'
2
+ require 'active_model'
3
+ require 'active_support/hash_with_indifferent_access'
4
+
5
+ module CouchbaseOrm
6
+
7
+ # Set of methods defined in ActiveRecord and needed for CouchbaseOrm
8
+ # try to avoid dependencies on too many active record classes
9
+ # by exemple we don't want to go down to the concept of tables
10
+ module ActiveRecordCompat
11
+
12
+ extend ActiveSupport::Concern
13
+
14
+ module ClassMethods
15
+
16
+ def primary_key
17
+ 'id'
18
+ end
19
+
20
+ def base_class?
21
+ true
22
+ end
23
+
24
+ def column_names # can't be an alias for now
25
+ attribute_names
26
+ end
27
+
28
+ def abstract_class?
29
+ false
30
+ end
31
+
32
+ def connected?
33
+ true
34
+ end
35
+
36
+ def table_exists?
37
+ true
38
+ end
39
+
40
+ def _reflect_on_association(_attribute)
41
+ false
42
+ end
43
+
44
+ def type_for_attribute(attribute)
45
+ attribute_types[attribute]
46
+ end
47
+
48
+ if ActiveModel::VERSION::MAJOR < 6
49
+ def attribute_names
50
+ attribute_types.keys
51
+ end
52
+ end
53
+ end
54
+
55
+ def slice(*methods)
56
+ HashWithIndifferentAccess.new(methods.flatten.to_h { |method| [method, public_send(method)] })
57
+ end
58
+
59
+ def values_at(*methods)
60
+ methods.flatten.map! { |method| public_send(method) }
61
+ end
62
+
63
+ def _has_attribute?(attr_name)
64
+ attribute_names.include?(attr_name.to_s)
65
+ end
66
+
67
+ def attribute_for_inspect(attr_name)
68
+ value = send(attr_name)
69
+ value.inspect
70
+ end
71
+
72
+ if ActiveModel::VERSION::MAJOR < 6
73
+ def attribute_names
74
+ self.class.attribute_names
75
+ end
76
+
77
+ def has_attribute?(attr_name)
78
+ @attributes.key?(attr_name.to_s)
79
+ end
80
+
81
+ def attribute_present?(attribute)
82
+ value = send(attribute)
83
+ !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
84
+ end
85
+
86
+ def _write_attribute(attr_name, value)
87
+ @attributes.write_from_user(attr_name.to_s, value)
88
+ value
89
+ end
90
+ end
91
+ end
92
+ end