couchobject 0.5.0 → 0.6.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 (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