montage_rails 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +28 -0
  4. data/lib/montage_rails/base/column.rb +57 -0
  5. data/lib/montage_rails/base.rb +406 -0
  6. data/lib/montage_rails/errors.rb +7 -0
  7. data/lib/montage_rails/log_subscriber.rb +48 -0
  8. data/lib/montage_rails/query_cache.rb +44 -0
  9. data/lib/montage_rails/railtie.rb +0 -0
  10. data/lib/montage_rails/relation.rb +88 -0
  11. data/lib/montage_rails/version.rb +3 -0
  12. data/lib/montage_rails.rb +57 -0
  13. data/lib/tasks/montage_rails_tasks.rake +4 -0
  14. data/test/dummy/README.rdoc +28 -0
  15. data/test/dummy/Rakefile +6 -0
  16. data/test/dummy/app/assets/javascripts/application.js +13 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  18. data/test/dummy/app/controllers/application_controller.rb +5 -0
  19. data/test/dummy/app/helpers/application_helper.rb +2 -0
  20. data/test/dummy/app/models/actor.rb +3 -0
  21. data/test/dummy/app/models/movie.rb +30 -0
  22. data/test/dummy/app/models/studio.rb +3 -0
  23. data/test/dummy/app/models/test.rb +2 -0
  24. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  25. data/test/dummy/bin/bundle +3 -0
  26. data/test/dummy/bin/rails +4 -0
  27. data/test/dummy/bin/rake +4 -0
  28. data/test/dummy/bin/setup +29 -0
  29. data/test/dummy/config/application.rb +25 -0
  30. data/test/dummy/config/boot.rb +5 -0
  31. data/test/dummy/config/database.yml +25 -0
  32. data/test/dummy/config/environment.rb +5 -0
  33. data/test/dummy/config/environments/development.rb +41 -0
  34. data/test/dummy/config/environments/production.rb +79 -0
  35. data/test/dummy/config/environments/test.rb +42 -0
  36. data/test/dummy/config/initializers/assets.rb +11 -0
  37. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  39. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  40. data/test/dummy/config/initializers/inflections.rb +16 -0
  41. data/test/dummy/config/initializers/mime_types.rb +4 -0
  42. data/test/dummy/config/initializers/montage.rb +4 -0
  43. data/test/dummy/config/initializers/session_store.rb +3 -0
  44. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/test/dummy/config/locales/en.yml +23 -0
  46. data/test/dummy/config/routes.rb +56 -0
  47. data/test/dummy/config/secrets.yml +22 -0
  48. data/test/dummy/config.ru +4 -0
  49. data/test/dummy/db/development.sqlite3 +0 -0
  50. data/test/dummy/db/schema.rb +16 -0
  51. data/test/dummy/db/test.sqlite3 +0 -0
  52. data/test/dummy/log/RAILS_ENV=development.log +0 -0
  53. data/test/dummy/log/development.log +1038 -0
  54. data/test/dummy/log/test.log +12368 -0
  55. data/test/dummy/public/404.html +67 -0
  56. data/test/dummy/public/422.html +67 -0
  57. data/test/dummy/public/500.html +66 -0
  58. data/test/dummy/public/favicon.ico +0 -0
  59. data/test/montage_rails/base/column_test.rb +59 -0
  60. data/test/montage_rails/base_test.rb +375 -0
  61. data/test/montage_rails/query_cache_test.rb +68 -0
  62. data/test/montage_rails/relation_test.rb +114 -0
  63. data/test/montage_rails_test.rb +97 -0
  64. data/test/resources/actor_resource.rb +141 -0
  65. data/test/resources/movie_resource.rb +160 -0
  66. data/test/resources/studio_resource.rb +56 -0
  67. data/test/test_helper.rb +196 -0
  68. metadata +123 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c5c2bc4fde46dd63c234dc588be50b964f28174
4
- data.tar.gz: 4194ed623e630feddb105b251dbc175627d5eeb4
3
+ metadata.gz: 277cfc7ce7155045e893a43b55fb439cefcc1fb1
4
+ data.tar.gz: 0b8338daff90f24f126bf6d18daac01205b2a8e8
5
5
  SHA512:
