couch_potato 0.2.20 → 0.2.21

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## Changes
2
2
 
3
+ ### 0.2.21
4
+ * automatically set a database instance on results of CouchPotato::Database#view (langalex)
5
+ * improved auto loading of unloaded constants - can now load constants that have never been loaded before (langalex)
6
+ * raise exception on invalid parameters passed to a couchdb view query (langalex)
7
+ * when querying a view: pass in ranges as key instead of startkey/endkey, pass in plain value instead of hash with key (langalex)
8
+
3
9
  ### 0.2.20
4
10
  * support for :boolean properties (jweiss)
5
11
  * return the total_rows when querying a view (langalex)
data/VERSION.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  ---
2
+ :build:
2
3
  :major: 0
3
4
  :minor: 2
4
- :patch: 20
5
+ :patch: 21
@@ -13,25 +13,42 @@ module CouchPotato
13
13
  end
14
14
 
15
15
  # executes a view and return the results. you pass in a view spec
16
- # which is usually a result of a SomePersistentClass.view call.
16
+ # which is usually a result of a SomePersistentClass.some_view call.
17
17
  # also return the total_rows returned by CouchDB as an accessor on the results.
18
18
  #
19
19
  # Example:
20
20
  #
21
21
  # class User
22
22
  # include CouchPotato::Persistence
23
- # view :all, key: :created_at
23
+ # property :age
24
+ # view :all, key: :age
24
25
  # end
26
+ # db = CouchPotato.database
25
27
  #
26
- # CouchPotato.database.view(User.all) # => [user1, user2]
27
- # CouchPotato.database.view(User.all).total_rows # => 2
28
+ # db.view(User.all) # => [user1, user2]
29
+ # db.view(User.all).total_rows # => 2
28
30
  #
31
+ # You can pass the usual parameters you can pass to a couchdb view to the view:
32
+ #
33
+ # db.view(User.all(limit: 5, startkey: 2, reduce: false))
34
+ #
35
+ # For your convenience when passing a has with only a key parameter you can just pass in the value
36
+ #
37
+ # db.view(User.all(key: 1)) == db.view(User.all(1))
38
+ #
39
+ # Instead of passing a startkey and endkey you can pass in a key with a range:
40
+ #
41
+ # db.view(User.all(key: 1..20)) == db.view(startkey: 1, endkey: 20) == db.view(User.all(1..20))
42
+ #
29
43
  def view(spec)
30
44
  results = CouchPotato::View::ViewQuery.new(database,
31
45
  spec.design_document, spec.view_name, spec.map_function,
32
46
  spec.reduce_function).query_view!(spec.view_parameters)
33
47
  processed_results = spec.process_results results
34
48
  processed_results.instance_eval "def total_rows; #{results['total_rows']}; end" if results['total_rows']
49
+ processed_results.each do |document|
50
+ document.database = self if document.respond_to?(:database=)
51
+ end if processed_results.respond_to?(:each)
35
52
  processed_results
36
53
  end
37
54
 
@@ -16,7 +16,7 @@ require File.dirname(__FILE__) + '/view/view_query'
16
16
  module CouchPotato
17
17
  module Persistence
18
18
 
19
- def self.included(base)
19
+ def self.included(base) #:nodoc:
20
20
  base.send :include, Properties, Callbacks, Validation, Json, CouchPotato::View::CustomViews
21
21
  base.send :include, DirtyAttributes, GhostAttributes, Attachments
22
22
  base.send :include, MagicTimestamps
@@ -1,6 +1,6 @@
1
1
  module CouchPotato
2
2
  module Attachments
3
- def self.included(base)
3
+ def self.included(base) #:nodoc:
4
4
  base.class_eval do
5
5
  attr_writer :_attachments
6
6
 
@@ -1,7 +1,7 @@
1
1
  module CouchPotato
2
2
  module Persistence
3
3
  module Callbacks
4
- def self.included(base)
4
+ def self.included(base) #:nodoc:
5
5
  base.extend ClassMethods
6
6
 
7
7
  base.class_eval do
@@ -2,7 +2,7 @@ module CouchPotato
2
2
  module Persistence
3
3
  module DirtyAttributes
4
4
 
5
- def self.included(base)
5
+ def self.included(base) #:nodoc:
6
6
  base.class_eval do
7
7
  after_save :reset_dirty_attributes
8
8
 
@@ -1,5 +1,5 @@
1
1
  module CouchPotato
2
- module GhostAttributes
2
+ module GhostAttributes #:nodoc:
3
3
  def self.included(base)
4
4
  base.class_eval do
5
5
  attr_accessor :_document
@@ -1,7 +1,7 @@
1
1
  module CouchPotato
2
2
  module Persistence
3
3
  module Json
4
- def self.included(base)
4
+ def self.included(base) #:nodoc:
5
5
  base.extend ClassMethods
6
6
  end
