couch_potato 0.6.0 → 0.7.0.pre.1

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 (58) hide show
  1. data/.gitignore +0 -1
  2. data/.travis.yml +9 -1
  3. data/CHANGES.md +12 -0
  4. data/Gemfile.lock +29 -23
  5. data/MIT-LICENSE.txt +1 -1
  6. data/README.md +68 -38
  7. data/Rakefile +19 -60
  8. data/active_support_3_0.lock +4 -0
  9. data/active_support_3_1.lock +4 -0
  10. data/active_support_3_2 +4 -0
  11. data/active_support_3_2.lock +55 -0
  12. data/couch_potato.gemspec +1 -0
  13. data/lib/couch_potato/database.rb +55 -27
  14. data/lib/couch_potato/persistence/active_model_compliance.rb +6 -2
  15. data/lib/couch_potato/persistence/callbacks.rb +0 -1
  16. data/lib/couch_potato/persistence/dirty_attributes.rb +8 -17
  17. data/lib/couch_potato/persistence/json.rb +3 -2
  18. data/lib/couch_potato/persistence/properties.rb +19 -8
  19. data/lib/couch_potato/persistence/simple_property.rb +1 -3
  20. data/lib/couch_potato/persistence/type_caster.rb +7 -2
  21. data/lib/couch_potato/persistence.rb +27 -13
  22. data/lib/couch_potato/railtie.rb +1 -1
  23. data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +12 -12
  24. data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +8 -8
  25. data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +10 -10
  26. data/lib/couch_potato/rspec/matchers.rb +8 -7
  27. data/lib/couch_potato/validation.rb +15 -11
  28. data/lib/couch_potato/version.rb +1 -1
  29. data/lib/couch_potato/view/base_view_spec.rb +14 -12
  30. data/lib/couch_potato/view/model_view_spec.rb +134 -39
  31. data/lib/couch_potato/view/properties_view_spec.rb +11 -7
  32. data/lib/couch_potato/view/view_query.rb +10 -9
  33. data/lib/couch_potato.rb +25 -10
  34. data/spec/callbacks_spec.rb +88 -0
  35. data/spec/create_spec.rb +22 -4
  36. data/spec/default_property_spec.rb +6 -0
  37. data/spec/property_spec.rb +81 -44
  38. data/spec/railtie_spec.rb +20 -18
  39. data/spec/spec_helper.rb +19 -1
  40. data/spec/unit/active_model_compliance_spec.rb +17 -11
  41. data/spec/unit/attributes_spec.rb +33 -10
  42. data/spec/unit/base_view_spec_spec.rb +19 -5
  43. data/spec/unit/couch_potato_spec.rb +1 -20
  44. data/spec/unit/create_spec.rb +2 -2
  45. data/spec/unit/database_spec.rb +113 -47
  46. data/spec/unit/dirty_attributes_spec.rb +19 -19
  47. data/spec/unit/model_view_spec_spec.rb +78 -1
  48. data/spec/unit/properties_view_spec_spec.rb +18 -5
  49. data/spec/unit/validation_spec.rb +1 -55
  50. data/spec/unit/view_query_spec.rb +48 -26
  51. data/spec/{custom_view_spec.rb → views_spec.rb} +59 -17
  52. metadata +88 -90
  53. data/.rvmrc +0 -1
  54. data/lib/core_ext/object.rb +0 -5
  55. data/lib/core_ext/string.rb +0 -12
  56. data/lib/core_ext/symbol.rb +0 -15
  57. data/lib/couch_potato/validation/with_active_model.rb +0 -27
  58. data/lib/couch_potato/validation/with_validatable.rb +0 -41
@@ -35,11 +35,11 @@ module CouchPotato
35
35
  # For your convenience when passing a hash with only a key parameter you can just pass in the value
36
36
  #
37
37
  # db.view(User.all(key: 1)) == db.view(User.all(1))
38
- #
38
+ #
39
39
  # Instead of passing a startkey and endkey you can pass in a key with a range:
40
40
  #
41
41
  # db.view(User.all(key: 1..20)) == db.view(startkey: 1, endkey: 20) == db.view(User.all(1..20))
42
- #
42
+ #
43
43
  # You can also pass in multiple keys:
44
44
  #
45
45
  # db.view(User.all(keys: [1, 2, 3]))
@@ -51,7 +51,8 @@ module CouchPotato
51
51
  :map => spec.map_function,
52
52
  :reduce => spec.reduce_function}
53
53
  },
