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,134 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ # class Build
4
+ # include CouchTomato::Persistence
5
+ #
6
+ # property :state
7
+ # property :time
8
+ #
9
+ # view :timeline, :key => :time
10
+ # view :count, :key => :time, :reduce => true
11
+ # view :minimal_timeline, :key => :time, :properties => [:state], :type => :properties
12
+ # view :key_array_timeline, :key => [:time, :state]
13
+ # view :custom_timeline, :map => "function(doc) { emit(doc._id, {state: 'custom_' + doc.state}); }", :type => :custom
14
+ # view :custom_timeline_returns_docs, :map => "function(doc) { emit(doc._id, null); }", :include_docs => true, :type => :custom
15
+ # view :raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}"
16
+ # view :filtered_raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}", :results_filter => lambda{|res| res['rows'].map{|row| row['value']}}
17
+ # view :with_view_options, :group => true, :key => :time
18
+ # end
19
+ #
20
+ # describe 'view' do
21
+ # before(:each) do
22
+ # recreate_db
23
+ # end
24
+ #
25
+ # it "should return instances of the class" do
26
+ # CouchTomato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
27
+ # results = CouchTomato.database.view(Build.timeline)
28
+ # results.map(&:class).should == [Build]
29
+ # end
30
+ #
31
+ # it "should pass the view options to the view query" do
32
+ # query = mock 'query'
33
+ # CouchTomato::View::ViewQuery.stub!(:new).and_return(query)
34
+ # query.should_receive(:query_view!).with(hash_including(:key => 1)).and_return('rows' => [])
35
+ # CouchTomato.database.view Build.timeline(:key => 1)
36
+ # end
37
+ #
38
+ # it "should not return documents that don't have a matching ruby_class" do
39
+ # CouchTomato.couchrest_database.save_doc({:time => 'x'})
40
+ # CouchTomato.database.view(Build.timeline).should == []
41
+ # end
42
+ #
43
+ # it "should count documents" do
44
+ # CouchTomato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
45
+ # CouchTomato.database.view(Build.count(:reduce => true)).should == 1
46
+ # end
47
+ #
48
+ # it "should count zero documents" do
49
+ # CouchTomato.database.view(Build.count(:reduce => true)).should == 0
50
+ # end
51
+ #
52
+ # describe "properties defined" do
53
+ # it "should assign the configured properties" do
54
+ # CouchTomato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', :ruby_class => 'Build')
55
+ # CouchTomato.database.view(Build.minimal_timeline).first.state.should == 'success'
56
+ # end
57
+ #
58
+ # it "should not assign the properties not configured" do
59
+ # CouchTomato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', :ruby_class => 'Build')
60
+ # CouchTomato.database.view(Build.minimal_timeline).first.time.should be_nil
61
+ # end
62
+ #
63
+ # it "should assign the id even if it is not configured" do
64
+ # id = CouchTomato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', :ruby_class => 'Build')['id']
65
+ # CouchTomato.database.view(Build.minimal_timeline).first._id.should == id
66
+ # end
67
+ # end
68
+ #
69
+ # describe "no properties defined" do
70
+ # it "should assign all properties to the objects by default" do
71
+ # id = CouchTomato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01', :ruby_class => 'Build'})['id']
72
+ # result = CouchTomato.database.view(Build.timeline).first
73
+ # result.state.should == 'success'
74
+ # result.time.should == '2008-01-01'
75
+ # result._id.should == id
76
+ # end
77
+ # end
78
+ #
79
+ # describe "map function given" do
80
+ # it "should still return instances of the class" do
81
+ # CouchTomato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
82
+ # CouchTomato.database.view(Build.custom_timeline).map(&:class).should == [Build]
83
+ # end
84
+ #
85
+ # it "should assign the properties from the value" do
86
+ # CouchTomato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
87
+ # CouchTomato.database.view(Build.custom_timeline).map(&:state).should == ['custom_success']
88
+ # end
89
+ #
90
+ # it "should leave the other properties blank" do
91
+ # CouchTomato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
92
+ # CouchTomato.database.view(Build.custom_timeline).map(&:time).should == [nil]
93
+ # end
94
+ #
95
+ # describe "that returns null documents" do
96
+ # it "should return instances of the class" do
97
+ # CouchTomato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
98
+ # CouchTomato.database.view(Build.custom_timeline_returns_docs).map(&:class).should == [Build]
99
+ # end
100
+ #
101
+ # it "should assign the properties from the value" do
102
+ # CouchTomato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
103
+ # CouchTomato.database.view(Build.custom_timeline_returns_docs).map(&:state).should == ['success']
104
+ # end
105
+ # end
106
+ # end
107
+ #
108
+ # describe "with array as key" do
109
+ # it "should create a map function with the composite key" do
110
+ # CouchTomato::View::ViewQuery.should_receive(:new).with(anything, anything, anything, string_matching(/emit\(\[doc\['time'\], doc\['state'\]\]/), anything).and_return(stub('view query').as_null_object)
111
+ # CouchTomato.database.view Build.key_array_timeline
112
+ # end
113
+ # end
114
+ #
115
+ # describe "raw view" do
116
+ # it "should return the raw data" do
117
+ # CouchTomato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
118
+ # CouchTomato.database.view(Build.raw)['rows'][0]['value'].should == 'success'
119
+ # end
120
+ #
121
+ # it "should return filtred raw data" do
122
+ # CouchTomato.database.save_document Build.new(:state => 'success', :time => '2008-01-01')
123
+ # CouchTomato.database.view(Build.filtered_raw).should == ['success']
124
+ # end
125
+ #
126
+ # it "should pass view options declared in the view declaration to the query" do
127
+ # view_query = mock 'view_query'
128
+ # CouchTomato::View::ViewQuery.stub!(:new).and_return(view_query)
129
+ # view_query.should_receive(:query_view!).with(hash_including(:group => true)).and_return({'rows' => []})
130
+ # CouchTomato.database.view(Build.with_view_options)
131
+ # end
132
+ # end
133
+ #
134
+ # end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'destroy' do
4
+ before(:all) do
5
+ recreate_db
6
+ end
7
+
8
+ before(:each) do
9
+ @comment = Comment.new :title => 'title'
10
+ CouchTomato.database.save_document! @comment
11
+ @comment_id = @comment.id
12
+ CouchTomato.database.destroy_document @comment
13
+ end
14
+
15
+ it "should unset the id" do
16
+ @comment._id.should be_nil
17
+ end
18
+
19
+ it "should unset the revision" do
20
+ @comment._rev.should be_nil
21
+ end
22
+
23
+ it "should remove the document from the database" do
24
+ lambda {
25
+ CouchTomato.couchrest_database.get(@comment_id).should
26
+ }.should raise_error(RestClient::ResourceNotFound)
27
+ end
28
+
29
+ end
@@ -0,0 +1,9 @@
1
+ class Address
2
+ include CouchTomato::Persistence
3
+
4
+ property :street
5
+ property :city
6
+ property :state
7
+ property :zip
8
+ property :country
9
+ end
@@ -0,0 +1,6 @@
1
+ class Person
2
+ include CouchTomato::Persistence
3
+
4
+ property :name
5
+ property :ship_address, :type => Address
6
+ end
@@ -0,0 +1,103 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.join(File.dirname(__FILE__), 'fixtures', 'address')
3
+ require File.join(File.dirname(__FILE__), 'fixtures', 'person')
4
+
5
+ class Watch
6
+ include CouchTomato::Persistence
7
+
8
+ property :time, :type => Time
9
+ end
10
+
11
+
12
+ describe 'properties' do
13
+ before(:all) do
14
+ recreate_db
15
+ reload_test_class("Comment")
16
+ end
17
+
18
+ it "should return the property names" do
19
+ Comment.property_names.should == [:created_at, :updated_at, :title, :commenter]
20
+ end
21
+
22
+ it "should persist a string" do
23
+ c = Comment.new :title => 'my title'
24
+ CouchTomato.database.save_document! c
25
+ c = CouchTomato.database.load_document c.id
26
+ c.title.should == 'my title'
27
+ end
28
+
29
+ # Explicitly declaring `:type => String` on a property raises an exception
30
+ # "wrong argument type String (expected Hash)" on load. The json gem defines a
31
+ # String.json_create method that expects a hash in the form `{'raw' => [0x61,
32
+ # 0x62]}` where `[0x61, 0x62].pack => 'ab'`
33
+ #
34
+ # CouchTomato expects various `json_create` methods to accept a String (and
35
+ # return an instance). Currently, if `:type` is not specified, the type of the
36
+ # value is assumed to be String and `json_create` is not called.
37
+
38
+ it "Should raise an exception if a property is of a JSON native type" do
39
+ [Float, String, Integer, Array, Hash, Fixnum].each do |klass|
40
+ doing {
41
+ Comment.class_eval do
42
+ property :name, :type => klass
43
+ end
44
+ }.should raise_error("#{klass} is a native JSON type, only custom types should be specified")
45
+ end
46
+ end
47
+
48
+ it "should persist a number" do
49
+ c = Comment.new :title => 3
50
+ CouchTomato.database.save_document! c
51
+ c = CouchTomato.database.load_document c.id
52
+ c.title.should == 3
53
+ end
54
+
55
+ it "should persist a hash" do
56
+ c = Comment.new :title => {'key' => 'value'}
57
+ CouchTomato.database.save_document! c
58
+ c = CouchTomato.database.load_document c.id
59
+ c.title.should == {'key' => 'value'}
60
+ end
61
+
62
+ it "should persist a Time object" do
63
+ w = Watch.new :time => Time.now
64
+ CouchTomato.database.save_document! w
65
+ w = CouchTomato.database.load_document w.id
66
+ w.time.year.should == Time.now.year
67
+ end
68
+
69
+ it "should persist an object" do
70
+ p = Person.new :name => 'Bob'
71
+ a = Address.new :city => 'Denver'
72
+ p.ship_address = a
73
+ CouchTomato.database.save_document! p
74
+ p = CouchTomato.database.load_document p.id
75
+ p.ship_address.should === a
76
+ end
77
+
78
+ it "should persist null for a null " do
79
+ p = Person.new :name => 'Bob'
80
+ p.ship_address = nil
81
+ CouchTomato.database.save_document! p
82
+ p = CouchTomato.database.load_document p.id
83
+ p.ship_address.should be_nil
84
+ end
85
+
86
+ describe "predicate" do
87
+ it "should return true if property set" do
88
+ Comment.new(:title => 'title').title?.should be_true
89
+ end
90
+
91
+ it "should return false if property nil" do
92
+ Comment.new.title?.should be_false
93
+ end
94
+
95
+ it "should return false if property false" do
96
+ Comment.new(:title => false).title?.should be_false
97
+ end
98
+
99
+ it "should return false if property blank" do
100
+ Comment.new(:title => '').title?.should be_false
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
5
+ alias :doing :lambda
6
+
7
+ require 'couch_tomato'
8
+ require File.dirname(__FILE__) + "/comment"
9
+
10
+ CouchTomato::Config.database_name = 'couch_tomato_test'
11
+ CouchTomato::Config.database_server = 'http://127.0.0.1:5984/'
12
+
13
+
14
+ def recreate_db
15
+ CouchTomato.couchrest_database.delete! rescue nil
16
+ CouchTomato.couchrest_database.server.create_db CouchTomato::Config.database_name
17
+ end
18
+ recreate_db
19
+
20
+ Spec::Matchers.define :string_matching do |regex|
21
+ match do |string|
22
+ string =~ regex
23
+ end
24
+ end
25
+
26
+ def reload_test_class (class_name)
27
+ Object.class_eval do
28
+ if const_defined? class_name
29
+ remove_const class_name
30
+ end
31
+ end
32
+
33
+ file_name = class_name.gsub(/::/, '/').
34
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
35
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
36
+ tr("-", "_").
37
+ downcase
38
+
39
+ load File.dirname(__FILE__) + "/#{file_name}.rb"
40
+ end
@@ -0,0 +1,26 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ class Plant
4
+ include CouchTomato::Persistence
5
+ property :leaf_count
6
+ end
7
+
8
+ describe "attributes" do
9
+
10
+ describe 'attributes=' do
11
+ it "should assign the attributes" do
12
+ plant = Plant.new
13
+ plant.attributes = {:leaf_count => 1}
14
+ plant.leaf_count.should == 1
15
+ end
16
+ end
17
+
18
+ describe "attributes" do
19
+ it "should return the attributes" do
20
+ plant = Plant.new(:leaf_count => 1)
21
+ plant.attributes.should == {:leaf_count => 1, :created_at => nil, :updated_at => nil}
22
+ end
23
+ end
24
+
25
+ end
26
+
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ class Tree
4
+ include CouchTomato::Persistence
5
+ before_validation :water!
6
+ before_validation lambda {|tree| tree.root_count += 1 }
7
+
8
+ property :leaf_count
9
+ property :root_count
10
+
11
+ def water!
12
+ self.leaf_count += 1
13
+ end
14
+ end
15
+
16
+
17
+ describe 'before_validation callback' do
18
+ before :each do
19
+ @tree = Tree.new(:leaf_count => 1, :root_count => 1)
20
+ end
21
+
22
+ it "should call water! when validated" do
23
+ @tree.leaf_count.should == 1
24
+ @tree.should be_valid
25
+ @tree.leaf_count.should == 2
26
+ end
27
+
28
+ it "should call lambda when validated" do
29
+ @tree.root_count.should == 1
30
+ @tree.should be_valid
31
+ @tree.root_count.should == 2
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "create" do
4
+
5
+ describe "succeeds" do
6
+ before(:each) do
7
+ @comment = Comment.new :title => 'my_title'
8
+ CouchTomato::Database.new(stub('database', :save_doc => {'rev' => '123', 'id' => '456'}, :info => nil)).save_document!(@comment)
9
+ end
10
+
11
+ it "should assign the id" do
12
+ @comment._id.should == '456'
13
+ end
14
+
15
+ it "should assign the revision" do
16
+ @comment._rev.should == '123'
17
+ end
18
+
19
+ it "should set created at" do
20
+ @comment.created_at.should >= Time.now - 10
21
+ end
22
+
23
+ it "should set updated at" do
24
+ @comment.updated_at.should >= Time.now - 10
25
+ end
26
+ end
27
+
28
+ describe "fails" do
29
+ before(:each) do
30
+ @comment = Comment.new
31
+ CouchTomato::Database.new(stub('database', :info => nil)).save_document(@comment)
32
+ end
33
+
34
+ it "should not assign an id" do
35
+ @comment._id.should be_nil
36
+ end
37
+
38
+ it "should not assign a revision" do
39
+ @comment._rev.should be_nil
40
+ end
41
+
42
+ it "should not set created at" do
43
+ @comment.created_at.should be_nil
44
+ end
45
+
46
+ it "should set updated at" do
47
+ @comment.updated_at.should be_nil
48
+ end
49
+
50
+ describe "with bank" do
51
+ it "should raise an exception" do
52
+ lambda {
53
+ @comment.save!
54
+ }.should raise_error
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe CouchTomato::View::CustomViews do
4
+
5
+ class MyViewSpec; end
6
+ class ModelWithView
7
+ include CouchTomato::Persistence
8
+ view :all, :type => MyViewSpec
9
+ end
10
+
11
+ it "should use a custom viewspec class" do
12
+ MyViewSpec.should_receive(:new)
13
+ ModelWithView.all
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ class DbTestUser
4
+ end
5
+
6
+ describe CouchTomato::Database, 'new' do
7
+ it "should raise an exception if the database doesn't exist" do
8
+ lambda {
9
+ CouchTomato::Database.new CouchRest.database('couch_tomato_invalid')
10
+ }.should raise_error('Database \'couch_tomato_invalid\' does not exist.')
11
+ end
12
+ end
13
+
14
+ describe CouchTomato::Database, 'load' do
15
+ it "should raise an exception if nil given" do
16
+ db = CouchTomato::Database.new(stub('couchrest db', :info => nil))
17
+ lambda {
18
+ db.load nil
19
+ }.should raise_error("Can't load a document without an id (got nil)")
20
+ end
21
+
22
+ it "should set itself on the model" do
23
+ user = mock 'user'
24
+ DbTestUser.stub!(:new).and_return(user)
25
+ db = CouchTomato::Database.new(stub('couchrest db', :info => nil, :get => {'ruby_class' => 'DbTestUser'}))
26
+ user.should_receive(:database=).with(db)
27
+ db.load '1'
28
+ end
29
+ end
30
+
31
+ describe CouchTomato::Database, 'save_document' do
32
+ it "should set itself on the model for a new object before doing anything else" do
33
+ db = CouchTomato::Database.new(stub('couchrest db', :info => nil))
34
+ user = stub('user', :new? => true, :valid? => false).as_null_object
35
+ user.should_receive(:database=).with(db)
36
+ db.save_document user
37
+ end
38
+ end
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ class Plate
4
+ include CouchTomato::Persistence
5
+
6
+ property :food
7
+ end
8
+
9
+ describe 'dirty attribute tracking' do
10
+ before(:each) do
11
+ @couchrest_db = stub('database', :save_doc => {'id' => '1', 'rev' => '2'}, :info => nil)
12
+ @db = CouchTomato::Database.new(@couchrest_db)
13
+ end
14
+
15
+ describe "save" do
16
+ it "should not save when nothing dirty" do
17
+ plate = Plate.new :food => 'sushi'
18
+ @db.save_document!(plate)
19
+ @couchrest_db.should_not_receive(:save_doc)
20
+ @db.save_document(plate)
21
+ end
22
+
23
+ it "should return true when not dirty" do
24
+ plate = Plate.new :food => 'sushi'
25
+ @db.save_document!(plate)
26
+ @db.save_document(plate).should be_true
27
+ end
28
+
29
+ it "should save when there are dirty attributes" do
30
+ plate = Plate.new :food => 'sushi'
31
+ @db.save_document!(plate)
32
+ plate.food = 'burger'
33
+ @couchrest_db.should_receive(:save_doc)
34
+ @db.save_document(plate)
35
+ end
36
+ end
37
+
38
+ describe "newly created object" do
39
+
40
+ before(:each) do
41
+ @plate = Plate.new :food => 'sushi'
42
+ end
43
+
44
+ describe "access old values" do
45
+ it "should return the old value" do
46
+ @plate.food = 'burger'
47
+ @plate.food_was.should == 'sushi'
48
+ end
49
+ end
50
+
51
+ describe "check for dirty" do
52
+ it "should return true if attribute changed" do
53
+ @plate.food = 'burger'
54
+ @plate.should be_food_changed
55
+ end
56
+
57
+ it "should return false if attribute not changed" do
58
+ @plate.should_not be_food_changed
59
+ end
60
+
61
+ it "should return false if attribute forced not changed" do
62
+ @plate.food = 'burger'
63
+ @plate.food_not_changed
64
+ @plate.should_not be_food_changed
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "object loaded from database" do
70
+ before(:each) do
71
+ couchrest_db = stub('database', :get => {'_id' => '1', '_rev' => '2', 'food' => 'sushi', 'ruby_class' => 'Plate'}, :info => nil)
72
+ @plate = CouchTomato::Database.new(couchrest_db).load_document '1'
73
+ end
74
+
75
+ describe "access old values" do
76
+ it "should return the old value" do
77
+ @plate.food = 'burger'
78
+ @plate.food_was.should == 'sushi'
79
+ end
80
+ end
81
+
82
+ describe "check for dirty" do
83
+ it "should return true if attribute changed" do
84
+ @plate.food = 'burger'
85
+ @plate.should be_food_changed
86
+ end
87
+
88
+ it "should return true if array attribute changed" do
89
+ couchrest_db = stub('database', :get => {'_id' => '1', '_rev' => '2', 'food' => ['sushi'], 'ruby_class' => 'Plate'}, :info => nil)
90
+ plate = CouchTomato::Database.new(couchrest_db).load_document '1'
91
+ plate.food << 'burger'
92
+ plate.should be_food_changed
93
+ end
94
+
95
+ it "should return false if attribute not changed" do
96
+ @plate.should_not be_food_changed
97
+ end
98
+ end
99
+ end
100
+
101
+
102
+ describe "after save" do
103
+ it "should reset all attributes to not dirty" do
104
+ couchrest_db = stub('database', :get => {'_id' => '1', '_rev' => '2', 'food' => 'sushi', 'ruby_class' => 'Plate'}, :info => nil, :save_doc => {})
105
+ db = CouchTomato::Database.new(couchrest_db)
106
+ @plate = db.load_document '1'
107
+ @plate.food = 'burger'
108
+ db.save! @plate
109
+ @plate.should_not be_food_changed
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe String, 'camelize' do
4
+ it "should camelize a string" do
5
+ 'my_string'.camelize.should == 'MyString'
6
+ end
7
+ end
8
+
9
+ describe String, 'underscore' do
10
+ it "should underscore a string" do
11
+ 'MyString'.underscore.should == 'my_string'
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe CouchTomato::View::ViewQuery, 'query_view' do
4
+ it "should not pass a key if conditions are empty" do
5
+ db = mock 'db'
6
+ db.should_receive(:view).with(anything, {})
7
+ CouchTomato::View::ViewQuery.new(db, '', '', '', '').query_view!
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "create" do
4
+ before(:all) do
5
+ recreate_db
6
+ end
7
+
8
+ before(:each) do
9
+ @comment = Comment.new :title => 'my_title'
10
+ CouchTomato.database.save_document! @comment
11
+ end
12
+
13
+ it "should update the revision" do
14
+ old_rev = @comment._rev
15
+ @comment.title = 'xyz'
16
+ CouchTomato.database.save_document! @comment
17
+ @comment._rev.should_not == old_rev
18
+ @comment._rev.should_not be_nil
19
+ end
20
+
21
+ it "should not update created at" do
22
+ old_created_at = @comment.created_at
23
+ @comment.title = 'xyz'
24
+ CouchTomato.database.save_document! @comment
25
+ @comment.created_at.should == old_created_at
26
+ end
27
+
28
+ it "should update updated at" do
29
+ old_updated_at = @comment.updated_at
30
+ @comment.title = 'xyz'
31
+ CouchTomato.database.save_document! @comment
32
+ @comment.updated_at.should > old_updated_at
33
+ end
34
+
35
+ it "should update the attributes" do
36
+ @comment.title = 'new title'
37
+ CouchTomato.database.save_document! @comment
38
+ CouchTomato.couchrest_database.get("#{@comment.id}")['title'].should == 'new title'
39
+ end
40
+ end