couchobject 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/History.txt +10 -0
  2. data/Manifest.txt +30 -6
  3. data/README.txt +580 -42
  4. data/TODO +2 -2
  5. data/config/hoe.rb +1 -1
  6. data/lib/couch_object.rb +7 -2
  7. data/lib/couch_object/database.rb +19 -34
  8. data/lib/couch_object/document.rb +13 -6
  9. data/lib/couch_object/error_classes.rb +110 -0
  10. data/lib/couch_object/persistable.rb +954 -36
  11. data/lib/couch_object/persistable/has_many_relations_array.rb +91 -0
  12. data/lib/couch_object/persistable/meta_classes.rb +568 -0
  13. data/lib/couch_object/persistable/overloaded_methods.rb +209 -0
  14. data/lib/couch_object/server.rb +1 -1
  15. data/lib/couch_object/utils.rb +44 -0
  16. data/lib/couch_object/version.rb +1 -1
  17. data/lib/couch_object/view.rb +129 -6
  18. data/script/console +0 -0
  19. data/script/destroy +0 -0
  20. data/script/generate +0 -0
  21. data/script/txt2html +0 -0
  22. data/spec/database_spec.rb +23 -31
  23. data/spec/database_spec.rb.orig +173 -0
  24. data/spec/document_spec.rb +21 -3
  25. data/spec/integration/database_integration_spec.rb +46 -15
  26. data/spec/integration/integration_helper.rb +3 -3
  27. data/spec/persistable/callback.rb +44 -0
  28. data/spec/persistable/callback_spec.rb +44 -0
  29. data/spec/persistable/cloning.rb +77 -0
  30. data/spec/persistable/cloning_spec.rb +77 -0
  31. data/spec/persistable/comparing_objects.rb +350 -0
  32. data/spec/persistable/comparing_objects_spec.rb +350 -0
  33. data/spec/persistable/deleting.rb +113 -0
  34. data/spec/persistable/deleting_spec.rb +113 -0
  35. data/spec/persistable/error_messages.rb +32 -0
  36. data/spec/persistable/error_messages_spec.rb +32 -0
  37. data/spec/persistable/loading.rb +339 -0
  38. data/spec/persistable/loading_spec.rb +339 -0
  39. data/spec/persistable/new_methods.rb +70 -0
  40. data/spec/persistable/new_methods_spec.rb +70 -0
  41. data/spec/persistable/persistable_helper.rb +194 -0
  42. data/spec/persistable/relations.rb +470 -0
  43. data/spec/persistable/relations_spec.rb +470 -0
  44. data/spec/persistable/saving.rb +137 -0
  45. data/spec/persistable/saving_spec.rb +137 -0
  46. data/spec/persistable/setting_storage_location.rb +65 -0
  47. data/spec/persistable/setting_storage_location_spec.rb +65 -0
  48. data/spec/persistable/timestamps.rb +76 -0
  49. data/spec/persistable/timestamps_spec.rb +76 -0
  50. data/spec/persistable/unsaved_changes.rb +211 -0
  51. data/spec/persistable/unsaved_changes_spec.rb +211 -0
  52. data/spec/server_spec.rb +5 -5
  53. data/spec/utils_spec.rb +60 -0
  54. data/spec/view_spec.rb +40 -7
  55. data/website/index.html +22 -7
  56. data/website/index.txt +13 -5
  57. metadata +93 -61
  58. data/bin/couch_ruby_view_requestor +0 -81
  59. data/lib/couch_object/model.rb +0 -5
  60. data/lib/couch_object/proc_condition.rb +0 -14
  61. data/spec/model_spec.rb +0 -5
  62. data/spec/persistable_spec.rb +0 -91
  63. data/spec/proc_condition_spec.rb +0 -26
