davber_couch_potato 0.3.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 (80) hide show
  1. data/CHANGES.md +106 -0
  2. data/MIT-LICENSE.txt +19 -0
  3. data/README.md +409 -0
  4. data/VERSION.yml +5 -0
  5. data/init.rb +3 -0
  6. data/lib/core_ext/date.rb +21 -0
  7. data/lib/core_ext/object.rb +5 -0
  8. data/lib/core_ext/string.rb +8 -0
  9. data/lib/core_ext/symbol.rb +15 -0
  10. data/lib/core_ext/time.rb +21 -0
  11. data/lib/couch_potato/database.rb +161 -0
  12. data/lib/couch_potato/persistence/active_model_compliance.rb +44 -0
  13. data/lib/couch_potato/persistence/attachments.rb +31 -0
  14. data/lib/couch_potato/persistence/callbacks.rb +62 -0
  15. data/lib/couch_potato/persistence/dirty_attributes.rb +56 -0
  16. data/lib/couch_potato/persistence/ghost_attributes.rb +22 -0
  17. data/lib/couch_potato/persistence/json.rb +46 -0
  18. data/lib/couch_potato/persistence/magic_timestamps.rb +20 -0
  19. data/lib/couch_potato/persistence/properties.rb +86 -0
  20. data/lib/couch_potato/persistence/simple_property.rb +72 -0
  21. data/lib/couch_potato/persistence/type_caster.rb +40 -0
  22. data/lib/couch_potato/persistence.rb +105 -0
  23. data/lib/couch_potato/railtie.rb +18 -0
  24. data/lib/couch_potato/rspec/matchers/json2.js +482 -0
  25. data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +54 -0
  26. data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +49 -0
  27. data/lib/couch_potato/rspec/matchers/print_r.js +60 -0
  28. data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +50 -0
  29. data/lib/couch_potato/rspec/matchers.rb +39 -0
  30. data/lib/couch_potato/rspec/stub_db.rb +46 -0
  31. data/lib/couch_potato/rspec.rb +2 -0
  32. data/lib/couch_potato/validation/with_active_model.rb +27 -0
  33. data/lib/couch_potato/validation/with_validatable.rb +37 -0
  34. data/lib/couch_potato/validation.rb +16 -0
  35. data/lib/couch_potato/view/base_view_spec.rb +67 -0
  36. data/lib/couch_potato/view/custom_view_spec.rb +42 -0
  37. data/lib/couch_potato/view/custom_views.rb +52 -0
  38. data/lib/couch_potato/view/lists.rb +23 -0
  39. data/lib/couch_potato/view/model_view_spec.rb +75 -0
  40. data/lib/couch_potato/view/properties_view_spec.rb +47 -0
  41. data/lib/couch_potato/view/raw_view_spec.rb +25 -0
  42. data/lib/couch_potato/view/view_query.rb +78 -0
  43. data/lib/couch_potato.rb +79 -0
  44. data/rails/init.rb +4 -0
  45. data/rails/reload_classes.rb +47 -0
  46. data/spec/attachments_spec.rb +23 -0
  47. data/spec/callbacks_spec.rb +308 -0
  48. data/spec/create_spec.rb +34 -0
  49. data/spec/custom_view_spec.rb +239 -0
  50. data/spec/default_property_spec.rb +38 -0
  51. data/spec/destroy_spec.rb +29 -0
  52. data/spec/fixtures/address.rb +10 -0
  53. data/spec/fixtures/person.rb +6 -0
  54. data/spec/property_spec.rb +315 -0
  55. data/spec/rails_spec.rb +51 -0
  56. data/spec/spec.opts +4 -0
  57. data/spec/spec_helper.rb +47 -0
  58. data/spec/unit/active_model_compliance_spec.rb +98 -0
  59. data/spec/unit/attributes_spec.rb +125 -0
  60. data/spec/unit/base_view_spec_spec.rb +73 -0
  61. data/spec/unit/callbacks_spec.rb +72 -0
  62. data/spec/unit/couch_potato_spec.rb +39 -0
  63. data/spec/unit/create_spec.rb +58 -0
  64. data/spec/unit/custom_views_spec.rb +15 -0
  65. data/spec/unit/database_spec.rb +266 -0
  66. data/spec/unit/date_spec.rb +22 -0
  67. data/spec/unit/dirty_attributes_spec.rb +166 -0
  68. data/spec/unit/json_create_id_spec.rb +14 -0
  69. data/spec/unit/lists_spec.rb +20 -0
  70. data/spec/unit/model_view_spec_spec.rb +13 -0
  71. data/spec/unit/properties_view_spec_spec.rb +31 -0
  72. data/spec/unit/rspec_matchers_spec.rb +124 -0
  73. data/spec/unit/rspec_stub_db_spec.rb +35 -0
  74. data/spec/unit/string_spec.rb +7 -0
  75. data/spec/unit/time_spec.rb +22 -0
  76. data/spec/unit/validation_spec.rb +67 -0
  77. data/spec/unit/view_query_spec.rb +78 -0
  78. data/spec/update_spec.rb +40 -0
  79. data/spec/view_updates_spec.rb +28 -0
  80. metadata +205 -0
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
5
+
6
+ require 'couch_potato'
7
+
8
+ if ENV["RUN_CODE_RUN"]
9
+ CouchPotato::Config.database_name = 'http://langalex.couch.io/couch_potato_test'
10
+ else
11
+ CouchPotato::Config.database_name = 'couch_potato_test'
12
+ end
13
+
14
+
15
+ CouchPotato::Config.validation_framework = ENV['VALIDATION_FRAMEWORK'].to_sym unless ENV['VALIDATION_FRAMEWORK'].blank?
16
+
17
+ # silence deprecation warnings from ActiveModel as the Spec uses Errors#on
18
+ begin
19
+ ActiveSupport::Deprecation.silenced = true
20
+ rescue
21
+ # ignore errors, ActiveSupport is probably not installed
22
+ end
23
+
24
+ class Child
25
+ include CouchPotato::Persistence
26
+
27
+ property :text
28
+ end
29
+
30
+ class Comment
31
+ include CouchPotato::Persistence
32
+
33
+ validates_presence_of :title
34
+
35
+ property :title
36
+ end
37
+
38
+ def recreate_db
39
+ CouchPotato.couchrest_database.recreate!
40
+ end
41
+ recreate_db
42
+
43
+ Spec::Matchers.define :string_matching do |regex|
44
+ match do |string|
45
+ string =~ regex
46
+ end
47
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ begin
4
+ require 'active_model'
5
+
6
+ describe 'ActiveModel conformance of couch potato objects' do
7
+ include ActiveModel::Lint::Tests
8
+
9
+ instance_methods.sort.select{|method| method.to_s.scan(/^test_/).first}.each do |method|
10
+ it "should #{method.to_s.sub(/^test_/, '').gsub('_', ' ')}" do
11
+ send method
12
+ end
13
+ end
14
+
15
+ class ActiveComment
16
+ include CouchPotato::Persistence
17
+ property :name
18
+ property :email
19
+ validates_presence_of :name, :email
20
+ validates_format_of :email, :with => /.+@.+/
21
+ end
22
+
23
+ before(:each) do
24
+ @model = ActiveComment.new
25
+ end
26
+
27
+ describe "#persisted?" do
28
+ it "should return false if it is a new document " do
29
+ @model.should_not be_persisted
30
+ end
31
+
32
+ it "should be true if it was saved" do
33
+ @comment = ActiveComment.new(:name => 'Thilo', :email => 'test@local.host')
34
+ CouchPotato.database.save_document! @comment
35
+ @comment.should be_persisted
36
+ end
37
+ end
38
+
39
+ describe "#to_key" do
40
+ it "should return nil if the document was not persisted" do
41
+ @model.to_key.should be_nil
42
+ end
43
+
44
+ it "should return the id of the document if it was persisted" do
45
+ @comment = ActiveComment.new(:name => 'Thilo', :email => 'test@local.host')
46
+ CouchPotato.database.save_document! @comment
47
+ @comment.to_key.should == [@comment.id]
48
+ end
49
+ end
50
+
51
+
52
+ describe "#errors" do
53
+ it "should return a single error as array" do
54
+ @model.valid?
55
+ @model.errors[:name].should be_kind_of(Array)
56
+ end
57
+
58
+ it "should return multiple errors as array" do
59
+ @model.valid?
60
+ @model.errors[:email].size.should == 2
61
+ end
62
+
63
+ it "should return no error as an empty array" do
64
+ @model.errors[:name].should == []
65
+ end
66
+
67
+ it "should be able to be Marshal.dump'ed" do
68
+ lambda { Marshal.dump(@model.errors) }.should_not raise_error
69
+ end
70
+ end
71
+
72
+ describe "#destroyed" do
73
+ it "should return destroyed if the object is deleted" do
74
+ @model._deleted = true
75
+ @model.should be_destroyed
76
+ end
77
+
78
+ it "should not return destroyed if it's not deleted" do
79
+ @model.should_not be_destroyed
80
+ end
81
+ end
82
+
83
+ def assert(boolean, message = '')
84
+ boolean || raise(message)
85
+ end
86
+
87
+ def assert_kind_of(klass, object)
88
+ object.should be_a(klass)
89
+ end
90
+ end
91
+
92
+ rescue LoadError
93
+ STDERR.puts "WARNING: active_model gem not installed. Not running ActiveModel specs."
94
+ end
95
+
96
+
97
+
98
+
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ class Plant
4
+ include CouchPotato::Persistence
5
+ property :leaf_count
6
+ property :typed_leaf_count, :type => Fixnum
7
+ property :typed_leaf_size, :type => Float
8
+ end
9
+
10
+ describe "attributes" do
11
+
12
+ describe 'attributes=' do
13
+ it "should assign the attributes" do
14
+ plant = Plant.new
15
+ plant.attributes = {:leaf_count => 1}
16
+ plant.leaf_count.should == 1
17
+ end
18
+ end
19
+
20
+ describe "attributes" do
21
+ it "should return the attributes" do
22
+ plant = Plant.new(:leaf_count => 1)
23
+ plant.attributes.should == {:leaf_count => 1, :created_at => nil, :updated_at => nil, :typed_leaf_count => nil, :typed_leaf_size => nil}
24
+ end
25
+ end
26
+
27
+ # useful when loading models from custom views
28
+ describe "accessing ghost attributes" do
29
+ it "should allow me to access attributes that are in the couchdb document but not defined as a property" do
30
+ plant = Plant.json_create({JSON.create_id => "Plant", "color" => "red", "leaf_count" => 1})
31
+ plant.color.should == 'red'
32
+ end
33
+
34
+ it "should raise a no method error when trying to read attributes that are not in the document" do
35
+ plant = Plant.json_create({JSON.create_id => "Plant", "leaf_count" => 1})
36
+ lambda do
37
+ plant.length
38
+ end.should raise_error(NoMethodError)
39
+ end
40
+
41
+ it "should raise a no method error if the document hasn't been loaded from the database" do
42
+ plant = Plant.new
43
+ lambda do
44
+ plant.length
45
+ end.should raise_error(NoMethodError, /undefined method `length'/)
46
+ end
47
+ end
48
+
49
+ describe 'typed attributes' do
50
+
51
+ before(:each) do
52
+ @plant = Plant.new
53
+ end
54
+
55
+ describe "fixnum" do
56
+ it "should convert a string into a fixnum" do
57
+ @plant.typed_leaf_count = '4'
58
+ @plant.typed_leaf_count.should == 4
59
+ end
60
+
61
+ it "should leave a fixnum as is" do
62
+ @plant.typed_leaf_count = 4
63
+ @plant.typed_leaf_count.should == 4
64
+ end
65
+
66
+ it "should leave nil as is" do
67
+ @plant.typed_leaf_count = nil
68
+ @plant.typed_leaf_count.should be_nil
69
+ end
70
+
71
+ it "should set the attributes to zero if a string given" do
72
+ @plant.typed_leaf_count = 'x'
73
+ @plant.typed_leaf_count.should == 0
74
+ end
75
+
76
+ it "should parse numbers out of a string" do
77
+ @plant.typed_leaf_count = 'x123'
78
+ @plant.typed_leaf_count.should == 123
79
+ end
80
+
81
+ it "should set the attributes to nil if given a blank string" do
82
+ @plant.typed_leaf_count = ''
83
+ @plant.typed_leaf_count.should be_nil
84
+ end
85
+ end
86
+
87
+ describe "float" do
88
+ it "should convert a number in a string with a decimal place" do
89
+ @plant.typed_leaf_size = '0.5001'
90
+ @plant.typed_leaf_size.should == 0.5001
91
+ end
92
+
93
+ it "should convert a number in a string without a decimal place" do
94
+ @plant.typed_leaf_size = '5'
95
+ @plant.typed_leaf_size.should == 5.0
96
+ end
97
+
98
+ it "should leave a float as it is" do
99
+ @plant.typed_leaf_size = 0.5
100
+ @plant.typed_leaf_size.should == 0.5
101
+ end
102
+
103
+ it "should leave nil as is" do
104
+ @plant.typed_leaf_size = nil
105
+ @plant.typed_leaf_size.should be_nil
106
+ end
107
+
108
+ it "should set the attributes to zero if a string given" do
109
+ @plant.typed_leaf_size = 'x'
110
+ @plant.typed_leaf_size.should == 0
111
+ end
112
+
113
+ it "should parse numbers out of a string" do
114
+ @plant.typed_leaf_size = 'x00.123'
115
+ @plant.typed_leaf_size.should == 0.123
116
+ end
117
+
118
+ it "should set the attributes to nil if given a blank string" do
119
+ @plant.typed_leaf_size = ''
120
+ @plant.typed_leaf_size.should be_nil
121
+ end
122
+ end
123
+ end
124
+ end
125
+
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato::View::BaseViewSpec, 'initialize' do
4
+ describe "view parameters" do
5
+ it "should raise an error when passing invalid view parameters" do
6
+ lambda {
7
+ CouchPotato::View::BaseViewSpec.new Object, 'all', {}, {:start_key => '1'}
8
+ }.should raise_error(ArgumentError, "invalid view parameter: start_key")
9
+ end
10
+
11
+ it "should not raise an error when passing valid view parameters" do
12
+ lambda {
13
+ CouchPotato::View::BaseViewSpec.new Object, 'all', {}, {
14
+ :key => 'keyvalue',
15
+ :startkey => 'keyvalue',
16
+ :startkey_docid => 'docid',
17
+ :endkey => 'keyvalue',
18
+ :endkey_docid => 'docid',
19
+ :limit => 3,
20
+ :stale => 'ok',
21
+ :descending => true,
22
+ :skip => 1,
23
+ :group => true,
24
+ :group_level => 1,
25
+ :reduce => false,
26
+ :include_docs => true,
27
+ :inclusive_end => true
28
+ }
29
+ }.should_not raise_error
30
+ end
31
+
32
+ it "should convert a range passed as key into startkey and endkey" do
33
+ spec = CouchPotato::View::BaseViewSpec.new Object, 'all', {}, {:key => '1'..'2'}
34
+ spec.view_parameters.should == {:startkey => '1', :endkey => '2'}
35
+ end
36
+
37
+ it "should convert a plain value to a hash with a key" do
38
+ spec = CouchPotato::View::BaseViewSpec.new Object, 'all', {}, '2'
39
+ spec.view_parameters.should == {:key => '2'}
40
+ end
41
+
42
+ it "should generate the design document path by snake_casing the class name but keeping double colons" do
43
+ spec = CouchPotato::View::BaseViewSpec.new 'Foo::BarBaz', '', {}, ''
44
+ spec.design_document.should == 'foo::bar_baz'
45
+ end
46
+
47
+ it "should extract the list name from the options" do
48
+ spec = CouchPotato::View::BaseViewSpec.new stub(:lists => nil), 'all', {:list => 'test_list'}, {}
49
+ spec.list_name.should == 'test_list'
50
+ end
51
+
52
+ it "should extract the list from the view parameters" do
53
+ spec = CouchPotato::View::BaseViewSpec.new stub(:lists => nil), 'all', {}, {:list => 'test_list'}
54
+ spec.list_name.should == 'test_list'
55
+ end
56
+
57
+ it "should prefer the list name from the view parameters over the one from the options" do
58
+ spec = CouchPotato::View::BaseViewSpec.new stub(:lists => nil), 'all', {:list => 'my_list'}, {:list => 'test_list'}
59
+ spec.list_name.should == 'test_list'
60
+ end
61
+
62
+ it "should return the list function" do
63
+ klass = stub 'class'
64
+ klass.stub(:lists).with('test_list').and_return('<list_code>')
65
+ spec = CouchPotato::View::BaseViewSpec.new klass, 'all', {:list => 'test_list'}, {}
66
+ spec.list_function.should == '<list_code>'
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'callbacks' do
4
+ class Tree
5
+ include CouchPotato::Persistence
6
+
7
+ before_validation :grow_leaf, 'grow_branch', lambda {|tree| tree.root_count ||= 0; tree.root_count += 1 }
8
+
9
+ property :leaf_count
10
+ property :root_count
11
+ property :branch_count
12
+ property :watered
13
+
14
+
15
+ def grow_leaf
16
+ self.leaf_count ||= 0
17
+ self.leaf_count += 1
18
+ end
19
+
20
+ def grow_branch
21
+ self.branch_count ||= 0
22
+ self.branch_count += 1
23
+ end
24
+ end
25
+
26
+ class AppleTree < Tree
27
+ attr_accessor :watered
28
+
29
+ before_validation :water
30
+
31
+ def water
32
+ self.watered = true
33
+ end
34
+
35
+ def watered?
36
+ watered
37
+ end
38
+ end
39
+
40
+ it "should call a method from a symbol when validated" do
41
+ tree = Tree.new(:leaf_count => 1, :root_count => 1)
42
+ tree.valid?
43
+ tree.leaf_count.should == 2
44
+ end
45
+
46
+ it "should call a method from a string when validated" do
47
+ tree = Tree.new(:branch_count => 0)
48
+ tree.valid?
49
+ tree.branch_count.should == 1
50
+ end
51
+
52
+ it "should call a lambda when validated" do
53
+ tree = Tree.new(:leaf_count => 1, :root_count => 1)
54
+ tree.valid?
55
+ tree.root_count.should == 2
56
+ end
57
+
58
+ context 'inheritance' do
59
+ it "should call the callbacks of the super class" do
60
+ tree = AppleTree.new :leaf_count => 1
61
+ tree.valid?
62
+ tree.leaf_count.should == 2
63
+ end
64
+
65
+ it "should call the callbacks of the child class" do
66
+ tree = AppleTree.new :leaf_count => 1
67
+ tree.valid?
68
+ tree.should be_watered
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato, 'full_url_to_database' do
4
+ before(:each) do
5
+ @original_database_name = CouchPotato::Config.database_name
6
+ end
7
+ after(:each) do
8
+ CouchPotato::Config.database_name = @original_database_name
9
+ end
10
+
11
+ it "should add the default localhost and port if only a name is set" do
12
+ CouchPotato::Config.database_name = 'test'
13
+ CouchPotato.full_url_to_database.should == 'http://127.0.0.1:5984/test'
14
+ end
15
+
16
+ it "should return the set url" do
17
+ CouchPotato::Config.database_name = 'http://db.local/test'
18
+ CouchPotato.full_url_to_database.should == 'http://db.local/test'
19
+ end
20
+ end
21
+
22
+ describe CouchPotato, 'validation_framework' do
23
+ before(:each) do
24
+ @original_validation_framework = CouchPotato::Config.validation_framework
25
+ end
26
+ after(:each) do
27
+ CouchPotato::Config.validation_framework = @original_validation_framework
28
+ end
29
+
30
+ it "should allow setting the validation_framework to :active_model" do
31
+ CouchPotato::Config.validation_framework = :active_model
32
+ CouchPotato::Config.validation_framework.should == :active_model
33
+ end
34
+
35
+ it "should allow setting the validation_framework to :validatable" do
36
+ CouchPotato::Config.validation_framework = :validatable
37
+ CouchPotato::Config.validation_framework.should == :validatable
38
+ end
39
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe "create" do
4
+
5
+ describe "succeeds" do
6
+ before(:each) do
7
+ @comment = Comment.new :title => 'my_title'
8
+ CouchPotato::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
+ CouchPotato::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 'spec_helper'
2
+
3
+ describe CouchPotato::View::CustomViews do
4
+
5
+ class MyViewSpec; end
6
+ class ModelWithView
7
+ include CouchPotato::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