In Ruby, you can create a class (Model) like this:
class User < ActiveRecord::Base
before_save :do_something
def save
# do something will get called here magically
...
end
def do_something
end
end
Now say in scala I want to do the same thing, is this possible?
Basically whenever any method is called in a scala class, do_something will get called before it.
Note: You could add more methods to get called like:
before_save :do_something, do_something_else
Take a look at the callbacks hooks of the Scala ActiveRecord library.
Scala ActiveRecord provides callback hooks for executing actions
before or after the model is saved, deleted, or validated. You can
override callback methods and implement logic, if necessary. Nothing
is done by default.
Related
In a Rails app, I'm using an after_update call back that runs multiple methods upon passing a conditional method such as below:
app/models/my_model.rb
class MyModel < ApplicationRecord
after_update :method_1, :method_2, :method_3, if: :this_happens?
#some custom methods
private
def this_happens?
# a condition that returns true or false here
end
end
I've noticed that the method this_happens? is executed three times, just before :method_1, :method_2, :method_3.
This make sense as any of those three call back methods could change the data in such a way that the condition has to be evaluated again to make sure it's met in every case. However, when you know those 3 methods are not changing the data in any way that could alter the condition evaluation, is it possible to run this_happens? only once and gain some efficiency?
I don't seem to find anything online and wonder if anyone could advise.
PS. using Rails 5.1
Encapsulation is the one of the easiest way that you can choose to overcome this situation.
Just wrap your methods in another one, then it will only check this_happens? one time.
class MyModel < ApplicationRecord
after_update :combine_methods, if: :this_happens?
#some custom methods
private
def combine_methods
method_1
method_2
method_3
end
def this_happens?
# a condition that returns true or false here
end
end
Consider my model to be like this:
class SampleProject < ActiveRecord::Base
#consider all these callbacks to be custom defined ones
#I'm using default callbacks just to explain my problem
after_save :simple_testing
after_update :simple_testing
before_save :simple_testing
before_create :simple_testing
after_commit :simple_testing
def simple_testing
#my custom code
end
end
So here the same method is being invoked after all the call backs. So is there any simple way to define that all this callbacks should evoke simple_testing method ?
I don't understand why you would ever want to do this, but remember you can use Ruby code anywhere in your class definition.
callbacks = [:after_save, :after_update, :before_save, :before_create, after_commit]
callbacks.each do |callback|
self.send(callback, :simple_testing)
end
I've not tried this but it should work i think.
I'm not phrasing the question correctly in the title, but heres what I'd like to do.
I have a method, like such:
class User < ActiveRecord::Base
def myMethod(abc, xyz)
#do stuff
end
end
I want 5 different models in my app to call this function on their after_create callback.
It seems very anti-DRY to put a function in each of those models to call this function.
Is there a way in this model (above) that holds the method - to remotely use the callbacks of the other models?
Or can anyone suggest a different way I should be approaching something like this?
That's what I would do:
Create a module:
module MyCallbacks
extend ActiveSupport::Concern
included do
after_create :my_method
end
def my_method
#do stuff
end
end
And then, you just need to include this module in the models of your choice:
class MyModel < ActiveRecord::Base
include MyCallbacks
end
I am having an ActiveRecord model with a polymorphic association like this:
class Reach < ActiveRecord::Base
belongs_to :reachable, :polymorphic => true
end
This model acts like a proxy. What I need to do is to forward all method calls on that object to the associated object :reachable. I think delegate won't help here because I have to explicitly name all the methods I need to delegate. I need something like delegate :all to delegate all methods (not all method).
Since Rails 5.1+ you can delegate everything not implemented with delegate_missing_to :reachable
Basically, do what you expect.
You could read more on the Api Doc
If you are stuck in a previous version then just recommend using the method_missing from #Veraticus answer, is less performance-wise as mentioned but I think is the more flexible approach.
There are two things you can do here:
The slower (performance-wise) but easier method is to use method_missing:
class Reach < ActiveRecord::Base
def method_missing(method, *args)
return reachable.send(method, *args) if reachable.respond_to?(method)
super
end
end
The faster performing method would be to define each method dynamically that you want to delegate:
class Reach < ActiveRecord::Base
[:all, :my, :methods, :here].each do |m|
define_method(m) do |*args|
reachable.send(m, *args)
end
end
end
You could even use that method in a more dynamic manner, if you wanted, by taking the Reach class, finding the methods that are defined on it and it alone, and defining only those on Reachable. I would do it by hand though because there are some you probably won't want to include.
For Rails, I did the following:
class User < ApplicationRecord
has_one :member
delegate (Member.new.attributes.keys - User.new.attributes.keys), to: :member
end
the - User.new... is to not override existing attributes on User (e.g., created_at)
I'm not sure how this approach would work with polymorphism, however.
I have found a neat way to approach the problem using refinements. There already is a class in the standard library that allows the delegation of every method call to a target object. Delegator and by extension SimpleDelegator
Now there is a way to insert SimpleDelegator into your inheritance chain without inheriting from it directly using refinements:
def self.include_delegator
mod = Module.new do
include refine(SimpleDelegator) { yield if block_given? }
end
self.send :include, mod
end
include_delegator
Now in order to take advantage of SimpleDelegator set the delegation target in an after initialize callback like so:
after_initialize do |instance|
__setobj__(instance.reachable)
end
This is equivalent to inheriting directly from SimpleDelegator and setting the delegation in the construction, there is no manual housekeeping of methods to delegate and you can avoid using method missing.
I have an after_destroy model callback that regenerates cache after the model instance has been destroyed. It does this by calling open("http://domain.com/page-to-cache") for as many pages as need to be re-cached.
The problem is that the model instance apparently isn't fully destroyed yet at this time, because those open url requests still register its presence, and the regenerated cache looks exactly like the pre-destroy cache. How can I run those calls after the model instance has been actually destroyed?
You may be able to use an after_commit callback to do something after the entire transaction has gone through to the database. This is different depending on the version of Rails you're using (2.3.x versus 3.x.x), but is essentially something like the following:
# model_observer.rb
class ModelObserver < ActiveRecord::Observer
def after_commit(instance)
do_something if instance.destroyed?
end
end
You can read some documentation about the Rails 3 after_commit callback here. If your version of Rails doesn't have an after_commit hook, you can try using this gem which will provide the functionality.
You could try adding an after_save callback like:
after_save :my_after_save_callback
def my_after_save_callback
do_something if destroyed?
end