couch_potato 1.6.4 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +38 -0
  3. data/.gitignore +3 -0
  4. data/CHANGES.md +193 -122
  5. data/Gemfile +4 -0
  6. data/README.md +61 -85
  7. data/Rakefile +11 -10
  8. data/couch_potato-rspec.gemspec +3 -2
  9. data/couch_potato.gemspec +9 -7
  10. data/gemfiles/active_support_5_0 +7 -0
  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 +168 -71
  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/revisions.rb +14 -0
  20. data/lib/couch_potato/persistence/simple_property.rb +0 -4
  21. data/lib/couch_potato/persistence/type_caster.rb +11 -6
  22. data/lib/couch_potato/persistence.rb +5 -3
  23. data/lib/couch_potato/railtie.rb +7 -12
  24. data/lib/couch_potato/validation.rb +8 -0
  25. data/lib/couch_potato/version.rb +2 -2
  26. data/lib/couch_potato/view/base_view_spec.rb +8 -32
  27. data/lib/couch_potato/view/custom_views.rb +4 -3
  28. data/lib/couch_potato/view/flex_view_spec.rb +121 -0
  29. data/lib/couch_potato/view/view_parameters.rb +34 -0
  30. data/lib/couch_potato.rb +37 -16
  31. data/spec/callbacks_spec.rb +45 -19
  32. data/spec/conflict_handling_spec.rb +1 -2
  33. data/spec/property_spec.rb +12 -3
  34. data/spec/railtie_spec.rb +17 -1
  35. data/spec/revisions_spec.rb +25 -0
  36. data/spec/spec_helper.rb +4 -3
  37. data/spec/unit/active_model_compliance_spec.rb +7 -3
  38. data/spec/unit/attributes_spec.rb +54 -1
  39. data/spec/unit/caching_spec.rb +105 -0
  40. data/spec/unit/couch_potato_spec.rb +70 -5
  41. data/spec/unit/create_spec.rb +5 -4
  42. data/spec/unit/database_spec.rb +235 -135
  43. data/spec/unit/dirty_attributes_spec.rb +5 -26
  44. data/spec/unit/flex_view_spec_spec.rb +17 -0
  45. data/spec/unit/model_view_spec_spec.rb +1 -1
  46. data/spec/unit/rspec_stub_db_spec.rb +31 -0
  47. data/spec/unit/validation_spec.rb +42 -2
  48. data/spec/views_spec.rb +214 -103
  49. data/vendor/pouchdb-collate/LICENSE +202 -0
  50. data/vendor/pouchdb-collate/pouchdb-collate.js +430 -0
  51. metadata +46 -33
  52. data/.ruby-version +0 -1
  53. data/.travis.yml +0 -20
  54. data/gemfiles/active_support_4_0 +0 -11
  55. data/gemfiles/active_support_4_1 +0 -11
  56. data/gemfiles/active_support_4_2 +0 -11
  57. data/lib/couch_potato/persistence/deep_dirty_attributes.rb +0 -180
  58. 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') 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,17 +159,94 @@ 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)
148
- document = document.reload
243
+ cache&.clear
149
244
  if retries == 5
150
- raise CouchPotato::Conflict.new
245
+ raise CouchPotato::Conflict
151
246
  else
247
+ reloaded = document.reload
248
+ document.attributes = reloaded.attributes
249
+ document._rev = reloaded._rev
152
250
  save_document document, validate, retries + 1, &block
153
251
  end
154
252
  end
@@ -172,10 +270,10 @@ module CouchPotato
172
270
 
173
271
  def bulk_load(ids)
174
272
  response = couchrest_database.bulk_load ids
175
- docs = response['rows'].map{|row| row["doc"]}.compact
176
- docs.each{|doc|
273
+ docs = response['rows'].map { |row| row['doc'] }.compact
274
+ docs.each do |doc|
177
275
  doc.database = self if doc.respond_to?(:database=)
178
- }
276
+ end
179
277
  end
180
278
 
181
279
  def create_document(document, validate)
@@ -183,51 +281,50 @@ module CouchPotato
183
281
 
184
282
  if validate
185
283
  document.errors.clear
186
- return false if false == document.run_callbacks(:validation_on_save) do
187
- 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
188
286
  return false unless valid_document?(document)
189
- end
190
- end
287
+ end == false
288
+ end == false
191
289
  end
192
290
 
193
- return false if false == document.run_callbacks(:save) do
194
- 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
195
293
  res = couchrest_database.save_doc document.to_hash