7
7
 
@@ -32,7 +32,7 @@ module CouchPotato
32
32
  end
33
33
  end
34
34
 
35
- def self.included(base)
35
+ def self.included(base) #:nodoc:
36
36
  base.extend ClassMethods
37
37
  base.class_eval do
38
38
  def self.properties
@@ -42,7 +42,7 @@ module CouchPotato
42
42
  end
43
43
  end
44
44
 
45
- def type_caster
45
+ def type_caster #:nodoc:
46
46
  @type_caster ||= TypeCaster.new
47
47
  end
48
48
 
@@ -1,6 +1,6 @@
1
1
  module CouchPotato
2
2
  module Persistence
3
- class TypeCaster
3
+ class TypeCaster #:nodoc:
4
4
  def cast(value, type)
5
5
  if type == :boolean
6
6
  cast_boolen(value)
@@ -3,7 +3,7 @@ require 'validatable'
3
3
  module CouchPotato
4
4
  module Persistence
5
5
  module Validation
6
- def self.included(base)
6
+ def self.included(base) #:nodoc:
7
7
  base.send :include, Validatable
8
8
  base.class_eval do
9
9
  # Override the validate method to first run before_validation callback
@@ -5,6 +5,8 @@ module CouchPotato
5
5
  private :klass, :options
6
6
 
7
7
  def initialize(klass, view_name, options, view_parameters)
8
+ normalized_view_parameters = normalize_view_parameters view_parameters
9
+ assert_valid_view_parameters normalized_view_parameters
8
10
  @klass = klass
9
11
  @design_document = klass.to_s.underscore
10
12
  @view_name = view_name
@@ -13,12 +15,47 @@ module CouchPotato
13
15
  [:group, :include_docs, :descending, :group_level, :limit].each do |key|
14
16
  @view_parameters[key] = options[key] if options.include?(key)
15
17
  end
16
- @view_parameters.merge!(view_parameters)
18
+ @view_parameters.merge!(normalized_view_parameters)
17
19
  end
18
20
 
19
21
  def process_results(results)
20
22
  results
21
23
  end
24
+
25
+ private
26
+
27
+ def normalize_view_parameters(params)
28
+ normalized_params = params.dup
29
+ hash = wrap_in_hash params
30
+ replace_range_key hash
31
+ end
32
+
33
+ def wrap_in_hash(params)
34
+ if params.is_a?(Hash)
35
+ params
36
+ else
37
+ {:key => params}
38
+ end
39
+ end
40
+
41
+ def replace_range_key(params)
42
+ if((key = params[:key]).is_a?(Range))
43
+ params.delete :key
44
+ params[:startkey] = key.first
45
+ params[:endkey] = key.last
46
+ end
47
+ params
48
+ end
49
+
50
+ def assert_valid_view_parameters(params)
51
+ params.keys.each do |key|
52
+ raise ArgumentError.new("invalid view parameter: #{key}") unless valid_view_parameters.include?(key.to_s)
53
+ end
54
+ end
55
+
56
+ def valid_view_parameters
57
+ %w(key startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end)
58
+ end
22
59
  end
23
60
  end
24
61
  end
@@ -9,13 +9,12 @@ module CouchPotato
9
9
  module View
10
10
  module CustomViews
11
11
 
12
- def self.included(base)
12
+ def self.included(base) #:nodoc:
13
13
  base.extend ClassMethods
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- # Declare a CouchDB view, for examples on how to use see the *ViewSpec classes in CouchPotato::View
18
- def views(view_name = nil)
17
+ def views(view_name = nil) #:nodoc:
19
18
  if view_name
20
19
  _find_view(view_name)
21
20
  else
@@ -23,10 +22,11 @@ module CouchPotato
23
22
  end
24
23
  end
25
24
 
26
- def execute_view(view_name, view_parameters)
25
+ def execute_view(view_name, view_parameters) #:nodoc:
27
26
  view_spec_class(views(view_name)[:type]).new(self, view_name, views(view_name), view_parameters)
28
27
  end
29
-
28
+
29
+ # Declare a CouchDB view, for examples on how to use see the *ViewSpec classes in CouchPotato::View
30
30
  def view(view_name, options)
31
31
  view_name = view_name.to_s
32
32
  views[view_name] = options
@@ -34,7 +34,7 @@ module CouchPotato
34
34
  self.instance_eval(method_str)
35
35
  end
36
36
 
37
- def view_spec_class(type)
37
+ def view_spec_class(type) #:nodoc:
38
38
  if type && type.is_a?(Class)
39
39
  type
40
40
  else
@@ -43,7 +43,7 @@ module CouchPotato
43
43
  end
44
44
  end
45
45
 
46
- def _find_view(view)
46
+ def _find_view(view) #:nodoc:
47
47
  return @views[view] if @views && @views[view]
