couch_potato 1.18.0 → 1.19.0
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/ruby.yml +5 -3
- data/CHANGES.md +5 -0
- data/README.md +8 -52
- data/lib/couch_potato/database.rb +0 -2
- data/lib/couch_potato/persistence.rb +9 -6
- data/lib/couch_potato/version.rb +2 -2
- data/lib/couch_potato/view/base_view_spec.rb +21 -12
- data/lib/couch_potato/view/custom_view_spec.rb +0 -4
- data/lib/couch_potato/view/custom_views.rb +12 -0
- data/lib/couch_potato/view/flex_view_spec.rb +1 -1
- data/lib/couch_potato/view/raw_view_spec.rb +2 -6
- data/lib/couch_potato/view/view_query.rb +26 -28
- data/lib/couch_potato.rb +9 -1
- data/spec/property_spec.rb +1 -1
- data/spec/single_design_document_spec.rb +63 -0
- data/spec/unit/attributes_spec.rb +1 -1
- data/spec/unit/base_view_spec_spec.rb +62 -68
- data/spec/unit/couch_potato_spec.rb +3 -0
- data/spec/unit/database_spec.rb +1 -53
- data/spec/unit/rspec_matchers_spec.rb +2 -66
- data/spec/unit/view_query_spec.rb +5 -99
- data/spec/view_updates_spec.rb +53 -5
- data/spec/views_spec.rb +1 -34
- metadata +4 -5
- data/lib/couch_potato/view/lists.rb +0 -23
- data/spec/unit/lists_spec.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46b162f592d8c1c7dada2e150793fbfc5f8c76c1e9bb8a3364f8f2895505335b
|
4
|
+
data.tar.gz: eca37db69c74f6f58ec6bb518028a100e9b74ce1bfbe7edce16b899c5caa7406
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4f8edddc5e9a667bb09cb1fa9c5ee5bbb6f374088f27d039bfedd21ce59f13be2a963803bb40be0677916ab9d1d3543eb325849dd5f05010089092ceb0ce891
|
7
|
+
data.tar.gz: 4952e15f37cce9db1e07032f8588a24d28d990cba4e657b657cd4621cf8786a08af9289daadcc1c65a1f6d7f55ea4aef447f7e7e409c488bb706e6ea39e61c4f
|
data/.github/workflows/ruby.yml
CHANGED
@@ -24,11 +24,11 @@ jobs:
|
|
24
24
|
- ruby: "jruby"
|
25
25
|
gemfile: "active_support_8_0"
|
26
26
|
steps:
|
27
|
-
- uses: actions/checkout@
|
27
|
+
- uses: actions/checkout@v4
|
28
28
|
- name: Set up CouchDB
|
29
|
-
uses: cobot/couchdb-action@
|
29
|
+
uses: cobot/couchdb-action@v5
|
30
30
|
with:
|
31
|
-
couchdb
|
31
|
+
couchdb-version: "2.3.1"
|
32
32
|
- name: Set up Ruby
|
33
33
|
uses: ruby/setup-ruby@v1
|
34
34
|
with:
|
@@ -36,3 +36,5 @@ jobs:
|
|
36
36
|
bundler-cache: true
|
37
37
|
- name: Run tests
|
38
38
|
run: bundle exec rake spec_ci
|
39
|
+
env:
|
40
|
+
DATABASE: http://admin:admin@localhost:5984/couch_potato_test
|
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -80,6 +80,12 @@ Another switch allows you to store each CouchDB view in its own design document.
|
|
80
80
|
CouchPotato::Config.split_design_documents_per_view = true
|
81
81
|
```
|
82
82
|
|
83
|
+
With the following switch, couch potato only creates a single design document for all views:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
CouchPotato::Config.single_design_document = true
|
87
|
+
```
|
88
|
+
|
83
89
|
If you are using more than one database from your app, you can create aliases:
|
84
90
|
|
85
91
|
```ruby
|
@@ -95,6 +101,7 @@ Create a `config/couchdb.yml`:
|
|
95
101
|
default: &default
|
96
102
|
split_design_documents_per_view: true # optional, default is false
|
97
103
|
digest_view_names: true # optional, default is false
|
104
|
+
single_design_document: true # optional, default is false
|
98
105
|
default_language: :erlang # optional, default is javascript
|
99
106
|
database_host: "http://127.0.0.1:5984"
|
100
107
|
|
@@ -299,7 +306,7 @@ user.valid? # => false
|
|
299
306
|
user.errors[:name] # => ['can't be blank']
|
300
307
|
```
|
301
308
|
|
302
|
-
#### Finding stuff / views
|
309
|
+
#### Finding stuff / views
|
303
310
|
|
304
311
|
In order to find data in your CouchDB you have to create a [view](http://books.couchdb.org/relax/design-documents/views) first. Couch Potato offers you to create and manage those views for you. All you have to do is declare them in your classes:
|
305
312
|
|
@@ -403,14 +410,6 @@ class User
|
|
403
410
|
end
|
404
411
|
```
|
405
412
|
|
406
|
-
commonJS modules can also be used in custom views:
|
407
|
-
|
408
|
-
```ruby
|
409
|
-
class User
|
410
|
-
view :all, :map => "function(doc) { emit(null, require("views/lib/test").test)}", :lib => {:test => "exports.test = 'test'"}, :include_docs => true, :type => :custom
|
411
|
-
end
|
412
|
-
```
|
413
|
-
|
414
413
|
If you don't want the results to be converted into models the raw view is your friend:
|
415
414
|
|
416
415
|
```ruby
|
@@ -450,49 +449,6 @@ You can pass in your own view specifications by passing in `:type => MyViewSpecC
|
|
450
449
|
|
451
450
|
If turned on, Couch Potato will append an MD5 digest of the map function to each view name. This makes sure (together with split_design_documents_per_view) that no views/design documents are ever updated. Instead, new ones are created. Since reindexing can take a long time once your database is larger, you want to avoid blocking your app while CouchDB is busy. Instead, you create a new view, warm it up, and only then start using it.
|
452
451
|
|
453
|
-
##### Lists
|
454
|
-
|
455
|
-
CouchPotato also supports [CouchDB lists](http://books.couchdb.org/relax/design-documents/lists). With lists you can process the result of a view query with another JavaScript function. This can be useful for example if you want to filter your results, or add some data to each document.
|
456
|
-
|
457
|
-
Defining a list works similarly to views:
|
458
|
-
|
459
|
-
```ruby
|
460
|
-
class User
|
461
|
-
include CouchPotato::Persistence
|
462
|
-
|
463
|
-
property :first_name
|
464
|
-
view :with_full_name, key: first_namne, list: :add_last_name
|
465
|
-
view :all, key: :first_name
|
466
|
-
|
467
|
-
list :add_last_name, <<-JS
|
468
|
-
function(head, req) {
|
469
|
-
var row;
|
470
|
-
send('{"rows": [');
|
471
|
-
while(row = getRow()) {
|
472
|
-
row.doc.name = row.doc.first_name + ' doe';
|
473
|
-
send(JSON.stringify(row));
|
474
|
-
};
|
475
|
-
send(']}');
|
476
|
-
}
|
477
|
-
JS
|
478
|
-
end
|
479
|
-
|
480
|
-
CouchPotato.database.save User.new(first_name: 'joe')
|
481
|
-
CouchPotato.database.view(User.with_full_name).first.name # => 'joe doe'
|
482
|
-
```
|
483
|
-
|
484
|
-
You can also pass in the list at query time:
|
485
|
-
|
486
|
-
```ruby
|
487
|
-
CouchPotato.database.view(User.all(list: :add_last_name))
|
488
|
-
```
|
489
|
-
|
490
|
-
And you can pass parameters to the list:
|
491
|
-
|
492
|
-
```ruby
|
493
|
-
CouchPotato.database.view(User.all(list: :add_last_name, list_params: {filter: '*'}))
|
494
|
-
```
|
495
|
-
|
496
452
|
#### Associations
|
497
453
|
|
498
454
|
Not supported. Not sure if they ever will be. You can implement those yourself using views and custom methods on your models.
|
@@ -12,16 +12,20 @@ require File.dirname(__FILE__) + '/persistence/type_caster'
|
|
12
12
|
require File.dirname(__FILE__) + '/persistence/revisions'
|
13
13
|
require File.dirname(__FILE__) + '/forbidden_attributes_protection'
|
14
14
|
require File.dirname(__FILE__) + '/view/custom_views'
|
15
|
-
require File.dirname(__FILE__) + '/view/lists'
|
16
15
|
require File.dirname(__FILE__) + '/view/view_query'
|
17
16
|
|
18
17
|
|
19
18
|
module CouchPotato
|
20
19
|
module Persistence
|
20
|
+
module TrackModels
|
21
|
+
def inherited(child)
|
22
|
+
super
|
23
|
+
CouchPotato.models << child
|
24
|
+
end
|
25
|
+
end
|
21
26
|
|
22
27
|
def self.included(base) #:nodoc:
|
23
|
-
base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews
|
24
|
-
CouchPotato::View::Lists
|
28
|
+
base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews
|
25
29
|
base.send :include, DirtyAttributes, GhostAttributes, Attachments
|
26
30
|
base.send :include, MagicTimestamps, ActiveModelCompliance,
|
27
31
|
ForbiddenAttributesProtection, Revisions
|
@@ -31,9 +35,8 @@ module CouchPotato
|
|
31
35
|
alias_method :id, :_id
|
32
36
|
alias_method :id=, :_id=
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
CouchPotato.models << child
|
38
|
+
class << self
|
39
|
+
prepend TrackModels
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
data/lib/couch_potato/version.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module CouchPotato
|
4
4
|
module View
|
5
5
|
class BaseViewSpec
|
6
|
-
attr_reader :reduce_function, :
|
6
|
+
attr_reader :reduce_function, :design_document, :view_name, :klass, :options, :language
|
7
7
|
attr_accessor :view_parameters
|
8
8
|
|
9
9
|
private :klass, :options
|
@@ -11,7 +11,7 @@ module CouchPotato
|
|
11
11
|
def initialize(klass, view_name, options, view_parameters)
|
12
12
|
normalized_view_parameters = ViewParameters.normalize_view_parameters view_parameters
|
13
13
|
|
14
|
-
|
14
|
+
|
15
15
|
@language = options[:language] || Config.default_language
|
16
16
|
|
17
17
|
assert_valid_view_parameters normalized_view_parameters
|
@@ -19,16 +19,12 @@ module CouchPotato
|
|
19
19
|
@options = options
|
20
20
|
@view_name = compute_view_name(view_name,
|
21
21
|
options.key?(:digest_view_name) ? options[:digest_view_name] : Config.digest_view_names)
|
22
|
-
@design_document =
|
23
|
-
@list_params = normalized_view_parameters.delete :list_params
|
24
|
-
|
25
|
-
@list_function = klass.lists(@list_name) if @list_name
|
22
|
+
@design_document = design_doc_name
|
26
23
|
@view_parameters = {}
|
27
24
|
%i[group include_docs descending group_level limit].each do |key|
|
28
25
|
@view_parameters[key] = options[key] if options.include?(key)
|
29
26
|
end
|
30
27
|
@view_parameters.merge!(normalized_view_parameters)
|
31
|
-
@view_parameters.merge!(@list_params) if @list_params
|
32
28
|
end
|
33
29
|
|
34
30
|
def process_results(results)
|
@@ -42,10 +38,16 @@ module CouchPotato
|
|
42
38
|
private
|
43
39
|
|
44
40
|
def compute_view_name(view_name, digest)
|
41
|
+
name = if CouchPotato::Config.single_design_document
|
42
|
+
"#{translate_to_design_doc_name(klass.to_s, view_name)}-#{view_name}"
|
43
|
+
else
|
44
|
+
view_name
|
45
|
+
end
|
46
|
+
|
45
47
|
if digest
|
46
|
-
"#{
|
48
|
+
"#{name}-#{Digest::MD5.hexdigest(map_function + reduce_function.to_s)}"
|
47
49
|
else
|
48
|
-
|
50
|
+
name
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
@@ -56,10 +58,18 @@ module CouchPotato
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def valid_view_parameters
|
59
|
-
%w[
|
61
|
+
%w[key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end]
|
62
|
+
end
|
63
|
+
|
64
|
+
def design_doc_name
|
65
|
+
if CouchPotato::Config.single_design_document
|
66
|
+
'couch_potato'
|
67
|
+
else
|
68
|
+
translate_to_design_doc_name(klass.to_s, view_name)
|
69
|
+
end
|
60
70
|
end
|
61
71
|
|
62
|
-
def translate_to_design_doc_name(klass_name, view_name
|
72
|
+
def translate_to_design_doc_name(klass_name, view_name)
|
63
73
|
klass_name = klass_name.dup
|
64
74
|
klass_name.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
65
75
|
klass_name.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
@@ -68,7 +78,6 @@ module CouchPotato
|
|
68
78
|
|
69
79
|
if CouchPotato::Config.split_design_documents_per_view
|
70
80
|
doc_name += "_view_#{view_name}" if view_name.present?
|
71
|
-
doc_name += "_list_#{list_name}" if list_name.present?
|
72
81
|
end
|
73
82
|
doc_name
|
74
83
|
end
|
@@ -9,9 +9,21 @@ require 'couch_potato/view/view_parameters'
|
|
9
9
|
module CouchPotato
|
10
10
|
module View
|
11
11
|
module CustomViews
|
12
|
+
module TrackViews
|
13
|
+
def inherited(child)
|
14
|
+
super
|
15
|
+
CouchPotato.views << child
|
16
|
+
end
|
17
|
+
end
|
12
18
|
|
13
19
|
def self.included(base) #:nodoc:
|
14
20
|
base.extend ClassMethods
|
21
|
+
CouchPotato.views << base
|
22
|
+
base.class_eval do
|
23
|
+
class << self
|
24
|
+
prepend TrackViews
|
25
|
+
end
|
26
|
+
end
|
15
27
|
end
|
16
28
|
|
17
29
|
module ClassMethods
|
@@ -96,7 +96,7 @@ module CouchPotato
|
|
96
96
|
end
|
97
97
|
|
98
98
|
delegate :view_name, :view_parameters, :design_document, :map_function,
|
99
|
-
:reduce_function, :
|
99
|
+
:reduce_function, :language, to: :view_spec_delegate
|
100
100
|
|
101
101
|
def process_results(results)
|
102
102
|
results = Results.new(results)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module CouchPotato
|
2
2
|
module View
|
3
|
-
# A view for custom map/reduce functions that returns the raw data
|
3
|
+
# A view for custom map/reduce functions that returns the raw data from couchdb
|
4
4
|
#
|
5
5
|
# example:
|
6
6
|
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :reduce => nil
|
@@ -10,7 +10,7 @@ module CouchPotato
|
|
10
10
|
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :results_filter => lambda{|results| results['rows].map{|row| row['value']}}
|
11
11
|
#
|
12
12
|
# example:
|
13
|
-
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw
|
13
|
+
# view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw"
|
14
14
|
class RawViewSpec < BaseViewSpec
|
15
15
|
def map_function
|
16
16
|
options[:map]
|
@@ -19,10 +19,6 @@ module CouchPotato
|
|
19
19
|
def reduce_function
|
20
20
|
options[:reduce]
|
21
21
|
end
|
22
|
-
|
23
|
-
def lib
|
24
|
-
options[:lib]
|
25
|
-
end
|
26
22
|
end
|
27
23
|
end
|
28
24
|
end
|
@@ -2,18 +2,13 @@ module CouchPotato
|
|
2
2
|
module View
|
3
3
|
# Used to query views (and create them if they don't exist). Usually you won't have to use this class directly. Instead it is used internally by the CouchPotato::Database.view method.
|
4
4
|
class ViewQuery
|
5
|
-
def initialize(couchrest_database, design_document_name, view,
|
5
|
+
def initialize(couchrest_database, design_document_name, view, language = :javascript)
|
6
6
|
@database = couchrest_database
|
7
7
|
@design_document_name = design_document_name
|
8
8
|
@view_name = view.keys[0]
|
9
9
|
@map_function = view.values[0][:map]
|
10
10
|
@reduce_function = view.values[0][:reduce]
|
11
|
-
@lib = lib
|
12
11
|
@language = language
|
13
|
-
if list
|
14
|
-
@list_function = list.values[0]
|
15
|
-
@list_name = list.keys[0]
|
16
|
-
end
|
17
12
|
end
|
18
13
|
|
19
14
|
def query_view!(parameters = {})
|
@@ -42,34 +37,41 @@ module CouchPotato
|
|
42
37
|
def update_view
|
43
38
|
design_doc = @database.get "_design/#{@design_document_name}" rescue nil
|
44
39
|
original_views = design_doc && design_doc['views'].dup
|
45
|
-
|
46
|
-
view_updated unless design_doc.nil?
|
40
|
+
view_updated
|
47
41
|
design_doc ||= empty_design_document
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
if CouchPotato::Config.single_design_document
|
43
|
+
design_doc['views'] = all_views
|
44
|
+
else
|
45
|
+
design_doc['views'][@view_name.to_s] = view_functions
|
51
46
|
end
|
52
|
-
if
|
53
|
-
design_doc
|
54
|
-
design_doc['lists'][@list_name.to_s] = @list_function
|
47
|
+
if original_views != design_doc['views']
|
48
|
+
@database.save_doc(design_doc)
|
55
49
|
end
|
56
|
-
@database.save_doc(design_doc) if original_views != design_doc['views'] || original_lists != design_doc['lists']
|
57
50
|
end
|
58
51
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
52
|
+
def all_views
|
53
|
+
CouchPotato.views.flat_map do |klass|
|
54
|
+
specs = klass.views.map { |view_name, view| klass.execute_view(view_name, {}) }
|
55
|
+
specs.map do |klass_spec|
|
56
|
+
{ klass_spec.view_name => view_functions(klass_spec.map_function, klass_spec.reduce_function) }
|
57
|
+
end
|
58
|
+
end.inject(&:merge)
|
59
|
+
end
|
60
|
+
|
61
|
+
def view_functions(map_function = @map_function, reduce_function = @reduce_function)
|
62
|
+
{'map' => map_function, 'reduce' => reduce_function}.compact
|
65
63
|
end
|
66
64
|
|
67
65
|
def empty_design_document
|
68
|
-
{'views' => {},
|
66
|
+
{'views' => {}, "_id" => "_design/#{@design_document_name}", "language" => @language.to_s}
|
69
67
|
end
|
70
68
|
|
71
69
|
def view_has_been_updated?
|
72
|
-
|
70
|
+
if CouchPotato::Config.single_design_document
|
71
|
+
updated_views.any?
|
72
|
+
else
|
73
|
+
updated_views[[@design_document_name, @view_name]]
|
74
|
+
end
|
73
75
|
end
|
74
76
|
|
75
77
|
def view_updated
|
@@ -81,11 +83,7 @@ module CouchPotato
|
|
81
83
|
end
|
82
84
|
|
83
85
|
def query_view(parameters)
|
84
|
-
|
85
|
-
@database.connection.get CouchRest.paramify_url("/#{@database.name}/_design/#{@design_document_name}/_list/#{@list_name}/#{@view_name}", parameters)
|
86
|
-
else
|
87
|
-
@database.view view_url, parameters
|
88
|
-
end
|
86
|
+
@database.view view_url, parameters
|
89
87
|
end
|
90
88
|
|
91
89
|
def view_url
|
data/lib/couch_potato.rb
CHANGED
@@ -9,9 +9,10 @@ JSON.create_id = 'ruby_class'
|
|
9
9
|
CouchRest.decode_json_objects = true
|
10
10
|
|
11
11
|
module CouchPotato
|
12
|
-
Config = Struct.new(:database_host, :database_name, :digest_view_names,
|
12
|
+
Config = Struct.new(:database_host, :database_name, :digest_view_names, :single_design_document,
|
13
13
|
:split_design_documents_per_view, :default_language, :additional_databases).new
|
14
14
|
Config.split_design_documents_per_view = false
|
15
|
+
Config.single_design_document = false
|
15
16
|
Config.digest_view_names = false
|
16
17
|
Config.default_language = :javascript
|
17
18
|
Config.database_host = 'http://127.0.0.1:5984'
|
@@ -29,6 +30,7 @@ module CouchPotato
|
|
29
30
|
Config.database_host = config['database_host'] if config['database_host']
|
30
31
|
Config.additional_databases = config['additional_databases'].stringify_keys if config['additional_databases']
|
31
32
|
Config.split_design_documents_per_view = config['split_design_documents_per_view'] if config['split_design_documents_per_view']
|
33
|
+
Config.single_design_document = config['single_design_document'] if config['single_design_document']
|
32
34
|
Config.digest_view_names = config['digest_view_names'] if config['digest_view_names']
|
33
35
|
Config.default_language = config['default_language'] if config['default_language']
|
34
36
|
end
|
@@ -40,6 +42,12 @@ module CouchPotato
|
|
40
42
|
@models
|
41
43
|
end
|
42
44
|
|
45
|
+
# returns all the classes that include the CouchPotato::View::CustomViews module
|
46
|
+
def self.views
|
47
|
+
@views ||= []
|
48
|
+
@views
|
49
|
+
end
|
50
|
+
|
43
51
|
# Returns a database instance which you can then use to create objects and query views. You have to set the CouchPotato::Config.database_name before this works.
|
44
52
|
def self.database
|
45
53
|
Thread.current[:__couch_potato_database] ||= Database.new(couchrest_database)
|
data/spec/property_spec.rb
CHANGED
@@ -418,7 +418,7 @@ describe 'properties' do
|
|
418
418
|
|
419
419
|
it "should include complex datatypes fully inspected" do
|
420
420
|
comment.title = {'en' => 'Blog post'}
|
421
|
-
expect(comment.inspect).to
|
421
|
+
expect(comment.inspect).to match(/title: {"en"\s*=>\s*"Blog post"}/)
|
422
422
|
|
423
423
|
comment.title = nil
|
424
424
|
expect(comment.inspect).to include('title: nil')
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'single design document' do
|
4
|
+
let(:db) { CouchPotato.database }
|
5
|
+
let(:couchrest_db) { db.couchrest_database }
|
6
|
+
|
7
|
+
class Thing1
|
8
|
+
include CouchPotato::Persistence
|
9
|
+
|
10
|
+
property :title
|
11
|
+
|
12
|
+
view :all, key: :title
|
13
|
+
end
|
14
|
+
|
15
|
+
class Thing2
|
16
|
+
include CouchPotato::Persistence
|
17
|
+
|
18
|
+
property :name
|
19
|
+
|
20
|
+
view :all, key: :name
|
21
|
+
end
|
22
|
+
|
23
|
+
class Thing3 < Thing1 # should work with inheritance
|
24
|
+
property :tag
|
25
|
+
|
26
|
+
view :by_tag, key: :tag
|
27
|
+
end
|
28
|
+
|
29
|
+
before(:each) do
|
30
|
+
recreate_db
|
31
|
+
CouchPotato::Config.single_design_document = true
|
32
|
+
CouchPotato.views.select! { |v| [Thing1, Thing2, Thing3].include?(v) } # clear classes from other specs
|
33
|
+
end
|
34
|
+
|
35
|
+
after(:each) do
|
36
|
+
CouchPotato::Config.single_design_document = false
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'creates a single design document for all views' do
|
40
|
+
thing1 = Thing1.new title: 't1'
|
41
|
+
db.save! thing1
|
42
|
+
thing2 = Thing2.new name: 'n2'
|
43
|
+
db.save! thing2
|
44
|
+
|
45
|
+
db.view(Thing1.all) # create all views when querying the first one
|
46
|
+
|
47
|
+
expect(couchrest_db.get('_design/couch_potato')['views'].keys)
|
48
|
+
.to(eq(['thing1-all', 'thing2-all', 'thing3-by_tag']))
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns the correct models' do
|
52
|
+
thing1 = Thing1.new title: 't1'
|
53
|
+
db.save! thing1
|
54
|
+
thing2 = Thing2.new name: 'n2'
|
55
|
+
db.save! thing2
|
56
|
+
thing3 = Thing3.new tag: 'tag1'
|
57
|
+
db.save! thing3
|
58
|
+
|
59
|
+
expect(db.view(Thing1.all('t1'))).to eq([thing1])
|
60
|
+
expect(db.view(Thing2.all('n2'))).to eq([thing2])
|
61
|
+
expect(db.view(Thing3.by_tag('tag1'))).to eq([thing3])
|
62
|
+
end
|
63
|
+
end
|
@@ -33,8 +33,7 @@ describe CouchPotato::View::BaseViewSpec, 'initialize' do
|
|
33
33
|
:group_level => 1,
|
34
34
|
:reduce => false,
|
35
35
|
:include_docs => true,
|
36
|
-
:inclusive_end => true
|
37
|
-
:list_params => {}
|
36
|
+
:inclusive_end => true
|
38
37
|
}
|
39
38
|
}.not_to raise_error
|
40
39
|
end
|
@@ -54,70 +53,72 @@ describe CouchPotato::View::BaseViewSpec, 'initialize' do
|
|
54
53
|
expect(spec.view_parameters).to eq({:key => '2'})
|
55
54
|
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
it "generates the design document path by snake_casing the class name but keeping double colons" do
|
63
|
-
spec = CouchPotato::View::BaseViewSpec.new 'Foo::BarBaz', '', {}, ''
|
64
|
-
expect(spec.design_document).to eq('foo::bar_baz')
|
65
|
-
end
|
66
|
-
|
67
|
-
it "generates the design document independent of the view name by default" do
|
68
|
-
CouchPotato::Config.split_design_documents_per_view = false
|
69
|
-
spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, ''
|
70
|
-
expect(spec.design_document).to eq('user')
|
71
|
-
end
|
72
|
-
|
73
|
-
it "generates the design document per view if configured to" do
|
74
|
-
CouchPotato::Config.split_design_documents_per_view = true
|
75
|
-
spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, ''
|
76
|
-
expect(spec.design_document).to eq('user_view_by_login_and_email')
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'adds the view name digest to the design doc name' do
|
80
|
-
CouchPotato::Config.split_design_documents_per_view = true
|
81
|
-
spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email',
|
82
|
-
{digest_view_name: true, map: 'function(doc) {}'}, ''
|
83
|
-
|
84
|
-
expect(spec.design_document).to eq('user_view_by_login_and_email-375c815fcb4f977f330a2edfadc7f74d')
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'builds the name digest by hashing the map and reduce function if there is one' do
|
88
|
-
CouchPotato::Config.split_design_documents_per_view = true
|
89
|
-
spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email',
|
90
|
-
{digest_view_name: true, map: 'function(doc) {}', reduce: 'function(key, values) {}'}, ''
|
91
|
-
|
92
|
-
expect(spec.design_document).to eq('user_view_by_login_and_email-c9f83cec3dab954a8ca56330006f187e')
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
it "generates the design document independent of the list name by default" do
|
97
|
-
CouchPotato::Config.split_design_documents_per_view = false
|
98
|
-
spec = CouchPotato::View::BaseViewSpec.new double(lists: nil, :to_s => 'User'), '', {list: 'test_list'}, {}
|
99
|
-
expect(spec.design_document).to eq('user')
|
100
|
-
end
|
56
|
+
context 'when single design document is enabled' do
|
57
|
+
before(:each) do
|
58
|
+
CouchPotato::Config.single_design_document = true
|
59
|
+
end
|
101
60
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
61
|
+
after(:each) do
|
62
|
+
CouchPotato::Config.single_design_document = false
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'generates one design document for all views' do
|
66
|
+
spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, ''
|
107
67
|
|
108
|
-
|
109
|
-
|
110
|
-
expect(spec.list_name).to eq(:test_list)
|
68
|
+
expect(spec.design_document).to eq('couch_potato')
|
69
|
+
end
|
111
70
|
end
|
112
71
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
72
|
+
context 'when single design document is disabled' do
|
73
|
+
before(:each) do
|
74
|
+
CouchPotato::Config.single_design_document = false
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'and split design documents per view is enabled' do
|
78
|
+
before(:each) do
|
79
|
+
CouchPotato::Config.split_design_documents_per_view = true
|
80
|
+
end
|
81
|
+
|
82
|
+
after(:each) do
|
83
|
+
CouchPotato::Config.split_design_documents_per_view = false
|
84
|
+
end
|
85
|
+
|
86
|
+
it "generates one design document per view" do
|
87
|
+
spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, ''
|
88
|
+
expect(spec.design_document).to eq('user_view_by_login_and_email')
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'adds the view name digest to the design doc name' do
|
92
|
+
spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email',
|
93
|
+
{digest_view_name: true, map: 'function(doc) {}'}, ''
|
94
|
+
|
95
|
+
expect(spec.design_document).to eq('user_view_by_login_and_email-375c815fcb4f977f330a2edfadc7f74d')
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'builds the name digest by hashing the map and reduce function if there is one' do
|
99
|
+
spec = CouchPotato::View::RawViewSpec.new 'User', 'by_login_and_email',
|
100
|
+
{digest_view_name: true, map: 'function(doc) {}', reduce: 'function(key, values) {}'}, ''
|
101
|
+
|
102
|
+
expect(spec.design_document).to eq('user_view_by_login_and_email-c9f83cec3dab954a8ca56330006f187e')
|
103
|
+
end
|
104
|
+
end
|
117
105
|
|
118
|
-
|
119
|
-
|
120
|
-
|
106
|
+
context 'and split design documents per view is disabled' do
|
107
|
+
before(:each) do
|
108
|
+
CouchPotato::Config.split_design_documents_per_view = false
|
109
|
+
end
|
110
|
+
|
111
|
+
it "generates the design document path by snake_casing the class name but keeping double colons" do
|
112
|
+
spec = CouchPotato::View::BaseViewSpec.new 'Foo::BarBaz', '', {}, ''
|
113
|
+
expect(spec.design_document).to eq('foo::bar_baz')
|
114
|
+
end
|
115
|
+
|
116
|
+
it "generates the design document independent of the view name" do
|
117
|
+
spec = CouchPotato::View::BaseViewSpec.new 'User', 'by_login_and_email', {}, ''
|
118
|
+
expect(spec.design_document).to eq('user')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
121
122
|
end
|
122
123
|
|
123
124
|
it 'returns the view name' do
|
@@ -146,13 +147,6 @@ describe CouchPotato::View::BaseViewSpec, 'initialize' do
|
|
146
147
|
end
|
147
148
|
end
|
148
149
|
|
149
|
-
it "returns the list function" do
|
150
|
-
klass = double 'class'
|
151
|
-
allow(klass).to receive(:lists).with('test_list').and_return('<list_code>')
|
152
|
-
spec = CouchPotato::View::BaseViewSpec.new klass, 'all', {list: 'test_list'}, {}
|
153
|
-
expect(spec.list_function).to eq('<list_code>')
|
154
|
-
end
|
155
|
-
|
156
150
|
it 'reads the language from the couch potato config by default' do
|
157
151
|
CouchPotato::Config.default_language = :ruby
|
158
152
|
spec = CouchPotato::View::BaseViewSpec.new Object, 'all', {}, {}
|
@@ -8,6 +8,7 @@ describe CouchPotato, '.configure' do
|
|
8
8
|
CouchPotato::Config.database_name = nil
|
9
9
|
CouchPotato::Config.split_design_documents_per_view = false
|
10
10
|
CouchPotato::Config.digest_view_names = false
|
11
|
+
CouchPotato::Config.single_design_document = false
|
11
12
|
CouchPotato::Config.default_language = :javascript
|
12
13
|
CouchPotato::Config.database_host = 'http://127.0.0.1:5984'
|
13
14
|
CouchPotato::Config.additional_databases = {}
|
@@ -35,12 +36,14 @@ describe CouchPotato, '.configure' do
|
|
35
36
|
test2: 'test2_db'
|
36
37
|
},
|
37
38
|
split_design_documents_per_view: true,
|
39
|
+
single_design_document: true,
|
38
40
|
digest_view_names: true,
|
39
41
|
default_language: 'erlang'
|
40
42
|
)
|
41
43
|
|
42
44
|
expect(CouchPotato::Config.database_name).to eq('testdb')
|
43
45
|
expect(CouchPotato::Config.split_design_documents_per_view).to eq(true)
|
46
|
+
expect(CouchPotato::Config.single_design_document).to eq(true)
|
44
47
|
expect(CouchPotato::Config.digest_view_names).to eq(true)
|
45
48
|
expect(CouchPotato::Config.default_language).to eq('erlang')
|
46
49
|
expect(CouchPotato::Config.database_host).to eq('http://10.0.0.1:2000')
|
data/spec/unit/database_spec.rb
CHANGED
@@ -415,29 +415,10 @@ describe CouchPotato::Database, 'view' do
|
|
415
415
|
allow(CouchPotato::View::ViewQuery).to receive_messages(new: double('view query', query_view!: { 'rows' => [@result] }))
|
416
416
|
end
|
417
417
|
|
418
|
-
it 'initializes a view query with map/reduce/list/lib funtions' do
|
419
|
-
allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view',
|
420
|
-
map_function: '<map_code>', reduce_function: '<reduce_code>',
|
421
|
-
lib: { test: '<test_code>' },
|
422
|
-
list_name: 'my_list', list_function: '<list_code>', language: 'javascript')
|
423
|
-
expect(CouchPotato::View::ViewQuery).to receive(:new).with(
|
424
|
-
@couchrest_db,
|
425
|
-
'design_doc',
|
426
|
-
{ 'my_view' => {
|
427
|
-
map: '<map_code>',
|
428
|
-
reduce: '<reduce_code>'
|
429
|
-
} },
|
430
|
-
{ 'my_list' => '<list_code>' },
|
431
|
-
{ test: '<test_code>' },
|
432
|
-
'javascript'
|
433
|
-
)
|
434
|
-
@db.view(@spec)
|
435
|
-
end
|
436
418
|
|
437
|
-
it 'initializes a view query with map/reduce
|
419
|
+
it 'initializes a view query with map/reduce functions' do
|
438
420
|
allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view',
|
439
421
|
map_function: '<map_code>', reduce_function: '<reduce_code>',
|
440
|
-
lib: nil, list_name: 'my_list', list_function: '<list_code>',
|
441
422
|
language: 'javascript')
|
442
423
|
expect(CouchPotato::View::ViewQuery).to receive(:new).with(
|
443
424
|
@couchrest_db,
|
@@ -446,44 +427,11 @@ describe CouchPotato::Database, 'view' do
|
|
446
427
|
map: '<map_code>',
|
447
428
|
reduce: '<reduce_code>'
|
448
429
|
} },
|
449
|
-
{ 'my_list' => '<list_code>' },
|
450
|
-
nil,
|
451
430
|
'javascript'
|
452
431
|
)
|
453
432
|
@db.view(@spec)
|
454
433
|
end
|
455
434
|
|
456
|
-
it 'initializes a view query with only map/reduce/lib functions' do
|
457
|
-
allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view',
|
458
|
-
map_function: '<map_code>', reduce_function: '<reduce_code>',
|
459
|
-
list_name: nil, list_function: nil,
|
460
|
-
lib: { test: '<test_code>' })
|
461
|
-
expect(CouchPotato::View::ViewQuery).to receive(:new).with(
|
462
|
-
@couchrest_db,
|
463
|
-
'design_doc',
|
464
|
-
{ 'my_view' => {
|
465
|
-
map: '<map_code>',
|
466
|
-
reduce: '<reduce_code>'
|
467
|
-
} }, nil, { test: '<test_code>' }, anything
|
468
|
-
)
|
469
|
-
@db.view(@spec)
|
470
|
-
end
|
471
|
-
|
472
|
-
it 'initializes a view query with only map/reduce functions' do
|
473
|
-
allow(@spec).to receive_messages(design_document: 'design_doc', view_name: 'my_view',
|
474
|
-
map_function: '<map_code>', reduce_function: '<reduce_code>',
|
475
|
-
lib: nil, list_name: nil, list_function: nil)
|
476
|
-
expect(CouchPotato::View::ViewQuery).to receive(:new).with(
|
477
|
-
@couchrest_db,
|
478
|
-
'design_doc',
|
479
|
-
{ 'my_view' => {
|
480
|
-
map: '<map_code>',
|
481
|
-
reduce: '<reduce_code>'
|
482
|
-
} }, nil, nil, anything
|
483
|
-
)
|
484
|
-
@db.view(@spec)
|
485
|
-
end
|
486
|
-
|
487
435
|
it 'sets itself on returned docs that have an accessor' do
|
488
436
|
allow(@result).to receive(:respond_to?).and_return(false)
|
489
437
|
allow(@result).to receive(:respond_to?).with(:database=).and_return(true)
|
@@ -36,22 +36,6 @@ describe CouchPotato::RSpec::MapToMatcher do
|
|
36
36
|
expect(spec).to map({}).to([nil, "2013-05-17T15:00:00.000Z"])
|
37
37
|
end
|
38
38
|
|
39
|
-
it "should work with commonJS modules that use 'exports'" do
|
40
|
-
spec = double(
|
41
|
-
:map_function => "function(doc) { var test = require('views/lib/test'); emit(null, test.test); }",
|
42
|
-
:lib => {:test => "exports.test = 'test';"}
|
43
|
-
)
|
44
|
-
expect(spec).to map({}).to([nil, "test"])
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should work with commonJS modules that use 'module.exports'" do
|
48
|
-
spec = double(
|
49
|
-
:map_function => "function(doc) { var test = require('views/lib/test'); emit(null, test.test); }",
|
50
|
-
:lib => {:test => "module.exports.test = 'test';"}
|
51
|
-
)
|
52
|
-
expect(spec).to map({}).to([nil, "test"])
|
53
|
-
end
|
54
|
-
|
55
39
|
describe "failing specs" do
|
56
40
|
before(:each) do
|
57
41
|
@view_spec = double(:map_function => "function(doc) {emit(doc.name, null)}")
|
@@ -145,22 +129,6 @@ describe CouchPotato::RSpec::MapReduceToMatcher do
|
|
145
129
|
expect(spec).to map_reduce({}).to({"key" => nil, "value" => "2013-05-17T15:00:00.000Z"})
|
146
130
|
end
|
147
131
|
|
148
|
-
it "should handle CommonJS requires for modules that use 'exports'" do
|
149
|
-
spec = double(
|
150
|
-
:map_function => "function() { var test = require('views/lib/test'); emit(null, test.test); }",
|
151
|
-
:reduce_function => "function(keys, values) { return 'test' }",
|
152
|
-
:lib => {:test => "exports.test = 'test'"})
|
153
|
-
expect(spec).to map_reduce({}).to({"key" => nil, "value" => "test"})
|
154
|
-
end
|
155
|
-
|
156
|
-
it "should handle CommonJS requires for modules that use 'module.exports'" do
|
157
|
-
spec = double(
|
158
|
-
:map_function => "function() { var test = require('views/lib/test'); emit(null, test.test); }",
|
159
|
-
:reduce_function => "function(keys, values) { return 'test' }",
|
160
|
-
:lib => {:test => "module.exports.test = 'test'"})
|
161
|
-
expect(spec).to map_reduce({}).to({"key" => nil, "value" => "test"})
|
162
|
-
end
|
163
|
-
|
164
132
|
it "should handle sum function" do
|
165
133
|
spec = double(
|
166
134
|
:map_function => "function(doc) { emit(null, doc.age); }",
|
@@ -240,13 +208,13 @@ describe CouchPotato::RSpec::MapReduceToMatcher do
|
|
240
208
|
it "should have a nice error message for failing should" do
|
241
209
|
expect {
|
242
210
|
expect(@view_spec).to map_reduce(@docs).with_options(:group => false).to({"key" => nil, "value" => 9})
|
243
|
-
}.to raise_error(
|
211
|
+
}.to raise_error(%r{Expected to map/reduce to \[{"key"\s*=>\s*nil, "value"\s*=>\s*9}\] but got \[{"key"\s*=>\s*nil, "value"\s*=>\s*8}\].})
|
244
212
|
end
|
245
213
|
|
246
214
|
it "should have a nice error message for failing should not" do
|
247
215
|
expect {
|
248
216
|
expect(@view_spec).not_to map_reduce(@docs).with_options(:group => false).to({"key" => nil, "value" => 8})
|
249
|
-
}.to raise_error(
|
217
|
+
}.to raise_error(%r{Expected not to map/reduce to \[{"key"\s*=>\s*nil, "value"\s*=>\s*8}\] but did.})
|
250
218
|
end
|
251
219
|
end
|
252
220
|
|
@@ -283,35 +251,3 @@ describe CouchPotato::RSpec::MapReduceToMatcher do
|
|
283
251
|
end
|
284
252
|
end
|
285
253
|
|
286
|
-
describe CouchPotato::RSpec::ListAsMatcher do
|
287
|
-
before(:each) do
|
288
|
-
@view_spec = double(:list_function => "function() {var row = getRow(); send(JSON.stringify([{text: row.text + ' world'}]));}")
|
289
|
-
end
|
290
|
-
|
291
|
-
it "should pass if the function return the expected json" do
|
292
|
-
expect(@view_spec).to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}])
|
293
|
-
end
|
294
|
-
|
295
|
-
it "should not pass if the function does not return the expected json" do
|
296
|
-
expect(@view_spec).not_to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}])
|
297
|
-
end
|
298
|
-
|
299
|
-
it "should work with date values" do
|
300
|
-
spec = double(:list_function => "function() { send(JSON.stringify([{date: new Date(1368802800000)}])); }")
|
301
|
-
expect(spec).to list({"rows" => [{}]}).as([{"date" => "2013-05-17T15:00:00.000Z"}])
|
302
|
-
end
|
303
|
-
|
304
|
-
describe "failing specs" do
|
305
|
-
it "should have a nice error message for failing should" do
|
306
|
-
expect {
|
307
|
-
expect(@view_spec).to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}])
|
308
|
-
}.to raise_error('Expected to list as [{"text"=>"hello there"}] but got [{"text"=>"hello world"}].')
|
309
|
-
end
|
310
|
-
|
311
|
-
it "should have a nice error message for failing should not" do
|
312
|
-
expect {
|
313
|
-
expect(@view_spec).not_to list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}])
|
314
|
-
}.to raise_error('Expected to not list as [{"text"=>"hello world"}] but did.')
|
315
|
-
end
|
316
|
-
end
|
317
|
-
end
|
@@ -16,14 +16,13 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
|
|
16
16
|
it 'updates a view if it does not exist' do
|
17
17
|
expect(db).to receive(:save_doc).with(
|
18
18
|
{
|
19
|
-
'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}
|
20
|
-
'lists' => {},
|
19
|
+
'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}},
|
21
20
|
"_id" => "_design/design",
|
22
21
|
"language" => "javascript"
|
23
22
|
}
|
24
23
|
)
|
25
24
|
|
26
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}
|
25
|
+
CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}).query_view!
|
27
26
|
end
|
28
27
|
|
29
28
|
it 'only updates a view once' do
|
@@ -50,33 +49,19 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
|
|
50
49
|
expect(db).to receive(:save_doc).with(
|
51
50
|
{
|
52
51
|
'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}},
|
53
|
-
|
52
|
+
"_id" => "_design/design", "language" => "erlang"
|
54
53
|
}
|
55
54
|
)
|
56
55
|
|
57
56
|
CouchPotato::View::ViewQuery.new(db, 'design',
|
58
57
|
{:view => {:map => '<map_code>', :reduce => '<reduce_code>'}},
|
59
|
-
|
58
|
+
:erlang).query_view!
|
60
59
|
end
|
61
60
|
|
62
61
|
it "does not update a view when the views object haven't changed" do
|
63
62
|
allow(db).to receive(:get).and_return({'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}}})
|
64
63
|
expect(db).not_to receive(:save_doc)
|
65
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}
|
66
|
-
end
|
67
|
-
|
68
|
-
it "does not update a view when the list function hasn't changed" do
|
69
|
-
allow(db).to receive(:get).and_return({'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}}, 'lists' => {'list0' => '<list_code>'}})
|
70
|
-
expect(db).not_to receive(:save_doc)
|
71
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}, :list0 => '<list_code>').query_view!
|
72
|
-
end
|
73
|
-
|
74
|
-
it "does not update a view when the lib function hasn't changed" do
|
75
|
-
allow(db).to receive(:get).and_return({'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}, 'lib' => {'test' => '<lib_code>'}}})
|
76
|
-
|
77
|
-
expect(db).not_to receive(:save_doc)
|
78
|
-
|
79
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}, nil, {'test' => "<lib_code>"}).query_view!
|
64
|
+
CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}).query_view!
|
80
65
|
end
|
81
66
|
|
82
67
|
it 'updates a view when the map function has changed' do
|
@@ -91,88 +76,9 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
|
|
91
76
|
CouchPotato::View::ViewQuery.new(db, 'design', :view3 => {:map => '<new map_code>'}).query_view!
|
92
77
|
end
|
93
78
|
|
94
|
-
it 'updates a view when the lib hash has changed' do
|
95
|
-
allow(db).to receive(:get).and_return({'views' => {'view4' => {'map' => '<map_code>'}}}, 'lib' => {'test' => "<test_lib>"})
|
96
|
-
|
97
|
-
expect(db).to receive(:save_doc)
|
98
|
-
|
99
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view4 => {:map => '<map_code>'}}, nil, {:test => "<test_lib>"}).query_view!
|
100
|
-
end
|
101
|
-
|
102
|
-
it "doesn't override libs with different names" do
|
103
|
-
allow(db).to receive(:get).and_return({'views' => {'view5' => {'map' => '<map_code>'}, 'lib' => {'test' => "<test_lib>"}}})
|
104
|
-
expect(db).to receive(:save_doc).with({
|
105
|
-
'views' => {
|
106
|
-
'view5' => {'map' => '<map_code>'},
|
107
|
-
'lib' => {'test' => '<test_lib>', 'test1' => '<test1_lib>'}
|
108
|
-
}
|
109
|
-
})
|
110
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view5 => {:map => '<map_code>'}}, nil, {'test1' => '<test1_lib>'}).query_view!
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'overrides libs with the same name' do
|
114
|
-
allow(db).to receive(:get).and_return({'views' => {'view6' => {'map' => '<map_code>'}, 'lib' => {'test' => "<test_lib>"}}})
|
115
|
-
|
116
|
-
expect(db).to receive(:save_doc).with({
|
117
|
-
'views' => {
|
118
|
-
'view6' => {'map' => '<map_code>'},
|
119
|
-
'lib' => {'test' => '<test1_lib>'}
|
120
|
-
},
|
121
|
-
})
|
122
|
-
|
123
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view6 => {:map => '<map_code>'}}, nil, {'test' => '<test1_lib>'}).query_view!
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'does not pass in reduce or lib keys if there is no lib or reduce object' do
|
127
|
-
allow(db).to receive(:get).and_return({'views' => {}})
|
128
|
-
expect(db).to receive(:save_doc).with({'views' => {'view7' => {'map' => '<map code>'}}})
|
129
|
-
CouchPotato::View::ViewQuery.new(db, 'design', :view7 => {:map => '<map code>', :reduce => nil}).query_view!
|
130
|
-
end
|
131
|
-
|
132
79
|
it 'updates a view when the reduce function has changed' do
|
133
80
|
allow(db).to receive(:get).and_return({'views' => {'view8' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}}})
|
134
81
|
expect(db).to receive(:save_doc)
|
135
82
|
CouchPotato::View::ViewQuery.new(db, 'design', :view8 => {:map => '<map_code>', :reduce => '<new reduce_code>'}).query_view!
|
136
83
|
end
|
137
|
-
|
138
|
-
it 'updates a view when the list function has changed' do
|
139
|
-
allow(db).to receive(:get).and_return({
|
140
|
-
'views' => {'view9' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}},
|
141
|
-
'lists' => {'list1' => '<list_code>'}
|
142
|
-
})
|
143
|
-
expect(db).to receive(:save_doc)
|
144
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view9 => {:map => '<map_code>', :reduce => '<reduce_code>'}}, :list1 => '<new_list_code>').query_view!
|
145
|
-
end
|
146
|
-
|
147
|
-
it "updates a view when there wasn't a list function but now there is one" do
|
148
|
-
allow(db).to receive(:get).and_return({
|
149
|
-
'views' => {'view10' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}}
|
150
|
-
})
|
151
|
-
expect(db).to receive(:save_doc)
|
152
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view10 => {:map => '<map_code>', :reduce => '<reduce_code>'}}, :list1 => '<new_list_code>').query_view!
|
153
|
-
end
|
154
|
-
|
155
|
-
it "does not update a view when there is a list function but no list function is passed" do
|
156
|
-
allow(db).to receive(:get).and_return({
|
157
|
-
'views' => {'view11' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}},
|
158
|
-
'lists' => {'list1' => '<list_code>'}
|
159
|
-
})
|
160
|
-
expect(db).not_to receive(:save_doc)
|
161
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view11 => {:map => '<map_code>', :reduce => '<reduce_code>'}}, {}).query_view!
|
162
|
-
end
|
163
|
-
|
164
|
-
it "does not update a view when there were no lists before and no list function is passed" do
|
165
|
-
allow(db).to receive(:get).and_return({
|
166
|
-
'views' => {'view12' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}}
|
167
|
-
})
|
168
|
-
expect(db).not_to receive(:save_doc)
|
169
|
-
CouchPotato::View::ViewQuery.new(db, 'design', {:view12 => {:map => '<map_code>', :reduce => '<reduce_code>'}}, {}).query_view!
|
170
|
-
end
|
171
|
-
|
172
|
-
it "queries the database directly when querying a list" do
|
173
|
-
allow(db).to receive(:name){'my_database'}
|
174
|
-
|
175
|
-
expect(db.connection).to receive(:get).with('/my_database/_design/my_design/_list/list1/view13?key=1')
|
176
|
-
CouchPotato::View::ViewQuery.new(db, 'my_design', {:view13 => {:map => '<map_code>', :reduce => '<reduce_code>'}}, :list1 => '<new_list_code>').query_view!(:key => 1)
|
177
|
-
end
|
178
84
|
end
|
data/spec/view_updates_spec.rb
CHANGED
@@ -7,8 +7,27 @@ describe "automatic view updates" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should update a view that doesn't match the given functions" do
|
10
|
-
CouchPotato::View::ViewQuery.new(
|
11
|
-
|
10
|
+
CouchPotato::View::ViewQuery.new(
|
11
|
+
@db,
|
12
|
+
'test_design1', {
|
13
|
+
'test_view' => {
|
14
|
+
:map => 'function(doc) { }',
|
15
|
+
:reduce => 'function(key, values) { return []; }'
|
16
|
+
}
|
17
|
+
}
|
18
|
+
).query_view! # create view
|
19
|
+
CouchPotato::View::ViewQuery.clear_cache
|
20
|
+
CouchPotato::View::ViewQuery.new(
|
21
|
+
@db,
|
22
|
+
'test_design1',
|
23
|
+
{
|
24
|
+
'test_view' => {
|
25
|
+
:map => 'function(doc) {emit(doc.id, null)}',
|
26
|
+
:reduce => 'function(key, values) {return sum(values)}'
|
27
|
+
}
|
28
|
+
}
|
29
|
+
).query_view!
|
30
|
+
|
12
31
|
expect(CouchPotato.database.load('_design/test_design1')['views']['test_view']).to eq({
|
13
32
|
'map' => 'function(doc) {emit(doc.id, null)}',
|
14
33
|
'reduce' => 'function(key, values) {return sum(values)}'
|
@@ -16,9 +35,38 @@ describe "automatic view updates" do
|
|
16
35
|
end
|
17
36
|
|
18
37
|
it "should only update a view once to avoid writing the view for every request" do
|
19
|
-
CouchPotato::View::ViewQuery.new(
|
20
|
-
|
21
|
-
|
38
|
+
CouchPotato::View::ViewQuery.new(
|
39
|
+
@db,
|
40
|
+
'test_design2',
|
41
|
+
{
|
42
|
+
'test_view' => {
|
43
|
+
:map => 'function(doc) {}',
|
44
|
+
:reduce => 'function() {}'
|
45
|
+
}
|
46
|
+
}
|
47
|
+
).query_view! # create view
|
48
|
+
CouchPotato::View::ViewQuery.clear_cache
|
49
|
+
CouchPotato::View::ViewQuery.new(
|
50
|
+
@db,
|
51
|
+
'test_design2',
|
52
|
+
{
|
53
|
+
'test_view' => {
|
54
|
+
:map => 'function(doc) {emit(doc.id, null)}',
|
55
|
+
:reduce => 'function(key, values) {return sum(values)}'
|
56
|
+
}
|
57
|
+
}
|
58
|
+
).query_view!
|
59
|
+
CouchPotato::View::ViewQuery.new(
|
60
|
+
@db,
|
61
|
+
'test_design2',
|
62
|
+
{
|
63
|
+
'test_view' => {
|
64
|
+
:map => 'function(doc) {}',
|
65
|
+
:reduce => 'function() {}'
|
66
|
+
}
|
67
|
+
}
|
68
|
+
).query_view!
|
69
|
+
|
22
70
|
expect(CouchPotato.database.load('_design/test_design2')['views']['test_view']).to eq({
|
23
71
|
'map' => 'function(doc) {emit(doc.id, null)}',
|
24
72
|
'reduce' => 'function(key, values) {return sum(values)}'
|
data/spec/views_spec.rb
CHANGED
@@ -225,7 +225,7 @@ describe 'views' do
|
|
225
225
|
|
226
226
|
describe 'with array as key' do
|
227
227
|
it 'should create a map function with the composite key' do
|
228
|
-
expect(CouchPotato::View::ViewQuery).to receive(:new) do |_db, _design_name, view
|
228
|
+
expect(CouchPotato::View::ViewQuery).to receive(:new) do |_db, _design_name, view|
|
229
229
|
expect(view['key_array_timeline'][:map]).to match(/emit\(\[doc\['time'\], doc\['state'\]\]/)
|
230
230
|
|
231
231
|
double('view query', query_view!: { 'rows' => [] })
|
@@ -362,39 +362,6 @@ describe 'views' do
|
|
362
362
|
end
|
363
363
|
end
|
364
364
|
|
365
|
-
describe 'list functions' do
|
366
|
-
class Coworker
|
367
|
-
include CouchPotato::Persistence
|
368
|
-
|
369
|
-
property :name
|
370
|
-
|
371
|
-
view :all_with_list, key: :name, list: :append_doe
|
372
|
-
view :all, key: :name
|
373
|
-
|
374
|
-
list :append_doe, <<-JS
|
375
|
-
function(head, req) {
|
376
|
-
var row;
|
377
|
-
send('{"rows": [');
|
378
|
-
while(row = getRow()) {
|
379
|
-
row.doc.name = row.doc.name + ' doe';
|
380
|
-
send(JSON.stringify(row));
|
381
|
-
};
|
382
|
-
send(']}');
|
383
|
-
}
|
384
|
-
JS
|
385
|
-
end
|
386
|
-
|
387
|
-
it 'should use the list function declared at class level' do
|
388
|
-
@db.save! Coworker.new(name: 'joe')
|
389
|
-
expect(@db.view(Coworker.all_with_list).first.name).to eq('joe doe')
|
390
|
-
end
|
391
|
-
|
392
|
-
it 'should use the list function passed at runtime' do
|
393
|
-
@db.save! Coworker.new(name: 'joe')
|
394
|
-
expect(@db.view(Coworker.all(list: :append_doe)).first.name).to eq('joe doe')
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
365
|
describe 'with stale views' do
|
399
366
|
it 'does not return deleted documents' do
|
400
367
|
build = Build.new
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: couch_potato
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Lang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -159,7 +159,6 @@ files:
|
|
159
159
|
- lib/couch_potato/view/custom_view_spec.rb
|
160
160
|
- lib/couch_potato/view/custom_views.rb
|
161
161
|
- lib/couch_potato/view/flex_view_spec.rb
|
162
|
-
- lib/couch_potato/view/lists.rb
|
163
162
|
- lib/couch_potato/view/model_view_spec.rb
|
164
163
|
- lib/couch_potato/view/properties_view_spec.rb
|
165
164
|
- lib/couch_potato/view/raw_view_spec.rb
|
@@ -179,6 +178,7 @@ files:
|
|
179
178
|
- spec/railtie_spec.rb
|
180
179
|
- spec/reload_spec.rb
|
181
180
|
- spec/revisions_spec.rb
|
181
|
+
- spec/single_design_document_spec.rb
|
182
182
|
- spec/spec.opts
|
183
183
|
- spec/spec_helper.rb
|
184
184
|
- spec/unit/active_model_compliance_spec.rb
|
@@ -197,7 +197,6 @@ files:
|
|
197
197
|
- spec/unit/forbidden_attributes_protection_spec.rb
|
198
198
|
- spec/unit/initialize_spec.rb
|
199
199
|
- spec/unit/json_spec.rb
|
200
|
-
- spec/unit/lists_spec.rb
|
201
200
|
- spec/unit/model_view_spec_spec.rb
|
202
201
|
- spec/unit/persistence_spec.rb
|
203
202
|
- spec/unit/properties_view_spec_spec.rb
|
@@ -248,6 +247,7 @@ test_files:
|
|
248
247
|
- spec/railtie_spec.rb
|
249
248
|
- spec/reload_spec.rb
|
250
249
|
- spec/revisions_spec.rb
|
250
|
+
- spec/single_design_document_spec.rb
|
251
251
|
- spec/spec.opts
|
252
252
|
- spec/spec_helper.rb
|
253
253
|
- spec/unit/active_model_compliance_spec.rb
|
@@ -266,7 +266,6 @@ test_files:
|
|
266
266
|
- spec/unit/forbidden_attributes_protection_spec.rb
|
267
267
|
- spec/unit/initialize_spec.rb
|
268
268
|
- spec/unit/json_spec.rb
|
269
|
-
- spec/unit/lists_spec.rb
|
270
269
|
- spec/unit/model_view_spec_spec.rb
|
271
270
|
- spec/unit/persistence_spec.rb
|
272
271
|
- spec/unit/properties_view_spec_spec.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module CouchPotato
|
2
|
-
module View
|
3
|
-
module Lists
|
4
|
-
def self.included(base)
|
5
|
-
base.send :extend, ClassMethods
|
6
|
-
end
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
def list(name, function)
|
10
|
-
lists[name] = function
|
11
|
-
end
|
12
|
-
|
13
|
-
def lists(name = nil)
|
14
|
-
if name.nil?
|
15
|
-
@lists ||= {}
|
16
|
-
else
|
17
|
-
(@lists && @lists[name]) || (superclass.lists(name) if superclass.respond_to?(:lists))
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/spec/unit/lists_spec.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe CouchPotato::View::Lists, '.list' do
|
4
|
-
it 'should make the list function available via .lists' do
|
5
|
-
clazz = Class.new
|
6
|
-
clazz.send :include, CouchPotato::View::Lists
|
7
|
-
clazz.list 'my_list', '<list_code>'
|
8
|
-
|
9
|
-
expect(clazz.lists('my_list')).to eq('<list_code>')
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'should make the list available to subclasses' do
|
13
|
-
clazz = Class.new
|
14
|
-
clazz.send :include, CouchPotato::View::Lists
|
15
|
-
clazz.list 'my_list', '<list_code>'
|
16
|
-
sub_clazz = Class.new clazz
|
17
|
-
|
18
|
-
expect(sub_clazz.lists('my_list')).to eq('<list_code>')
|
19
|
-
end
|
20
|
-
end
|