@@ -0,0 +1,173 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe CouchObject::Database do
4
+ before(:each) do
5
+ @server = mock("Couch server")
6
+ @uri = "http://localhost:8888"
7
+ @response = mock("Net::HTTP::Response")
8
+ CouchObject::Server.should_receive(:new).with(@uri).and_return(@server)
9
+ @response.stub!(:code).and_return(200)
10
+ @response.stub!(:body).and_return("[\"db1\", \"db2\"]")
11
+ @document_response = {
12
+ "_id" => "123BAC",
13
+ "_rev" => "946B7D1C",
14
+ "attributes" => {
15
+ "wheels" => 3
16
+ }
17
+ }
18
+ end
19
+
20
+ it "should create a database" do
21
+ @server.should_receive(:put).with("/foo", "").and_return(@response)
22
+ CouchObject::Database.create(@uri, "foo")
23
+ end
24
+
25
+ it "should delete a database" do
26
+ @server.should_receive(:delete).with("/foo").and_return(@response)
27
+ CouchObject::Database.delete!(@uri, "foo")
28
+ end
29
+
30
+ it "should get all databases" do
31
+ @server.should_receive(:get).with("/_all_dbs").and_return(@response)
32
+ CouchObject::Database.all_databases(@uri)
33
+ end
34
+
35
+ it "should return all databases as an array" do
36
+ @server.should_receive(:get).with("/_all_dbs").and_return(@response)
37
+ dbs = CouchObject::Database.all_databases(@uri)
38
+ dbs.should == ["db1", "db2"]
39
+ end
40
+
41
+ it "should open a connection to the server" do
42
+ db = CouchObject::Database.new(@uri, "foo")
43
+ db.server.should == @server
44
+ end
45
+
46
+ it "should have a name" do
47
+ db = CouchObject::Database.new(@uri, "foo")
48
+ db.name.should == "foo"
49
+ end
50
+
51
+ it "should lint the database name from slashes with ::open" do
52
+ db = CouchObject::Database.open("http://localhost:8888/foo")
53
+ class << db
54
+ attr_accessor :dbname
55
+ end
56
+ db.dbname.should == "foo"
57
+ end
58
+
59
+ it "should GET" do
60
+ db = CouchObject::Database.new(@uri, "foo")
61
+ @server.should_receive(:get).with("/foo/123").and_return(@response)
62
+ db.get("123")
63
+ end
64
+
65
+ it "should POST" do
66
+ db = CouchObject::Database.new(@uri, "foo")
67
+ @server.should_receive(:post).with("/foo/123", "postdata").and_return(@response)
68
+ db.post("123", "postdata")
69
+ end
70
+
71
+ it "should PUT" do
72
+ db = CouchObject::Database.new(@uri, "foo")
73
+ @server.should_receive(:put).with("/foo/123", "postdata").and_return(@response)
74
+ db.put("123", "postdata")
75
+ end
76
+
77
+ it "should DELETE" do
78
+ db = CouchObject::Database.new(@uri, "foo")
79
+ @server.should_receive(:delete).with("/foo/123").and_return(@response)
80
+ db.delete("123")
81
+ end
82
+
83
+ it "should open a new connection from a full uri spec" do
84
+ proc{ CouchObject::Database.open("http://localhost:8888/foo") }.should_not raise_error
85
+ end
86
+
87
+ it "should know the database name" do
88
+ db = CouchObject::Database.new(@uri, "foo")
89
+ db.name.should == "foo"
90
+ end
91
+
92
+ it "should know the full uri" do
93
+ db = CouchObject::Database.new(@uri, "foo")
94
+ db.url.should == "http://localhost:8888/foo"
95
+ end
96
+
97
+ it "should load a document from id with #[]" do
98
+ db = CouchObject::Database.new(@uri, "foo")
99
+ db.should_receive(:get).with("123").twice.and_return(@document_response)
100
+ proc{ db["123"] }.should_not raise_error
101
+ db["123"].should == @document_response
102
+ end
103
+
104
+ it "should accept symbols for #[] too" do
105
+ db = CouchObject::Database.new(@uri, "foo")
106
+ db.should_receive(:get).with("foo").and_return(@document_response)
107
+ db[:foo]
108
+ end
109
+
110
+ it "should get a document by id" do
111
+ db = CouchObject::Database.new(@uri, "foo")
112
+ db.should_receive(:get).with("foo").twice.and_return(@document_response)
113
+ proc{ db.document("foo") }.should_not raise_error
114
+ db.document("foo").should == @document_response
115
+ end
116
+
117
+ it "should get a document by id and revision" do
118
+ db = CouchObject::Database.new(@uri, "foo")
119
+ db.should_receive(:get).with("foo?rev=123").twice.and_return(@document_response)
120
+ proc{ db.document("foo", "123") }.should_not raise_error
121
+ db.document("foo", "123").should == @document_response
122
+ end
123
+
124
+ it "should query the view" do
125
+ db = CouchObject::Database.new(@uri, "foo")
126
+ CouchObject::View.should_receive(:new).with(db, "myview").and_return(view = mock("View mock"))
127
+ view.should_receive(:query).and_return(nil)
128
+ db.views("myview")
129
+ end
130
+
131
+ it "should get a list of all documents" do
132
+ db = CouchObject::Database.new(@uri, "foo")
133
+ resp = mock("response")
134
+ resp.stub!(:body).and_return(JSON.unparse("rows" => [{"_rev"=>123, "_id"=>"123ABC"}]))
135
+ resp.stub!(:to_document).and_return(
136
+ CouchObject::Document.new("rows" => [{"_rev"=>123, "_id"=>"123ABC"}])
137
+ )
138
+ db.should_receive(:get).with("_all_docs").and_return(resp)
139
+ db.all_documents.should == [{"_rev"=>123, "_id"=>"123ABC"}]
140
+ end
141
+
142
+ it "should store a document" do
143
+ db = CouchObject::Database.new(@uri, "foo")
144
+ doc = CouchObject::Document.new({"foo" => "bar"})
145
+ db.should_receive(:post).with("", JSON.unparse("foo" => "bar"))
146
+ db.store(doc)
147
+ end
148
+
149
+ it "should return the rows when filtering" do
150
+ db = CouchObject::Database.new(@uri, "foo")
151
+ rowdata = { "_rev"=>1,
152
+ "_id"=>"1",
153
+ "value"=> {
154
+ "_id"=>"1",
155
+ "_rev"=>1,
156
+ "foo"=>"bar"
157
+ }
158
+ }
159
+ resp = mock("response")
160
+ resp.stub!(:body).and_return(JSON.unparse("rows" => [rowdata]))
161
+ resp.stub!(:parse).and_return(resp)
162
+ resp.stub!(:to_document).and_return(
163
+ CouchObject::Document.new("rows" => [rowdata])
164
+ )
165
+ db.should_receive(:post).with(
166
+ "_temp_view", "proc { |doc|\n (doc[\"foo\"] == \"bar\")\n}").and_return(resp)
167
+ rows = db.filter{|doc| doc["foo"] == "bar"}
168
+ rows.size.should == 1
169
+ rows.first["value"]["foo"].should == "bar"
170
+ end
171
+
172
+ #it "should url encode paths"
173
+ end
@@ -10,10 +10,14 @@ describe CouchObject::Document do
10
10
  "foo" => "bar",