54
- ({spec.list_name => spec.list_function} unless spec.list_name.nil?)
54
+ ({spec.list_name => spec.list_function} unless spec.list_name.nil?),
55
+ spec.language
55
56
  ).query_view!(spec.view_parameters)
56
57
  processed_results = spec.process_results results
57
58
  processed_results.instance_eval "def total_rows; #{results['total_rows']}; end" if results['total_rows']
@@ -60,12 +61,12 @@ module CouchPotato
60
61
  end if processed_results.respond_to?(:each)
61
62
  processed_results
62
63
  end
63
-
64
+
64
65
  # returns the first result from a #view query or nil
65
66
  def first(spec)
66
67
  view(spec).first
67
68
  end
68
-
69
+
69
70
  # returns th first result from a #view or raises CouchPotato::NotFound
70
71
  def first!(spec)
71
72
  first(spec) || raise(CouchPotato::NotFound)
@@ -81,7 +82,7 @@ module CouchPotato
81
82
  end
82
83
  end
83
84
  alias_method :save, :save_document
84
-
85
+
85
86
  # saves a document, raises a CouchPotato::Database::ValidationsFailedError on failure
86
87
  def save_document!(document)
87
88
  save_document(document) || raise(ValidationsFailedError.new(document.errors.full_messages))
@@ -98,27 +99,43 @@ module CouchPotato
98
99
  end
99
100
  alias_method :destroy, :destroy_document
100
101
 
101
- # loads a document by its id
102
+ # loads a document by its id(s)
103
+ # id - either a single id or an array of ids
104
+ # returns either a single document or an array of documents (if an array of ids was passed).
105
+ # returns nil if the single document could not be found. when passing an array and some documents
106
+ # could not be found these are omitted from the returned array
102
107
  def load_document(id)
103
108
  raise "Can't load a document without an id (got nil)" if id.nil?
104
- begin
105
- instance = couchrest_database.get(id)
106
- instance.database = self
107
- instance
108
- rescue(RestClient::ResourceNotFound)
109
- nil
109
+
110
+ if id.is_a?(Array)
111
+ bulk_load id
112
+ else
113
+ begin
114
+ instance = couchrest_database.get(id)
115
+ instance.database = self
116
+ instance
117
+ rescue(RestClient::ResourceNotFound)
118
+ nil
119
+ end
110
120
  end
111
121
  end
112
122
  alias_method :load, :load_document
113
-
123
+
124
+ # loads one or more documents by its id(s)
125
+ # behaves like #load except it raises a CouchPotato::NotFound if any of the documents could not be found
114
126
  def load!(id)
115
- load(id) || raise(CouchPotato::NotFound)
127
+ doc = load(id)
128
+ if id.is_a?(Array)
129
+ missing_docs = id - doc.map(&:id)
130
+ end
131
+ raise(CouchPotato::NotFound, missing_docs.try(:join, ', ')) if doc.nil? || missing_docs.try(:any?)
132
+ doc
116
133
  end
117
134
 
118
135
  def inspect #:nodoc:
119
136
  "#<CouchPotato::Database @root=\"#{couchrest_database.root}\">"
120
137
  end
121
-
138
+
122
139
  # returns the underlying CouchRest::Database instance
123
140
  def couchrest_database
124
141
  @couchrest_database
@@ -126,20 +143,27 @@ module CouchPotato
126
143
 
127
144
  private
128
145
 
146
+ def bulk_load(ids)
147
+ response = couchrest_database.bulk_load ids
148
+ existing_rows = response['rows'].select{|row| row.key? 'doc'}
149
+ docs = existing_rows.map{|row| row["doc"]}
150
+ docs.each{|doc| doc.database = self}
151
+ end
152
+
129
153
  def create_document(document, validate)
130
154
  document.database = self
131
-
155
+
132
156
  if validate
133
157
  document.errors.clear
134
- document.run_callbacks :validation_on_save do
135
- document.run_callbacks :validation_on_create do
158
+ return false if false == document.run_callbacks(:validation_on_save) do
159
+ return false if false == document.run_callbacks(:validation_on_create) do
136
160
  return false unless valid_document?(document)
137
161
  end
138
162
  end
139
163
  end
140
-
141
- document.run_callbacks :save do
142
- document.run_callbacks :create do
164
+
165
+ return false if false == document.run_callbacks(:save) do
166
+ return false if false == document.run_callbacks(:create) do
143
167
  res = couchrest_database.save_doc document.to_hash
