couch_potato-rails2 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGES.md +148 -0
  4. data/CREDITS +6 -0
  5. data/Gemfile +4 -0
  6. data/MIT-LICENSE.txt +19 -0
  7. data/README.md +450 -0
  8. data/Rakefile +82 -0
  9. data/couch_potato.gemspec +27 -0
  10. data/init.rb +3 -0
  11. data/lib/core_ext/date.rb +14 -0
  12. data/lib/core_ext/object.rb +5 -0
  13. data/lib/core_ext/string.rb +12 -0
  14. data/lib/core_ext/symbol.rb +15 -0
  15. data/lib/core_ext/time.rb +23 -0
  16. data/lib/couch_potato.rb +48 -0
  17. data/lib/couch_potato/database.rb +179 -0
  18. data/lib/couch_potato/persistence.rb +124 -0
  19. data/lib/couch_potato/persistence/active_model_compliance.rb +44 -0
  20. data/lib/couch_potato/persistence/attachments.rb +31 -0
  21. data/lib/couch_potato/persistence/callbacks.rb +29 -0
  22. data/lib/couch_potato/persistence/dirty_attributes.rb +56 -0
  23. data/lib/couch_potato/persistence/ghost_attributes.rb +12 -0
  24. data/lib/couch_potato/persistence/json.rb +47 -0
  25. data/lib/couch_potato/persistence/magic_timestamps.rb +23 -0
  26. data/lib/couch_potato/persistence/properties.rb +79 -0
  27. data/lib/couch_potato/persistence/simple_property.rb +82 -0
  28. data/lib/couch_potato/persistence/type_caster.rb +40 -0
  29. data/lib/couch_potato/railtie.rb +25 -0
  30. data/lib/couch_potato/rspec.rb +2 -0
  31. data/lib/couch_potato/rspec/matchers.rb +39 -0
  32. data/lib/couch_potato/rspec/matchers/json2.js +482 -0
  33. data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +54 -0
  34. data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +49 -0
  35. data/lib/couch_potato/rspec/matchers/print_r.js +60 -0
  36. data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +50 -0
  37. data/lib/couch_potato/rspec/stub_db.rb +46 -0
  38. data/lib/couch_potato/validation.rb +16 -0
  39. data/lib/couch_potato/validation/with_active_model.rb +27 -0
  40. data/lib/couch_potato/validation/with_validatable.rb +41 -0
  41. data/lib/couch_potato/version.rb +3 -0
  42. data/lib/couch_potato/view/base_view_spec.rb +84 -0
  43. data/lib/couch_potato/view/custom_view_spec.rb +42 -0
  44. data/lib/couch_potato/view/custom_views.rb +52 -0
  45. data/lib/couch_potato/view/lists.rb +23 -0
  46. data/lib/couch_potato/view/model_view_spec.rb +75 -0
  47. data/lib/couch_potato/view/properties_view_spec.rb +47 -0
  48. data/lib/couch_potato/view/raw_view_spec.rb +25 -0
  49. data/lib/couch_potato/view/view_query.rb +82 -0
  50. data/rails/init.rb +4 -0
  51. data/rails/reload_classes.rb +47 -0
  52. data/spec/attachments_spec.rb +23 -0
  53. data/spec/callbacks_spec.rb +297 -0
  54. data/spec/create_spec.rb +35 -0
  55. data/spec/custom_view_spec.rb +239 -0
  56. data/spec/default_property_spec.rb +38 -0
  57. data/spec/destroy_spec.rb +29 -0
  58. data/spec/fixtures/address.rb +10 -0
  59. data/spec/fixtures/person.rb +6 -0
  60. data/spec/property_spec.rb +323 -0
  61. data/spec/rails_spec.rb +50 -0
  62. data/spec/railtie_spec.rb +65 -0
  63. data/spec/spec.opts +2 -0
  64. data/spec/spec_helper.rb +44 -0
  65. data/spec/unit/active_model_compliance_spec.rb +98 -0
  66. data/spec/unit/attributes_spec.rb +135 -0
  67. data/spec/unit/base_view_spec_spec.rb +106 -0
  68. data/spec/unit/callbacks_spec.rb +46 -0
  69. data/spec/unit/couch_potato_spec.rb +39 -0
  70. data/spec/unit/create_spec.rb +69 -0
  71. data/spec/unit/custom_views_spec.rb +15 -0
  72. data/spec/unit/database_spec.rb +317 -0
  73. data/spec/unit/date_spec.rb +22 -0
  74. data/spec/unit/dirty_attributes_spec.rb +136 -0
  75. data/spec/unit/initialize_spec.rb +38 -0
  76. data/spec/unit/json_spec.rb +30 -0
  77. data/spec/unit/lists_spec.rb +20 -0
  78. data/spec/unit/model_view_spec_spec.rb +13 -0
  79. data/spec/unit/properties_view_spec_spec.rb +31 -0
  80. data/spec/unit/rspec_matchers_spec.rb +124 -0
  81. data/spec/unit/rspec_stub_db_spec.rb +35 -0
  82. data/spec/unit/string_spec.rb +7 -0
  83. data/spec/unit/time_spec.rb +15 -0
  84. data/spec/unit/validation_spec.rb +67 -0
  85. data/spec/unit/view_query_spec.rb +86 -0
  86. data/spec/update_spec.rb +40 -0
  87. data/spec/view_updates_spec.rb +28 -0
  88. metadata +243 -0
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Date, 'to_json' do
4
+ it "should format the date in a way that i can use it for sorting in couchdb" do
5
+ date = Date.parse('2009-01-01')
6
+ date.to_json.should == "\"2009/01/01\""
7
+ end
8
+ end
9
+
10
+ describe Date, 'as_json' do
11
+ it "should format it in the same way as to_json does so i can use this to do queries over date attributes" do
12
+ date = Date.parse('2009-01-01')
13
+ date.as_json.should == "2009/01/01"
14
+ end
15
+ end
16
+
17
+ describe Date, 'to_s' do
18
+ it "should leave the original to_s untouched" do
19
+ date = Date.parse('2009-01-01')
20
+ date.to_s.should == "2009-01-01"
21
+ end
22
+ end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ class Plate
4
+ include CouchPotato::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 = CouchPotato::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
+
50
+ describe "with type BigDecimal" do
51
+ before(:each) do
52
+ class Bowl
53
+ include CouchPotato::Persistence
54
+ property :price
55
+ end
56
+ end
57
+ it "should not dup BigDecimal" do
58
+
59
+ lambda {
60
+ Bowl.new :price => BigDecimal.new("5.23")
61
+ }.should_not raise_error(TypeError)
62
+ end
63
+
64
+ it "should return the old value" do
65
+ bowl = Bowl.new :price => BigDecimal.new("5.23")
66
+ bowl.price = BigDecimal.new("2.23")
67
+ bowl.price_was.should == 5.23
68
+ end
69
+
70
+ end
71
+ end
72
+
73
+ describe "check for dirty" do
74
+ it "should return true if attribute changed" do
75
+ @plate.food = 'burger'
76
+ @plate.should be_food_changed
77
+ end
78
+
79
+ it "should return false if attribute not changed" do
80
+ Plate.new.should_not be_food_changed
81
+ end
82
+
83
+ it "should return true if forced dirty" do
84
+ @plate.is_dirty
85
+ @plate.should be_dirty
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "object loaded from database" do
91
+ before(:each) do
92
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => 'sushi', JSON.create_id => 'Plate'}), :info => nil)
93
+ @plate = CouchPotato::Database.new(couchrest_db).load_document '1'
94
+ end
95
+
96
+ describe "access old values" do
97
+ it "should return the old value" do
98
+ @plate.food = 'burger'
99
+ @plate.food_was.should == 'sushi'
100
+ end
101
+ end
102
+
103
+ describe "check for dirty" do
104
+ it "should return true if attribute changed" do
105
+ @plate.food = 'burger'
106
+ @plate.should be_food_changed
107
+ end
108
+
109
+ it "should return false if attribute not changed" do
110
+ @plate.should_not be_food_changed
111
+ end
112
+ end
113
+ end
114
+
115
+
116
+ describe "after save" do
117
+ it "should reset all attributes to not dirty" do
118
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => 'sushi', JSON.create_id => 'Plate'}), :info => nil, :save_doc => {})
119
+ db = CouchPotato::Database.new(couchrest_db)
120
+ @plate = db.load_document '1'
121
+ @plate.food = 'burger'
122
+ db.save! @plate
123
+ @plate.should_not be_food_changed
124
+ end
125
+
126
+ it "should reset a forced dirty state" do
127
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => 'sushi', JSON.create_id => 'Plate'}), :info => nil, :save_doc => {'rev' => '3'})
128
+ db = CouchPotato::Database.new(couchrest_db)
129
+ @plate = db.load_document '1'
130
+ @plate.is_dirty
131
+ db.save! @plate
132
+ @plate.should_not be_dirty
133
+ end
134
+ end
135
+
136
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ class Document
4
+ include CouchPotato::Persistence
5
+
6
+ property :title
7
+ property :content
8
+ end
9
+
10
+ describe "new" do
11
+ context "without arguments" do
12
+ subject { Document.new }
13
+
14
+ it { should be_a(Document) }
15
+ its(:title) { should be_nil }
16
+ its(:content) { should be_nil }
17
+ end
18
+
19
+ context "with an argument hash" do
20
+ subject { Document.new(:title => 'My Title') }
21
+
22
+ it { should be_a(Document) }
23
+ its(:title) { should == 'My Title'}
24
+ its(:content) { should be_nil }
25
+ end
26
+
27
+ context "yielding to a block" do
28
+ subject {
29
+ Document.new(:title => 'My Title') do |doc|
30
+ doc.content = 'My Content'
31
+ end
32
+ }
33
+
34
+ it { should be_a(Document) }
35
+ its(:title) { should == 'My Title'}
36
+ its(:content) { should == 'My Content'}
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ class Drink
4
+ include CouchPotato::Persistence
5
+
6
+ property :alcohol
7
+ end
8
+
9
+ describe CouchPotato::Persistence::Json do
10
+ context '#to_hash' do
11
+ it "should inject JSON.create_id into the hash representation of a persistent object" do
12
+ sake = Drink.new(:alcohol => "18%")
13
+ sake.to_hash[JSON.create_id].should eql("Drink")
14
+ end
15
+
16
+ it "should not include _attachments if there are none" do
17
+ sake = Drink.new(:alcohol => "18%")
18
+ sake.to_hash.keys.should_not include('_attachments')
19
+ end
20
+
21
+ end
22
+
23
+ context '.json_create' do
24
+ it 'should assign the _document' do
25
+ sake = Drink.json_create({"alcohol" => "18%"})
26
+ sake._document.should == {"alcohol" => "18%"}
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato::View::Lists, '.list' do
4
+ it "should make the list function available via .lists" do
5
+ clazz = Class.new
6
+ clazz.send :include, CouchPotato::View::Lists
7
+ clazz.list 'my_list', '<list_code>'
8
+
9
+ clazz.lists('my_list').should == '<list_code>'
10
+ end
11
+
12
+ it "should make the list available to subclasses" do
13
+ clazz = Class.new
14
+ clazz.send :include, CouchPotato::View::Lists
15
+ clazz.list 'my_list', '<list_code>'
16
+ sub_clazz = Class.new clazz
17
+
18
+ sub_clazz.lists('my_list').should == '<list_code>'
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato::View::ModelViewSpec, 'map_function' do
4
+ it "should include conditions" do
5
+ spec = CouchPotato::View::ModelViewSpec.new Object, 'all', {:conditions => 'doc.closed = true'}, {}
6
+ spec.map_function.should include('if(doc.ruby_class && doc.ruby_class == \'Object\' && (doc.closed = true))')
7
+ end
8
+
9
+ it "should not include conditions when they are nil" do
10
+ spec = CouchPotato::View::ModelViewSpec.new Object, 'all', {}, {}
11
+ spec.map_function.should include('if(doc.ruby_class && doc.ruby_class == \'Object\')')
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require 'couch_potato/rspec'
3
+
4
+ class Contract
5
+ include CouchPotato::Persistence
6
+
7
+ property :date
8
+ property :terms
9
+
10
+ view :by_date, :type => :properties, :key => :_id, :properties => [:date]
11
+ end
12
+
13
+ describe CouchPotato::View::PropertiesViewSpec do
14
+ it "should map the given properties" do
15
+ Contract.by_date.should map(
16
+ Contract.new(:date => '2010-01-01', :_id => '1')
17
+ ).to(['1', {"date" => "2010-01-01"}])
18
+ end
19
+
20
+ it "should reduce to the number of documents" do
21
+ Contract.by_date.should reduce(
22
+ ['1', {"date" => "2010-01-01"}], ['2', {"date" => "2010-01-02"}]
23
+ ).to(2)
24
+ end
25
+
26
+ it "should rereduce the number of documents" do
27
+ Contract.by_date.should rereduce(
28
+ nil, [12, 13]
29
+ ).to(25)
30
+ end
31
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+ require 'couch_potato/rspec'
3
+
4
+ describe CouchPotato::RSpec::MapToMatcher do
5
+
6
+ describe "basic map function" do
7
+ before(:each) do
8
+ @view_spec = stub(:map_function => "function(doc) {emit(doc.name, doc.tags.length);}")
9
+ end
10
+
11
+ it "should pass if the given function emits the expected javascript" do
12
+ @view_spec.should map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 2])
13
+ end
14
+
15
+ it "should not pass if the given function emits different javascript" do
16
+ @view_spec.should_not map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 3])
17
+ end
18
+ end
19
+
20
+ describe "functions emitting multiple times" do
21
+ before(:each) do
22
+ @view_spec = stub(:map_function => "function(doc) {emit(doc.name, doc.tags.length); emit(doc.tags[0], doc.tags[1])};")
23
+ end
24
+
25
+ it "should pass if the given function emits the expected javascript" do
26
+ @view_spec.should map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 2], ['person', 'male'])
27
+ end
28
+
29
+ it "should return false if the given function emits different javascript" do
30
+ @view_spec.should_not map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 2], ['male', 'person'])
31
+ end
32
+ end
33
+
34
+ describe "failing specs" do
35
+ before(:each) do
36
+ @view_spec = stub(:map_function => "function(doc) {emit(doc.name, null)}")
37
+ end
38
+
39
+ it "should have a nice error message for failing should" do
40
+ lambda {
41
+ @view_spec.should map({:name => 'bill'}).to(['linus', nil])
42
+ }.should raise_error('Expected to map to [["linus", nil]] but got [["bill", nil]].')
43
+ end
44
+
45
+ it "should have a nice error message for failing should not" do
46
+ lambda {
47
+ @view_spec.should_not map({:name => 'bill'}).to(['bill', nil])
48
+ }.should raise_error('Expected not to map to [["bill", nil]] but did.')
49
+ end
50
+ end
51
+ end
52
+
53
+ describe CouchPotato::RSpec::ReduceToMatcher do
54
+ before(:each) do
55
+ @view_spec = stub(:reduce_function => "function(docs, keys, rereduce) {
56
+ if(rereduce) {
57
+ return(sum(keys) * 2);
58
+ } else {
59
+ return(sum(keys));
60
+ };
61
+ }")
62
+ end
63
+
64
+ it "should pass if the given function return the expected javascript" do
65
+ @view_spec.should reduce([], [1, 2, 3]).to(6)
66
+ end
67
+
68
+ it "should not pass if the given function returns different javascript" do
69
+ @view_spec.should_not reduce([], [1, 2, 3]).to(7)
70
+ end
71
+
72
+ describe "rereduce" do
73
+ it "should pass if the given function return the expected javascript" do
74
+ @view_spec.should rereduce([], [1, 2, 3]).to(12)
75
+ end
76
+
77
+ it "should not pass if the given function returns different javascript" do
78
+ @view_spec.should_not rereduce([], [1, 2, 3]).to(13)
79
+ end
80
+ end
81
+
82
+ describe 'failing specs' do
83
+
84
+ it "should have a nice error message for failing should" do
85
+ lambda {
86
+ @view_spec.should reduce([], [1, 2, 3]).to(7)
87
+ }.should raise_error('Expected to reduce to 7 but got 6.')
88
+ end
89
+
90
+ it "should have a nice error message for failing should not" do
91
+ lambda {
92
+ @view_spec.should_not reduce([], [1, 2, 3]).to(6)
93
+ }.should raise_error('Expected not to reduce to 6 but did.')
94
+ end
95
+ end
96
+ end
97
+
98
+ describe CouchPotato::RSpec::ListAsMatcher do
99
+ before(:each) do
100
+ @view_spec = stub(:list_function => "function() {var row = getRow(); send(JSON.stringify([{text: row.text + ' world'}]));}")
101
+ end
102
+
103
+ it "should pass if the function return the expected json" do
104
+ @view_spec.should list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}])
105
+ end
106
+
107
+ it "should not pass if the function does not return the expected json" do
108
+ @view_spec.should_not list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}])
109
+ end
110
+
111
+ describe "failing specs" do
112
+ it "should have a nice error message for failing should" do
113
+ lambda {
114
+ @view_spec.should list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}])
115
+ }.should raise_error('Expected to list as [{"text"=>"hello there"}] but got [{"text"=>"hello world"}].')
116
+ end
117
+
118
+ it "should have a nice error message for failing should not" do
119
+ lambda {
120
+ @view_spec.should_not list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}])
121
+ }.should raise_error('Expected to not list as [{"text"=>"hello world"}] but did.')
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'couch_potato/rspec'
3
+
4
+ class WithStubbedView
5
+ include CouchPotato::Persistence
6
+
7
+ view :stubbed_view, :key => :x
8
+ end
9
+
10
+ describe "stubbing the db" do
11
+ it "should replace CouchPotato.database with a stub" do
12
+ stub_db
13
+ CouchPotato.database.should be_a(RSpec::Mocks::Mock)
14
+ end
15
+
16
+ it "should return the stub" do
17
+ db = stub_db
18
+ CouchPotato.database.should == db
19
+ end
20
+ end
21
+
22
+ describe "stubbing a view" do
23
+ before(:each) do
24
+ @db = stub_db
25
+ @db.stub_view(WithStubbedView, :stubbed_view).with('123').and_return([:result])
26
+ end
27
+
28
+ it "should stub the view to return a stub" do
29
+ WithStubbedView.stubbed_view('123').should be_a(RSpec::Mocks::Mock)
30
+ end
31
+
32
+ it "should stub the database to return fake results when called with the stub" do
33
+ @db.view(WithStubbedView.stubbed_view('123')).should == [:result]
34
+ end
35
+ end