couch_tomato 0.1.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.
Files changed (52) hide show
  1. data/MIT-LICENSE.txt +19 -0
  2. data/README.md +96 -0
  3. data/init.rb +3 -0
  4. data/lib/core_ext/date.rb +10 -0
  5. data/lib/core_ext/duplicable.rb +43 -0
  6. data/lib/core_ext/extract_options.rb +14 -0
  7. data/lib/core_ext/inheritable_attributes.rb +222 -0
  8. data/lib/core_ext/object.rb +5 -0
  9. data/lib/core_ext/string.rb +19 -0
  10. data/lib/core_ext/symbol.rb +15 -0
  11. data/lib/core_ext/time.rb +12 -0
  12. data/lib/couch_tomato/database.rb +279 -0
  13. data/lib/couch_tomato/js_view_source.rb +182 -0
  14. data/lib/couch_tomato/migration.rb +52 -0
  15. data/lib/couch_tomato/migrator.rb +235 -0
  16. data/lib/couch_tomato/persistence/base.rb +62 -0
  17. data/lib/couch_tomato/persistence/belongs_to_property.rb +58 -0
  18. data/lib/couch_tomato/persistence/callbacks.rb +60 -0
  19. data/lib/couch_tomato/persistence/dirty_attributes.rb +27 -0
  20. data/lib/couch_tomato/persistence/json.rb +48 -0
  21. data/lib/couch_tomato/persistence/magic_timestamps.rb +15 -0
  22. data/lib/couch_tomato/persistence/properties.rb +58 -0
  23. data/lib/couch_tomato/persistence/simple_property.rb +97 -0
  24. data/lib/couch_tomato/persistence/validation.rb +18 -0
  25. data/lib/couch_tomato/persistence.rb +85 -0
  26. data/lib/couch_tomato/replicator.rb +50 -0
  27. data/lib/couch_tomato.rb +46 -0
  28. data/lib/tasks/couch_tomato.rake +128 -0
  29. data/rails/init.rb +7 -0
  30. data/spec/callbacks_spec.rb +271 -0
  31. data/spec/comment.rb +8 -0
  32. data/spec/create_spec.rb +22 -0
  33. data/spec/custom_view_spec.rb +134 -0
  34. data/spec/destroy_spec.rb +29 -0
  35. data/spec/fixtures/address.rb +9 -0
  36. data/spec/fixtures/person.rb +6 -0
  37. data/spec/property_spec.rb +103 -0
  38. data/spec/spec_helper.rb +40 -0
  39. data/spec/unit/attributes_spec.rb +26 -0
  40. data/spec/unit/callbacks_spec.rb +33 -0
  41. data/spec/unit/create_spec.rb +58 -0
  42. data/spec/unit/customs_views_spec.rb +15 -0
  43. data/spec/unit/database_spec.rb +38 -0
  44. data/spec/unit/dirty_attributes_spec.rb +113 -0
  45. data/spec/unit/string_spec.rb +13 -0
  46. data/spec/unit/view_query_spec.rb +9 -0
  47. data/spec/update_spec.rb +40 -0
  48. data/test/test_helper.rb +63 -0
  49. data/test/unit/database_test.rb +285 -0
  50. data/test/unit/js_view_test.rb +362 -0
  51. data/test/unit/property_test.rb +193 -0
  52. metadata +133 -0
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'rr'
5
+ require 'couchrest'
6
+ require File.dirname(__FILE__) + '/../lib/couch_tomato.rb'
7
+
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+
11
+ unless Test::Unit::TestCase.include?(RR::Adapters::TestUnit)
12
+ class Test::Unit::TestCase
13
+ include RR::Adapters::TestUnit
14
+ end
15
+ end
16
+
17
+
18
+ def unload_const (klass)
19
+ if Object.send :const_defined?, klass
20
+ Object.send :remove_const, klass
21
+ end
22
+ end
23
+
24
+ def create_const(super_klass=nil)
25
+ super_klass ? Class.new(super_klass) : Class.new
26
+ end
27
+
28
+ # TestDb = create_const(CouchTomato::Database)
29
+
30
+ # class Test::Unit::TestCase
31
+ # include RR::Adapters::TestUnit unless include?(RR::Adapters::TestUnit)
32
+ # end
33
+
34
+ # 3.times {puts}
35
+
36
+ # class TestDb > CouchTomato::Database
37
+ # name 'couch_tomato_test'
38
+ # end
39
+
40
+ # CouchTomato::Config.database_name = 'couch_tomato_test'
41
+ # CouchTomato::Config.database_server = 'http://127.0.0.1:5984/'
42
+
43
+
44
+ # class Comment
45
+ # include CouchTomato::Persistence
46
+ #
47
+ # validates_presence_of :title
48
+ #
49
+ # property :title
50
+ # belongs_to :commenter
51
+ # end
52
+ #
53
+ # def recreate_db
54
+ # CouchTomato.couchrest_database.delete! rescue nil
55
+ # CouchTomato.couchrest_database.server.create_db CouchTomato::Config.database_name
56
+ # end
57
+ # recreate_db
58
+ #
59
+ # Spec::Matchers.define :string_matching do |regex|
60
+ # match do |string|
61
+ # string =~ regex
62
+ # end
63
+ # end
@@ -0,0 +1,285 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require File.dirname(__FILE__) + '/../../lib/couch_tomato.rb'
3
+
4
+ module Foo
5
+ class Bar
6
+ end
7
+ end
8
+
9
+ class DatabaseTes < Test::Unit::TestCase
10
+ context "A Database class" do
11
+ setup do
12
+ unload_const('TestDb')
13
+ ::TestDb = create_const(CouchTomato::Database)
14
+ end
15
+
16
+ should "have a database name" do
17
+ TestDb.class_eval do
18
+ name 'hello-world'
19
+ end
20
+ assert_equal TestDb.database_name, 'hello-world'
21
+ end # have db name
22
+
23
+ should "assign the localhost as server if none is given" do
24
+ @db = Object.new
25
+ stub(TestDb).database_name{'hello-world'}
26
+ stub(TestDb).database{@db}
27
+ stub(@db).info{1}
28
+
29
+ TestDb.class_eval do
30
+ server
31
+ end
32
+ assert_equal TestDb.database_server, 'http://127.0.0.1:5984/'
33
+ end #instatiate couchrest
34
+
35
+ should "raise an exception if the database doesn't exist" do
36
+ assert_raise RestClient::RequestFailed do
37
+ TestDb.class_eval do
38
+ name 'DoesNotExist'
39
+ server # 'http://127.0.0.1:5984/does-not-exist'
40
+ end
41
+ TestDb.database
42
+ end
43
+ end
44
+
45
+ context "with a correctly configured database and server" do
46
+ setup do
47
+ stub(TestDb).database_name{'hello-world'}
48
+ stub(TestDb).database_server{'http://127.0.0.1:5984/'}
49
+ stub(TestDb.database).info{1}
50
+ end
51
+
52
+ should "raise an exception if no nemonic is given for the view" do
53
+ assert_raise ArgumentError do
54
+ TestDb.class_eval do
55
+ view
56
+ end
57
+ end
58
+ end # raise an exception
59
+
60
+ should "use nemonic name for view name if none is given" do
61
+ TestDb.class_eval do
62
+ view :hello
63
+ end
64
+ assert_equal TestDb.views[:hello][:view_name], 'hello'
65
+ end #should have data from view
66
+
67
+ should "use database name for design doc name if none is given" do
68
+ TestDb.class_eval do
69
+ view :hello
70
+ end
71
+ assert_equal TestDb.views[:hello][:design_doc].to_s, 'hello-world'
72
+ end #should have data from view
73
+
74
+ context "if given a document to save" do
75
+ setup do
76
+ @document = Object.new
77
+ stub(@document).dirty?{1}
78
+ end
79
+
80
+ should "call create_doc if the document is new" do
81
+ stub(@document).new?{1}
82
+ stub(TestDb).create_document {true}
83
+ dont_allow(TestDb).update_document(@document)
84
+ assert_equal TestDb.save_document(@document), true
85
+ end
86
+
87
+ should "call update_doc if the document is not new" do
88
+ stub(@document).new?{nil}
89
+ stub(TestDb).update_document {true}
90
+ dont_allow(TestDb).create_document(@document)
91
+ assert_equal TestDb.save_document(@document), true
92
+ end
93
+
94
+ context ", " do
95
+ setup do
96
+ stub(@document).valid?{1}
97
+ stub(@document).to_hash{1}
98
+
99
+ stub(TestDb.database).save_doc{{'rev' => '1', 'id' => '123', }}
100
+ stub(@document)._rev=('1')
101
+
102
+ mock(@document).run_callbacks(:before_validation_on_save)
103
+ mock(@document).run_callbacks(:before_save)
104
+ mock(@document).run_callbacks(:after_save)
105
+ end
106
+
107
+ should ", if the document is new, call the required callbacks before saving it" do
108
+ stub(@document).new?{1}
109
+
110
+ stub(@document).database=(TestDb)
111
+ stub(@document)._id=('123')
112
+
113
+ mock(@document).run_callbacks(:before_validation_on_create)
114
+ mock(@document).run_callbacks(:before_create)
115
+ mock(@document).run_callbacks(:after_create)
116
+
117
+ assert_equal TestDb.save_document(@document), true
118
+ end
119
+
120
+ should ", if the document is not new, call the required callbacks before saving it" do
121
+ stub(@document).new?{nil}
122
+
123
+ mock(@document).run_callbacks(:before_validation_on_update)
124
+ mock(@document).run_callbacks(:before_update)
125
+ mock(@document).run_callbacks(:after_update)
126
+
127
+ assert_equal TestDb.save_document(@document), true
128
+ end
129
+ end # context ,
130
+ end #context if given a doc to save
131
+
132
+ context "if loading a document" do
133
+ setup do
134
+
135
+ end
136
+
137
+ should "raise an exception if no id is given" do
138
+ assert_raise ArgumentError do
139
+ TestDb.load_doc
140
+ end
141
+ end
142
+
143
+ should "raise an exception if a nil id is given" do
144
+ assert_raise RuntimeError do
145
+ TestDb.load_doc nil
146
+ end
147
+ end
148
+
149
+ should "return nil if no document matching the given id is found" do
150
+ stub(TestDb.database).get('123456789'){raise RestClient::ResourceNotFound}
151
+ document = TestDb.load_doc '123456789'
152
+ assert_equal document, nil
153
+ end
154
+
155
+ should "return a hash if :model => :raw is given as an option" do
156
+ stub(TestDb.database).get('123456789'){{:key => "value", :key2 => "value2", }}
157
+ document = TestDb.load_doc '123456789', :model => :raw
158
+ assert_equal document, {:key => "value", :key2 => "value2", }
159
+ end
160
+
161
+ should "return a hash if the document does not specify a class" do
162
+ stub(TestDb.database).get('123456789'){{:key => "value", :key2 => "value2", }}
163
+ document = TestDb.load_doc '123456789'
164
+ assert_equal document, {:key => "value", :key2 => "value2", }
165
+ end
166
+
167
+ should "return the right class of object if one is specified in the document" do
168
+ stub(TestDb.database).get('123456789'){{'ruby_class' => 'Object', :key2 => "value2", }}
169
+ document = TestDb.load_doc '123456789'
170
+ # document = TestDb.load_doc '123456789', :model => Object
171
+
172
+ assert_equal document.class, Object
173
+ end
174
+
175
+ should "find classes namespeced within other classes" do
176
+ stub(TestDb.database).get('123456789'){{'ruby_class' => 'Foo::Bar', :key2 => "value2", }}
177
+ document = TestDb.load_doc '123456789'
178
+
179
+ assert_equal document.class, Foo::Bar
180
+ end
181
+ end #context loading document
182
+
183
+ context "if querying a view" do
184
+ setup do
185
+
186
+ end
187
+
188
+ should "raise an exception if the view is not found in the DB class" do
189
+ # self.query_view(name, options.merge(self.views[name][:couch_options]))
190
+ assert_raise RuntimeError do
191
+ TestDb.query_view!(:query_name)
192
+ end
193
+ end
194
+
195
+ should "raise an exception if the view is not found in the database" do
196
+ stub(TestDb).query_view(:query_name,{}){raise RestClient::ResourceNotFound}
197
+ stub(TestDb).views{{:query_name => "query_name"}}
198
+
199
+ assert_raise RestClient::ResourceNotFound do
200
+ TestDb.query_view!(:query_name)
201
+ end
202
+
203
+ end
204
+
205
+ context "the results" do
206
+ setup do
207
+ @fields = {:"key" => "value", :"key2" => "value2"}
208
+ @fields2 = {:"key3" => "value3", :"key4" => "value4"}
209
+
210
+ @row1 = {"id" => "123456789", "value" => @fields, "key" => "7654321"}
211
+ @row2 = {"id" => "987654321", "value" => @fields2, "key" => "1234567"}
212
+ end
213
+
214
+ should "return an array of hashes if the documents do not cotain Class info" do
215
+ stub(TestDb).query_view(:query_name,{}){{"rows" => [@row1, @row2], "offset" => 0, "total_rows" => 2 }}
216
+ stub(TestDb).views{{:query_name => {:anything => "query_name"}}}
217
+
218
+ assert_equal TestDb.query_view!(:query_name), [@fields, @fields2]
219
+ end
220
+
221
+ should "find classes namespeced within other classes"
222
+
223
+ should "return an empty array if results is empty" do
224
+ stub(TestDb).views{{:query_name => {:anything => "query_name"}}}
225
+
226
+ empty_results = {"rows" => [], "offset" => 0, "total_rows" => 2 }
227
+ assert_equal TestDb.process_results(:query_name, empty_results), []
228
+ end
229
+
230
+ should "return an array of hashes if the documents cotain Class info but the user specified :model=> :raw" do
231
+ @fields.merge!({"ruby_class" => "Object"})
232
+ @fields2.merge!({"ruby_class" => "Object"})
233
+
234
+ stub(TestDb).query_view(:query_name,{:model => :raw}){{"rows" => [@row1, @row2], "offset" => 0, "total_rows" => 2 }}
235
+ stub(TestDb).views{{:query_name => {:anything => "query_name"}}}
236
+
237
+ assert_equal TestDb.query_view!(:query_name, :model => :raw ), [@fields, @fields2]
238
+
239
+ end
240
+
241
+ should "return an array of Objects if the view definition specified :model=> Object" do
242
+ @fields.merge!({"ruby_class" => "Object"})
243
+ @fields2.merge!({"ruby_class" => "Object"})
244
+
245
+ stub(TestDb).query_view(:query_name,{}){{"rows" => [@row1, @row2], "offset" => 0, "total_rows" => 2 }}
246
+ stub(TestDb).views{{:query_name => {:model => Object}}}
247
+ stub(Object).json_create(@fields, {"id"=>"123456789", "key"=>"7654321"}){Object.new}
248
+ stub(Object).json_create(@fields2, {"id"=>"987654321", "key"=>"1234567"}){Object.new}
249
+
250
+
251
+ assert_equal TestDb.query_view!(:query_name, {})[0].class, Object
252
+ assert_equal TestDb.query_view!(:query_name, {})[1].class, Object
253
+ end
254
+
255
+ should "return an array of Objects if the view definition did not specify :model=> Object but the docs contain 'ruby_class' info" do
256
+ @fields.merge!({"ruby_class" => "Object"})
257
+ @fields2.merge!({"ruby_class" => "Object"})
258
+
259
+ stub(TestDb).query_view(:query_name,{}){{"rows" => [@row1, @row2], "offset" => 0, "total_rows" => 2 }}
260
+ stub(TestDb).views{{:query_name => {:anything => "query_name"}}}
261
+ stub(Object).json_create(@fields, {"id"=>"123456789", "key"=>"7654321"}){Object.new}
262
+ stub(Object).json_create(@fields2, {"id"=>"987654321", "key"=>"1234567"}){Object.new}
263
+
264
+ assert_equal TestDb.query_view!(:query_name, {})[0].class, Object
265
+ assert_equal TestDb.query_view!(:query_name, {})[1].class, Object
266
+
267
+ end
268
+
269
+ should "return an mixed array of Objects and hashes if the view definition did not specify :model=> Object but some docs contain 'ruby_class' info" do
270
+ @fields.merge!({"ruby_class" => "Object"})
271
+
272
+ stub(TestDb).query_view(:query_name,{}){{"rows" => [@row1, @row2], "offset" => 0, "total_rows" => 2 }}
273
+ stub(TestDb).views{{:query_name => {:anything => "query_name"}}}
274
+ stub(Object).json_create(@fields, {"id"=>"123456789", "key"=>"7654321"}){Object.new}
275
+ stub(Hash).json_create(@fields2, {"id"=>"987654321", "key"=>"1234567"}){Object.new}
276
+
277
+ assert_equal TestDb.query_view!(:query_name, {})[0].class, Object
278
+ assert_equal TestDb.query_view!(:query_name, {})[1].class, Hash
279
+
280
+ end
281
+ end # context the results
282
+ end # context querying a view
283
+ end # context with db and serv
284
+ end #context a Db
285
+ end # class