Module: Guacamole::Callbacks

Extended by:
ActiveSupport::Concern
Included in:
DefaultCallback
Defined in:
lib/guacamole/callbacks.rb

Overview

Note:

Wether you define a callback class for your model or not, internally there always will be a callback.

Define callbacks for different life cycle events of a model

Callbacks in Guacamole are defined in dedicated classes only. There will be no interface to implement them in the model or the collection context. This was done due to the nature of the data mapper pattern:

  • The model doesn't know anything about the database thus defining persistence related callbacks (i.e. before_create) would weaken this separation.
  • Validation happens in the context of the model and thus defining all callbacks in the collection would still be somewhat awkward.

Due to those reasons Guacamole employs the concept of external callbacks. Just define a class and include the Guacamole::Callbacks module and you can define all kinds of callbacks. Under the hood ActiveModel::Callbacks is used to provide the callback execution functionality.

Since maintaining the time stamp attributes of your models are implemented as callbacks as well your callback class will have those methods included.

Binding a callback class to a model will happen in the Model.callbacks method.

Each callback class will be instantiated with the appropriate model instance. That instance is accessible through the object method.

Examples:

Define a callback to hash the password prior creation

class UserCallbacks
  include Guacamole::Callbacks

  before_create :encrypt_password

  def encrypt_password
    object.encrypted_password = BCrypt::Password.create(object.password)
  end
end

Defined Under Namespace

Classes: CallbackProxy, DefaultCallback

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.after_create(method_name) ⇒ Object

Registers a method to be run after the creation of the model

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.after_delete(method_name) ⇒ Object

Registers a method to be run after the deletion of the model has happened

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.after_save(method_name) ⇒ Object

Registers a method to be run after the collection class has saved the model

Saving a model will always happen, no matter if the model will be created or updated.

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.after_update(method_name) ⇒ Object

Registers a method to be run after the model has been updated

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.after_validate(method_name) ⇒ Object

Registers a method to be run after the validation happened

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.around_create(method_name) ⇒ Object

Note:

You must yield at some point in the method.

Registers a method to be run before and after creating the model

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.around_delete(method_name) ⇒ Object

Note:

You must yield at some point in the method.

Registers a method to be run before and after the deletion

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.around_save(method_name) ⇒ Object

Note:

You must yield at some point in the method.

Registers a method to be run before and after saving the model

Saving a model will always happen, no matter if the model will be created or updated.

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.around_update(method_name) ⇒ Object

Note:

You must yield at some point in the method.

Registers a method to be run before and after updating the model

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.around_validate(method_name) ⇒ Object

Note:

You must yield at some point in the method.

Registers a method to be run before and after the validation will happen.

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.before_create(method_name) ⇒ Object

Registers a method to be run before initially creating the model in the database

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.before_delete(method_name) ⇒ Object

Registers a method to be run before deleting the model

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.before_save(method_name) ⇒ Object

Registers a method to be run before the collection class saves the model

Saving a model will always happen, no matter if the model will be created or updated.

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.before_update(method_name) ⇒ Object

Registers a method to be run before updating the model

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.before_validate(method_name) ⇒ Object

Registers a method to be run before the validation will happen

Parameters:

  • method_name (Symbol)

    The name of the method to be executed


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/guacamole/callbacks.rb', line 147

