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,49 @@
1
+ require 'json'
2
+
3
+
4
+ module CouchPotato
5
+ module RSpec
6
+ class MapToProxy
7
+ def initialize(input_ruby)
8
+ @input_ruby = input_ruby
9
+ end
10
+
11
+ def to(*expected_ruby)
12
+ MapToMatcher.new(expected_ruby, @input_ruby)
13
+ end
14
+ end
15
+
16
+ class MapToMatcher
17
+ include RunJS
18
+
19
+ def initialize(expected_ruby, input_ruby)
20
+ @expected_ruby = expected_ruby
21
+ @input_ruby = input_ruby
22
+ end
23
+
24
+ def matches?(view_spec)
25
+ js = <<-JS
26
+ #{File.read(File.dirname(__FILE__) + '/print_r.js')}
27
+ var doc = #{@input_ruby.to_json};
28
+ var map = #{view_spec.map_function};
29
+ var result = [];
30
+ var emit = function(key, value) {
31
+ result.push([key, value]);
32
+ };
33
+ map(doc);
34
+ print(print_r(result));
35
+ JS
36
+ @actual_ruby = JSON.parse(run_js(js))
37
+ @expected_ruby == @actual_ruby
38
+ end
39
+
40
+ def failure_message_for_should
41
+ "Expected to map to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
42
+ end
43
+
44
+ def failure_message_for_should_not
45
+ "Expected not to map to #{@actual_ruby.inspect} but did."
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ // taken and adapted from http://scriptnode.com/article/javascript-print_r-or-var_dump-equivalent/
2
+
3
+ /**
4
+ * Concatenates the values of a variable into an easily readable string
5
+ * by Matt Hackett [scriptnode.com]
6
+ * @param {Object} x The variable to debug
7
+ * @param {Number} max The maximum number of recursions allowed (keep low, around 5 for HTML elements to prevent errors) [default: 10]
8
+ * @param {String} sep The separator to use between [default: a single space ' ']
9
+ * @param {Number} l The current level deep (amount of recursion). Do not use this parameter: it's for the function's own use
10
+ */
11
+ function print_r(x, max, sep, l) {
12
+
13
+ l = l || 0;
14
+ max = max || 10;
15
+ sep = sep || ' ';
16
+
17
+ if (l > max) {
18
+ throw("Too much recursion");
19
+ };
20
+
21
+ var r = '';
22
+
23
+ if (x === null) {
24
+ r += "null";
25
+ } else if (is_array(x)) {
26
+ r += '[' + x.map(function(i) {
27
+ return print_r(i, max, sep, (l + 1));
28
+ }).join(', ') + ']';
29
+ } else if(is_object(x)) {
30
+ r += '{'
31
+ var pairs = [];
32
+ for (i in x) {
33
+ pairs.push('"' + i + '": ' + print_r(x[i], max, sep, (l + 1)));
34
+ }
35
+ r += pairs.join(', ');
36
+ r += '}';
37
+ } else if(is_string(x)) {
38
+ r += '"' + x + "\"";
39
+ } else {
40
+ r += x;
41
+ };
42
+
43
+ return r;
44
+
45
+ function is_string(a) {
46
+ return typeof a === 'string';
47
+ };
48
+
49
+ function is_array(a) {
50
+ return (a &&
51
+ typeof a === 'object' &&
52
+ a.constructor === Array);
53
+ };
54
+
55
+ function is_object(a) {
56
+ return a && typeof a == 'object'
57
+ };
58
+
59
+ };
60
+
@@ -0,0 +1,50 @@
1
+ module CouchPotato
2
+ module RSpec
3
+ class ReduceToProxy
4
+ def initialize(docs, keys, rereduce = false)
5
+ @docs, @keys, @rereduce = docs, keys, rereduce
6
+ end
7
+
8
+ def to(expected_ruby)
9
+ ReduceToMatcher.new(expected_ruby, @docs, @keys, @rereduce)
10
+ end
11
+ end
12
+
13
+ class ReduceToMatcher
14
+ include RunJS
15
+
16
+ def initialize(expected_ruby, docs, keys, rereduce = false)
17
+ @expected_ruby, @docs, @keys, @rereduce = expected_ruby, docs, keys, rereduce
18
+ end
19
+
20
+ def matches?(view_spec)
21
+ js = <<-JS
22
+ #{File.read(File.dirname(__FILE__) + '/print_r.js')}
23
+
24
+ sum = function(values) {
25
+ var rv = 0;
26
+ for (var i in values) {
27
+ rv += values[i];
28
+ }
29
+ return rv;
30
+ };
31
+
32
+ var docs = #{@docs.to_json};
33
+ var keys = #{@keys.to_json};
34
+ var reduce = #{view_spec.reduce_function};
35
+ print(print_r({result: reduce(docs, keys, #{@rereduce})}));
36
+ JS
37
+ @actual_ruby = JSON.parse(run_js(js))['result']
38
+ @expected_ruby == @actual_ruby
39
+ end
40
+
41
+ def failure_message_for_should
42
+ "Expected to reduce to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
43
+ end
44
+
45
+ def failure_message_for_should_not
46
+ "Expected not to reduce to #{@actual_ruby.inspect} but did."
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,39 @@
1
+ module CouchPotato
2
+ module RSpec
3
+ module RunJS
4
+ private
5
+
6
+ def run_js(js)
7
+ path = 'couch_potato_js_runner.js'
8
+ File.open(path, 'w') {|f| f << js}
9
+ `js #{path}`
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ require 'couch_potato/rspec/matchers/map_to_matcher'
17
+ require 'couch_potato/rspec/matchers/reduce_to_matcher'
18
+ require 'couch_potato/rspec/matchers/list_as_matcher'
19
+
20
+ module Spec
21
+ module Matchers
22
+ def map(document)
23
+ CouchPotato::RSpec::MapToProxy.new(document)
24
+ end
25
+
26
+ def reduce(docs, keys)
27
+ CouchPotato::RSpec::ReduceToProxy.new(docs, keys)
28
+ end
29
+
30
+ def rereduce(docs, keys)
31
+ CouchPotato::RSpec::ReduceToProxy.new(docs, keys, true)
32
+ end
33
+
34
+ def list(results)
35
+ CouchPotato::RSpec::ListAsProxy.new(results)
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,46 @@
1
+ module CouchPotato::RSpec
2
+
3
+ module StubView
4
+ class ViewStub
5
+ def initialize(clazz, view, db)
6
+ @clazz = clazz
7
+ @view = view
8
+ @db = db
9
+ end
10
+
11
+ def with(*args)
12
+ @args = args
13
+ self
14
+ end
15
+
16
+ def and_return(return_value)
17
+ view_stub = Spec::Mocks::Mock.new("#{@clazz}.#{@view}(#{@args.try(:join, ', ')}) view")
18
+ _stub = @clazz.stub(@view)
19
+ _stub.with(*@args) if @args
20
+ _stub.and_return(view_stub)
21
+ @db.stub(:view).with(view_stub).and_return(return_value)
22
+ end
23
+ end
24
+
25
+ def stub_view(clazz, view)
26
+ ViewStub.new clazz, view, self
27
+ end
28
+ end
29
+
30
+ module StubDb
31
+ def stub_db(options = {})
32
+ db = stub('db', options)
33
+ db.extend CouchPotato::RSpec::StubView
34
+ CouchPotato.stub(:database => db)
35
+ db
36
+ end
37
+ end
38
+ end
39
+
40
+ module Spec
41
+ module Mocks
42
+ module ExampleMethods
43
+ include CouchPotato::RSpec::StubDb
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,2 @@
1
+ require 'couch_potato/rspec/stub_db'
2
+ require 'couch_potato/rspec/matchers'
@@ -0,0 +1,27 @@
1
+ module CouchPotato
2
+ module Validation
3
+ module WithActiveModel
4
+ def self.included(base)
5
+ require 'active_model'
6
+ require 'active_model/translation'
7
+ base.send :include, ::ActiveModel::Validations
8
+ base.instance_eval do
9
+ def before_validation(*names)
10
+ names.each do |name|
11
+ validate name
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # provide same interface to errors object as in Validatable
21
+ module ::ActiveModel
22
+ class Errors
23
+ def errors
24
+ self
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ module CouchPotato
2
+ module Validation
3
+ module WithValidatable
4
+ def self.included(base)
5
+ begin
6
+ require 'validatable'
7
+ rescue LoadError
8
+ puts "Please install the gem validatable using 'gem install validatable'"
9
+ raise
10
+ end
11
+ base.send :include, ::Validatable
12
+ base.class_eval do
13
+ # Override the validate method to first run before_validation callback
14
+ def valid?
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)}
21
+ end
22
+ errors.empty?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ # add [] method to Validatable's implementation of the Errors class
31
+ module Validatable
32
+ class Errors
33
+ def [](attribute)
34
+ [on(attribute)].flatten.compact
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ module CouchPotato
2
+ module Validation
3
+ def self.included(base) #:nodoc:
4
+ case CouchPotato::Config.validation_framework
5
+ when :validatable
6
+ require 'couch_potato/validation/with_validatable'
7
+ base.send :include, CouchPotato::Validation::WithValidatable
8
+ when :active_model
9
+ require 'couch_potato/validation/with_active_model'
10
+ base.send :include, CouchPotato::Validation::WithActiveModel
11
+ else
12
+ raise "Unknown CouchPotato::Config.validation_framework #{CouchPotato::Config.validation_framework.inspect}, options are :validatable or :active_model"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,67 @@
1
+ module CouchPotato
2
+ module View
3
+ class BaseViewSpec
4
+ attr_reader :reduce_function, :list_name, :list_function, :design_document, :view_name, :view_parameters, :klass, :options
5
+ private :klass, :options
6
+
7
+ def initialize(klass, view_name, options, view_parameters)
8
+ normalized_view_parameters = normalize_view_parameters view_parameters
9
+
10
+ @list_name = normalized_view_parameters.delete(:list) || options[:list]
11
+
12
+ assert_valid_view_parameters normalized_view_parameters
13
+ @klass = klass
14
+ # If there is a specified design name generator, we use that one
15
+ @design_document = CouchPotato.design_name_fun.call klass
16
+ @view_name = view_name
17
+ @options = options
18
+
19
+ @list_function = klass.lists(@list_name) if @list_name
20
+ @view_parameters = {}
21
+ [:group, :include_docs, :descending, :group_level, :limit].each do |key|
22
+ @view_parameters[key] = options[key] if options.include?(key)
23
+ end
24
+ @view_parameters.merge!(normalized_view_parameters)
25
+ end
26
+
27
+ def process_results(results)
28
+ results
29
+ end
30
+
31
+ private
32
+
33
+ def normalize_view_parameters(params)
34
+ normalized_params = params.dup
35
+ hash = wrap_in_hash params
36
+ replace_range_key hash
37
+ end
38
+
39
+ def wrap_in_hash(params)
40
+ if params.is_a?(Hash)
41
+ params
42
+ else
43
+ {:key => params}
44
+ end
45
+ end
46
+
47
+ def replace_range_key(params)
48
+ if((key = params[:key]).is_a?(Range))
49
+ params.delete :key
50
+ params[:startkey] = key.first
51
+ params[:endkey] = key.last
52
+ end
53
+ params
54
+ end
55
+
56
+ def assert_valid_view_parameters(params)
57
+ params.keys.each do |key|
58
+ raise ArgumentError.new("invalid view parameter: #{key}") unless valid_view_parameters.include?(key.to_s)
59
+ end
60
+ end
61
+
62
+ def valid_view_parameters
63
+ %w(key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,42 @@
1
+ module CouchPotato
2
+ module View
3
+ # a view for custom map/reduce functions that still returns model instances
4
+ #
5
+ # example:
6
+ # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :include_docs => true, :type => :custom, :reduce => nil
7
+ class CustomViewSpec < BaseViewSpec
8
+ def map_function
9
+ options[:map]
10
+ end
11
+
12
+ def reduce_function
13
+ options[:reduce]
14
+ end
15
+
16
+ def view_parameters
17
+ {:include_docs => options[:include_docs] || false}.merge(super)
18
+ end
19
+
20
+ def process_results(results)
21
+ if count?
22
+ results['rows'].first.try(:[], 'value') || 0
23
+ else
24
+ results['rows'].map do |row|
25
+ if row['doc'].instance_of?(klass)
26
+ row['doc']
27
+ else
28
+ klass.json_create row['doc'] || row['value'].merge(:_id => row['id'] || row['key'])
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def count?
37
+ view_parameters[:reduce]
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ require 'couch_potato/view/base_view_spec'
2
+ require 'couch_potato/view/model_view_spec'
3
+ require 'couch_potato/view/properties_view_spec'
4
+ require 'couch_potato/view/custom_view_spec'
5
+ require 'couch_potato/view/raw_view_spec'
6
+
7
+
8
+ module CouchPotato
9
+ module View
10
+ module CustomViews
11
+
12
+ def self.included(base) #:nodoc:
13
+ base.extend ClassMethods
14
+ end
15
+
16
+ module ClassMethods
17
+ def views(view_name = nil) #:nodoc:
18
+ if view_name
19
+ _find_view(view_name)
20
+ else
21
+ @views ||= {}
22
+ end
23
+ end
24
+
25
+ def execute_view(view_name, view_parameters) #:nodoc:
26
+ view_spec_class(views(view_name)[:type]).new(self, view_name, views(view_name), view_parameters)
27
+ end
28
+
29
+ # Declare a CouchDB view, for examples on how to use see the *ViewSpec classes in CouchPotato::View
30
+ def view(view_name, options)
31
+ view_name = view_name.to_s
32
+ views[view_name] = options
33
+ method_str = "def #{view_name}(view_parameters = {}); execute_view(\"#{view_name}\", view_parameters); end"
34
+ self.instance_eval(method_str)
35
+ end
36
+
37
+ def view_spec_class(type) #:nodoc:
38
+ if type && type.is_a?(Class)
39
+ type
40
+ else
41
+ name = type.nil? ? 'Model' : type.to_s.camelize
42
+ CouchPotato::View.const_get("#{name}ViewSpec")
43
+ end
44
+ end
45
+
46
+ def _find_view(view) #:nodoc:
47
+ (@views && @views[view]) || (superclass._find_view(view) if superclass.respond_to?(:_find_view))
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ module CouchPotato
2
+ module View
3
+ module Lists
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def list(name, function)
10
+ lists[name] = function
11
+ end
12
+
13
+ def lists(name = nil)
14
+ if name.nil?
15
+ @lists ||= {}
16
+ else
17
+ (@lists && @lists[name]) || (superclass.lists(name) if superclass.respond_to?(:lists))
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,75 @@
1
+ module CouchPotato
2
+ module View
3
+ # A view to return model instances by searching its properties.
4
+ # If you pass reduce => true will count instead
5
+ #
6
+ # example:
7
+ # view :my_view, :key => :name
8
+ #
9
+ # in addition you can pass in conditions as a javascript string
10
+ # view :my_view_only_completed, :key => :name, :conditions => 'doc.completed = true'
11
+ class ModelViewSpec < BaseViewSpec
12
+
13
+ def view_parameters
14
+ _super = super
15
+ if _super[:reduce]
16
+ _super
17
+ else
18
+ {:include_docs => true, :reduce => false}.merge(_super)
19
+ end
20
+ end
21
+
22
+ def map_function
23
+ map_body do
24
+ "emit(#{formatted_key(key)}, 1);"
25
+ end
26
+ end
27
+
28
+ def reduce_function
29
+ "function(key, values) {
30
+ return sum(values);
31
+ }"
32
+ end
33
+
34
+ def process_results(results)
35
+ if count?
36
+ results['rows'].first.try(:[], 'value') || 0
37
+ else
38
+ results['rows'].map { |row| row['doc'] || row['id'] }
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def map_body(&block)
45
+ "function(doc) {
46
+ if(doc.#{JSON.create_id} && doc.#{JSON.create_id} == '#{@klass.name}'#{conditions_js}) {
47
+ " + yield + "
48
+ }
49
+ }"
50
+
51
+ end
52
+
53
+ def conditions_js
54
+ " && (#{options[:conditions]})" if options[:conditions]
55
+ end
56
+
57
+ def count?
58
+ view_parameters[:reduce]
59
+ end
60
+
61
+ def key
62
+ options[:key]
63
+ end
64
+
65
+ def formatted_key(key)
66
+ if key.is_a? Array
67
+ '[' + key.map{|attribute| formatted_key(attribute)}.join(', ') + ']'
68
+ else
69
+ "doc['#{key}']"
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ module CouchPotato
2
+ module View
3
+ # A view to return model instances with only some properties poulated by searching its properties, e.g. for very large documents where you are only interested in some of their data
4
+ #
5
+ # example:
6
+ # view :my_view, :key => :name, :properties => [:name, :author], :type => :properties
7
+ class PropertiesViewSpec < ModelViewSpec
8
+ def map_function
9
+ map_body do
10
+ "emit(#{formatted_key(key)}, #{properties_for_map(properties)});"
11
+ end
12
+ end
13
+
14
+ def reduce_function
15
+ <<-JS
16
+ function(key, values, rereduce) {
17
+ if(rereduce) {
18
+ return sum(values);
19
+ } else {
20
+ return values.length;
21
+ }
22
+ }
23
+ JS
24
+ end
25
+
26
+ def process_results(results)
27
+ results['rows'].map do |row|
28
+ klass.json_create row['value'].merge(:_id => row['id'])
29
+ end
30
+ end
31
+
32
+ def view_parameters
33
+ {:include_docs => false}.merge(super)
34
+ end
35
+
36
+ private
37
+
38
+ def properties
39
+ options[:properties]
40
+ end
41
+
42
+ def properties_for_map(properties)
43
+ '{' + properties.map{|p| "#{p}: doc.#{p}"}.join(', ') + '}'
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module CouchPotato
2
+ module View
3
+ # A view for custom map/reduce functions that returns the raw data fromcouchdb
4
+ #
5
+ # example:
6
+ # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :reduce => nil
7
+ # optionally you can pass in a results filter which you can use to process the raw couchdb results before returning them
8
+ #
9
+ # example:
10
+ # view :my_custom_view, :map => "function(doc) { emit(doc._id, null); }", :type => :raw, :results_filter => lambda{|results| results['rows].map{|row| row['value']}}
11
+ class RawViewSpec < BaseViewSpec
12
+ def map_function
13
+ options[:map]
14
+ end
15
+
16
+ def process_results(results)
17
+ options[:results_filter] ? options[:results_filter].call(results) : results
18
+ end
19
+
20
+ def reduce_function
21
+ options[:reduce]
22
+ end
23
+ end
24
+ end
25
+ end