couch_potato 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -18,6 +18,13 @@ Lastly Couch Potato aims to provide a seamless integration with Ruby on Rails, e
18
18
  * declarative views with either custom or generated map/reduce functions
19
19
  * extensive spec suite
20
20
 
21
+ ### Supported Environments
22
+
23
+ * Ruby 1.8.7, 1.9.1, 1.9.2
24
+ * CouchDB 1.0.1
25
+
26
+ (Supported means I run the specs against those before releasing a new gem.)
27
+
21
28
  ### Installation
22
29
 
23
30
  Couch Potato is hosted as a gem which you can install like this:
@@ -54,9 +61,19 @@ Add to your config/environment.rb:
54
61
 
55
62
  Then create a config/couchdb.yml:
56
63
 
57
- development: development_db_name
58
- test: test_db_name
59
- production: http://db.server/production_db_name
64
+ default: &default
65
+ validation_framework: :active_model #optional
66
+
67
+ development:
68
+ <<: *default
69
+ database: development_db_name
70
+ test:
71
+ <<: *default
72
+ database: test_db_name
73
+ production:
74
+ <<: *default
75
+ database: <%= ENV['DB_NAME'] %>
76
+
60
77
 
61
78
  Alternatively you can also install Couch Potato directly as a plugin.
62
79
 
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :major: 0
3
2
  :minor: 3
4
- :patch: 0
3
+ :patch: 1
5
4
  :build:
5
+ :major: 0
@@ -4,5 +4,9 @@ module ActiveSupportMethods
4
4
  $1.upcase
5
5
  end
6
6
  end
7
+
8
+ def blank?
9
+ empty?
10
+ end
7
11
  end
8
- String.send :include, ActiveSupportMethods unless String.new.respond_to?(:underscore)
12
+ String.send :include, ActiveSupportMethods unless String.new.respond_to?(:underscore)
@@ -79,10 +79,10 @@ module CouchPotato
79
79
  alias_method :save!, :save_document!
80
80
 
81
81
  def destroy_document(document)
82
- document.run_callbacks :before_destroy
83
- document._deleted = true
84
- database.delete_doc document.to_hash
85
- document.run_callbacks :after_destroy
82
+ document.run_callbacks :destroy do
83
+ document._deleted = true
84
+ database.delete_doc document.to_hash
85
+ end
86
86
  document._id = nil
87
87
  document._rev = nil
88
88
  end
@@ -102,7 +102,7 @@ module CouchPotato
102
102
  alias_method :load, :load_document
103
103
 
104
104
  def inspect #:nodoc:
105
- "#<CouchPotato::Database>"
105
+ "#<CouchPotato::Database @root=\"#{database.root}\">"
106
106
  end
107
107
 
108
108
  private
@@ -112,35 +112,39 @@ module CouchPotato
112
112
 
113
113
  if validate
114
114
  document.errors.clear
115
- document.run_callbacks :before_validation_on_save
116
- document.run_callbacks :before_validation_on_create
117
- return false unless valid_document?(document)
115
+ document.run_callbacks :validation_on_save do
116
+ document.run_callbacks :validation_on_create do
117
+ return false unless valid_document?(document)
118
+ end
119
+ end
118
120
  end
119
121
 
120
- document.run_callbacks :before_save
121
- document.run_callbacks :before_create
122
- res = database.save_doc document.to_hash
123
- document._rev = res['rev']
124
- document._id = res['id']
125
- document.run_callbacks :after_save
126
- document.run_callbacks :after_create
122
+ document.run_callbacks :save do
123
+ document.run_callbacks :create do
124
+ res = database.save_doc document.to_hash
125
+ document._rev = res['rev']
126
+ document._id = res['id']
127
+ end
128
+ end
127
129
  true
128
130
  end
129
131
 
130
132
  def update_document(document, validate)
131
133
  if validate
132
134
  document.errors.clear
133
- document.run_callbacks :before_validation_on_save
134
- document.run_callbacks :before_validation_on_update
135
- return false unless valid_document?(document)
135
+ document.run_callbacks :validation_on_save do
136
+ document.run_callbacks :validation_on_update do
137
+ return false unless valid_document?(document)
138
+ end
139
+ end
140
+ end
141
+
142
+ document.run_callbacks :save do
143
+ document.run_callbacks :update do
144
+ res = database.save_doc document.to_hash
145
+ document._rev = res['rev']
146
+ end
136
147
  end