module Callbacks
  extend ActiveSupport::Concern
  # @!parse extend ActiveModel:Callbacks

  included do
    extend ActiveModel::Callbacks

    define_model_callbacks :validate, :save, :create, :update, :delete

    before_create :add_create_timestamps
    before_update :update_updated_at_timestamp
  end

  # The default callback to be used if no custom callback was defined. This is done because it simplifies
  # the callback invocation code and allows us to use callbacks for adding time stamps to the models.
  class DefaultCallback
    include Guacamole::Callbacks
  end

  # A proxy class around the callback class itself.
  #
  # The sole reason for its existence is to specify multiple callback runs at once. The alternative
  # would have been to nest the `run_callbacks` calls within the caller. It was decided to have bit
  # more complex proxy class to hide those details from the caller.
  #
  # @example
  #   callbacks = Callbacks.callbacks_for(model)
  #   callbacks.run_callbacks :save, :create do
  #     CakeCollection.create model
  #   end
  # @private
  class CallbackProxy
    attr_reader :callbacks

    # Create a new proxy with the original callbacks class as input
    #
    # @param [Callbacks] callbacks The original callback class to be executed
    def initialize(callbacks)
      @callbacks = callbacks
    end

    # Runs the given kinds of callbacks
    #
    # @param [Array<Symbol>] callbacks_to_run One or more kinds of callbacks to be run
    # @yield Will call the code block wrapped by the given callbacks
    def run_callbacks(*callbacks_to_run, &block)
      outer = callbacks_to_run.pop

      if callbacks_to_run.empty?
        @callbacks.run_callbacks(outer, &block)
      else
        @callbacks = run_callbacks(*callbacks_to_run) do
          @callbacks.run_callbacks(outer, &block)
        end
      end
    end
  end

  class << self
    # Register a callback class to be used with the model
    #
    # @api private
    # @param [Model] model_class The class of a model
    # @param [Callbacks] callback_class The class of the callbacks
    def register_callback(model_class, callback_class)
      registry[model_class] = callback_class
    end

    # Retrieve the callback instance for the given model
    #
    # @api private
    # @params [Model] model The model instance for which callbacks must be executed
    # @return [Callbacks] A callback instance with the given model accessible via `object`
    def callbacks_for(model)
      CallbackProxy.new registry[model.class].new(model)
    end

    # The internal storage of the callback-model pairs
    #
    # @api private
    # @return [Hash] A hash with the default set to the `DefaultCallback`
    def registry
      @registry ||= Hash.new(DefaultCallback)
    end
  end

  # Create a new callback instance with the given model instance
  #
  # @param [Model] model_instance The model instance the callbacks should be executed for
  def initialize(model_instance)
    @object = model_instance
  end

  # Provides access to the model instance.
  #
  # @retun [Model] A model instance
  # @api public
  def object
    @object
  end

  # Sets `created_at` and `updated_at` to `Time.now`
  def add_create_timestamps
    object.created_at = Time.now
    update_updated_at_timestamp
  end

  # Sets `updated_at` to `Time.now`
  def update_updated_at_timestamp
    object.updated_at = Time.now
  end
end

.callbacks_for(model) ⇒ Callbacks

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Retrieve the callback instance for the given model

Returns:

  • (Callbacks)

    A callback instance with the given model accessible via object


220
221
222
# File 'lib/guacamole/callbacks.rb', line 220

def callbacks_for(model)
  CallbackProxy.new registry[model.class].new(model)
end

.register_callback(model_class, callback_class) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Register a callback class to be used with the model

Parameters:

  • model_class (Model)

    The class of a model

  • callback_class (Callbacks)

    The class of the callbacks


211
212
213
# File 'lib/guacamole/callbacks.rb', line 211

def register_callback(model_class, callback_class)
  registry[model_class] = callback_class
end

.registryHash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The internal storage of the callback-model pairs

Returns:

  • (Hash)

    A hash with the default set to the DefaultCallback


228
229
230
# File 'lib/guacamole/callbacks.rb', line 228

def registry
  @registry ||= Hash.new(DefaultCallback)
end

Instance Method Details

#add_create_timestampsObject

Sets created_at and updated_at to Time.now


249
250
251
252
# File 'lib/guacamole/callbacks.rb', line 249

def add_create_timestamps
  object.created_at = Time.now
  update_updated_at_timestamp
end

#initialize(model_instance) ⇒ Object

Create a new callback instance with the given model instance

Parameters:

  • model_instance (Model)

    The model instance the callbacks should be executed for


236
237
238
# File 'lib/guacamole/callbacks.rb', line 236

def initialize(model_instance)
  @object = model_instance
end

#objectObject

Provides access to the model instance.


244
245
246
# File 'lib/guacamole/callbacks.rb', line 244

def object
  @object
end

#update_updated_at_timestampObject

Sets updated_at to Time.now


255
256
257
# File 'lib/guacamole/callbacks.rb', line 255

def update_updated_at_timestamp
  object.updated_at = Time.now
end