Call Delayed Job for an Api Controller - ruby-on-rails

I have the following controller
class V1::LpDeveloperReferralsController < V1::BaseController
def create
add(a, b).delay
end
def add(a, b)
end
end
I want to be able to call the add method in create method but run it as a delayed job.
At the moment, it doesnt do that.

Do you want to queue this Api call to make external call or to another endpoint?
You can try below if this helps u..
send_request - > can be your own method to make API call to external
object -> your controller instance
class ExampleRequestJob
def initialize(object)
#object = object
Delayed::Job.enqueue self
end
def perform
#response = #object.send_request().with_indifferent_access
end
def after(job)
# want to reattempt if failed
end
def error(job, exception)
#failed
end
def success(_job)
# your success
end
end

Related

Rails change two services with similar logic into one service

In my Rails6/Grape API app my controller starting to look a little bit heavy - there is a too much logic going so I wrap webhook distribution data into two services Activities::WebhookData and Journeys::WebhookData like below:
endpoint
post do
name = CmsClient.fetch_model_name(model_id)
#some other logic
name == 'Journey' ? ::Journeys::WebhookData.new(params).call : ::Activities::WebhookData.new(params).call
end
Journeys::WebhookData
module Journeys
class WebhookData
def initialize(webhook)
#webhook = webhook
end
attr_accessor :webhook
def call
case webhook[:event_type]
when 'publish'
JourneyWorker.perform_async(webhook)
when 'delete'
HideJourneyWorker.perform_async(webhook)
end
end
end
end
Activities::WebhookData
module Activities
class WebhookData
def initialize(webhook)
#webhook = webhook
end
attr_accessor :webhook
def call
case webhook[:event_type]
when 'publish'
ActivityWorker.perform_async(webhook)
when 'delete'
DeleteActivityWorker.perform_async(webhook)
end
end
end
end
As you see both are pretty much the same, is there a better way to merge these two services into one instead?
I don't advice you to move that to a single class, because both classes have a different way to interpret the webhook data they receive. If you do so you're going to replace that with some if/else conditions.
You can create a class where you define the call method to fetch the values from a hash given the event_type in the attr_reader, then create two other classes inheriting from the first one and just define a hash of its own with the according classes where you're going to invoke perform_async on:
class Webhook
def call
event_types[webhook[:event_type]].public_send(:perform_async, webhook)
end
protected
def initialize(webhook)
#webhook = webhook
end
private
attr_reader :webhook
def event_types
raise NotImplementedError
end
end
module Journeys
class WebhookData < Webhook
def event_types
{ 'publish' => JourneyWorker, 'delete' => HideJourneyWorker }
end
end
end
module Activities
class WebhookData < Webhook
def event_types
{ 'publish' => ActivityWorker, 'delete' => DeleteActivityWorker }
end
end
end

call service method from a controller following correct factory patterns

I have the following class
class EvaluateService
def initialize
end
def get_url
end
def self.evaluate_service
#instance ||= new
end
end
class CheckController < ApplicationController
def index
get_url = EvaluateService.get_url
end
end
The problem here is that i know that i can do evaluate_service = EvaluateService.new and use the object evaluate_service.get_url and it will work fine but i also know that some frown upon the idea of initializing the service object this way and rather there is a way of initializing it via a call, send method in the service class.
Just wondering how do i do this?
I think what you're looking for is something like:
class Evaluate
def initialize(foo)
#foo = foo
end
def self.call(foo)
new(foo).call
end
def call
url
end
private
def url
# Implement me
end
end
Now you can do this in your controller:
class CheckController < ApplicationController
def index
#url = Evaluate.call(params)
end
end
The reason some prefer #call as the entry point is that it's polymorphic with lambdas. That is, anywhere you could use a lambda, you can substitute it for an instance of Evaluate, and vice versa.
There are various ways to approach this.
If the methods in EvaluateService don't need state, you could just use class methods, e.g.:
class EvaluateService
def self.get_url
# ...
end
end
class CheckController < ApplicationController
def index
#url = EvaluateService.get_url
end
end
In this scenario, EvaluateService should probably be a module.
If you want a single global EvaluateService instance, there's Singleton:
class EvaluateService
include Singleton
def get_url
# ...
end
end
class CheckController < ApplicationController
def index
#url = EvaluateService.instance.get_url
end
end
But global objects can be tricky.
Or you could use a helper method in your controller that creates a service instance (as needed) and memoizes it:
class EvaluateService
def get_url
# ...
end
end
class CheckController < ApplicationController
def index
#url = evaluate_service.get_url
end
private
def evaluate_service
#evaluate_service ||= EvaluateService.new
end
end
Maybe even move it up to your ApplicationController.

