couch_potato 1.7.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +38 -0
  3. data/.gitignore +3 -0
  4. data/CHANGES.md +169 -131
  5. data/Gemfile +4 -0
  6. data/README.md +55 -75
  7. data/Rakefile +11 -10
  8. data/couch_potato-rspec.gemspec +2 -1
  9. data/couch_potato.gemspec +8 -6
  10. data/gemfiles/active_support_5_0 +1 -5
  11. data/gemfiles/active_support_5_1 +7 -0
  12. data/gemfiles/active_support_5_2 +7 -0
  13. data/gemfiles/active_support_6_0 +7 -0
  14. data/gemfiles/active_support_6_1 +7 -0
  15. data/lib/couch_potato/database.rb +165 -70
  16. data/lib/couch_potato/persistence/dirty_attributes.rb +3 -21
  17. data/lib/couch_potato/persistence/magic_timestamps.rb +3 -3
  18. data/lib/couch_potato/persistence/properties.rb +15 -10
  19. data/lib/couch_potato/persistence/simple_property.rb +0 -4
  20. data/lib/couch_potato/persistence/type_caster.rb +9 -6
  21. data/lib/couch_potato/persistence.rb +0 -1
  22. data/lib/couch_potato/railtie.rb +6 -11
  23. data/lib/couch_potato/validation.rb +8 -0
  24. data/lib/couch_potato/version.rb +1 -1
  25. data/lib/couch_potato/view/base_view_spec.rb +8 -32
  26. data/lib/couch_potato/view/custom_views.rb +4 -3
  27. data/lib/couch_potato/view/flex_view_spec.rb +121 -0
  28. data/lib/couch_potato/view/view_parameters.rb +34 -0
  29. data/lib/couch_potato.rb +32 -9
  30. data/spec/callbacks_spec.rb +45 -19
  31. data/spec/conflict_handling_spec.rb +0 -1
  32. data/spec/property_spec.rb +2 -2
  33. data/spec/railtie_spec.rb +10 -0
  34. data/spec/spec_helper.rb +4 -3
  35. data/spec/unit/active_model_compliance_spec.rb +7 -3
  36. data/spec/unit/attributes_spec.rb +1 -1
  37. data/spec/unit/caching_spec.rb +105 -0
  38. data/spec/unit/couch_potato_spec.rb +70 -5
  39. data/spec/unit/create_spec.rb +5 -4
  40. data/spec/unit/database_spec.rb +235 -135
  41. data/spec/unit/dirty_attributes_spec.rb +5 -26
  42. data/spec/unit/flex_view_spec_spec.rb +17 -0
  43. data/spec/unit/model_view_spec_spec.rb +1 -1
  44. data/spec/unit/rspec_stub_db_spec.rb +31 -0
  45. data/spec/unit/validation_spec.rb +42 -2
  46. data/spec/views_spec.rb +214 -103
  47. data/vendor/pouchdb-collate/LICENSE +202 -0
  48. data/vendor/pouchdb-collate/pouchdb-collate.js +430 -0
  49. metadata +46 -42
  50. data/.ruby-version +0 -1
  51. data/.travis.yml +0 -21
  52. data/gemfiles/active_support_4_0 +0 -11
  53. data/gemfiles/active_support_4_1 +0 -11
  54. data/gemfiles/active_support_4_2 +0 -11
  55. data/lib/couch_potato/persistence/deep_dirty_attributes.rb +0 -180
  56. data/spec/unit/deep_dirty_attributes_spec.rb +0 -434
@@ -1,9 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CouchPotato
2
4
  class Database
3
-
4
5
  class ValidationsFailedError < ::StandardError; end
6
+ # Pass in a cache to enable caching #load calls.
7
+ # A cache needs to respond to #[], #[]= and #clear (just use a Hash).
8
+ attr_accessor :cache
9
+ cattr_accessor :default_batch_size
10
+ self.default_batch_size = 500
11
+ attr_reader :name # the (unresolved) name of the database unless this is the default database
5
12
 
6
- def initialize(couchrest_database)
13
+ def initialize(couchrest_database, name: nil)
14
+ @name = name
7
15
  @couchrest_database = couchrest_database
