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.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +38 -0
- data/.gitignore +3 -0
- data/CHANGES.md +169 -131
- data/Gemfile +4 -0
- data/README.md +55 -75
- data/Rakefile +11 -10
- data/couch_potato-rspec.gemspec +2 -1
- data/couch_potato.gemspec +8 -6
- data/gemfiles/active_support_5_0 +1 -5
- data/gemfiles/active_support_5_1 +7 -0
- data/gemfiles/active_support_5_2 +7 -0
- data/gemfiles/active_support_6_0 +7 -0
- data/gemfiles/active_support_6_1 +7 -0
- data/lib/couch_potato/database.rb +165 -70
- data/lib/couch_potato/persistence/dirty_attributes.rb +3 -21
- data/lib/couch_potato/persistence/magic_timestamps.rb +3 -3
- data/lib/couch_potato/persistence/properties.rb +15 -10
- data/lib/couch_potato/persistence/simple_property.rb +0 -4
- data/lib/couch_potato/persistence/type_caster.rb +9 -6
- data/lib/couch_potato/persistence.rb +0 -1
- data/lib/couch_potato/railtie.rb +6 -11
- data/lib/couch_potato/validation.rb +8 -0
- data/lib/couch_potato/version.rb +1 -1
- data/lib/couch_potato/view/base_view_spec.rb +8 -32
- data/lib/couch_potato/view/custom_views.rb +4 -3
- data/lib/couch_potato/view/flex_view_spec.rb +121 -0
- data/lib/couch_potato/view/view_parameters.rb +34 -0
- data/lib/couch_potato.rb +32 -9
- data/spec/callbacks_spec.rb +45 -19
- data/spec/conflict_handling_spec.rb +0 -1
- data/spec/property_spec.rb +2 -2
- data/spec/railtie_spec.rb +10 -0
- data/spec/spec_helper.rb +4 -3
- data/spec/unit/active_model_compliance_spec.rb +7 -3
- data/spec/unit/attributes_spec.rb +1 -1
- data/spec/unit/caching_spec.rb +105 -0
- data/spec/unit/couch_potato_spec.rb +70 -5
- data/spec/unit/create_spec.rb +5 -4
- data/spec/unit/database_spec.rb +235 -135
- data/spec/unit/dirty_attributes_spec.rb +5 -26
- data/spec/unit/flex_view_spec_spec.rb +17 -0
- data/spec/unit/model_view_spec_spec.rb +1 -1
- data/spec/unit/rspec_stub_db_spec.rb +31 -0
- data/spec/unit/validation_spec.rb +42 -2
- data/spec/views_spec.rb +214 -103
- data/vendor/pouchdb-collate/LICENSE +202 -0
- data/vendor/pouchdb-collate/pouchdb-collate.js +430 -0
- metadata +46 -42
- data/.ruby-version +0 -1
- data/.travis.yml +0 -21
- data/gemfiles/active_support_4_0 +0 -11
- data/gemfiles/active_support_4_1 +0 -11
- data/gemfiles/active_support_4_2 +0 -11
- data/lib/couch_potato/persistence/deep_dirty_attributes.rb +0 -180
- 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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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({:
|
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
|
97
|
+
block&.call document
|
79
98
|
save_document_without_conflict_handling(document, validate)
|
80
|
-
rescue CouchRest::Conflict
|
99
|
+
rescue CouchRest::Conflict
|
81
100
|
if block
|
82
101
|
handle_write_conflict document, validate, retries, &block
|
83
102
|
else
|
84
|
-
raise CouchPotato::Conflict
|
103
|
+
raise CouchPotato::Conflict
|
85
104
|
end
|
86
105
|
end
|
87
106
|
end
|
88
|
-
|
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
|
111
|
+
save_document(document) || raise(ValidationsFailedError, document.errors.full_messages)
|
93
112
|
end
|
94
|
-
|
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
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
118
|
-
|
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
|
-
|
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
|
-
|
142
|
-
|
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(©_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(©_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
|
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[
|
178
|
-
docs.each
|
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
|
189
|
-
return false if
|
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
|
196
|
-
return false if
|
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
|
209
|
-
return false if
|
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
|
216
|
-
return false if
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
-
|
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
|
-
|
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 :
|
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 [
|
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
|
-
|
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
|
-
|
12
|
+
clear_attribute_changes [:created_at]
|
13
13
|
model.updated_at ||= (Time.zone || Time).now
|
14
|
-
|
14
|
+
clear_attribute_changes [:updated_at]
|
15
15
|
}
|
16
16
|
before_update lambda {|model|
|
17
17
|
model.updated_at = (Time.zone || Time).now
|
18
|
-
|
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
|
-
|
89
|
-
|
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
|
-
|
95
|
-
active_support_module
|
96
|
-
|
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
|
@@ -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
|
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
|
-
|
32
|
-
|
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
|
-
|
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'
|
data/lib/couch_potato/railtie.rb
CHANGED
@@ -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
|
10
|
-
|
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
|
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 |
|
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
|
data/lib/couch_potato/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
[
|
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
|
-
|
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
|
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}"
|
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
|