thingtank 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/couchrest/extensions/list.rb +169 -0
- data/lib/couchrest/extensions/view.rb +14 -1
- data/lib/thingtank/callbacks.rb +2 -1
- data/lib/thingtank/character.rb +19 -0
- data/lib/thingtank.rb +3 -0
- data/test/test_lists.rb +294 -0
- data/test/test_views.rb +25 -0
- metadata +41 -28
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
thingtank:
|
1
|
+
thingtank: couchrest docs with multiple characters
|
2
2
|
=======================================================================
|
3
3
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/metakeule/thingtank.png)](https://secure.travis-ci.org/metakeule/thingtank)
|
data/Rakefile
CHANGED
@@ -17,7 +17,7 @@ Jeweler::Tasks.new do |gem|
|
|
17
17
|
gem.name = "thingtank"
|
18
18
|
gem.homepage = "http://github.com/metakeule/thingtank"
|
19
19
|
gem.license = "MIT"
|
20
|
-
gem.summary = %Q{
|
20
|
+
gem.summary = %Q{couchrest docs with multiple characters}
|
21
21
|
gem.email = "Base64.decode64(bGludXhAbWFyY3JlbmVhcm5zLmRl\n)"
|
22
22
|
gem.authors = ["Marc Rene Arns"]
|
23
23
|
# dependencies defined in Gemfile
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# https://raw.github.com/couchrest/couchrest/master/lib/couchrest/design.rb
|
2
|
+
module CouchRest
|
3
|
+
class Design < Document
|
4
|
+
|
5
|
+
# Dispatches to any named list.
|
6
|
+
# (using the database where this design doc was saved)
|
7
|
+
def list list_name, query={}, &block
|
8
|
+
list_on database, list_name, query, &block
|
9
|
+
end
|
10
|
+
|
11
|
+
# Dispatches to any named list in a specific database
|
12
|
+
def list_on db, list_name, view_name, query = {}, &block
|
13
|
+
raise ArgumentError, "List query options must be set as symbols!" if query.keys.find{|k| k.is_a?(String)}
|
14
|
+
list_name = list_name.to_s
|
15
|
+
list_slug = "#{name}/#{list_name}/#{view_name}"
|
16
|
+
# Set the default query options
|
17
|
+
query = list_defaults(list_name).merge(query)
|
18
|
+
|
19
|
+
db.list(list_slug, query, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the hash of default values to include in all queries sent
|
23
|
+
# to a list from couchrest.
|
24
|
+
def list_defaults(name)
|
25
|
+
(self['lists'][name.to_s] && self['lists'][name.to_s]["couchrest-defaults"]) || {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true or false if the view is available.
|
29
|
+
def has_list?(name)
|
30
|
+
!self['lists'][name.to_s].nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def fetch_list list_name, opts, &block
|
36
|
+
database.list(list_name, opts, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# https://raw.github.com/couchrest/couchrest/master/lib/couchrest/database.rb
|
43
|
+
module CouchRest
|
44
|
+
class Database
|
45
|
+
# == List based queries
|
46
|
+
# Query a CouchDB list as defined by a <tt>_design</tt> document. Accepts
|
47
|
+
# paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
|
48
|
+
def list(name, params = {}, payload = {}, &block)
|
49
|
+
payload['keys'] = params.delete(:keys) if params[:keys]
|
50
|
+
#params.delete(:keys)
|
51
|
+
# Try recognising the name, otherwise assume already prepared
|
52
|
+
list_path = name_to_list_path(name)
|
53
|
+
url = CouchRest.paramify_url "#{@root}/#{list_path}", params
|
54
|
+
#p [:url, url]
|
55
|
+
if block_given?
|
56
|
+
if !payload.empty?
|
57
|
+
@streamer.post url, payload, &block
|
58
|
+
else
|
59
|
+
@streamer.get url, &block
|
60
|
+
end
|
61
|
+
else
|
62
|
+
if !payload.empty?
|
63
|
+
CouchRest.post url, payload
|
64
|
+
else
|
65
|
+
CouchRest.get url
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
# Convert a simplified list name into a complete list path. If
|
72
|
+
# the name already starts with a "_" no alterations will be made.
|
73
|
+
def name_to_list_path(name)
|
74
|
+
name =~ /^([^_].+?)\/(.*)$/ ? "_design/#{$1}/_list/#{$2}" : name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# analogous to https://github.com/couchrest/couchrest_model/blob/master/lib/couchrest/model/designs/view.rb
|
80
|
+
module CouchRest
|
81
|
+
module Model
|
82
|
+
module Designs
|
83
|
+
class List < View
|
84
|
+
|
85
|
+
attr_accessor :view_name
|
86
|
+
|
87
|
+
def initialize(parent, new_query = {}, name = nil, view_name)
|
88
|
+
self.view_name = view_name
|
89
|
+
super(parent, new_query, name)
|
90
|
+
end
|
91
|
+
|
92
|
+
# == List Execution Methods
|
93
|
+
#
|
94
|
+
# Request to the CouchDB database using the current query values.
|
95
|
+
|
96
|
+
# Return each row wrapped in a ViewRow object. Unlike the raw
|
97
|
+
# CouchDB request, this will provide an empty array if there
|
98
|
+
# are no results.
|
99
|
+
def rows
|
100
|
+
return @rows if @rows
|
101
|
+
if execute && result['rows']
|
102
|
+
@rows ||= result['rows'].map{|v| ViewRow.new(v, model)}
|
103
|
+
else
|
104
|
+
[ ]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def execute
|
109
|
+
return self.result if result
|
110
|
+
raise "Database must be defined in model or list!" if use_database.nil?
|
111
|
+
|
112
|
+
# Remove the reduce value if its not needed to prevent CouchDB errors
|
113
|
+
#query.delete(:reduce) unless can_reduce?
|
114
|
+
|
115
|
+
if model.send(view_name.to_sym).can_reduce?
|
116
|
+
query[:reduce] = false if query[:include_docs] # don't reduce if we include_docs
|
117
|
+
end
|
118
|
+
|
119
|
+
model.save_design_doc(use_database)
|
120
|
+
|
121
|
+
self.result = model.design_doc.list_on(use_database, name, view_name, query.reject{|k,v| v.nil?})
|
122
|
+
end
|
123
|
+
|
124
|
+
class << self
|
125
|
+
# Simplified list creation. A new list will be added to the
|
126
|
+
# provided model's design document using the name and options.
|
127
|
+
#
|
128
|
+
# If the view name starts with "by_" and +:by+ is not provided in
|
129
|
+
# the options, the new list's map method will be interpreted and
|
130
|
+
# generated automatically. For example:
|
131
|
+
#
|
132
|
+
# List.create(Meeting, "by_date_and_name")
|
133
|
+
#
|
134
|
+
# Will create a list that searches by the date and name properties.
|
135
|
+
# Explicity setting the attributes to use is possible using the
|
136
|
+
# +:by+ option. For example:
|
137
|
+
#
|
138
|
+
# List.create(Meeting, "by_date_and_name", :by => [:date, :firstname, :lastname])
|
139
|
+
#
|
140
|
+
def create(model, name, function)
|
141
|
+
model.design_doc['lists'] ||= {}
|
142
|
+
list = model.design_doc['lists'][name.to_s] = function
|
143
|
+
list
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# https://github.com/couchrest/couchrest_model/blob/master/lib/couchrest/model/designs/view.rb overwritten
|
153
|
+
|
154
|
+
class CouchRest::Model::Designs::DesignMapper
|
155
|
+
|
156
|
+
def list(name, function)
|
157
|
+
CouchRest::Model::Designs::List.create(model, name, function) if model.auto_update_design_doc
|
158
|
+
create_list_method(name)
|
159
|
+
end
|
160
|
+
|
161
|
+
def create_list_method(name)
|
162
|
+
model.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
163
|
+
def self.list_#{name}(view_name, opts = {})
|
164
|
+
CouchRest::Model::Designs::List.new(self, opts, '#{name}', view_name)
|
165
|
+
end
|
166
|
+
EOS
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -4,11 +4,24 @@ class CouchRest::Model::Designs::DesignMapper
|
|
4
4
|
|
5
5
|
# generate a view to show only ThingTanks of a certain character, define them all in a ThingTank subclass (not in a character)
|
6
6
|
def character_view(klass, name, opts={})
|
7
|
-
name = "#{klass.to_s.
|
7
|
+
name = "#{klass.to_s.underscore.gsub('/', '_')}_#{name}"
|
8
8
|
opts ||= {}
|
9
9
|
opts[:guards] ||= []
|
10
10
|
# there is no "inArray" like function in couchdb, see http://stackoverflow.com/questions/3740464/i-have-to-write-every-function-i-need-for-couchdb
|
11
11
|
opts[:guards] << "((doc['characters'] !== undefined) && (function (item,arr) { for(p=0;p<arr.length;p++) if (item == arr[p]) return true; return false;})('#{klass.to_s}',doc['characters']))"
|
12
|
+
if opts[:emit]
|
13
|
+
# taken from # View#create and modified since there is no support for :emit
|
14
|
+
opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank]
|
15
|
+
opts[:guards] ||= []
|
16
|
+
opts[:guards].push "(doc['#{model.model_type_key}'] == '#{model.to_s}')"
|
17
|
+
opts[:map] = <<-EOF
|
18
|
+
function(doc) {
|
19
|
+
if (#{opts[:guards].join(' && ')}) {
|
20
|
+
#{opts[:emit]}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
EOF
|
24
|
+
end
|
12
25
|
view(name, opts)
|
13
26
|
end
|
14
27
|
|
data/lib/thingtank/callbacks.rb
CHANGED
@@ -19,7 +19,8 @@ class ThingTank
|
|
19
19
|
before_destroy do
|
20
20
|
ok = true
|
21
21
|
(self["characters"] || []).each do |klass|
|
22
|
-
|
22
|
+
real_klass = self.class.const_get("Character").__subclasses()[klass.to_sym]
|
23
|
+
document = self.as(real_klass)
|
23
24
|
(ok = false) if false == document.run_callbacks(:destroy) do
|
24
25
|
true
|
25
26
|
end
|
data/lib/thingtank/character.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
Defined.enable!
|
1
2
|
|
2
3
|
class ThingTank
|
3
4
|
|
@@ -7,10 +8,28 @@ class ThingTank
|
|
7
8
|
include ThingTank::SharedMethods
|
8
9
|
|
9
10
|
class << self
|
11
|
+
|
12
|
+
# we need this hack with after_inherited and defined or active record is going wild, see: http://stackoverflow.com/questions/790626/ruby-can-i-have-something-like-classinherited-thats-triggered-only-after-the
|
13
|
+
# 'I'm trying to add behavior to activerecord models, but I need all the model customizations to go through before I mess with it. I'm trying to add behavior to activerecord models, but I need all the model customizations to go through before I mess with it. '
|
14
|
+
# exactly my case
|
15
|
+
# simple inherited leads to chaos here
|
16
|
+
def after_inherited(child)
|
17
|
+
@__sub_classes ||= {}
|
18
|
+
@__sub_classes[child.to_s.to_sym] = child
|
19
|
+
end
|
20
|
+
|
21
|
+
def defined(*args)
|
22
|
+
superclass.after_inherited(self) if superclass.respond_to?(:after_inherited)
|
23
|
+
end
|
24
|
+
|
25
|
+
def __subclasses
|
26
|
+
@__sub_classes ||= {}
|
27
|
+
end
|
10
28
|
|
11
29
|
def property(name, *args)
|
12
30
|
@character_properties ||= []
|
13
31
|
@character_properties << name.to_s
|
32
|
+
#p [:prop, self.name, @character_properties]
|
14
33
|
super
|
15
34
|
end
|
16
35
|
|
data/lib/thingtank.rb
CHANGED
@@ -2,12 +2,15 @@ if RUBY_VERSION =~ /1.8/
|
|
2
2
|
require 'backports'
|
3
3
|
end
|
4
4
|
|
5
|
+
require "defined"
|
6
|
+
|
5
7
|
# TODO
|
6
8
|
# - check if we could get useful inspiration from https://github.com/givmo/couch_record
|
7
9
|
# - improve views
|
8
10
|
# - validation for characters
|
9
11
|
|
10
12
|
require_relative File.join('thingtank', 'thingtank.rb')
|
13
|
+
require_relative File.join('couchrest', 'extensions', 'list.rb')
|
11
14
|
require_relative File.join('couchrest', 'extensions', 'view.rb')
|
12
15
|
require_relative File.join('thingtank', 'dependencies.rb')
|
13
16
|
require_relative File.join('thingtank', 'callbacks.rb')
|
data/test/test_lists.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class Car < ThingTank::Character
|
5
|
+
property :name
|
6
|
+
property :project
|
7
|
+
end
|
8
|
+
|
9
|
+
class House < ThingTank::Character
|
10
|
+
property :name
|
11
|
+
property :project
|
12
|
+
end
|
13
|
+
|
14
|
+
$list_filter = "function(head, req) {
|
15
|
+
var row;
|
16
|
+
var rows = [];
|
17
|
+
var debug = [];
|
18
|
+
var list_filter = {};
|
19
|
+
if(req.query['list_filter']){
|
20
|
+
list_filter = JSON.parse(req.query['list_filter']);
|
21
|
+
}
|
22
|
+
while(row = getRow()) {
|
23
|
+
if(list_filter){
|
24
|
+
var add = true;
|
25
|
+
for(fi in list_filter){
|
26
|
+
var filter_array = list_filter[fi];
|
27
|
+
if(filter_array){
|
28
|
+
var doc_val = row.doc;
|
29
|
+
var filter_key = filter_array[0];
|
30
|
+
var filter_val = filter_array[1];
|
31
|
+
if(doc_val){
|
32
|
+
var fkeys = filter_key.split('.'); /* filter_key may be data.page.disabled, so we have to get through that */
|
33
|
+
for(fk in fkeys){
|
34
|
+
if(doc_val !== undefined)
|
35
|
+
doc_val = doc_val[fkeys[fk]];
|
36
|
+
}
|
37
|
+
if(doc_val !== filter_val){
|
38
|
+
add = false;
|
39
|
+
}
|
40
|
+
} else {
|
41
|
+
if(Object.prototype.toString.call(row.value) === '[object Array]'){
|
42
|
+
debug[debug.length] = ['array', row.value[fi], filter_val];
|
43
|
+
if(row.value[fi] !== filter_val){
|
44
|
+
add = false;
|
45
|
+
}
|
46
|
+
|
47
|
+
} else {
|
48
|
+
debug[debug.length] = ['no_array', row.value, filter_val];
|
49
|
+
if(row.value !== filter_val){
|
50
|
+
add = false;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
if(add)
|
57
|
+
rows[rows.length] = row;
|
58
|
+
} else {
|
59
|
+
rows[rows.length] = row;
|
60
|
+
}
|
61
|
+
|
62
|
+
};
|
63
|
+
send(JSON.stringify({'rows': rows, 'debug': debug}));
|
64
|
+
}"
|
65
|
+
|
66
|
+
|
67
|
+
class Tanker < ThingTank
|
68
|
+
design do
|
69
|
+
list :testlist, $list_filter # only when called with .all() / include_docs=true
|
70
|
+
#view :all_cars, :by => ['name']
|
71
|
+
character_view House, :by_name, :by => ['name']
|
72
|
+
character_view House, :by_special, :emit => "emit(doc.name, doc.project);"
|
73
|
+
character_view Car, :by_name, :by => ['name']
|
74
|
+
#character_view Car, :by_project, :emit => "emit(doc.project, 1);"
|
75
|
+
character_view Car, :by_project_and_name, :by => ['project', 'name']
|
76
|
+
|
77
|
+
character_view Car, :by_special, :map => "function(doc){ emit(doc.name, doc.project); }"
|
78
|
+
character_view Car, :by_special2, :map => "function(doc){ emit(doc.name, [doc.project, doc.name]); }"
|
79
|
+
character_view Car, :by_special3, :map => "function(doc){ emit(doc.name, [doc.name, doc.project]); }"
|
80
|
+
character_view Car, :by_special4, :emit => "emit(doc.name, [doc.name, doc.project]);"
|
81
|
+
character_view Car, :by_special5, :emit => "emit(doc.name, doc.project);"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
describe "with a car view" do
|
87
|
+
|
88
|
+
before do
|
89
|
+
reset_test_db!
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be able to handle rows without docs" do
|
93
|
+
doc = Tanker.new {}
|
94
|
+
doc.as(Car) do |c|
|
95
|
+
c.project = 'a'
|
96
|
+
c.name = 'A'
|
97
|
+
end
|
98
|
+
doc.save
|
99
|
+
|
100
|
+
doc2 = Tanker.new {}
|
101
|
+
doc2.as(House) do |c|
|
102
|
+
c.project = 'b'
|
103
|
+
c.name = 'B'
|
104
|
+
end
|
105
|
+
doc2.save
|
106
|
+
|
107
|
+
doc3 = Tanker.new {}
|
108
|
+
doc3.as(House) do |c|
|
109
|
+
c.project = 'a'
|
110
|
+
c.name = 'House a1'
|
111
|
+
end
|
112
|
+
doc3.save
|
113
|
+
|
114
|
+
doc4 = Tanker.new {}
|
115
|
+
doc4.as(Car) do |c|
|
116
|
+
c.project = 'a'
|
117
|
+
c.name = 'A1'
|
118
|
+
end
|
119
|
+
doc4.save
|
120
|
+
|
121
|
+
doc5 = Tanker.new {}
|
122
|
+
doc5.as(Car) do |c|
|
123
|
+
c.project = 'a'
|
124
|
+
c.name = 'A3'
|
125
|
+
end
|
126
|
+
doc5.save
|
127
|
+
|
128
|
+
doc6 = Tanker.new {}
|
129
|
+
doc6.as(House) do |c|
|
130
|
+
c.project = 'b'
|
131
|
+
c.name = 'B2'
|
132
|
+
end
|
133
|
+
doc6.save
|
134
|
+
|
135
|
+
#assert_equal 3, Tanker.house_by_name.all().size
|
136
|
+
assert_equal 3, Tanker.car_by_project_and_name.rows().size
|
137
|
+
|
138
|
+
#assert_equal 4, Tanker.list_testlist('all', :list_filter => {'project' => 'a'}.to_json).all().size
|
139
|
+
#assert_equal 2, Tanker.list_testlist('all', :list_filter => {'project' => 'b'}.to_json).all().size
|
140
|
+
|
141
|
+
#result = Tanker.list_testlist('car_by_special', :list_filter => [['project', 'a']].to_json).execute()
|
142
|
+
#if result['error']
|
143
|
+
# p [:error_project_a, result['reason'].split("\n").first]
|
144
|
+
#else
|
145
|
+
# p [:debug_project_a, result['debug'], result['rows']]
|
146
|
+
#end
|
147
|
+
|
148
|
+
#p [:project_a, Tanker.list_testlist('car_by_special', :list_filter => [['project', 'a']].to_json).rows()]
|
149
|
+
|
150
|
+
# should check the correct value if the value isn't an array but already our filter
|
151
|
+
assert_equal 4, Tanker.list_testlist('car_by_special', :list_filter => [['project', 'a']].to_json).rows().size
|
152
|
+
# same for docs
|
153
|
+
assert_equal 4, Tanker.list_testlist('car_by_special', :list_filter => [['project', 'a']].to_json).all().size
|
154
|
+
|
155
|
+
# should check the correct index in the values array, if the index is the first one
|
156
|
+
assert_equal 4, Tanker.list_testlist('car_by_special2', :list_filter => [['project', 'a']].to_json).rows().size
|
157
|
+
# same for docs
|
158
|
+
assert_equal 4, Tanker.list_testlist('car_by_special2', :list_filter => [['project', 'a']].to_json).all().size
|
159
|
+
|
160
|
+
# should check for the correct index in the values array, skipping nil filters
|
161
|
+
assert_equal 4, Tanker.list_testlist('car_by_special3', :list_filter => [nil, ['project', 'a']].to_json).rows().size
|
162
|
+
# same for docs
|
163
|
+
assert_equal 4, Tanker.list_testlist('car_by_special3', :list_filter => [nil, ['project', 'a']].to_json).all().size
|
164
|
+
|
165
|
+
|
166
|
+
# should check for the correct index in the values array, skipping nil filters
|
167
|
+
assert_equal 3, Tanker.list_testlist('car_by_special4', :list_filter => [nil, ['project', 'a']].to_json).rows().size
|
168
|
+
# same for docs
|
169
|
+
assert_equal 3, Tanker.list_testlist('car_by_special4', :list_filter => [nil, ['project', 'a']].to_json).all().size
|
170
|
+
|
171
|
+
assert_equal 1, Tanker.list_testlist('house_by_special', :list_filter => [['project', 'a']].to_json).all().size
|
172
|
+
assert_equal 2, Tanker.list_testlist('house_by_special', :list_filter => [['project', 'b']].to_json).all().size
|
173
|
+
assert_equal 3, Tanker.list_testlist('car_by_special5', :list_filter => [['project', 'a']].to_json).rows().size
|
174
|
+
assert_equal 0, Tanker.list_testlist('car_by_special5', :list_filter => [['project', 'b']].to_json).all().size
|
175
|
+
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should be able to use character_views as well as normal views" do
|
180
|
+
doc = Tanker.new {}
|
181
|
+
doc.as(Car) do |c|
|
182
|
+
c.project = 'a'
|
183
|
+
c.name = 'A'
|
184
|
+
end
|
185
|
+
doc.save
|
186
|
+
|
187
|
+
doc2 = Tanker.new {}
|
188
|
+
doc2.as(House) do |c|
|
189
|
+
c.project = 'b'
|
190
|
+
c.name = 'B'
|
191
|
+
end
|
192
|
+
doc2.save
|
193
|
+
|
194
|
+
doc3 = Tanker.new {}
|
195
|
+
doc3.as(House) do |c|
|
196
|
+
c.project = 'a'
|
197
|
+
c.name = 'House a1'
|
198
|
+
end
|
199
|
+
doc3.save
|
200
|
+
|
201
|
+
doc4 = Tanker.new {}
|
202
|
+
doc4.as(Car) do |c|
|
203
|
+
c.project = 'a'
|
204
|
+
c.name = 'A1'
|
205
|
+
end
|
206
|
+
doc4.save
|
207
|
+
|
208
|
+
doc5 = Tanker.new {}
|
209
|
+
doc5.as(Car) do |c|
|
210
|
+
c.project = 'a'
|
211
|
+
c.name = 'A3'
|
212
|
+
end
|
213
|
+
doc5.save
|
214
|
+
|
215
|
+
doc6 = Tanker.new {}
|
216
|
+
doc6.as(House) do |c|
|
217
|
+
c.project = 'b'
|
218
|
+
c.name = 'B2'
|
219
|
+
end
|
220
|
+
doc6.save
|
221
|
+
|
222
|
+
assert_equal 3, Tanker.house_by_name.all().size
|
223
|
+
assert_equal 3, Tanker.car_by_name.all().size
|
224
|
+
|
225
|
+
assert_equal 4, Tanker.list_testlist('all', :list_filter => [['project', 'a']].to_json).all().size
|
226
|
+
assert_equal 2, Tanker.list_testlist('all', :list_filter => [['project', 'b']].to_json).all().size
|
227
|
+
|
228
|
+
#result = Tanker.list_testlist('house_by_name', :list_filter => {'project' => 'a'}.to_json, :include_docs => true).execute()
|
229
|
+
#if result['error']
|
230
|
+
# p [:error, result['reason'].split("\n").first]
|
231
|
+
#else
|
232
|
+
# p [:debug, result['debug']]
|
233
|
+
#end
|
234
|
+
|
235
|
+
assert_equal 1, Tanker.list_testlist('house_by_name', :list_filter => [['project', 'a']].to_json).all().size
|
236
|
+
assert_equal 2, Tanker.list_testlist('house_by_name', :list_filter => [['project', 'b']].to_json).all().size
|
237
|
+
assert_equal 3, Tanker.list_testlist('car_by_name', :list_filter => [['project', 'a']].to_json).all().size
|
238
|
+
assert_equal 0, Tanker.list_testlist('car_by_name', :list_filter => [['project', 'b']].to_json).all().size
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should do something with the list" do
|
243
|
+
doc = Tanker.new {}
|
244
|
+
doc.as(Car) do |c|
|
245
|
+
c.project = 'a'
|
246
|
+
c.name = 'A'
|
247
|
+
end
|
248
|
+
doc.save
|
249
|
+
|
250
|
+
doc2 = Tanker.new {}
|
251
|
+
doc2.as(Car) do |c|
|
252
|
+
c.project = 'b'
|
253
|
+
c.name = 'B'
|
254
|
+
end
|
255
|
+
doc2.save
|
256
|
+
|
257
|
+
doc3 = Tanker.new {}
|
258
|
+
doc3.as(Car) do |c|
|
259
|
+
c.project = 'a'
|
260
|
+
c.name = 'a2'
|
261
|
+
end
|
262
|
+
doc3.save
|
263
|
+
|
264
|
+
|
265
|
+
#result = Tanker.list_testlist('all', :list_filter => {'project' => 'a'}.to_json, :include_docs => true).execute()
|
266
|
+
#if result['error']
|
267
|
+
# p [:error, result['reason'].split("\n").first]
|
268
|
+
#else
|
269
|
+
# p [:debug, result['debug']]
|
270
|
+
#end
|
271
|
+
assert_equal 3, Tanker.all.all().size
|
272
|
+
assert_equal 2, Tanker.list_testlist('all', :list_filter => [['project', 'a']].to_json).all().size
|
273
|
+
assert_equal 1, Tanker.list_testlist('all', :list_filter => [['project', 'b']].to_json).all().size
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
it "should have both without a filter list" do
|
278
|
+
doc = Tanker.new {}
|
279
|
+
doc.as(Car) do |c|
|
280
|
+
c.project = 'My Project'
|
281
|
+
c.name = 'My Name'
|
282
|
+
end
|
283
|
+
doc.save
|
284
|
+
|
285
|
+
doc2 = Tanker.new {}
|
286
|
+
doc2.as(Car) do |c|
|
287
|
+
c.project = 'Other Project'
|
288
|
+
c.name = 'My Name'
|
289
|
+
end
|
290
|
+
doc2.save
|
291
|
+
assert_equal 2, Tanker.all.all().size
|
292
|
+
assert_equal 2, Tanker.list_testlist('all').all().size
|
293
|
+
end
|
294
|
+
end
|
data/test/test_views.rb
CHANGED
@@ -21,6 +21,8 @@ class Tanker < ThingTank
|
|
21
21
|
character_view Car, :by_name, :by => ['name']
|
22
22
|
character_view House, :by_name, :by => ['name']
|
23
23
|
character_view Ship, :by_name, :by => ['name']
|
24
|
+
character_view Ship, :by_name_with_weight, :emit => 'emit(doc.name, doc.weight);'
|
25
|
+
character_view Ship, :by_name_with_weight2, :emit => 'emit(doc.name, [doc.name, doc.weight]);'
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
@@ -31,6 +33,29 @@ describe "with a car view" do
|
|
31
33
|
reset_test_db!
|
32
34
|
end
|
33
35
|
|
36
|
+
it "should get ships with name and weight as rows" do
|
37
|
+
ship = Tanker.create
|
38
|
+
ship.as(Ship) do |s|
|
39
|
+
s.name = "MS Dolphin"
|
40
|
+
s.weight = "1000 pound"
|
41
|
+
end
|
42
|
+
ship.save
|
43
|
+
|
44
|
+
result = Tanker.ship_by_name_with_weight.rows()
|
45
|
+
|
46
|
+
assert_equal 1, result.size
|
47
|
+
assert_equal 'MS Dolphin', result.first.key
|
48
|
+
assert_equal '1000 pound', result.first.value
|
49
|
+
|
50
|
+
result = Tanker.ship_by_name_with_weight2.rows()
|
51
|
+
|
52
|
+
assert_equal 1, result.size
|
53
|
+
assert_equal 'MS Dolphin', result.first.key
|
54
|
+
assert_equal 'MS Dolphin', result.first.value.first
|
55
|
+
assert_equal '1000 pound', result.first.value.last
|
56
|
+
|
57
|
+
end
|
58
|
+
|
34
59
|
it "should get all cars" do
|
35
60
|
car1 = Tanker.create
|
36
61
|
car1.as(Car) do |c|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thingtank
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-15 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
16
|
-
requirement: &
|
16
|
+
requirement: &14082040 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *14082040
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: couchrest
|
27
|
-
requirement: &
|
27
|
+
requirement: &14080680 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *14080680
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: couchrest_model
|
38
|
-
requirement: &
|
38
|
+
requirement: &14079940 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,21 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *14079940
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: defined
|
49
|
+
requirement: &14079100 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *14079100
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: guard
|
49
|
-
requirement: &
|
60
|
+
requirement: &14078280 !ruby/object:Gem::Requirement
|
50
61
|
none: false
|
51
62
|
requirements:
|
52
63
|
- - ! '>='
|
@@ -54,10 +65,10 @@ dependencies:
|
|
54
65
|
version: '0'
|
55
66
|
type: :development
|
56
67
|
prerelease: false
|
57
|
-
version_requirements: *
|
68
|
+
version_requirements: *14078280
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: libnotify
|
60
|
-
requirement: &
|
71
|
+
requirement: &14060560 !ruby/object:Gem::Requirement
|
61
72
|
none: false
|
62
73
|
requirements:
|
63
74
|
- - ! '>='
|
@@ -65,10 +76,10 @@ dependencies:
|
|
65
76
|
version: '0'
|
66
77
|
type: :development
|
67
78
|
prerelease: false
|
68
|
-
version_requirements: *
|
79
|
+
version_requirements: *14060560
|
69
80
|
- !ruby/object:Gem::Dependency
|
70
81
|
name: rb-inotify
|
71
|
-
requirement: &
|
82
|
+
requirement: &14059820 !ruby/object:Gem::Requirement
|
72
83
|
none: false
|
73
84
|
requirements:
|
74
85
|
- - ! '>='
|
@@ -76,10 +87,10 @@ dependencies:
|
|
76
87
|
version: '0'
|
77
88
|
type: :development
|
78
89
|
prerelease: false
|
79
|
-
version_requirements: *
|
90
|
+
version_requirements: *14059820
|
80
91
|
- !ruby/object:Gem::Dependency
|
81
92
|
name: guard-minitest
|
82
|
-
requirement: &
|
93
|
+
requirement: &14059120 !ruby/object:Gem::Requirement
|
83
94
|
none: false
|
84
95
|
requirements:
|
85
96
|
- - ! '>='
|
@@ -87,10 +98,10 @@ dependencies:
|
|
87
98
|
version: '0'
|
88
99
|
type: :development
|
89
100
|
prerelease: false
|
90
|
-
version_requirements: *
|
101
|
+
version_requirements: *14059120
|
91
102
|
- !ruby/object:Gem::Dependency
|
92
103
|
name: linecache19
|
93
|
-
requirement: &
|
104
|
+
requirement: &14058400 !ruby/object:Gem::Requirement
|
94
105
|
none: false
|
95
106
|
requirements:
|
96
107
|
- - ! '>='
|
@@ -98,10 +109,10 @@ dependencies:
|
|
98
109
|
version: '0'
|
99
110
|
type: :development
|
100
111
|
prerelease: false
|
101
|
-
version_requirements: *
|
112
|
+
version_requirements: *14058400
|
102
113
|
- !ruby/object:Gem::Dependency
|
103
114
|
name: ruby-debug19
|
104
|
-
requirement: &
|
115
|
+
requirement: &14057840 !ruby/object:Gem::Requirement
|
105
116
|
none: false
|
106
117
|
requirements:
|
107
118
|
- - ! '>='
|
@@ -109,10 +120,10 @@ dependencies:
|
|
109
120
|
version: '0'
|
110
121
|
type: :development
|
111
122
|
prerelease: false
|
112
|
-
version_requirements: *
|
123
|
+
version_requirements: *14057840
|
113
124
|
- !ruby/object:Gem::Dependency
|
114
125
|
name: yard
|
115
|
-
requirement: &
|
126
|
+
requirement: &14057060 !ruby/object:Gem::Requirement
|
116
127
|
none: false
|
117
128
|
requirements:
|
118
129
|
- - ~>
|
@@ -120,10 +131,10 @@ dependencies:
|
|
120
131
|
version: 0.6.0
|
121
132
|
type: :development
|
122
133
|
prerelease: false
|
123
|
-
version_requirements: *
|
134
|
+
version_requirements: *14057060
|
124
135
|
- !ruby/object:Gem::Dependency
|
125
136
|
name: bundler
|
126
|
-
requirement: &
|
137
|
+
requirement: &14056420 !ruby/object:Gem::Requirement
|
127
138
|
none: false
|
128
139
|
requirements:
|
129
140
|
- - ~>
|
@@ -131,10 +142,10 @@ dependencies:
|
|
131
142
|
version: 1.0.0
|
132
143
|
type: :development
|
133
144
|
prerelease: false
|
134
|
-
version_requirements: *
|
145
|
+
version_requirements: *14056420
|
135
146
|
- !ruby/object:Gem::Dependency
|
136
147
|
name: jeweler
|
137
|
-
requirement: &
|
148
|
+
requirement: &14055640 !ruby/object:Gem::Requirement
|
138
149
|
none: false
|
139
150
|
requirements:
|
140
151
|
- - ~>
|
@@ -142,7 +153,7 @@ dependencies:
|
|
142
153
|
version: 1.6.4
|
143
154
|
type: :development
|
144
155
|
prerelease: false
|
145
|
-
version_requirements: *
|
156
|
+
version_requirements: *14055640
|
146
157
|
description:
|
147
158
|
email: ! 'Base64.decode64(bGludXhAbWFyY3JlbmVhcm5zLmRl
|
148
159
|
|
@@ -166,6 +177,7 @@ files:
|
|
166
177
|
- examples/immortal_julius.rb
|
167
178
|
- examples/marriage_improvement.rb
|
168
179
|
- examples/second_marriage.rb
|
180
|
+
- lib/couchrest/extensions/list.rb
|
169
181
|
- lib/couchrest/extensions/view.rb
|
170
182
|
- lib/thingtank.rb
|
171
183
|
- lib/thingtank/callbacks.rb
|
@@ -185,6 +197,7 @@ files:
|
|
185
197
|
- test/examples/test_second_marriage.rb
|
186
198
|
- test/test_fakebase.rb
|
187
199
|
- test/test_helper.rb
|
200
|
+
- test/test_lists.rb
|
188
201
|
- test/test_thingtank.rb
|
189
202
|
- test/test_views.rb
|
190
203
|
homepage: http://github.com/metakeule/thingtank
|
@@ -202,7 +215,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
202
215
|
version: '0'
|
203
216
|
segments:
|
204
217
|
- 0
|
205
|
-
hash:
|
218
|
+
hash: 683834457971254246
|
206
219
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
220
|
none: false
|
208
221
|
requirements:
|
@@ -214,5 +227,5 @@ rubyforge_project:
|
|
214
227
|
rubygems_version: 1.8.15
|
215
228
|
signing_key:
|
216
229
|
specification_version: 3
|
217
|
-
summary:
|
230
|
+
summary: couchrest docs with multiple characters
|
218
231
|
test_files: []
|