8
16
  end
9
17
 
@@ -37,30 +45,40 @@ module CouchPotato
37
45
  #
38
46
  # db.view(User.all(keys: [1, 2, 3]))
39
47
  def view(spec)
40
- ActiveSupport::Notifications.instrument('couch_potato.view', name: "#{spec.design_document}/#{spec.view_name}") do
41
- results = CouchPotato::View::ViewQuery.new(
42
- couchrest_database,
43
- spec.design_document,
44
- {spec.view_name => {
45
- :map => spec.map_function,
46
- :reduce => spec.reduce_function
47
- }
48
- },
49
- ({spec.list_name => spec.list_function} unless spec.list_name.nil?),
50
- spec.lib,
51
- spec.language
52
- ).query_view!(spec.view_parameters)
53
- processed_results = spec.process_results results
54
- processed_results.each do |document|
55
- document.database = self if document.respond_to?(:database=)
56
- end if processed_results.respond_to?(:each)
57
- processed_results
48
+ id = view_cache_id(spec)
49
+ cached = cache && id.is_a?(String) && cache[id]
50
+ if cache
51
+ if cached
52
+ ActiveSupport::Notifications.instrument('couch_potato.view.cached') do
53
+ cached
54
+ end
55
+ else
56
+ cache[id] = view_without_caching(spec)
57
+ cache[id]
58
+ end
59
+ else
60
+ view_without_caching(spec)
61
+ end
62
+ end
63
+
64
+ # Same as #view but instead of returning the results, it yields them
65
+ # to a given block in batches of the given size, making multiple
66
+ # requests with according skip/limit params sent to CouchDB.
67
+ def view_in_batches(spec, batch_size: default_batch_size)
68
+ batch = 0
69
+ loop do
70
+ spec.view_parameters = spec.view_parameters.merge({ skip: batch * batch_size, limit: batch_size })
71
+ results = view(spec)
72
+ yield results
73
+ break if results.size < batch_size
74
+
75
+ batch += 1
58
76
  end
59
77
  end
60
78
 
61
79
  # returns the first result from a #view query or nil
62
80
  def first(spec)
63
- spec.view_parameters = spec.view_parameters.merge({:limit => 1})
81
+ spec.view_parameters = spec.view_parameters.merge({ limit: 1 })
64
82
  view(spec).first
65
83
  end
66
84
 
@@ -74,33 +92,35 @@ module CouchPotato
74
92
  # * yield the object to be saved to the block and run if once before saving
75
93
  # * on conflict: reload the document, run the block again and retry saving
76
94
  def save_document(document, validate = true, retries = 0, &block)
95
+ cache&.clear
77
96
  begin
78
- block.call document if block
97
+ block&.call document
79
98
  save_document_without_conflict_handling(document, validate)
80
- rescue CouchRest::Conflict => e
99
+ rescue CouchRest::Conflict
81
100
  if block
82
101
  handle_write_conflict document, validate, retries, &block
83
102
  else
84
- raise CouchPotato::Conflict.new
103
+ raise CouchPotato::Conflict
85
104
  end
86
105
  end
87
106
  end
88
- alias_method :save, :save_document
107
+ alias save save_document
89
108
 
90
109
  # saves a document, raises a CouchPotato::Database::ValidationsFailedError on failure
91
110
  def save_document!(document)
92
- save_document(document) || raise(ValidationsFailedError.new(document.errors.full_messages))
111
+ save_document(document) || raise(ValidationsFailedError, document.errors.full_messages)
93
112
  end
94
- alias_method :save!, :save_document!
113
+ alias save! save_document!
95
114
 
96
115
  def destroy_document(document)
116
+ cache&.clear
97
117
  begin
98
118
  destroy_document_without_conflict_handling document
99
119
  rescue CouchRest::Conflict
100
120
  retry if document = document.reload
101
121
  end
102
122
  end
103
- alias_method :destroy, :destroy_document
123
+ alias destroy destroy_document
104
124
 
105
125
  # loads a document by its id(s)
106
126
  # id - either a single id or an array of ids