137
-
138
- document.run_callbacks :before_save
139
- document.run_callbacks :before_update
140
- res = database.save_doc document.to_hash
141
- document._rev = res['rev']
142
- document.run_callbacks :after_save
143
- document.run_callbacks :after_update
144
148
  true
145
149
  end
146
150
 
@@ -158,4 +162,4 @@ module CouchPotato
158
162
  end
159
163
 
160
164
  end
161
- end
165
+ end
@@ -1,62 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'active_model/callbacks'
3
+
1
4
  module CouchPotato
2
5
  module Persistence
3
6
  module Callbacks
4
7
  def self.included(base) #:nodoc:
5
- base.extend ClassMethods
8
+ base.extend ActiveModel::Callbacks
6
9
 
7
10
  base.class_eval do
8
11
  attr_accessor :skip_callbacks
9
- def self.callbacks #:nodoc:
10
- @callbacks ||= {:before_validation => [], :before_validation_on_create => [],
11
- :before_validation_on_update => [], :before_validation_on_save => [], :before_create => [],
12
- :after_create => [], :before_update => [], :after_update => [],
13
- :before_save => [], :after_save => [],
14
- :before_destroy => [], :after_destroy => []}
15
- end
12
+
13
+ define_model_callbacks :create, :save, :update, :destroy
14
+ define_model_callbacks *[:save, :create, :update].map {|c| :"validation_on_#{c}"}
15
+ define_model_callbacks :validation unless Config.validation_framework == :active_model
16
16
  end
17
17
  end
18
18
 
19
19
  # Runs all callbacks on a model with the given name, e.g. :after_create.
20
20
  #
21
21
  # This method is called by the CouchPotato::Database object when saving/destroying an object
22
- def run_callbacks(name)
22
+ def run_callbacks(name, &block)
23
23
  return if skip_callbacks
24
-
25
- callbacks = self.class.ancestors.map do |clazz|
26
- clazz.callbacks[name] if clazz.respond_to?(:callbacks)
27
- end.flatten.compact.uniq
28
-
29
- callbacks.each do |callback|
30
- if [Symbol, String].include?(callback.class)
31
- send callback
32
- elsif callback.is_a?(Proc)
33
- callback.call self
34
- else
35
- raise "Don't know how to handle callback of type #{callback.class.name}"
36
- end
37
- end
38
- end
39
24
 
40
- module ClassMethods
41
- [
42
- :before_validation,
43
- :before_validation_on_create,
44
- :before_validation_on_update,
45
- :before_validation_on_save,
46
- :before_create,
47
- :before_save,
48
- :before_update,
49
- :before_destroy,
50
- :after_update,
51
- :after_save,
52
- :after_create,
53
- :after_destroy
54
- ].each do |callback|
55
- define_method callback do |*names|
56
- callbacks[callback].push *names
57
- end
58
- end
25
+ send(:"_run_#{name}_callbacks", &block)
59
26
  end
60
27
  end
61
28
  end
62
- end
29
+ end
@@ -6,14 +6,14 @@ module CouchPotato
6
6
  def self.included(base) #:nodoc:
7
7
  base.class_eval do
8
8
  after_save :reset_dirty_attributes
9
-
10
- def initialize(attributes = {})
11
- super
12
- assign_attribute_copies_for_dirty_tracking
13
- end
14
9
  end
15
10
  end
16
11
 
12
+ def initialize(attributes = {})
13
+ super
14
+ assign_attribute_copies_for_dirty_tracking
15
+ end
16
+
17
17
  # returns true if a model has dirty attributes, i.e. their value has changed since the last save
18
18
  def dirty?
19
19
  new? || @forced_dirty || self.class.properties.inject(false) do |res, property|
@@ -8,15 +8,16 @@ module CouchPotato
8
8
  instance._document = json if json
9
9
  instance
10
10
  end
11
-
12
- def method_missing(name, *args)
13
- if(value = _document && _document[name.to_s])
14
- value
15
- else
16
- super
17
- end
18
- end
19
11
  end
20
12
  end