11
11
  "baz" => {"quux" => [1,2,3]},
12
12
  }
13
+
13
14
  @response = mock("Response mock")
14
15
  @response.stub!(:code).and_return(200)
15
16
  @response.stub!(:body).and_return("")
16
17
  @response.stub!(:to_document).and_return(CouchObject::Document.new(@test_data))
18
+
19
+ @db.stub!(:post).and_return(@response)
20
+ @db.stub!(:put).and_return(@response)
17
21
  end
18
22
 
19
23
  it "should initialize with a bunch of attributes" do
@@ -71,11 +75,25 @@ describe CouchObject::Document do
71
75
  doc.save(@db)
72
76
  end
73
77
 
78
+ it "should set id and rev on create with POST" do
79
+ doc = CouchObject::Document.new({"foo" => "bar"})
80
+ doc.save(@db)
81
+ doc.id.should == '123'
82
+ doc.revision.should == '666'
83
+ end
84
+
74
85
  it "should PUT to the database with create" do
75
- doc = CouchObject::Document.new(@test_data)
76
- doc.should_receive(:to_json).and_return("JSON")
77
- @db.should_receive(:put).with(@test_data["_id"], "JSON").and_return(@response)
86
+ doc = CouchObject::Document.new({"foo" => "bar", "_id" => "quux"})
87
+ doc.should_receive(:to_json).with(no_args).and_return("JSON")
88
+ @db.should_receive(:put).with("quux", "JSON").and_return(@response)
89
+ doc.save(@db)
90
+ end
91
+
92
+ it "should set id and rev on save with PUT" do
93
+ doc = CouchObject::Document.new({"foo" => "bar", "_id" => "quux"})
78
94
  doc.save(@db)
