couch_potato 0.6.0 → 0.7.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
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