144
168
  document._rev = res['rev']
145
169
  document._id = res['id']
@@ -151,15 +175,15 @@ module CouchPotato
151
175
  def update_document(document, validate)
152
176
  if validate
153
177
  document.errors.clear
154
- document.run_callbacks :validation_on_save do
155
- document.run_callbacks :validation_on_update do
178
+ return false if false == document.run_callbacks(:validation_on_save) do
179
+ return false if false == document.run_callbacks(:validation_on_update) do
156
180
  return false unless valid_document?(document)
157
181
  end
158
182
  end
159
183
  end
160
184
 
161
- document.run_callbacks :save do
162
- document.run_callbacks :update do
185
+ return false if false == document.run_callbacks(:save) do
186
+ return false if false == document.run_callbacks(:update) do
163
187
  res = couchrest_database.save_doc document.to_hash
164
188
  document._rev = res['rev']
165
189
  end
@@ -172,7 +196,11 @@ module CouchPotato
172
196
  errors.instance_variable_set("@messages", errors.messages.dup) if errors.respond_to?(:messages)
173
197
  document.valid?
174
198
  errors.each do |k, v|
175
- document.errors.add(k, v)
199
+ if v.respond_to?(:each)
200
+ v.each {|message| document.errors.add(k, message)}
201
+ else
202
+ document.errors.add(k, v)
203
+ end
176
204
  end
177
205
  document.errors.empty?
178
206
  end
@@ -12,14 +12,18 @@ module CouchPotato
12
12
  self
13
13
  end
14
14
 
15
+ def to_partial_path
16
+ "#{self.class.name.underscore.pluralize}/#{self.class.name.underscore}"
17
+ end
18
+
15
19
  def errors
16
20
  super || {}
17
21
  end
18
-
22
+
19
23
  def persisted?
20
24
  !self.new?
21
25
  end
22
-
26
+
23
27
  def to_key
24
28
  persisted? ? [to_param] : nil
25
29
  end
@@ -12,7 +12,6 @@ module CouchPotato
12
12
 
13
13
  define_model_callbacks :create, :save, :update, :destroy
14
14
  define_model_callbacks *[:save, :create, :update].map {|c| :"validation_on_#{c}"}
15
- define_model_callbacks :validation unless Config.validation_framework == :active_model
16
15
  end
17
16
  end
18
17
 
@@ -2,45 +2,36 @@ require 'bigdecimal'
2
2
  module CouchPotato
3
3
  module Persistence
4
4
  module DirtyAttributes
5
-
5
+
6
6
  def self.included(base) #:nodoc:
7
7
  base.send :include, ActiveModel::Dirty
8
8
  base.class_eval do
9
9
  after_save :reset_dirty_attributes
10
10
  end
11
11
  end
12
-
12
+
13
13
  def initialize(attributes = {})
14
14
  super
15
15
  end
16
-
16
+
17
17
  # returns true if a model has dirty attributes, i.e. their value has changed since the last save
18
- def dirty?
18
+ def dirty?
19
19
  changed? || @forced_dirty
20
20
  end
21
-
21
+
22
22
  # marks a model as dirty
23
23
  def is_dirty
24
24
  @forced_dirty = true
25
25
  end
26
-
27
- def method_missing(name, *args)
28
- if(name.to_s.include?('_will_change!'))
29
- self.class.define_attribute_methods self.class.property_names
30
- send(name, *args)
31
- else
32
- super
33
- end
34
- end
35
-
26
+
36
27
  private
37
-
28
+
38
29
  def reset_dirty_attributes
39
30
  @previously_changed = changes
40
31
  @changed_attributes.clear
41
32
  @forced_dirty = nil
42
33
  end
43
-
34
+
44
35
  def clone_attribute(value)
45
36
  if [Fixnum, Symbol, TrueClass, FalseClass, NilClass, Float, BigDecimal].include?(value.class)
46
37
  value
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash'
2
+
1
3
  module CouchPotato
2
4
  module Persistence
3
5
  module Json
@@ -39,10 +41,9 @@ module CouchPotato
39
41
  # creates a model instance from JSON
40
42
  def json_create(json)
41
43
  return if json.nil?
42
- instance = self.new
44
+ instance = self.new :_document => HashWithIndifferentAccess.new(json)
43
45
  instance._id = json[:_id] || json['_id']
44
46
  instance._rev = json[:_rev] || json['_rev']
45
- instance._document = json
46
47
  instance
47
48
  end
48
49
  end