@@ -108,28 +128,29 @@ module CouchPotato
108
128
  # returns nil if the single document could not be found. when passing an array and some documents
109
129
  # could not be found these are omitted from the returned array
110
130
  def load_document(id)
111
- raise "Can't load a document without an id (got nil)" if id.nil?
112
-
113
- ActiveSupport::Notifications.instrument('couch_potato.load') do
114
- if id.is_a?(Array)
115
- bulk_load id
131
+ cached = cache && id.is_a?(String) && cache[id]
132
+ if cache
133
+ if cached
134
+ ActiveSupport::Notifications.instrument('couch_potato.load.cached') do
135
+ cached
136
+ end
116
137
  else
117
- instance = couchrest_database.get(id)
118
- instance.database = self if instance
119
- instance
138
+ cache[id] = load_document_without_caching(id)
139
+ cache[id]
120
140
  end
141
+ else
142
+ load_document_without_caching(id)
121
143
  end
122
144
  end
123
- alias_method :load, :load_document
145
+ alias load load_document
124
146
 
125
147
  # loads one or more documents by its id(s)
126
148
  # behaves like #load except it raises a CouchPotato::NotFound if any of the documents could not be found
127
149
  def load!(id)
128
150
  doc = load(id)
129
- if id.is_a?(Array)
130
- missing_docs = id - doc.map(&:id)
131
- end
151
+ missing_docs = id - doc.map(&:id) if id.is_a?(Array)
132
152
  raise(CouchPotato::NotFound, missing_docs.try(:join, ', ')) if doc.nil? || missing_docs.try(:any?)
153
+
133
154
  doc
134
155
  end
135
156
 
@@ -138,15 +159,90 @@ module CouchPotato
138
159
  end
139
160
 
140
161
  # returns the underlying CouchRest::Database instance
141
- def couchrest_database
142
- @couchrest_database
162
+ attr_reader :couchrest_database
163
+
164
+ # returns a new database instance connected to the CouchDB database
165
+ # with the given name. the name is passed through the
166
+ # additional_databases configuration to resolve it to a database
167
+ # configured there.
168
+ # if the current database has a cache, the new database will receive
169
+ # a cleared copy of it.
170
+ def switch_to(database_name)
171
+ resolved_database_name = CouchPotato.resolve_database_name(database_name)
172
+ self
173
+ .class
174
+ .new(CouchPotato.couchrest_database_for_name(resolved_database_name), name: database_name)
175
+ .tap(&copy_clear_cache_proc)
176
+ end
177
+
178
+ # returns a new database instance connected to the default CouchDB database.
179
+ # if the current database has a cache, the new database will receive
180
+ # a cleared copy of it.
181
+ def switch_to_default
182
+ self
183
+ .class
184
+ .new(CouchPotato.couchrest_database)
185
+ .tap(&copy_clear_cache_proc)
143
186
  end
144
187
 
145
188
  private
146
189
 
190
+ def copy_clear_cache_proc
191
+ lambda { |db|
192
+ next unless cache
193
+
194
+ db.cache = cache.dup
195
+ db.cache.clear
196
+ }
197
+ end
198
+
199
+ def view_without_caching(spec)
200
+ ActiveSupport::Notifications.instrument('couch_potato.view', name: "#{spec.design_document}/#{spec.view_name}") do
201
+ results = CouchPotato::View::ViewQuery.new(
202
+ couchrest_database,
203
+ spec.design_document,
204
+ { spec.view_name => {
205
+ map: spec.map_function,
206
+ reduce: spec.reduce_function
207
+ } },
208
+ ({ spec.list_name => spec.list_function } unless spec.list_name.nil?),
209
+ spec.lib,
210
+ spec.language
211
+ ).query_view!(spec.view_parameters)
212
+ processed_results = spec.process_results results
213
+ if processed_results.respond_to?(:database=)
214
+ processed_results.database = self
215
+ elsif processed_results.respond_to?(:each)
216
+ processed_results.each do |document|
217
+ document.database = self if document.respond_to?(:database=)
218
+ end
219
+ end
220
+ processed_results
221
+ end
222
+ end
223
+
224
+ def load_document_without_caching(id)
225
+ raise "Can't load a document without an id (got nil)" if id.nil?
226
+
227
+ ActiveSupport::Notifications.instrument('couch_potato.load') do
228
+ if id.is_a?(Array)
229
+ bulk_load id
230
+ else
231
+ instance = couchrest_database.get(id)
232
+ instance.database = self if instance
233
+ instance
234
+ end
235
+ end
236
+ end
237
+
238
+ def view_cache_id(spec)
239
+ spec.send(:klass).to_s + spec.view_name.to_s + spec.view_parameters.to_s
240
+ end
241
+
147
242
  def handle_write_conflict(document, validate, retries, &block)