6
- metadata.gz: 6e78ab4b49d5a76751ce218d8639659f1519a853b554330b535ae338c8fb8e56813f1ed09dcae1e0db914af74c4626b0421f5d7faf2110616bc2077946ba275b
7
- data.tar.gz: bfefffaa585a50703b62c89ea77ee293d973ea53d357becc5bb26d24e5e2ebb44bde279272e7c00ce3ffafdd2d840a277b0df1e5f3058cc61a58a00b36b6f431
6
+ metadata.gz: 87b3d4b9ac86ea2310c782091869252e94f23426f2cf226ef23e0fbfc7b27fd8d3f12941731b7226310ba624510a494203ed0d89695b2e4e28e9f7deb9c5166e
7
+ data.tar.gz: a5a907dceb7a233c2ac321c4984c24a193c91292dc5be1263f473278d0aa6e2f905dd3a9df19aca4b8fbcffd97f5394ce52cc745a3c449ae26223e7813d94a44
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 dphaener
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'MontageRails'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ Bundler::GemHelper.install_tasks
18
+
19
+ require 'rake/testtask'
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.libs << 'lib'
23
+ t.libs << 'test'
24
+ t.pattern = 'test/**/*_test.rb'
25
+ t.verbose = false
26
+ end
27
+
28
+ task default: :test
@@ -0,0 +1,57 @@
1
+ module MontageRails
2
+ class Base
3
+ class Column
4
+ TYPE_MAP = {
5
+ "integer" => Integer,
6
+ "float" => Float,
7
+ "text" => String,
8
+ "date" => Date,
9
+ "time" => Time,
10
+ "datetime" => DateTime,
11
+ "numeric" => Numeric
12
+ }
13
+
14
+ attr_accessor :name, :type, :required
15
+
16
+ alias_method :required?, :required
17
+
18
+ def initialize(name, type, required = false)
19
+ @name = name
20
+ @type = type
21
+ @required = required
22
+ end
23
+
24
+ def value_valid?(value)
25
+ !(required? && value.nil?)
26
+ end
27
+
28
+ # Determines if the string value passed in is an integer
29
+ # Returns true or false
30
+ #
31
+ def is_i?(value)
32
+ /\A\d+\z/ =~ value
33
+ end
34
+
35
+ # Determines if the string value passed in is a float
36
+ # Returns true or false
37
+ #
38
+ def is_f?(value)
39
+ /\A\d+\.\d+\z/ =~ value
40
+ end
41
+
42
+ def coerce(value)
43
+ return value if value.is_a?(TYPE_MAP[type])
44
+
45
+ if is_i?(value)
46
+ coerce_to = Integer
47
+ elsif is_f?(value)
48
+ coerce_to = Float
49
+ else
50
+ coerce_to = TYPE_MAP[type]
51
+ end
52
+
53
+ Virtus::Attribute.build(coerce_to).coerce(value)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,406 @@
1
+ require 'montage_rails/log_subscriber'
2
+ require 'montage_rails/relation'
3
+ require 'montage_rails/base/column'
4
+ require 'active_model'
5
+ require 'virtus'
6
+
7
+ module MontageRails
8
+ class Base
9
+ extend ActiveModel::Callbacks
10
+ include ActiveModel::Model
11
+ include Virtus.model
12
+
13
+ define_model_callbacks :save, :create
14
+
15
+ class << self
16
+ # Delegates all of the relation methods to the class level object, so they can be called on the base class
17
+ #
18
+ delegate :limit, :offset, :order, :where, :first, to: :relation
19
+
20
+ # Delegate the connection to the base module for ease of reference
21
+ #
22
+ delegate :connection, :notify, to: MontageRails
23
+
24
+ cattr_accessor :table_name
25
+
26
+ # Define a new instance of the query cache
27
+ #
28
+ def cache
29
+ @cache ||= QueryCache.new
30
+ end
31
+
32
+ # Hook into the Rails logger
33
+ #
34
+ def logger
35
+ @logger ||= Rails.logger
36
+ end
37
+
38
+ # Setup a class level instance of the MontageRails::Relation object
39
+ #
40
+ def relation
41
+ @relation = Relation.new(self)
42
+ end
43
+
44
+ # Define a has_many relationship
45
+ #
46
+ def has_many(table)
47
+ class_eval do
48
+ define_method(table.to_s.tableize.to_sym) do
49
+ table.to_s.classify.constantize.where("#{self.class.table_name.demodulize.underscore.singularize.foreign_key} = #{id}")
50
+ end
51
+ end
52
+ end
53
+
54
+ # Define a belongs_to relationship
55
+ #
56
+ def belongs_to(table)
57
+ class_eval do
58
+ define_method(table.to_s.tableize.singularize.to_sym) do
59
+ table.to_s.classify.constantize.find_by_id(__send__(table.to_s.foreign_key))
60
+ end
61
+
62
+ define_method("#{table.to_s.tableize.singularize}=") do |record|
63
+ self.__send__("#{table.to_s.foreign_key}=", record.id)
64
+ self
65
+ end
66
+ end
67
+ end
68
+
69
+ # The pluralized table name used in API requests
70
+ #
71
+ def table_name
72
+ self.name.demodulize.underscore.pluralize
73
+ end
74
+
75
+ # Redefine the table name
76
+ #
77
+ def set_table_name(value)
78
+ instance_eval do
79
+ define_singleton_method(:table_name) do
80
+ value
81
+ end
82
+ end
83
+ end
84
+
85
+ alias_method :table_name=, :set_table_name
86
+
87
+ # Returns an array of MontageRails::Base::Column's for the schema
88
+ #
89
+ def columns
90
+ @columns ||= [].tap do |ary|
91
+ response = connection.schema(table_name)
92
+
93
+ return [] unless response.schema.respond_to?(:fields)
94
+
95
+ ary << Column.new("id", "text", false)
96
+ ary << Column.new("created_at", "datetime", false)
97
+ ary << Column.new("updated_at", "datetime", false)
98
+
99
+ response.schema.fields.each do |field|
100
+ ary << Column.new(field["name"], field["datatype"], field["required"])
101
+
102
+ instance_eval do
103
+ define_singleton_method("find_by_#{field["name"]}") do |value|
104
+ where("#{field["name"]} = '#{value}'").first
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ # Fetch all the documents
112
+ #
113
+ def all
114
+ relation.to_a
115
+ end
116
+
117
+ # Find a record by the id
118
+ #
119
+ def find_by_id(value)
120
+ response = cache.get_or_set_query(self, value) { connection.document(table_name, value) }
121
+
122
+ if response.success?
123
+ new(response.document.items.merge(persisted: true))
124
+ else
125
+ nil
126
+ end
127
+ end
128
+
129
+ alias_method :find, :find_by_id
130
+
131
+ # Find the record using the given params, or initialize a new one with those params
132
+ #
133
+ def find_or_initialize_by(params = {})
134
+ return nil if params.empty?
135
+
136
+ query = relation.where(params)
137
+
138
+ response = cache.get_or_set_query(self, query) { connection.documents(table_name, query) }
139
+
140
+ if response.success? && response.documents.any?
141
+ new(attributes_from_response(response).merge(persisted: true))
142
+ else
143
+ new(params)
144
+ end
145
+ end
146
+
147
+ # Returns an array of the column names for the table
148
+ #
149
+ def column_names
150
+ columns.map { |c| c.name }
151
+ end
152
+
153
+ # Initialize and save a new instance of the object
154
+ #
155
+ def create(params = {})
156
+ new(params).save
157
+ end
158
+
159
+ # Returns a string like 'Post id:integer, title:string, body:text'
160
+ #
161
+ def inspect
162
+ if self == Base
163
+ super
164
+ else
165
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
166
+ "#{super}(#{attr_list})"
167
+ end
168
+ end
169
+
170
+ def method_missing(method_name, *args, &block)
171
+ __send__(:columns)
172
+
173
+ if respond_to?(method_name.to_sym)
174
+ __send__(method_name.to_sym, *args)
175
+ else
176
+ super(method_name, *args, &block)
177
+ end
178
+ end
179
+
180
+ def respond_to_missing?(method_name, include_private = false)
181
+ __send__(:column_names).include?(method_name.to_s.split("_").first) || super(method_name, include_private)
182
+ end
183
+
184
+ def attributes_from_response(response)
185
+ case response.members
186
+ when Montage::Documents then response.documents.first.attributes.merge(persisted: true)
187
+ when Montage::Document then response.document.attributes.merge(persisted: true)
188
+ when Montage::Errors then raise MontageAPIError, "There was an error with the Montage API: #{response.errors.attributes}"
189
+ when Montage::Error then raise MontageAPIError, "There was an error with the Montage API: #{response.error.attributes}"
190
+ else raise MontageAPIError, "There was an error with the Montage API, please try again."
191
+ end
192
+ end
193
+ end
194
+
195
+ attr_accessor :persisted
196
+
197
+ alias_method :persisted?, :persisted
198
+
199
+ delegate :connection, :notify, to: MontageRails
200
+ delegate :attributes_from_response, to: "self.class"
201
+
202
+ def initialize(params = {})
203
+ initialize_columns
204
+ @persisted = params[:persisted] ? params[:persisted] : false
205
+ @current_method = "Load"
206
+ super(params)
207
+ end
208
+
209
+ # Save the record to the database
210
+ #
211
+ # Will return nil if the attributes are not valid
212
+ #
213
+ # Upon successful creation or update, will return an instance of self, otherwise returns nil
214
+ #
215
+ def save
216
+ run_callbacks :save do
217
+ return nil unless attributes_valid?
218
+
219
+ if persisted?
220
+ @current_method = "Update"
221
+
222
+ response = notify(self) do
223
+ connection.create_or_update_documents(self.class.table_name, [updateable_attributes(true)])
224
+ end
225
+ else
226
+ @current_method = "Create"
227
+
228
+ response = notify(self) do
229
+ connection.create_or_update_documents(self.class.table_name, [updateable_attributes(false)])
230
+ end
231
+ end
232
+
233
+ if response.success?
234
+ if persisted?
235
+ initialize(attributes_from_response(response))
236
+ else
237
+ run_callbacks :create do
238
+ initialize(attributes_from_response(response))
239
+ end
240
+ end
241
+
242
+ self
243
+ else
244
+ response
245
+ end
246
+ end
247
+ end
248
+
249
+ # The bang method for save, which will raise an exception if saving is not successful
250
+ #
251
+ def save!
252
+ unless save
253
+ raise MontageAPIError, response.errors.attributes
254
+ end
255
+ end
256
+
257
+ # Update the given attributes for the document
258
+ #
259
+ # Returns false if the given attributes aren't valid
260
+ #
261
+ # Returns a copy of self if updating is successful
262
+ #
263
+ def update_attributes(params)
264
+ old_attributes = attributes.clone
265
+
266
+ params.each do |key, value|
267
+ if respond_to?(key.to_sym)
268
+ coerced_value = column_for(key.to_s).coerce(value)
269
+ send("#{key}=", coerced_value)
270
+ end
271
+ end
272
+
273
+ return self if old_attributes == attributes
274
+
275
+ if attributes_valid?
276
+ @current_method = id.nil? ? "Create" : "Update"
277
+
278
+ response = notify(self) do
279
+ connection.create_or_update_documents(self.class.table_name, [updateable_attributes(!id.nil?)])
280
+ end
281
+
282
+ initialize(attributes_from_response(response))
283
+ @persisted = true
284
+ self
285
+ else
286
+ initialize(old_attributes)
287
+ false
288
+ end
289
+ end
290
+
291
+ # Destroy the copy of this record from the database
292
+ #
293
+ def destroy
294
+ @current_method = "Delete"
295
+ notify(self) { connection.delete_document(self.class.table_name, id) }
296
+
297
+ @persisted = false
298
+ self
299
+ end
300
+
301
+ # Reload the current document
302
+ #
303
+ def reload
304
+ @current_method = "Load"
305
+
306
+ response = notify(self) do
307
+ connection.document(self.class.table_name, id)
308
+ end
309
+
310
+ initialize(attributes_from_response(response))
311
+ @persisted = true
312
+ self
313
+ end
314
+
315
+ def new_record?
316
+ !persisted?
317
+ end
318
+
319
+ # Returns the Column class instance for the attribute passed in
320
+ #
321
+ def column_for(name)
322
+ self.class.columns.select { |column| column.name == name }.first
323
+ end
324
+
325
+ # Performs a check to ensure that required columns have a value
326
+ #
327
+ def attributes_valid?
328
+ attributes.each do |key, value|
329
+ next unless column_class = column_for(key.to_s)
330
+ return false unless column_class.value_valid?(value)
331
+ end
332
+ end
333
+
334
+ # The attributes used to update the document
335
+ #
336
+ def updateable_attributes(include_id = false)
337
+ include_id ? attributes.except(:created_at, :updated_at) : attributes.except(:created_at, :updated_at, :id)
338
+ end
339
+
340
+ # Required for notifications to work, returns a payload suitable
341
+ # for the log subscriber
342
+ #
343
+ def payload
344
+ {
345
+ reql: reql_payload[@current_method],
346
+ name: "#{self.class.name} #{@current_method}"
347
+ }
348
+ end
349
+
350
+ # Returns an <tt>#inspect</tt>-like string for the value of the
351
+ # attribute +attr_name+. String attributes are elided after 50
352
+ # characters, and Date and Time attributes are returned in the
353
+ # <tt>:db</tt> format. Other attributes return the value of
354
+ # <tt>#inspect</tt> without modification.
355
+ #
356
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
357
+ #
358
+ # person.attribute_for_inspect(:name)
359
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
360
+ #
361
+ # person.attribute_for_inspect(:created_at)
362
+ # # => '"2009-01-12 04:48:57"'
363
+ #
364
+ def attribute_for_inspect(attr_name)
365
+ value = attributes[attr_name]
366
+
367
+ if value.is_a?(String) && value.length > 50
368
+ "#{value[0..50]}...".inspect
369
+ elsif value.is_a?(Date) || value.is_a?(Time)
370
+ %("#{value.to_s(:db)}")
371
+ else
372
+ value.inspect
373
+ end
374
+ end
375
+
376
+ # Returns the contents of the record as a nicely formatted string.
377
+ #
378
+ def inspect
379
+ attributes_as_nice_string = self.class.column_names.collect { |name|
380
+ if attributes[name.to_sym] || new_record?
381
+ "#{name}: #{attribute_for_inspect(name.to_sym)}"
382
+ end
383
+ }.compact.join(", ")
384
+ "#<#{self.class} #{attributes_as_nice_string}>"
385
+ end
386
+
387
+ private
388
+
389
+
390
+ def initialize_columns
391
+ self.class.columns.each do |column|
392
+ self.class.__send__(:attribute, column.name.to_sym, Column::TYPE_MAP[column.type])
393
+ end
394
+ end
395
+
396
+ def reql_payload
397
+ {
398
+ "Load" => id,
399
+ "Update" => "#{id}: #{updateable_attributes(true)}",
400
+ "Create" => updateable_attributes,
401
+ "Delete" => id,
402
+ "Save" => updateable_attributes
403
+ }
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,7 @@
1
+ module MontageRails
2
+ class AttributeMissingError < StandardError; end
3
+
4
+ class RelationError < StandardError; end
5
+
6
+ class MontageAPIError < StandardError; end
7
+ end
@@ -0,0 +1,48 @@
1
+ module MontageRails
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ def self.runtime=(value)
4
+ Thread.current["montage_rails_reql_runtime"] = value
5
+ end
6
+
7
+ def self.runtime
8
+ Thread.current["montage_rails_reql_runtime"] ||= 0
9
+ end
10
+
11
+ def self.reset_runtime
12
+ rt, self.runtime = runtime, 0
13
+ rt
14
+ end
15
+
16
+ def initialize
17
+ super
18
+ @odd_or_even = false
19
+ end
20
+
21
+ def reql(event)
22
+ self.class.runtime += event.duration
23
+ return unless logger.debug?
24
+
25
+ name = '%s (%.1fms)' % [event.payload[:name], event.duration]
26
+ reql = event.payload[:reql]
27
+
28
+ if odd?
29
+ name = color(name, CYAN, true)
30
+ reql = color(reql, nil, true)
31
+ else
32
+ name = color(name, MAGENTA, true)
33
+ end
34
+
35
+ debug " #{name} #{reql}"
36
+ end
37
+
38
+ def odd?
39
+ @odd_or_even = !@odd_or_even
40
+ end
41
+
42
+ def logger
43
+ MontageRails::Base.logger
44
+ end
45
+ end
46
+ end
47
+
48
+ MontageRails::LogSubscriber.attach_to :montage_rails
@@ -0,0 +1,44 @@
1
+ module MontageRails
2
+ class QueryCache
3
+ attr_reader :cache
4
+
5
+ def initialize
6
+ @cache = {}
7
+ end
8
+
9
+ def get_or_set_query(klass, query)
10
+ cached = cache.keys.include?("#{klass}/#{query}")
11
+ ActiveSupport::Notifications.instrument("reql.montage_rails", notification_payload(query, klass, cached)) do
12
+ if cached
13
+ cache["#{klass}/#{query}"]
14
+ else
15
+ response = yield
16
+ cache["#{klass}/#{query}"] = response
17
+ response
18
+ end
19
+ end
20
+ end
21
+
22
+ # Clear the entire query cache
23
+ #
24
+ def clear
25
+ @cache = {}
26
+ end
27
+
28
+ # Remove a certain key from the cache
29
+ # Returns the removed value, or nil if nothin was found
30
+ #
31
+ def remove(key)
32
+ cache.delete(key)
33
+ end
34
+
35
+ private
36
+
37
+ def notification_payload(query, klass, cached = false)
38
+ {
39
+ reql: query,
40
+ name: cached ? "#{klass} Load [CACHE]" : "#{klass} Load"
41
+ }
42
+ end
43
+ end
44
+ end
File without changes