@@ -5,22 +5,22 @@ module CouchPotato
5
5
  module Properties
6
6
  class PropertyList
7
7
  include Enumerable
8
-
8
+
9
9
  attr_accessor :list
10
-
10
+
11
11
  def initialize(clazz)
12
12
  @clazz = clazz
13
13
  @list = []
14
14
  end
15
-
15
+
16
16
  def each
17
17
  (list + inherited_properties).each {|property| yield property}
18
18
  end
19
-
19
+
20
20
  def <<(property)
21
21
  @list << property
22
22
  end
23
-
23
+
24
24
  def inherited_properties
25
25
  superclazz = @clazz.superclass
26
26
  properties = []
@@ -31,7 +31,7 @@ module CouchPotato
31
31
  properties.flatten
32
32
  end
33
33
  end
34
-
34
+
35
35
  def self.included(base) #:nodoc:
36
36
  base.extend ClassMethods
37
37
  base.class_eval do
@@ -41,11 +41,11 @@ module CouchPotato
41
41
  end
42
42
  end
43
43
  end
44
-
44
+
45
45
  def type_caster #:nodoc:
46
46
  @type_caster ||= TypeCaster.new
47
47
  end
48
-
48
+
49
49
  module ClassMethods
50
50
  # returns all the property names of a model class that have been defined using the #property method
51
51
  #
@@ -70,9 +70,20 @@ module CouchPotato
70
70
  # property :publisher, :type => Publisher
71
71
  # end
72
72
  def property(name, options = {})
73
+ undefine_attribute_methods
74
+ define_attribute_methods property_names + [name]
73
75
  properties << SimpleProperty.new(self, name, options)
76
+ remove_attribute_accessors_from_activesupport_module
74
77
  end
75
78
 
79
+ def remove_attribute_accessors_from_activesupport_module
80
+ active_support_module = ancestors[1..-1].find{|m| m.name.nil? && (property_names - m.instance_methods).empty?}
81
+ if active_support_module
82
+ property_names.each do |name|
83
+ active_support_module.send :remove_method, name if active_support_module.instance_methods.include?(name)
84
+ end
85
+ end
86
+ end
76
87
  end
77
88
  end
78
89
  end
@@ -52,12 +52,10 @@ module CouchPotato
52
52
 
53
53
  def define_accessors(base, name, options)
54
54
  base.class_eval do
55
- include PropertyMethods
56
-
57
55
  define_method "#{name}" do
58
56
  load_attribute_from_document(name) unless instance_variable_defined?("@#{name}")
59
57
  value = instance_variable_get("@#{name}")
60
- if value.nil? && options[:default]
58
+ if value.nil? && !options[:default].nil?
61
59
  default = clone_attribute(options[:default])
62
60
  self.instance_variable_set("@#{name}", default)
63
61
  default
@@ -1,9 +1,14 @@
1
1
  module CouchPotato
2
2
  module Persistence
3
3
  class TypeCaster #:nodoc:
4
+ NUMBER_REGEX = /-?\d+\.?\d*/
5
+
4
6
  def cast(value, type)
5
7
  if type == :boolean
6
8
  cast_boolean(value)
9
+ elsif type.instance_of?(Array)
10
+ nested_type = type.first
11
+ value.map { |val| cast_native(val, nested_type) } if value
7
12
  else
8
13
  cast_native(value, type)
9
14
  end
@@ -24,9 +29,9 @@ module CouchPotato
24
29
  def cast_native(value, type)
25
30
  if type && !value.instance_of?(type)
26
31
  if type == Fixnum
27
- value.to_s.scan(/-?\d+/).join.to_i unless value.blank?
32
+ BigDecimal.new(value.to_s.scan(NUMBER_REGEX).join).round unless value.blank?
28
33
  elsif type == Float
29
- value.to_s.scan(/-?\d+\.?\d*/).join.to_f unless value.blank?
34
+ value.to_s.scan(NUMBER_REGEX).join.to_f unless value.blank?
30
35
  else
31
36
  type.json_create value unless value.blank?
32
37
  end
@@ -16,7 +16,7 @@ require File.dirname(__FILE__) + '/view/view_query'
16
16
 
17
17
  module CouchPotato
18
18
  module Persistence
19
-
19
+
20
20
  def self.included(base) #:nodoc:
21
21
  base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews, CouchPotato::View::Lists
22
22
  base.send :include, DirtyAttributes, GhostAttributes, Attachments
@@ -27,15 +27,15 @@ module CouchPotato
27
27
  alias_method :id, :_id