243
+ cache&.clear
148
244
  if retries == 5
149
- raise CouchPotato::Conflict.new
245
+ raise CouchPotato::Conflict
150
246
  else
151
247
  reloaded = document.reload
152
248
  document.attributes = reloaded.attributes
@@ -174,10 +270,10 @@ module CouchPotato
174
270
 
175
271
  def bulk_load(ids)
176
272
  response = couchrest_database.bulk_load ids
177
- docs = response['rows'].map{|row| row["doc"]}.compact
178
- docs.each{|doc|
273
+ docs = response['rows'].map { |row| row['doc'] }.compact
274
+ docs.each do |doc|
179
275
  doc.database = self if doc.respond_to?(:database=)
180
- }
276
+ end
181
277
  end
182
278
 
183
279
  def create_document(document, validate)
@@ -185,51 +281,50 @@ module CouchPotato
185
281
 
186
282
  if validate
187
283
  document.errors.clear
188
- return false if false == document.run_callbacks(:validation_on_save) do
189
- return false if false == document.run_callbacks(:validation_on_create) do
284
+ return false if document.run_callbacks(:validation_on_save) do
285
+ return false if document.run_callbacks(:validation_on_create) do
190
286
  return false unless valid_document?(document)
191
- end
192
- end
287
+ end == false
288
+ end == false
193
289
  end
194
290
 
195
- return false if false == document.run_callbacks(:save) do
196
- return false if false == document.run_callbacks(:create) do
291
+ return false if document.run_callbacks(:save) do
292
+ return false if document.run_callbacks(:create) do
197
293
  res = couchrest_database.save_doc document.to_hash
198
294
  document._rev = res['rev']
199
295
  document._id = res['id']
200
- end
201
- end
296
+ end == false
297
+ end == false
298
+
202
299
  true
203
300
  end
204
301
 
205
302
  def update_document(document, validate)
206
303
  if validate
207
304
  document.errors.clear
208
- return false if false == document.run_callbacks(:validation_on_save) do
209
- return false if false == document.run_callbacks(:validation_on_update) do
305
+ return false if document.run_callbacks(:validation_on_save) do
306
+ return false if document.run_callbacks(:validation_on_update) do
210
307
  return false unless valid_document?(document)
211
- end
212
- end
308
+ end == false
309
+ end == false
213
310
  end
214
311
 
215
- return false if false == document.run_callbacks(:save) do
216
- return false if false == document.run_callbacks(:update) do
217
- if document.dirty?
218
- res = couchrest_database.save_doc document.to_hash
219
- document._rev = res['rev']
220
- end
221
- end
222
- end
312
+ return false if document.run_callbacks(:save) do
313
+ return false if document.run_callbacks(:update) do
314
+ res = couchrest_database.save_doc document.to_hash
315
+ document._rev = res['rev']
316
+ end == false
317
+ end == false
318
+
223
319
  true
224
320
  end
225
321
 
226
322
  def valid_document?(document)
227
- errors = document.errors.errors.dup
228
- errors.instance_variable_set("@messages", errors.messages.dup) if errors.respond_to?(:messages)
323
+ original_errors_hash = document.errors.to_hash
229
324
  document.valid?
230
- errors.each do |k, v|
325
+ original_errors_hash.each do |k, v|
231
326
  if v.respond_to?(:each)
232
- v.each {|message| document.errors.add(k, message)}
327
+ v.each { |message| document.errors.add(k, message) }
233
328
  else
234
329
  document.errors.add(k, v)
235
330
  end