95
+ doc.id.should == '123'
96
+ doc.revision.should == '666'
79
97
  end
80
98
 
81
99
  it "should send the current revision along when updating a doc" do
@@ -9,7 +9,7 @@ describe "Database operations" do
9
9
  end
10
10
 
11
11
  it "should create a database " do
12
- all_dbs = proc{ CouchObject::Database.all_databases("http://localhost:8888") }
12
+ all_dbs = proc{ CouchObject::Database.all_databases("http://localhost:5984") }
13
13
  all_dbs.call.include?("couchobject_test").should == false
14
14
 
15
15
  create_test_db.should == {"ok" => true}
@@ -18,12 +18,12 @@ describe "Database operations" do
18
18
  end
19
19
 
20
20
  it "should delete a database" do
21
- all_dbs = proc{ CouchObject::Database.all_databases("http://localhost:8888") }
21
+ all_dbs = proc{ CouchObject::Database.all_databases("http://localhost:5984") }
22
22
  all_dbs.call.include?("couchobject_test").should == false
23
23
 
24
24
  create_test_db.should == {"ok" => true}
25
25
  CouchObject::Database.delete!(
26
- "http://localhost:8888",
26
+ "http://localhost:5984",
27
27
  "couchobject_test"
28
28
  ).should == {"ok" => true}
29
29
 
@@ -33,7 +33,7 @@ describe "Database operations" do
33
33
  it "should open a db connection and know the dbname and uri" do
34
34
  db = open_test_db
35
35
  db.name.should == "couchobject_test"
36
- db.url.should == "http://localhost:8888/couchobject_test"
36
+ db.url.should == "http://localhost:5984/couchobject_test"
37
37
  end
38
38
 
39
39
  it "should GET a non existing document and return 404" do
@@ -68,24 +68,55 @@ describe "Database operations" do
68
68
  response = db.post("", JSON.unparse({"foo" => ["bar", "baz"]}))
69
69
  created_doc = response.to_document
70
70
 
71
- resp = db.delete(created_doc.id)
71
+ resp = db.delete(created_doc.id, created_doc.revision)
72
72
  resp.code.should == 202
73
73
 
74
74
  resp = db.get(created_doc.id)
75
75
  resp.code.should == 404
76
76
  end
77
77
 
78
- it "should filter documents" do
79
- db = create_and_open_test_db
80
- db.post("", JSON.unparse({"foo" => "bar"}))
81
- db.post("", JSON.unparse({"foo" => "baz"}))
82
- db.post("", JSON.unparse({"foo" => "qux"}))
83
- db.all_documents.size.should == 3
84
- results = db.filter do |doc|
85
- doc["foo"] =~ /ba/
86
- end
87
- results.size.should == 2
78
+ end
79
+
80
+
81
+ describe "A Database and some UT8" do
82
+ include IntegrationSpecHelper
83
+ # Translated form couch_test.js
84
+
85
+ before(:each) do
86
+ delete_test_db
87
+ end
88
+
89
+ def do_check_text(db, text)
90
+ resp = db.post("", JSON.unparse("text" => text))
91
+ resp.code.should == 201
88
92
 