48
48
  superclass._find_view(view) if superclass && superclass.respond_to?(:_find_view)
49
49
  end
@@ -1,32 +1,16 @@
1
1
  module CouchPotato
2
- module Persistence
3
-
4
- def self.persistent_classes #:nodoc:
5
- @persistent_classes ||= []
6
- end
7
-
8
- def self.reload_persistent_classes #:nodoc:
9
- persistent_classes.each do |clazz|
10
- eval clazz.name
11
- end
12
- end
13
-
14
-
15
- def self.included_with_class_reloading(base) #:nodoc:
16
- persistent_classes << base
17
- included_without_class_reloading(base)
18
- end
19
-
20
- class << self
21
- alias_method :included_without_class_reloading, :included
22
- alias_method :included, :included_with_class_reloading
23
- end
24
- end
25
-
26
2
  Database.class_eval do
27
3
  def load_document_with_class_reloading(id)
28
- Persistence.reload_persistent_classes
29
- load_document_without_class_reloading id
4
+ begin
5
+ load_document_without_class_reloading id
6
+ rescue ArgumentError => e
7
+ if(name = e.message.scan(/(can't find const|undefined class\/module) ([\w\:]+)/).first[1])
8
+ eval name
9
+ retry
10
+ else
11
+ raise e
12
+ end
13
+ end
30
14
  end
31
15
 
32
16
  alias_method :load_document_without_class_reloading, :load_document
data/spec/rails_spec.rb CHANGED
@@ -10,9 +10,7 @@ end
10
10
 
11
11
  describe CouchPotato::Database, 'rails specific behavior' do
12
12
 
13
- it "should load load models whose constants are currently uninitialized (like with rails in development mode)" do
14
- Autoloader::Uninitialized
15
- Autoloader.send :remove_const, 'Uninitialized'
13
+ it "should load models whose constants are currently uninitialized (like with rails in development mode)" do
16
14
  recreate_db
17
15
  CouchPotato.couchrest_database.save_doc(JSON.create_id => 'Autoloader::Uninitialized', '_id' => '1')
18
16
  CouchPotato.database.load('1').class.name.should == 'Autoloader::Uninitialized'
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/../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
+ end
43
+
44
+ end
45
+
46
+
@@ -205,3 +205,32 @@ describe CouchPotato::Database, 'save_document' do
205
205
 
206
206
  end
207
207
  end
208
+
209
+ describe CouchPotato::Database, 'view' do
210
+ before(:each) do
211
+ @db = CouchPotato::Database.new(stub('couchrest db').as_null_object)
212
+ @result = stub('result')
213
+ @spec = stub('view spec', :process_results => [@result]).as_null_object
214
+ CouchPotato::View::ViewQuery.stub(:new => stub('view query', :query_view! => {'rows' => [@result]}))
215
+ end
216
+
217
+ it "should set itself on returned results that have an accessor" do
218
+ @result.stub(:respond_to?).with(:database=).and_return(true)
219
+ @result.should_receive(:database=).with(@db)
220
+ @db.view(@spec)
221
+ end
222
+
223
+ it "should not set itself on returned results that don't have an accessor" do
224
+ @result.stub(:respond_to?).with(:database=).and_return(false)
225
+ @result.should_not_receive(:database=).with(@db)
226
+ @db.view(@spec)
227
+ end
228
+
229
+ it "should not try to set itself on result sets that are not collections" do
230
+ lambda {
231
+ @spec.stub(:process_results => 1)
232
+ }.should_not raise_error
233
+
234
+ @db.view(@spec)
235
+ end
236
+ end
@@ -10,4 +10,4 @@ describe CouchPotato::View::ModelViewSpec, 'map_function' do
10
10
  spec = CouchPotato::View::ModelViewSpec.new Object, 'all', {}, {}
11
11
  spec.map_function.should include('if(doc.ruby_class && doc.ruby_class == \'Object\')')
12
12
  end
13
- end
13
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_potato
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.20
4
+ version: 0.2.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Lang
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-11 00:00:00 +01:00
12
+ date: 2010-01-13 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -100,6 +100,7 @@ files:
100
100
  - spec/spec.opts
101
101
  - spec/spec_helper.rb
102
102
  - spec/unit/attributes_spec.rb
103
+ - spec/unit/base_view_spec_spec.rb
103
104
  - spec/unit/callbacks_spec.rb
104
105
  - spec/unit/couch_potato_spec.rb
105
106
  - spec/unit/create_spec.rb
@@ -153,6 +154,7 @@ test_files:
153
154
  - spec/rails_spec.rb
154
155
  - spec/spec_helper.rb
155
156
  - spec/unit/attributes_spec.rb
157
+ - spec/unit/base_view_spec_spec.rb
156
158
  - spec/unit/callbacks_spec.rb
157
159
  - spec/unit/couch_potato_spec.rb
158
160
  - spec/unit/create_spec.rb