@@ -6,37 +6,19 @@ module CouchPotato
6
6
  def self.included(base) #:nodoc:
7
7
  base.send :include, ActiveModel::Dirty
8
8
  base.class_eval do
9
- after_save :reset_dirty_attributes
9
+ after_save :clear_changes_information
10
10
  end
11
11
  end
12
12
 
13
- # returns true if a model has dirty attributes, i.e. their value has changed since the last save
14
- def dirty?
15
- changed? || @forced_dirty
16
- end
17
-
18
- # marks a model as dirty
19
- def is_dirty
20
- @forced_dirty = true
21
- end
22
-
23
- private
24
-
25
- def reset_dirty_attributes
26
- @previously_changed = changes
27
- @changed_attributes.clear
28
- @forced_dirty = nil
29
- end
30
-
31
13
  def clone_attribute(value)
32
- if [Fixnum, Symbol, TrueClass, FalseClass, NilClass, Float, BigDecimal].include?(value.class)
14
+ if [Integer, Symbol, TrueClass, FalseClass, NilClass, Float, BigDecimal].find{|klass| value.is_a?(klass)}
33
15
  value
34
16
  elsif [Hash, Array].include?(value.class)
35
17
  #Deep clone
36
18
  Marshal::load(Marshal::dump(value))
37
19
  else
38
20
  value.clone
39
- end
21
+ end
40
22
  end
41
23
  end
42
24
  end
@@ -9,13 +9,13 @@ module CouchPotato
9
9
 
10
10
  before_create lambda {|model|
11
11
  model.created_at ||= (Time.zone || Time).now
12
- @changed_attributes.try :delete, 'created_at'
12
+ clear_attribute_changes [:created_at]
13
13
  model.updated_at ||= (Time.zone || Time).now
14
- @changed_attributes.try :delete, 'updated_at'
14
+ clear_attribute_changes [:updated_at]
15
15
  }
16
16
  before_update lambda {|model|
17
17
  model.updated_at = (Time.zone || Time).now
18
- @changed_attributes.try :delete, 'updated_at'
18
+ clear_attribute_changes [:updated_at]
19
19
  }
20
20
  end
21
21
  end
@@ -85,18 +85,23 @@ module CouchPotato
85
85
  # property :next_year, default: ->(book) { book.year + 1 }
86
86
  # end
87
87
  def property(name, options = {})
88
- undefine_attribute_methods
89
- define_attribute_methods property_names + [name]
88
+ # ActiveModel::AttributeMethods.generated_attribute_methods
89
+ active_support_module = send(:generated_attribute_methods)
90
+
91
+ # Mimic ActiveModel::AttributeMethods.undefine_attribute_methods, but only for this
92
+ # property's accessor method
93
+ active_support_module.module_eval do
94
+ undef_method(name) if instance_methods.include?(name)
95
+ end
96
+ cache = send(:attribute_method_matchers_cache)
97
+ cache.delete(name)
98
+
99
+ define_attribute_method name
90
100
  properties << SimpleProperty.new(self, name, options)
91
- remove_attribute_accessors_from_activesupport_module
92
- end
93
101
 
94
- def remove_attribute_accessors_from_activesupport_module
95
- active_support_module = ancestors[1..-1].find{|m| m.name.nil? && (property_names - m.instance_methods).empty?}
96
- if active_support_module
97
- property_names.each do |name|
98
- active_support_module.send :remove_method, name if active_support_module.instance_methods.include?(name)
99
- end
102
+ # Remove the default ActiveModel::AttributeMethods accessor
103
+ if active_support_module.instance_methods.include?(name)
104
+ active_support_module.send(:remove_method, name)
100
105
  end
101
106
  end
102
107
  end
@@ -32,10 +32,6 @@ module CouchPotato
32
32
  object.send @setter_name, value
33
33
  end
34
34
 
35
- def dirty?(object)
36
- object.send("#{name}_changed?")
37
- end
38
-
39
35
  def serialize(json, object)
40
36
  json[name] = object.send name
41
37
  end
@@ -1,14 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal/util'
1
4
  module CouchPotato
2
5
  module Persistence
3
6
  class TypeCaster #:nodoc:
4
- NUMBER_REGEX = /-?\d*\.?\d*/
7
+ NUMBER_REGEX = /-?\d*\.?\d*/.freeze
5
8
 
6
9
  def cast(value, type)
7
10
  if type == :boolean
8
11
  cast_boolean(value)
9
12
  elsif type.instance_of?(Array)
10
13
  nested_type = type.first
11
- value.map { |val| cast_native(val, nested_type) } if value
14
+ value&.map { |val| cast_native(val, nested_type) }
12
15
  else
13
16
  cast_native(value, type)
14
17
  end
@@ -28,12 +31,13 @@ module CouchPotato
28
31
 
29
32
  def cast_native(value, type)
30
33
  if type && !value.is_a?(type)
31
- if [Fixnum, Integer].include? type
32
- BigDecimal.new(value.to_s.scan(NUMBER_REGEX).join).round unless value.blank?
34
+
35
+ if %w[Integer Bignum Fixnum].include?(type.to_s)
36
+ value.to_s.scan(NUMBER_REGEX).join.to_d.round unless value.blank?
33
37
  elsif type == Float
34
38
  value.to_s.scan(NUMBER_REGEX).join.to_f unless value.blank?
35
39
  elsif type == BigDecimal
36
- BigDecimal.new(value.to_s) unless value.blank?
40
+ value.to_d unless value.blank?
37
41
  elsif type == Hash
38
42
  value.to_hash unless value.blank?
39
43
  elsif type.ancestors.include?(CouchPotato::Persistence)
@@ -45,7 +49,6 @@ module CouchPotato
45
49
  value
46
50
  end
47
51
  end
48
-
49
52
  end
50
53
  end
51
54
  end
@@ -6,7 +6,6 @@ require File.dirname(__FILE__) + '/persistence/magic_timestamps'
6
6
  require File.dirname(__FILE__) + '/persistence/callbacks'
7
7
  require File.dirname(__FILE__) + '/persistence/json'
8
8
  require File.dirname(__FILE__) + '/persistence/dirty_attributes'
9
- require File.dirname(__FILE__) + '/persistence/deep_dirty_attributes'
10
9
  require File.dirname(__FILE__) + '/persistence/ghost_attributes'
11
10
  require File.dirname(__FILE__) + '/persistence/attachments'
12
11
  require File.dirname(__FILE__) + '/persistence/type_caster'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  module CouchPotato
@@ -6,23 +8,16 @@ module CouchPotato
6
8
  path = Rails.root.join('config/couchdb.yml')
7
9
  if File.exist?(path)
8
10
  require 'yaml'
9
- config = YAML::load(ERB.new(File.read(path)).result)[Rails.env]
10
- if config.is_a?(String)
11
- CouchPotato::Config.database_name = config
12
- else
13
- CouchPotato::Config.database_name = config['database']
14
- CouchPotato::Config.split_design_documents_per_view = config['split_design_documents_per_view'] if config['split_design_documents_per_view']
15
- CouchPotato::Config.digest_view_names = config['digest_view_names'] if config['digest_view_names']
16
- CouchPotato::Config.default_language = config['default_language'] if config['default_language']
17
- end
11
+ config = YAML.safe_load(ERB.new(File.read(path)).result, [Symbol], [], ['default'])[Rails.env]
12
+ CouchPotato.configure(config)
18
13
  else
19
- Rails.logger.warn "Rails.root/config/couchdb.yml does not exist. Not configuring a database."
14
+ Rails.logger.warn 'Rails.root/config/couchdb.yml does not exist. Not configuring a database.'
20
15
  end
21
16
  end
22
17
 
23
18
  if defined?(::Rails::Railtie)
24
19
  class Railtie < ::Rails::Railtie
25
- initializer 'couch_potato.load_config' do |app|
20
+ initializer 'couch_potato.load_config' do |_app|
26
21
  CouchPotato.rails_init
27
22
  end
28
23
  end
@@ -3,9 +3,17 @@ require 'active_model/translation'
3
3
 
4
4
  module CouchPotato
5
5
  module Validation