13
+
14
+ def method_missing(name, *args)
15
+ if(value = _document && _document[name.to_s])
16
+ value
17
+ else
18
+ super
19
+ end
20
+ end
21
+
21
22
  end
22
23
  end
@@ -3,15 +3,15 @@ module CouchPotato
3
3
  class TypeCaster #:nodoc:
4
4
  def cast(value, type)
5
5
  if type == :boolean
6
- cast_boolen(value)
6
+ cast_boolean(value)
7
7
  else
8
8
  cast_native(value, type)
9
9
  end
10
10
  end
11
-
11
+
12
12
  private
13
-
14
- def cast_boolen(value)
13
+
14
+ def cast_boolean(value)
15
15
  if [FalseClass, TrueClass].include?(value.class) || value.nil?
16
16
  value
17
17
  elsif [0, '0'].include?(value)
@@ -20,7 +20,7 @@ module CouchPotato
20
20
  true
21
21
  end
22
22
  end
23
-
23
+
24
24
  def cast_native(value, type)
25
25
  if type && !value.instance_of?(type)
26
26
  if type == Fixnum
@@ -34,7 +34,7 @@ module CouchPotato
34
34
  value
35
35
  end
36
36
  end
37
-
37
+
38
38
  end
39
39
  end
40
- end
40
+ end
@@ -18,9 +18,10 @@ module CouchPotato
18
18
  module Persistence
19
19
 
20
20
  def self.included(base) #:nodoc:
21
- base.send :include, Properties, Callbacks, Validation, Json, CouchPotato::View::CustomViews, CouchPotato::View::Lists
21
+ base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews, CouchPotato::View::Lists
22
22
  base.send :include, DirtyAttributes, GhostAttributes, Attachments
23
23
  base.send :include, MagicTimestamps, ActiveModelCompliance
24
+ base.send :include, Validation
24
25
  base.class_eval do
25
26
  attr_accessor :_id, :_rev, :_deleted, :database
26
27
  alias_method :id, :_id
@@ -1,19 +1,24 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../../rails/reload_classes')
2
+ require 'erb'
2
3
 
3
4
  module CouchPotato
4
-
5
5
  def self.rails_init
6
- CouchPotato::Config.database_name = YAML::load(File.read(Rails.root.join('config/couchdb.yml')))[Rails.env]
6
+ config = YAML::load(ERB.new(File.read(Rails.root.join('config/couchdb.yml'))).result)[RAILS_ENV]
7
+ if config.is_a?(String)
8
+ CouchPotato::Config.database_name = config
9
+ else
10
+ CouchPotato::Config.database_name = config['database']
11
+ CouchPotato::Config.validation_framework = config['validation_framework']
12
+ end
7
13
  end
8
14
 
9
15
  if defined?(::Rails::Railtie)
10
16
  class Railtie < ::Rails::Railtie
11
- config.after_initialize do |app|
17
+ initializer 'couch_potato.load_config' do |app|
12
18
  CouchPotato.rails_init
13
19
  end
14
20
  end
15
21
  else
16
22
  rails_init
17
23
  end
18
-
19
24
  end
@@ -11,16 +11,20 @@ module CouchPotato
11
11
  base.send :include, ::Validatable
12
12
  base.class_eval do
13
13
  # Override the validate method to first run before_validation callback
14
- def valid?
14
+ def valid_with_before_validation_callback?
15
15
  errors.clear
16
- run_callbacks :before_validation
17
- before_validation_errors = errors.errors.dup
18
- super
19
- before_validation_errors.each do |k, v|
20
- v.each {|message| errors.add(k, message)}
16
+ run_callbacks :validation do
17
+ before_validation_errors = errors.errors.dup
18
+ valid_without_before_validation_callback?
19
+ before_validation_errors.each do |k, v|
20
+ v.each {|message| errors.add(k, message)}
21
+ end
21
22
  end
22
23
  errors.empty?
23
24
  end
25
+
26
+ alias_method :valid_without_before_validation_callback?, :valid?
27
+ alias_method :valid?, :valid_with_before_validation_callback?
24
28
  end
25
29
  end
26
30
  end
@@ -35,7 +35,7 @@ module CouchPotato
35
35
  if count?
36
36
  results['rows'].first.try(:[], 'value') || 0
37
37
  else
38
- results['rows'].map { |row| row['doc'] }
38
+ results['rows'].map { |row| row['doc'] || row['id'] }
39
39
  end