93
+ r = db.get(resp.to_document.id)
94
+ r.code.should == 200
95
+ r.to_document.text.should == text
96
+ end
97
+
98
+ it "should deal with plain ascii nicely" do
99
+ db = create_and_open_test_db
100
+ do_check_text(db, "Ascii: hello")
101
+ end
102
+
103
+ it "should deal with Russian cyrillic" do
104
+ db = create_and_open_test_db
105
+ do_check_text(db, "Russian: На берегу пустынных волн")
106
+ end
107
+
108
+ it "should deal withn Math expressions" do
109
+ db = create_and_open_test_db
110
+ do_check_text(db, "Math: ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i),")
89
111
  end
90
112
 
113
+ it "should deal with Greek" do
114
+ db = create_and_open_test_db
115
+ do_check_text(db, "Geek: STARGΛ̊TE SG-1")
116
+ end
117
+
118
+ it "should braille" do
119
+ db = create_and_open_test_db
120
+ do_check_text(db, "Braille: ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌")
121
+ end
91
122
  end
@@ -2,15 +2,15 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
2
 
3
3
  module IntegrationSpecHelper
4
4
  def create_test_db(name="couchobject_test")
5
- CouchObject::Database.create!("http://localhost:8888", name)
5
+ CouchObject::Database.create("http://localhost:5984", name)
6
6
  end
7
7
 
8
8
  def open_test_db(name="couchobject_test")
9
- CouchObject::Database.open("http://localhost:8888/#{name}")
9
+ CouchObject::Database.open("http://localhost:5984/#{name}")
10
10
  end
11
11
 
12
12
  def delete_test_db(name="couchobject_test")
13
- CouchObject::Database.delete!("http://localhost:8888", "couchobject_test")
13
+ CouchObject::Database.delete!("http://localhost:5984", "couchobject_test")
14
14
  end
15
15
 