after_commit is never being called

I am on rails 4.2.10. I need to trigger a job using sidekiq in after_save method. But the job is triggered, before the object is committed into the database, so I get the error, object not found with id=xyz.
So, I need to use
after_commit :method_name, :on => [:create, :update]
But the changes that I made in object doesn't show up in above method. I have an attribute email. When I was calling above method after_save, email_changed? return true. But if I call the same method using after_commit, email_changed? returns `false.
Is it because I am using object.save method and not create method?
Below is the method, which I am calling to trigger the job:
def update_or_create_user
if email_changed?
ServiceUpdateDataJob.perform_later action: 'update', data: {type: 'user', user_id: self.id}
end
true
end
I recognize this isn't exactly an answer to your question as stated. However...
IMO, you're overloading your model's responsibilities. I suggest you create a service that triggers the job when your model is saved. It might look something like:
class FooService
attr_accessor :unsaved_record
class << self
def call(unsaved_record)
new(unsaved_record).call
end
end
def initialize(unsaved_record)
#unsaved_record = unsaved_record
end
def call
kick_off_job if unsaved_record.save
!unsaved_record.new_record?
end
private
def kick_off_job
# job logic
end
end
You might use the service in a controller something like:
class FooController < ApplicationController
def create
#new_record = ModelName.new(record_params)
if FooService.call(#new_record)
# do successful save stuff
else
# do unsuccessful save stuff
end
end
...
end

Call to action on the same controller

I am learning rails and I have a question
How can I call an action from another in the same controller?
def new
new_method()
end
private
def new_method
...
end
This would be the right way?
The parenthesis is optional in Ruby. But, one action receive a call from client and respond one output. Your private "action" is only a function or method.
class User
def create
make_something(params)
end
private
def make_something(params)
#some implementation
end
end

Invoke a method before running another method in Rails

I have a Model, which has method_1 to method_10. I also have ModelObserver.
I would like to notifiy ModelObserver before invoking method1 to method_9, but not method_10.
Is there a DRY way to write this, instead of repeating notify_observers(:after_something) in all 9 methods?
Add a file called monkey_patches.rb in config/initializers dirctory.
class Object
def self.method_hook(*args)
options = args.extract_options!
return unless (options[:before].present? or options[:after].present?)
args.each do |method_name|
old_method = instance_method(method_name) rescue next
define_method(method_name) do |*args|
# invoke before callback
if options[:before].present?
options[:before].is_a?(Proc) ? options[:before].call(method_name, self):
send(options[:before], method_name)
end
# you can modify the code to call after callback
# only when the old method returns true etc..
old_method.bind(self).call(*args)
# invoke after callback
if options[:after].present?
options[:after].is_a?(Proc) ? options[:after].call(method_name, self):
send(options[:after], method_name)
end
end
end
end
end
The patch enables you to add before and after callbacks on an instance method of a class. A hook can be:
The name of an instance method which accepts one parameter
A lambda accepting two parameters
Multiple hooks can be registered on a same method. The method being hooked should come before the hook.
E.g:
class Model < ActiveRecord::Base
def method1
end
def method2
end
def method3
end
def method4
end
def update_cache
end
# instance method name as `after` callback parameter
method_hook :method1, :method2, :after => :update_cache
# lambda as `before` callback parameter
method_hook :method1, :method2,
:before => lambda{|name, record| p name;p record}
# lambda as `after` callback parameter
method_hook :method3, :method4,
:after => lambda{|name, record|
Model2.increment_counter(:post_count, record.model2_id)}
end
How about something like this?
def notify; puts "Was notified."; end
def method1; end
def method2; end
def method3; end
def original
notify
method1
notify
method2
method3
end
def dry
[:method1, :method2].each do |m|
notify
send(m)
end
method3
end
original
dry

Resources