40
40
  end
41
41
 
@@ -22,9 +22,6 @@ class CallbackRecorder
22
22
 
23
23
  view :all, :key => :required_property
24
24
 
25
- attr_accessor :lambda_works
26
- before_create lambda {|model| model.lambda_works = true }
27
-
28
25
  def callbacks
29
26
  @callbacks ||= []
30
27
  end
@@ -58,7 +55,7 @@ describe "multiple callbacks at once" do
58
55
 
59
56
  it "should run all callback methods given to the callback method call" do
60
57
  monkey = Monkey.new
61
- monkey.run_callbacks :before_create
58
+ monkey.run_callbacks :create
62
59
  monkey.eaten_banana.should be_true
63
60
  monkey.eaten_apple.should be_true
64
61
  end
@@ -263,14 +260,6 @@ describe "destroy callbacks" do
263
260
  end
264
261
  end
265
262
 
266
- describe "lambda callbacks" do
267
- it "should run the lambda" do
268
- recorder = CallbackRecorder.new
269
- recorder.run_callbacks :before_create
270
- recorder.lambda_works.should be_true
271
- end
272
- end
273
-
274
263
  describe "validation callbacks" do
275
264
  class ValidatedUser
276
265
  include CouchPotato::Persistence
@@ -305,4 +294,4 @@ describe "validation callbacks" do
305
294
  user.valid?.should == true
306
295
  user.errors.on(:name).should == nil
307
296
  end
308
- end
297
+ end
@@ -33,6 +33,13 @@ describe 'view' do
33
33
  results = @db.view(Build.timeline)
34
34
  results.map(&:class).should == [Build]
35
35
  end
36
+
37
+ it "should return the ids if there document was not included" do
38
+ build = Build.new(:state => 'success', :time => '2008-01-01')
39
+ @db.save_document build
40
+ results = @db.view(Build.timeline(:include_docs => false))
41
+ results.should == [build.id]
42
+ end
36
43
 
37
44
  it "should pass the view options to the view query" do
38
45
  query = mock 'query'
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
- require File.join(File.dirname(__FILE__), 'fixtures', 'address')
3
- require File.join(File.dirname(__FILE__), 'fixtures', 'person')
2
+ require 'fixtures/address'
3
+ require 'fixtures/person'
4
4
 
5
5
  class Watch
6
6
  include CouchPotato::Persistence
data/spec/rails_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require File.dirname(__FILE__) + '/../rails/reload_classes'
3
2
 
4
3
  module LoadConst
5
4
  def const_missing(name)
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'spec/mocks'
4
+
5
+ RAILS_ENV = 'test'
6
+ module Rails
7
+ class Railtie
8
+ def self.initializer(*args)
9
+ end
10
+ end
11
+
12
+ def self.root
13
+ Spec::Mocks::Mock.new :join => ''
14
+ end
15
+ end
16
+
17
+ require 'couch_potato/railtie'
18
+
19
+ describe "railtie" do
20
+ context 'yaml file contains only database names' do
21
+ it "should set the database name from the yaml file" do
22
+ File.stub(:read => "test: test_db")
23
+
24
+ CouchPotato::Config.should_receive(:database_name=).with('test_db')
25
+
26
+ CouchPotato.rails_init
27
+ end
28
+ end
29
+
30
+ context 'yaml file contains more configuration' do
31
+ before(:each) do
32
+ File.stub(:read => "test: \n database: test_db\n validation_framework: :active_model")
33
+ end
34
+
35
+ it "should set the database name from the yaml file" do
36
+ CouchPotato::Config.should_receive(:database_name=).with('test_db')
37
+
38
+ CouchPotato.rails_init
39
+ end
40
+
41
+ it "should set the validation framework from the yaml file" do
42
+ CouchPotato::Config.should_receive(:validation_framework=).with(:active_model)
43
+
44
+ CouchPotato.rails_init
45
+ end
46
+ end
47
+
48
+ it "should process the yml file with erb" do
49
+ File.stub(:read => "test: \n database: <%= 'db' %>")
50
+
51
+ CouchPotato::Config.should_receive(:database_name=).with('db')
52
+
53
+ CouchPotato.rails_init
54
+ end
55
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
+ require 'time'
3
4
 
