thingtank 0.2.0 → 0.3.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.
data/Gemfile CHANGED
@@ -3,6 +3,7 @@ source "http://rubygems.org"
3
3
  gem 'json'
4
4
  gem 'couchrest'
5
5
  gem 'couchrest_model'
6
+ gem 'defined'
6
7
 
7
8
  group :development do
8
9
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- thingtank: let couchrest docs have multiple characters at the same time
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{let couchrest docs have multiple characters at the same time}
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.2.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.downcase}_#{name}"
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
 
@@ -19,7 +19,8 @@ class ThingTank
19
19
  before_destroy do
20
20
  ok = true
21
21
  (self["characters"] || []).each do |klass|
22
- document = self.as(klass.constantize)
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
@@ -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')
@@ -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.2.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-09 00:00:00.000000000Z
12
+ date: 2012-02-15 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &18327300 !ruby/object:Gem::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: *18327300
24
+ version_requirements: *14082040
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: couchrest
27
- requirement: &18326380 !ruby/object:Gem::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: *18326380
35
+ version_requirements: *14080680
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: couchrest_model
38
- requirement: &18325560 !ruby/object:Gem::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: *18325560
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: &18324940 !ruby/object:Gem::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: *18324940
68
+ version_requirements: *14078280
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: libnotify
60
- requirement: &18324160 !ruby/object:Gem::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: *18324160
79
+ version_requirements: *14060560
69
80
  - !ruby/object:Gem::Dependency
70
81
  name: rb-inotify
71
- requirement: &18323560 !ruby/object:Gem::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: *18323560
90
+ version_requirements: *14059820
80
91
  - !ruby/object:Gem::Dependency
81
92
  name: guard-minitest
82
- requirement: &18322820 !ruby/object:Gem::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: *18322820
101
+ version_requirements: *14059120
91
102
  - !ruby/object:Gem::Dependency
92
103
  name: linecache19
93
- requirement: &18322220 !ruby/object:Gem::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: *18322220
112
+ version_requirements: *14058400
102
113
  - !ruby/object:Gem::Dependency
103
114
  name: ruby-debug19
104
- requirement: &18321600 !ruby/object:Gem::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: *18321600
123
+ version_requirements: *14057840
113
124
  - !ruby/object:Gem::Dependency
114
125
  name: yard
115
- requirement: &18321000 !ruby/object:Gem::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: *18321000
134
+ version_requirements: *14057060
124
135
  - !ruby/object:Gem::Dependency
125
136
  name: bundler
126
- requirement: &18320380 !ruby/object:Gem::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: *18320380
145
+ version_requirements: *14056420
135
146
  - !ruby/object:Gem::Dependency
136
147
  name: jeweler
137
- requirement: &18292320 !ruby/object:Gem::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: *18292320
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: -405721453140054073
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: let couchrest docs have multiple characters at the same time
230
+ summary: couchrest docs with multiple characters
218
231
  test_files: []