couch_potato 1.7.1 → 1.9.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 (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