196
294
  document._rev = res['rev']
197
295
  document._id = res['id']
198
- end
199
- end
296
+ end == false
297
+ end == false
298
+
200
299
  true
201
300
  end
202
301
 
203
302
  def update_document(document, validate)
204
303
  if validate
205
304
  document.errors.clear
206
- return false if false == document.run_callbacks(:validation_on_save) do
207
- 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
208
307
  return false unless valid_document?(document)
209
- end
210
- end
308
+ end == false
309
+ end == false
211
310
  end
212
311
 
213
- return false if false == document.run_callbacks(:save) do
214
- return false if false == document.run_callbacks(:update) do
215
- if document.dirty?
216
- res = couchrest_database.save_doc document.to_hash
217
- document._rev = res['rev']
218
- end
219
- end
220
- 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
+
221
319
  true
222
320
  end
223
321
 
224
322
  def valid_document?(document)
225
- errors = document.errors.errors.dup
226
- errors.instance_variable_set("@messages", errors.messages.dup) if errors.respond_to?(:messages)
323
+ original_errors_hash = document.errors.to_hash
227
324
  document.valid?
228
- errors.each do |k, v|
325
+ original_errors_hash.each do |k, v|
229
326
  if v.respond_to?(:each)
230
- v.each {|message| document.errors.add(k, message)}
327
+ v.each { |message| document.errors.add(k, message) }
231
328
  else
232
329
  document.errors.add(k, v)
233
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
@@ -0,0 +1,14 @@
1
+ module CouchPotato
2
+ module Persistence
3
+ module Revisions
4
+ # returns all available revisions of a document, first to last.
5
+ # causes n+1 requests. do not use in production code.
6
+ def _revisions
7
+ with_revs = database.couchrest_database.get(id, revs: true, revs_info: true)._document
8
+ revs_info = with_revs[:_revs_info]
9
+ revs = revs_info.select {|info| info[:status] == 'available' }.map {|info| info[:rev] }
10
+ revs.reverse.map {|rev| database.couchrest_database.get(id, rev: rev) }
11
+ end
12
+ end
13
+ end
14
+ 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,14 +31,17 @@ module CouchPotato
28
31
 
29
32
  def cast_native(value, type)
30
33
  if type && !value.is_a?(type)
31
- if type == Fixnum
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?
43
+ elsif type.ancestors.include?(CouchPotato::Persistence)
44
+ type.new value unless value.blank?
39
45
  else
40
46
  type.json_create value unless value.blank?
41
47
  end
@@ -43,7 +49,6 @@ module CouchPotato
43
49
  value
44
50
  end
45
51
  end
46
-
47
52
  end
48
53
  end
49
54
  end
@@ -6,10 +6,10 @@ 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'
12
+ require File.dirname(__FILE__) + '/persistence/revisions'
13
13
  require File.dirname(__FILE__) + '/forbidden_attributes_protection'
14
14
  require File.dirname(__FILE__) + '/view/custom_views'
15
15
  require File.dirname(__FILE__) + '/view/lists'
@@ -20,9 +20,11 @@ module CouchPotato
20
20
  module Persistence
21
21
 
22
22
  def self.included(base) #:nodoc:
23
- base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews, CouchPotato::View::Lists
23
+ base.send :include, Properties, Callbacks, Json, CouchPotato::View::CustomViews,
24
+ CouchPotato::View::Lists
24
25
  base.send :include, DirtyAttributes, GhostAttributes, Attachments
25
- base.send :include, MagicTimestamps, ActiveModelCompliance, ForbiddenAttributesProtection
26
+ base.send :include, MagicTimestamps, ActiveModelCompliance,
27
+ ForbiddenAttributesProtection, Revisions
26
28
  base.send :include, Validation
27
29
  base.class_eval do
28
30
  attr_accessor :_id, :_rev, :_deleted, :database
@@ -1,28 +1,23 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../rails/reload_classes')
1
+ # frozen_string_literal: true
2
+
2
3
  require 'erb'
3
4
 
4
5
  module CouchPotato
5
6
  def self.rails_init
7
+ require File.expand_path(File.dirname(__FILE__) + '/../../rails/reload_classes') if Rails.env.development?
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.6.4'
3
- RSPEC_VERSION = '3.0.0'
2
+ VERSION = '1.9.0'.freeze
3
+ RSPEC_VERSION = '3.4.0'.freeze
4
4
  end