couchrest_model 2.2.0.beta1 → 2.2.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -4
- data/Dockerfile +11 -0
- data/Gemfile +2 -0
- data/Guardfile +31 -0
- data/VERSION +1 -1
- data/couchrest_model.gemspec +2 -2
- data/docker-compose.yml +26 -0
- data/history.md +14 -1
- data/lib/couchrest/model/connection.rb +15 -20
- data/lib/couchrest/model/connection_config.rb +32 -0
- data/lib/couchrest/model/designs/migrations.rb +3 -0
- data/lib/couchrest/model/designs/view.rb +49 -34
- data/lib/couchrest/model/document_queries.rb +18 -5
- data/lib/couchrest/model/persistence.rb +3 -1
- data/lib/couchrest/model/proxyable.rb +12 -3
- data/lib/couchrest/model/server_pool.rb +22 -0
- data/lib/couchrest/model/support/couchrest_database.rb +1 -1
- data/lib/couchrest/model/utils/migrate.rb +8 -5
- data/lib/couchrest_model.rb +2 -0
- data/spec/spec_helper.rb +16 -1
- data/spec/unit/connection_config_spec.rb +41 -0
- data/spec/unit/connection_spec.rb +3 -8
- data/spec/unit/designs/migrations_spec.rb +1 -0
- data/spec/unit/designs/view_spec.rb +61 -43
- data/spec/unit/designs_spec.rb +0 -66
- data/spec/unit/persistence_spec.rb +7 -0
- data/spec/unit/proxyable_spec.rb +37 -2
- data/spec/unit/server_pool_spec.rb +31 -0
- data/spec/unit/support/couchrest_database_spec.rb +15 -0
- data/spec/unit/utils/migrate_spec.rb +136 -1
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9da5f0b0e14df586c9cb3c85dea4f0b7de7263b
|
4
|
+
data.tar.gz: c921fb6038d169cc018f2144e7f64b233bedfd4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70086755f3a4955131dc7523b62a156812b1dbd53c748286fe7b4b0c87817ff68b106abfb2448d627c86f8089a894a261cabe127e2a08c6d34e084ddd0c711b2
|
7
|
+
data.tar.gz: 8c8099e69adc06ae21e634392be1f0bc4e1b629edf7cdb4e5a4fccf092ff93d4d662e25505ec0937bac6a80ac28d0b0867926274ec9cd8689d7ac0a97e808b90
|
data/.travis.yml
CHANGED
@@ -7,19 +7,22 @@ rvm:
|
|
7
7
|
- 2.2.4
|
8
8
|
- 2.1.10
|
9
9
|
- 2.0.0
|
10
|
-
- jruby
|
11
10
|
- rbx
|
12
11
|
services: couchdb
|
13
12
|
before_install:
|
14
13
|
- gem install bundler
|
15
|
-
env:
|
16
|
-
- JRUBY_OPTS=--2.0
|
17
14
|
matrix:
|
15
|
+
include:
|
16
|
+
# JRuby takes a lot more effort
|
17
|
+
- rvm: jruby-9.1.7.0
|
18
|
+
env: JRUBY_OPTS='--2.0'
|
19
|
+
gemfile: Gemfile.activesupport-4.x
|
18
20
|
allow_failures:
|
19
21
|
- rvm: 2.0.0
|
20
22
|
gemfile: Gemfile.activesupport-5.x
|
21
23
|
- rvm: 2.1.10
|
22
24
|
gemfile: Gemfile.activesupport-5.x
|
23
|
-
- rvm: jruby
|
25
|
+
- rvm: jruby-1.7.22
|
24
26
|
gemfile: Gemfile.activesupport-5.x
|
25
27
|
- rvm: rbx # Has problems with Array#insert inheritence
|
28
|
+
- rvm: jruby-9.1.7.0
|
data/Dockerfile
ADDED
data/Gemfile
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
guard :rspec, cmd: 'rspec' do
|
27
|
+
watch(%r{^spec/.+_spec\.rb$})
|
28
|
+
watch(%r{^lib/couchrest/model/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
29
|
+
watch('spec/spec_helper.rb') { "spec" }
|
30
|
+
end
|
31
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.2.0.
|
1
|
+
2.2.0.beta2
|
data/couchrest_model.gemspec
CHANGED
@@ -22,13 +22,13 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
23
|
s.require_paths = ["lib"]
|
24
24
|
|
25
|
-
s.add_dependency("couchrest", "2.0.
|
25
|
+
s.add_dependency("couchrest", "2.0.1")
|
26
26
|
s.add_dependency("activemodel", ">= 4.0.2")
|
27
27
|
s.add_dependency("tzinfo", ">= 0.3.22")
|
28
28
|
s.add_dependency("hashdiff", "~> 0.3")
|
29
29
|
s.add_development_dependency("rspec", "~> 3.5.0")
|
30
30
|
s.add_development_dependency("rack-test", ">= 0.5.7")
|
31
|
-
s.add_development_dependency("rake", ">= 0.8.0")
|
31
|
+
s.add_development_dependency("rake", ">= 0.8.0", "< 11.0")
|
32
32
|
s.add_development_dependency("test-unit")
|
33
33
|
s.add_development_dependency("minitest", "> 4.1") #, "< 5.0") # For Kaminari and activesupport, pending removal
|
34
34
|
s.add_development_dependency("kaminari", ">= 0.14.1", "< 0.16.0")
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
version: "2"
|
2
|
+
services:
|
3
|
+
test:
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
entrypoint: ["bundle", "exec"]
|
7
|
+
command: ["rspec"]
|
8
|
+
environment:
|
9
|
+
RAILS_ENV: test
|
10
|
+
COUCH_HOST: "http://couch:5984/"
|
11
|
+
depends_on:
|
12
|
+
- couch
|
13
|
+
volumes:
|
14
|
+
- ./:/usr/lib/couchrest_model
|
15
|
+
networks:
|
16
|
+
- couchrest_model
|
17
|
+
couch:
|
18
|
+
image: couchdb:1.6
|
19
|
+
ports:
|
20
|
+
- "5984"
|
21
|
+
networks:
|
22
|
+
- couchrest_model
|
23
|
+
- default
|
24
|
+
networks:
|
25
|
+
couchrest_model:
|
26
|
+
driver: bridge
|
data/history.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# CouchRest Model Change History
|
2
2
|
|
3
|
-
## 2.2.0.
|
3
|
+
## 2.2.0.beta2 - 2017-12-03
|
4
|
+
|
5
|
+
* Make View instance respond to model_name ([PR](https://github.com/couchrest/couchrest_model/pull/217) @adamcrown)
|
6
|
+
* Feature/proxyable custom databases ([PR](https://github.com/couchrest/couchrest_model/pull/218) @ellneal, @pacoguzman)
|
7
|
+
* Fix clear cache for delete db ([PR](https://github.com/couchrest/couchrest_model/pull/216) @MarkFull, @ktaragorn, @samlown)
|
8
|
+
* Migration improvements and tests ([PR](https://github.com/couchrest/couchrest_model/pull/215) @ellneal)
|
9
|
+
* Add some guards to prevent view errors when using custom emit values ([PR](https://github.com/couchrest/couchrest_model/pull/214) @ellneal)
|
10
|
+
* Making running the tests a bit easier using Docker ([PR](https://github.com/couchrest/couchrest_model/pull/213) @ellneal)
|
11
|
+
* Upgraded to Couchrest 2.0.1 ([PR](https://github.com/couchrest/couchrest_model/pull/220) @samlown)
|
12
|
+
* Removed :persistent and simplifying connection handling with aim to reduce number of connections and improve multithreading ([PR](https://github.com/couchrest/couchrest_model/pull/220) @samlown)
|
13
|
+
* Removed passing database in views and get calls, to simplify API. Use Proxying or instantiate objects manually to use a different database.([PR](https://github.com/couchrest/couchrest_model/pull/224) @samlown)
|
14
|
+
* Document#reload! now raises DocumentNotFound on deleted doc ([PR](https://github.com/couchrest/couchrest_model/pull/223) @azul)
|
15
|
+
|
16
|
+
## 2.2.0.beta1 - 2016-08-20
|
4
17
|
|
5
18
|
* Radical re-factor of dirty tracking using Hashdiff gem, providing reliable change detection with nested data. ([PR](https://github.com/couchrest/couchrest_model/pull/211) @samlown)
|
6
19
|
* Implement proxy for factory methods ([PR](https://github.com/couchrest/couchrest_model/pull/210) @ellneal)
|
@@ -9,33 +9,30 @@ module CouchRest
|
|
9
9
|
|
10
10
|
module ClassMethods
|
11
11
|
|
12
|
-
# Overwrite the
|
12
|
+
# Overwrite the CouchRest::Document.use_database method so that a database
|
13
13
|
# name can be provided instead of a full connection.
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# We prepare the database immediatly, so ensure any connection details
|
15
|
+
# are provided in advance.
|
16
|
+
# Note that this will not work correctly with proxied models.
|
16
17
|
def use_database(db)
|
17
|
-
@
|
18
|
+
@database = prepare_database(db)
|
18
19
|
end
|
19
20
|
|
20
21
|
# Overwrite the default database method so that it always
|
21
22
|
# provides something from the configuration.
|
22
23
|
# It will try to inherit the database from an ancester
|
23
|
-
# unless the use_database method has been used
|
24
|
-
# case a new connection will be started.
|
24
|
+
# unless the use_database method has been used.
|
25
25
|
def database
|
26
26
|
@database ||= prepare_database(super)
|
27
27
|
end
|
28
28
|
|
29
29
|
def server
|
30
|
-
@server ||=
|
30
|
+
@server ||= ServerPool.instance[prepare_server_uri]
|
31
31
|
end
|
32
32
|
|
33
33
|
def prepare_database(db = nil)
|
34
|
-
db = @_use_database unless @_use_database.nil?
|
35
34
|
if db.nil? || db.is_a?(String) || db.is_a?(Symbol)
|
36
|
-
|
37
|
-
db = [conf[:prefix], db.to_s, conf[:suffix]].reject{|s| s.to_s.empty?}.join(conf[:join])
|
38
|
-
self.server.database!(db)
|
35
|
+
self.server.database!(prepare_database_name(db))
|
39
36
|
else
|
40
37
|
db
|
41
38
|
end
|
@@ -43,6 +40,11 @@ module CouchRest
|
|
43
40
|
|
44
41
|
protected
|
45
42
|
|
43
|
+
def prepare_database_name(base)
|
44
|
+
conf = connection_configuration
|
45
|
+
[conf[:prefix], base.to_s, conf[:suffix]].reject{|s| s.to_s.empty?}.join(conf[:join])
|
46
|
+
end
|
47
|
+
|
46
48
|
def prepare_server_uri
|
47
49
|
conf = connection_configuration
|
48
50
|
userinfo = [conf[:username], conf[:password]].compact.join(':')
|
@@ -52,21 +54,14 @@ module CouchRest
|
|
52
54
|
|
53
55
|
def connection_configuration
|
54
56
|
@connection_configuration ||=
|
55
|
-
self.connection.
|
57
|
+
self.connection.merge(
|
56
58
|
(load_connection_config_file[environment.to_sym] || {}).symbolize_keys
|
57
59
|
)
|
58
60
|
end
|
59
61
|
|
60
62
|
def load_connection_config_file
|
61
63
|
file = connection_config_file
|
62
|
-
|
63
|
-
(File.exists?(file) ?
|
64
|
-
YAML::load(ERB.new(IO.read(file)).result) :
|
65
|
-
{ }).symbolize_keys
|
66
|
-
end
|
67
|
-
|
68
|
-
def connection_config_cache
|
69
|
-
Thread.current[:connection_config_cache] ||= {}
|
64
|
+
ConnectionConfig.instance[file]
|
70
65
|
end
|
71
66
|
|
72
67
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CouchRest
|
2
|
+
module Model
|
3
|
+
|
4
|
+
# Thead safe caching of connection configuration files.
|
5
|
+
class ConnectionConfig
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@config_files = {}
|
10
|
+
@mutex = Mutex.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](file)
|
14
|
+
@mutex.synchronize do
|
15
|
+
@config_files[file] ||= load_config(file)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_config(file)
|
22
|
+
if File.exists?(file)
|
23
|
+
YAML::load(ERB.new(IO.read(file)).result).symbolize_keys
|
24
|
+
else
|
25
|
+
{ }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -20,21 +20,21 @@ module CouchRest
|
|
20
20
|
# outside CouchRest Model.
|
21
21
|
def initialize(design_doc, parent, new_query = {}, name = nil)
|
22
22
|
self.design_doc = design_doc
|
23
|
-
|
24
|
-
#
|
23
|
+
|
24
|
+
# Extract options from query
|
25
25
|
proxy = new_query.delete(:proxy)
|
26
26
|
delete = new_query.delete(:delete)
|
27
27
|
|
28
28
|
if parent.is_a?(Class) && parent < CouchRest::Model::Base
|
29
29
|
raise "Name must be provided for view to be initialized" if name.nil?
|
30
|
-
self.
|
31
|
-
self.
|
30
|
+
self.owner = (proxy || parent)
|
31
|
+
self.model = parent
|
32
32
|
self.name = name.to_s
|
33
33
|
# Default options:
|
34
34
|
self.query = { }
|
35
35
|
elsif parent.is_a?(self.class)
|
36
|
-
self.
|
37
|
-
self.
|
36
|
+
self.owner = (proxy || parent.owner)
|
37
|
+
self.model = parent.model
|
38
38
|
self.name = parent.name
|
39
39
|
self.query = parent.query.dup
|
40
40
|
else
|
@@ -60,11 +60,11 @@ module CouchRest
|
|
60
60
|
return @rows if @rows
|
61
61
|
if block_given?
|
62
62
|
execute do |row|
|
63
|
-
yield ViewRow.new(row,
|
63
|
+
yield ViewRow.new(row, owner)
|
64
64
|
end
|
65
65
|
else
|
66
66
|
if execute && result['rows']
|
67
|
-
@rows ||= result['rows'].map{|v| ViewRow.new(v,
|
67
|
+
@rows ||= result['rows'].map{|v| ViewRow.new(v, owner)}
|
68
68
|
else
|
69
69
|
[ ]
|
70
70
|
end
|
@@ -334,12 +334,6 @@ module CouchRest
|
|
334
334
|
update_query(:stale => value.to_s)
|
335
335
|
end
|
336
336
|
|
337
|
-
# Specify the database the view should use. If not defined,
|
338
|
-
# an attempt will be made to load its value from the model.
|
339
|
-
def database(value)
|
340
|
-
update_query(:database => value)
|
341
|
-
end
|
342
|
-
|
343
337
|
# Set the view's proxy that will be used instead of the model
|
344
338
|
# for any future searches. As soon as this enters the
|
345
339
|
# new view's initializer it will be removed and set as the model
|
@@ -367,7 +361,7 @@ module CouchRest
|
|
367
361
|
#
|
368
362
|
|
369
363
|
def page(page)
|
370
|
-
limit(
|
364
|
+
limit(model.default_per_page).skip(model.default_per_page * ([page.to_i, 1].max - 1))
|
371
365
|
end
|
372
366
|
|
373
367
|
def per(num)
|
@@ -400,6 +394,18 @@ module CouchRest
|
|
400
394
|
(offset_value / limit_value) + 1
|
401
395
|
end
|
402
396
|
|
397
|
+
# == ActiveRecord compatibility support
|
398
|
+
|
399
|
+
# Return the model name for #model just as User::ActiveRecord_Relation
|
400
|
+
# would for better ActiveRecord interoperability
|
401
|
+
def model_name
|
402
|
+
ActiveModel::Name.new(model)
|
403
|
+
end
|
404
|
+
|
405
|
+
def database
|
406
|
+
owner.database
|
407
|
+
end
|
408
|
+
|
403
409
|
protected
|
404
410
|
|
405
411
|
def include_docs!
|
@@ -421,20 +427,16 @@ module CouchRest
|
|
421
427
|
!design_doc['views'][name]['reduce'].blank?
|
422
428
|
end
|
423
429
|
|
424
|
-
def use_database
|
425
|
-
query[:database] || model.database
|
426
|
-
end
|
427
|
-
|
428
430
|
def execute(&block)
|
429
431
|
return self.result if result
|
430
|
-
raise CouchRest::Model::DatabaseNotDefined if
|
432
|
+
raise CouchRest::Model::DatabaseNotDefined if database.nil?
|
431
433
|
|
432
434
|
# Remove the reduce value if its not needed to prevent CouchDB errors
|
433
435
|
query.delete(:reduce) unless can_reduce?
|
434
436
|
|
435
|
-
design_doc.sync(
|
437
|
+
design_doc.sync(database)
|
436
438
|
|
437
|
-
self.result = design_doc.view_on(
|
439
|
+
self.result = design_doc.view_on(database, name, query, &block)
|
438
440
|
end
|
439
441
|
|
440
442
|
# Class Methods
|
@@ -492,8 +494,16 @@ module CouchRest
|
|
492
494
|
raise "View cannot be created without recognised name, :map or :by options" if opts[:by].nil?
|
493
495
|
|
494
496
|
# convert emit symbols to properties
|
495
|
-
|
496
|
-
|
497
|
+
emits, emit_guards = if opts[:emit].is_a? Symbol
|
498
|
+
[["doc['#{opts[:emit]}']"], ["doc['#{opts[:emit]}']"]]
|
499
|
+
elsif opts[:emit].is_a? Array
|
500
|
+
[
|
501
|
+
opts[:emit].map { |i| i.is_a?(Symbol) ? "doc['#{i}']" : i },
|
502
|
+
opts[:emit].map { |i| i.is_a?(Symbol) ? "doc['#{i}']" : nil}.compact
|
503
|
+
]
|
504
|
+
else
|
505
|
+
[[opts[:emit] || 1], nil]
|
506
|
+
end
|
497
507
|
|
498
508
|
opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank]
|
499
509
|
opts[:guards] ||= []
|
@@ -501,9 +511,11 @@ module CouchRest
|
|
501
511
|
|
502
512
|
keys = opts[:by].map{|o| "doc['#{o}']"}
|
503
513
|
emit_keys = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
|
504
|
-
emit_value =
|
514
|
+
emit_value = emits.length == 1 ? emits.first : "[#{emits.join(', ')}]"
|
505
515
|
opts[:guards] += keys.map{|k| "(#{k} != null)"} unless opts[:allow_nil]
|
506
516
|
opts[:guards] += keys.map{|k| "(#{k} != '')"} unless opts[:allow_blank]
|
517
|
+
opts[:guards] += emit_guards.map{|k| "(#{k} != null)"} unless emit_guards.nil? || opts[:allow_nil]
|
518
|
+
opts[:guards] += emit_guards.map{|k| "(#{k} != '')"} unless emit_guards.nil? || opts[:allow_blank]
|
507
519
|
opts[:map] = <<-EOF
|
508
520
|
function(doc) {
|
509
521
|
if (#{opts[:guards].join(' && ')}) {
|
@@ -511,7 +523,7 @@ module CouchRest
|
|
511
523
|
}
|
512
524
|
}
|
513
525
|
EOF
|
514
|
-
if opts[:reduce].nil?
|
526
|
+
if opts[:reduce].nil? && emit_guards.nil?
|
515
527
|
# Use built-in sum function by default
|
516
528
|
opts[:reduce] = "_sum"
|
517
529
|
end
|
@@ -552,10 +564,15 @@ module CouchRest
|
|
552
564
|
# A special wrapper class that provides easy access to the key
|
553
565
|
# fields in a result row.
|
554
566
|
class ViewRow < Hash
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
567
|
+
|
568
|
+
# Owner will either be the original model, or a proxy and must respond
|
569
|
+
# to `#build_from_database` and `#get` calls.
|
570
|
+
# The owner is responsible for setting the correct database on instantiation,
|
571
|
+
# if required.
|
572
|
+
attr_reader :owner
|
573
|
+
|
574
|
+
def initialize(hash, owner)
|
575
|
+
@owner = owner
|
559
576
|
replace(hash)
|
560
577
|
end
|
561
578
|
def id
|
@@ -575,12 +592,10 @@ module CouchRest
|
|
575
592
|
def doc
|
576
593
|
@doc ||= begin
|
577
594
|
if self['doc']
|
578
|
-
|
579
|
-
obj.database ||= db
|
580
|
-
obj
|
595
|
+
owner.build_from_database(self['doc'])
|
581
596
|
else
|
582
597
|
doc_id = (value.is_a?(Hash) && value['_id']) ? value['_id'] : self.id
|
583
|
-
doc_id ?
|
598
|
+
doc_id ? owner.get(doc_id) : nil
|
584
599
|
end
|
585
600
|
end
|
586
601
|
end
|