4
5
  $:.unshift(File.dirname(__FILE__) + '/../lib')
5
6
 
@@ -4,23 +4,15 @@ describe 'callbacks' do
4
4
  class Tree
5
5
  include CouchPotato::Persistence
6
6
 
7
- before_validation :grow_leaf, 'grow_branch', lambda {|tree| tree.root_count ||= 0; tree.root_count += 1 }
7
+ before_validation :grow_leaf
8
8
 
9
9
  property :leaf_count
10
- property :root_count
11
- property :branch_count
12
10
  property :watered
13
-
14
11
 
15
12
  def grow_leaf
16
13
  self.leaf_count ||= 0
17
14
  self.leaf_count += 1
18
15
  end
19
-
20
- def grow_branch
21
- self.branch_count ||= 0
22
- self.branch_count += 1
23
- end
24
16
  end
25
17
 
26
18
  class AppleTree < Tree
@@ -37,31 +29,13 @@ describe 'callbacks' do
37
29
  end
38
30
  end
39
31
 
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
32
  context 'inheritance' do
59
33
  it "should call the callbacks of the super class" do
60
34
  tree = AppleTree.new :leaf_count => 1
61
35
  tree.valid?
62
36
  tree.leaf_count.should == 2
63
37
  end
64
-
38
+
65
39
  it "should call the callbacks of the child class" do
66
40
  tree = AppleTree.new :leaf_count => 1
67
41
  tree.valid?
@@ -69,4 +43,4 @@ describe 'callbacks' do
69
43
  end
70
44
  end
71
45
 
72
- end
46
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_potato
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 17
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 3
8
- - 0
9
- version: 0.3.0
9
+ - 1
10
+ version: 0.3.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Alexander Lang
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-07-23 00:00:00 +02:00
18
+ date: 2010-09-23 00:00:00 +02:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: json
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
@@ -33,15 +36,32 @@ dependencies:
33
36
  name: couchrest
34
37
  prerelease: false
35
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
36
40
  requirements:
37
41
  - - ">="
38
42
  - !ruby/object:Gem::Version
43
+ hash: 21
39
44
  segments:
45
+ - 1
40
46
  - 0
41
- - 24
42
- version: "0.24"
47
+ - 1
48
+ version: 1.0.1
43
49
  type: :runtime
44
50
  version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: activemodel
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :runtime
64
+ version_requirements: *id003
45
65
  description: Ruby persistence layer for CouchDB
46
66
  email: alex@upstream-berlin.com
47
67
  executables: []
@@ -106,6 +126,7 @@ files:
106
126
  - spec/fixtures/person.rb
107
127
  - spec/property_spec.rb
108
128
  - spec/rails_spec.rb
129
+ - spec/railtie_spec.rb
109
130
  - spec/spec.opts
110
131
  - spec/spec_helper.rb
111
132
  - spec/unit/active_model_compliance_spec.rb
@@ -140,23 +161,27 @@ rdoc_options:
140
161
  require_paths:
141
162
  - lib
142
163
  required_ruby_version: !ruby/object:Gem::Requirement
164
+ none: false
143
165
  requirements:
144
166
  - - ">="
145
167
  - !ruby/object:Gem::Version
168
+ hash: 3
146
169
  segments:
147
170
  - 0
148
171
  version: "0"
149
172
  required_rubygems_version: !ruby/object:Gem::Requirement
173
+ none: false
150
174
  requirements:
151
175
  - - ">="
152
176
  - !ruby/object:Gem::Version
177
+ hash: 3
153
178
  segments:
154
179
  - 0
155
180
  version: "0"
156
181
  requirements: []
157
182
 
158
183
  rubyforge_project:
159
- rubygems_version: 1.3.6
184
+ rubygems_version: 1.3.7
160
185
  signing_key:
161
186
  specification_version: 3
162
187
  summary: Ruby persistence layer for CouchDB
@@ -171,6 +196,7 @@ test_files:
171
196
  - spec/fixtures/person.rb
172
197
  - spec/property_spec.rb
173
198
  - spec/rails_spec.rb
199
+ - spec/railtie_spec.rb
174
200
  - spec/spec_helper.rb
175
201
  - spec/unit/active_model_compliance_spec.rb
176
202
  - spec/unit/attributes_spec.rb