couch_potato-rails2 0.5.6

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 (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