28
28
  alias_method :id=, :_id=
29
29
  end
30
-
30
+
31
31
  CouchPotato.models << base
32
32
  end
33
33
 
34
34
  # initialize a new instance of the model optionally passing it a hash of attributes.
35
35
  # the attributes have to be declared using the #property method.
36
36
  # the new model will be yielded to an optionally given block.
37
- #
38
- # example:
37
+ #
38
+ # example:
39
39
  # class Book
40
40
  # include CouchPotato::Persistence
41
41
  # property :title
@@ -50,13 +50,15 @@ module CouchPotato
50
50
  # book.title # => 'Time to Relax'
51
51
  def initialize(attributes = {})
52
52
  if attributes
53
+ @skip_dirty_tracking = true
53
54
  attributes.each do |name, value|
54
55
  self.send("#{name}=", value)
55
56
  end
57
+ @skip_dirty_tracking = false
56
58
  end
57
59
  yield self if block_given?
58
60
  end
59
-
61
+
60
62
  # assign multiple attributes at once.
61
63
  # the attributes have to be declared using the #property method
62
64
  #
@@ -75,7 +77,7 @@ module CouchPotato
75
77
  self.send "#{attribute}=", value
76
78
  end
77
79
  end
78
-
80
+
79
81
  # returns all of a model's attributes that have been defined using the #property method as a Hash
80
82
  #
81
83
  # example:
@@ -92,35 +94,47 @@ module CouchPotato
92
94
  res
93
95
  end
94
96
  end
95
-
97
+
98
+ def []=(attribute, value)
99
+ send "#{attribute}=", value
100
+ end
101
+
102
+ def [](attribute)
103
+ send(attribute)
104
+ end
105
+
106
+ def has_key?(key)
107
+ attributes.has_key?(key)
108
+ end
109
+
96
110
  # returns true if a model hasn't been saved yet, false otherwise
97
111
  def new?
98
112
  _rev.nil?
99
113
  end
100
114
  alias_method :new_record?, :new?
101
-
115
+
102
116
  # returns the document id
103
117
  # this is used by rails to construct URLs
104
118
  # can be overridden to for example use slugs for URLs instead if ids
105
119
  def to_param
106
120
  _id
107
121
  end
108
-
122
+
109
123
  def ==(other) #:nodoc:
110
124
  other.class == self.class && self.to_json == other.to_json
111
125
  end
112
-
126
+
113
127
  def eql?(other)
114
128
  self == other
115
129
  end
116
-
130
+
117
131
  def hash
118
132
  _id.hash * (_id.hash.to_s.size ** 10) + _rev.hash
119
133
  end
120
-
134
+
121
135
  def inspect
122
136
  attributes_as_string = attributes.map {|attribute, value| "#{attribute}: #{value.inspect}"}.join(", ")
123
137
  %Q{#<#{self.class} _id: "#{_id}", _rev: "#{_rev}", #{attributes_as_string}>}
124
138
  end
125
- end
139
+ end
126
140
  end
@@ -8,8 +8,8 @@ module CouchPotato
8
8
  CouchPotato::Config.database_name = config
9
9
  else
10
10
  CouchPotato::Config.database_name = config['database']
11
- CouchPotato::Config.validation_framework = config['validation_framework'] if config['validation_framework']
12
11
  CouchPotato::Config.split_design_documents_per_view = config['split_design_documents_per_view'] if config['split_design_documents_per_view']
12
+ CouchPotato::Config.default_language = config['default_language'] if config['default_language']
13
13
  end
14
14
  end
15
15
 
@@ -4,20 +4,20 @@ module CouchPotato
4
4
  def initialize(results_ruby)
5
5
  @results_ruby = results_ruby
6
6
  end
7
-
7
+
8
8
  def as(expected_ruby)
9
9
  ListAsMatcher.new(expected_ruby, @results_ruby)
10
10
  end
11
11
  end
12
-
12
+
13
13
  class ListAsMatcher
14
14
  include RunJS
15
-
15
+
16
16
  def initialize(expected_ruby, results_ruby)
17
17
  @expected_ruby = expected_ruby
18
18
  @results_ruby = results_ruby
19
19
  end
20
-
20
+
21
21
  def matches?(view_spec)
22
22
  js = <<-JS
23
23
  #{File.read(File.dirname(__FILE__) + '/print_r.js')}
@@ -25,7 +25,7 @@ module CouchPotato
25
25
  var results = #{@results_ruby.to_json};
26
26
  var listed = '';
27
27
  var list = #{view_spec.list_function};
28
-
28
+
29
29
  var getRow = function() {
30
30
  return results.rows.shift();
31
31
  };
@@ -33,22 +33,22 @@ module CouchPotato
33
33
  listed = listed + text;
34
34
  };