6
+ module ValidationContext
7
+ def valid?(context = nil)
8
+ context ||= new? ? :create : :update
9
+ super context
10
+ end
11
+ end
12
+
6
13
  def self.included(base) #:nodoc:
7
14
  base.send :include, ::ActiveModel::Validations
8
15
  base.send :include, ::ActiveModel::Validations::Callbacks
16
+ base.send :include, ValidationContext
9
17
  end
10
18
  end
11
19
  end
@@ -1,4 +1,4 @@
1
1
  module CouchPotato
2
- VERSION = '1.7.1'.freeze
2
+ VERSION = '1.9.0'.freeze
3
3
  RSPEC_VERSION = '3.4.0'.freeze
4
4
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CouchPotato
2
4
  module View
3
5
  class BaseViewSpec
@@ -7,7 +9,7 @@ module CouchPotato
7
9
  private :klass, :options
8
10
 
9
11
  def initialize(klass, view_name, options, view_parameters)
10
- normalized_view_parameters = normalize_view_parameters view_parameters
12
+ normalized_view_parameters = ViewParameters.normalize_view_parameters view_parameters
11
13
 
12
14
  @list_name = normalized_view_parameters.delete(:list) || options[:list]
13
15
  @language = options[:language] || Config.default_language
@@ -16,13 +18,13 @@ module CouchPotato
16
18
  @klass = klass
17
19
  @options = options
18
20
  @view_name = compute_view_name(view_name,
19
- options.key?(:digest_view_name) ? options[:digest_view_name] : Config.digest_view_names)
21
+ options.key?(:digest_view_name) ? options[:digest_view_name] : Config.digest_view_names)
20
22
  @design_document = translate_to_design_doc_name(klass.to_s, @view_name, @list_name)
21
23
  @list_params = normalized_view_parameters.delete :list_params
22
24
 
23
25
  @list_function = klass.lists(@list_name) if @list_name
24
26
  @view_parameters = {}
25
- [:group, :include_docs, :descending, :group_level, :limit].each do |key|
27
+ %i[group include_docs descending group_level limit].each do |key|
26
28
  @view_parameters[key] = options[key] if options.include?(key)
27
29
  end
28
30
  @view_parameters.merge!(normalized_view_parameters)
@@ -47,40 +49,14 @@ module CouchPotato
47
49
  end
48
50
  end
49
51
 
50
- def normalize_view_parameters(params)
51
- hash = wrap_in_hash params
52
- remove_nil_stale(replace_range_key(hash))
53
- end
54
-
55
- def remove_nil_stale(params)
56
- params.reject{|name, value| name.to_s == 'stale' && value.nil?}
57
- end
58
-
59
- def wrap_in_hash(params)
60
- if params.is_a?(Hash)
61
- params
62
- else
63
- {:key => params}
64
- end
65
- end
66
-
67
- def replace_range_key(params)
68
- if((key = params[:key]).is_a?(Range))
69
- params.delete :key
70
- params[:startkey] = key.first
71
- params[:endkey] = key.last
72
- end
73
- params
74
- end
75
-
76
52
  def assert_valid_view_parameters(params)
77
53
  params.keys.each do |key|
78
- fail ArgumentError, "invalid view parameter: #{key}" unless valid_view_parameters.include?(key.to_s)
54
+ raise ArgumentError, "invalid view parameter: #{key}" unless valid_view_parameters.include?(key.to_s)
79
55
  end
80
56
  end
81
57
 
82
58
  def valid_view_parameters
83
- %w(list_params key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end)
59
+ %w[list_params key keys startkey startkey_docid endkey endkey_docid limit stale descending skip group group_level reduce include_docs inclusive_end]
84
60
  end
85
61
 
86
62
  def translate_to_design_doc_name(klass_name, view_name, list_name)
@@ -91,7 +67,7 @@ module CouchPotato
91
67
  doc_name = klass_name.downcase
92
68
 
93
69
  if CouchPotato::Config.split_design_documents_per_view
94
- doc_name += "_view_#{view_name}" if view_name.present?
70
+ doc_name += "_view_#{view_name}" if view_name.present?
95
71
  doc_name += "_list_#{list_name}" if list_name.present?
96
72
  end
97
73
  doc_name