16
16
  def create_and_open_test_db(name="couchobject_test")
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "behaviour related to cloning and copying:" do
4
+ before(:each) do
5
+ @db = mock("mock db")
6
+
7
+ content = HTTPResponse.new(JSON.unparse({
8
+ "_id" => "123BAC",
9
+ "_rev" => "946B7D1C",
10
+ "class" => WithCallbacks,
11
+ "created_at" => Time.new,
12
+ "updated_at" => Time.new,
13
+ "attributes" => {}
14
+ }))
15
+
16
+ @empty_response = {}
17
+ @ok_response = {"ok" => true}
18
+ @document_response = CouchObject::Response.new(content)
19
+ end
20
+
21
+ it "all callback should be called" do
22
+ CouchObject::Database.should_receive(:open).exactly(3).and_return(@db)
23
+ @db.should_receive(:post).and_return(@document_response)
24
+ @db.should_receive(:put).and_return(@document_response)
25
+ @db.should_receive(:delete).and_return(@document_response)
26
+
27
+ wc = WithCallbacks.new
28
+ wc.save
29
+ wc.save
30
+ wc.delete
31
+
32
+ wc.calls.should == ["before_save",
33
+ "before_create",
34
+ "after_create",
35
+ "after_save",
36
+ "before_save",
37
+ "before_update",
38
+ "after_update",
39
+ "after_save",
40
+ "before_delete",
41
+ "after_delete"]
42
+
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "behaviour related to cloning and copying:" do
4
+ before(:each) do
5
+ @db = mock("mock db")
6
+
7
+ content = HTTPResponse.new(JSON.unparse({
8
+ "_id" => "123BAC",
9
+ "_rev" => "946B7D1C",
10
+ "class" => WithCallbacks,
11
+ "created_at" => Time.new,
12
+ "updated_at" => Time.new,
13
+ "attributes" => {}
14
+ }))
15
+
16
+ @empty_response = {}
17
+ @ok_response = {"ok" => true}
18
+ @document_response = CouchObject::Response.new(content)
19
+ end
20
+
21
+ it "all callback should be called" do
22
+ CouchObject::Database.should_receive(:open).exactly(3).and_return(@db)
23
+ @db.should_receive(:post).and_return(@document_response)
24
+ @db.should_receive(:put).and_return(@document_response)
25
+ @db.should_receive(:delete).and_return(@document_response)
26
+
27
+ wc = WithCallbacks.new
28
+ wc.save
29
+ wc.save
30
+ wc.delete
31
+
32
+ wc.calls.should == ["before_save",
33
+ "before_create",
34
+ "after_create",
35
+ "after_save",
36
+ "before_save",
37
+ "before_update",
38
+ "after_update",
39
+ "after_save",
40
+ "before_delete",
41
+ "after_delete"]
42
+
43
+ end
44
+ end
@@ -0,0 +1,77 @@
1
+ require File.dirname(__FILE__) + '/persistable_helper.rb'
2
+
3
+ describe CouchObject::Persistable, "behaviour related to cloning and copying:" do
4
+ before(:each) do
5
+ @bike = Bike.new
6
+
7
+ @db = mock("mock db")
8
+
9
+ content = HTTPResponse.new(JSON.unparse({
10
+ "_id" => "123BAC",
11
+ "_rev" => "946B7D1C",
12
+ "created_at" => Time.new,
13
+ "updated_at" => Time.new,
14
+ "attributes" => {
15
+ "wheels" => 3
16
+ }
17
+ }))
18
+
19
+ @empty_response = {}
20
+ @ok_response = {"ok" => true}
21
+ @document_response = CouchObject::Response.new(content)
22
+ end
23
+
24
+ it "clones and dupes should not have an ID and a revision number" do
25
+ CouchObject::Database.should_receive(:open).and_return(@db)
26
+ @db.should_receive(:post).
27
+ with("", @bike.to_json).and_return(@document_response)
28
+ @bike.set_location = "foo"
29
+ @bike.save
30
+
31
+ new_bike = @bike.clone
32
+ second_new_bike = @bike.dup
33
+
34
+ # clone
35
+ new_bike.id.should_not == @bike.id
36
+ new_bike.revision.should_not == @bike.revision
37
+
38
+ # dup
39
+ second_new_bike.id.should_not == @bike.id
40
+ second_new_bike.revision.should_not == @bike.revision
41
+ end
42
+
43
+ it "clones and dupes should share the same location" do
44
+ CouchObject::Database.should_receive(:open).and_return(@db)
45
+ @db.should_receive(:post).
46
+ with("", @bike.to_json).and_return(@document_response)
47
+ @bike.set_location = "foo"
48
+ @bike.save
49
+
50
+ new_bike = @bike.clone
51
+ second_new_bike = @bike.dup
52
+
53
+ # clone
54
+ new_bike.location.should == @bike.location
55
+
56
+ # dup
57
+ second_new_bike.location.should == @bike.location
58
+ end
59
+
60
+ it "clones should share attributes" do
61
+ CouchObject::Database.should_receive(:open).and_return(@db)
62
+ @db.should_receive(:post).
63
+ with("", @bike.to_json).and_return(@document_response)
64
+ @bike.set_location = "foo"
65
+ @bike.save
66
+
67
+ new_bike = @bike.clone
68
+ second_new_bike = @bike.dup
69
+
70
+ # clone
71
+ new_bike.wheels.should == @bike.wheels
72
+
73
+ # dup
74
+ second_new_bike.wheels.should == @bike.wheels
75
+ end
76
+
77
+ end