35
35
  list();
36
- print(print_r(JSON.parse(listed)));
36
+ print_r(JSON.parse(listed));
37
37
  JS
38
-
38
+
39
39
  @actual_ruby = JSON.parse(run_js(js))
40
-
40
+
41
41
  @expected_ruby == @actual_ruby
42
42
  end
43
-
43
+
44
44
  def failure_message_for_should
45
45
  "Expected to list as #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
46
46
  end
47
-
47
+
48
48
  def failure_message_for_should_not
49
49
  "Expected to not list as #{@expected_ruby.inspect} but did."
50
50
  end
51
-
51
+
52
52
  end
53
53
  end
54
- end
54
+ end
@@ -7,20 +7,20 @@ module CouchPotato
7
7
  def initialize(input_ruby)
8
8
  @input_ruby = input_ruby
9
9
  end
10
-
10
+
11
11
  def to(*expected_ruby)
12
12
  MapToMatcher.new(expected_ruby, @input_ruby)
13
13
  end
14
14
  end
15
-
15
+
16
16
  class MapToMatcher
17
17
  include RunJS
18
-
18
+
19
19
  def initialize(expected_ruby, input_ruby)
20
20
  @expected_ruby = expected_ruby
21
21
  @input_ruby = input_ruby
22
22
  end
23
-
23
+
24
24
  def matches?(view_spec)
25
25
  js = <<-JS
26
26
  #{File.read(File.dirname(__FILE__) + '/print_r.js')}
@@ -31,19 +31,19 @@ module CouchPotato
31
31
  result.push([key, value]);
32
32
  };
33
33
  map(doc);
34
- print(print_r(result));
34
+ print_r(result);
35
35
  JS
36
36
  @actual_ruby = JSON.parse(run_js(js))
37
37
  @expected_ruby == @actual_ruby
38
38
  end
39
-
39
+
40
40
  def failure_message_for_should
41
41
  "Expected to map to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
42
42
  end
43
-
43
+
44
44
  def failure_message_for_should_not
45
45
  "Expected not to map to #{@actual_ruby.inspect} but did."
46
46
  end
47
47
  end
48
48
  end
49
- end
49
+ end
@@ -4,23 +4,23 @@ module CouchPotato
4
4
  def initialize(docs, keys, rereduce = false)
5
5
  @docs, @keys, @rereduce = docs, keys, rereduce
6
6
  end
7
-
7
+
8
8
  def to(expected_ruby)
9
9
  ReduceToMatcher.new(expected_ruby, @docs, @keys, @rereduce)
10
10
  end
11
11
  end
12
-
12
+
13
13
  class ReduceToMatcher
14
14
  include RunJS
15
-
15
+
16
16
  def initialize(expected_ruby, docs, keys, rereduce = false)
17
17
  @expected_ruby, @docs, @keys, @rereduce = expected_ruby, docs, keys, rereduce
18
18
  end
19
-
19
+
20
20
  def matches?(view_spec)
21
21
  js = <<-JS
22
22
  #{File.read(File.dirname(__FILE__) + '/print_r.js')}
23
-
23
+
24
24
  sum = function(values) {
25
25
  var rv = 0;
26
26
  for (var i in values) {
@@ -28,23 +28,23 @@ module CouchPotato
28
28
  }
29
29
  return rv;
30
30
  };
31
-
31
+
32
32
  var docs = #{@docs.to_json};
33
33
  var keys = #{@keys.to_json};
34
34
  var reduce = #{view_spec.reduce_function};
35
- print(print_r({result: reduce(docs, keys, #{@rereduce})}));
35
+ print_r({result: reduce(docs, keys, #{@rereduce})});
36
36
  JS
37
37
  @actual_ruby = JSON.parse(run_js(js))['result']
38
38
  @expected_ruby == @actual_ruby
39
39
  end
40
-
40
+
41
41
  def failure_message_for_should
42
42
  "Expected to reduce to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
43
43
  end
44
-
44
+
45
45
  def failure_message_for_should_not
46
46
  "Expected not to reduce to #{@actual_ruby.inspect} but did."
47
47
  end
48
48
  end
49
49
  end
50
- end
50
+ end