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 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: []