Helper Classes for DelayedJob in Rails - ruby-on-rails

In order to start delayed_job's on a schedule you need to have helper classes with a perform method that delayed_job can call. These need to be defined before any of the classes that use them to create scheduled delayed_jobs are called. All very short, and many of them in my case. For example:
class AccountUpdateJob < Struct.new(:account_id)
def perform
acct = Account.find(account_id)
acct.api_update
end
end
I'm doing this in a file called "dj_helper_classes" in the initializers folder. Is that the right thing to do?

I keep mine in lib/jobs, one file per class. So, your example would be in lib/jobs/account_update_job.rb
module Jobs
class AccountUpdateJob < Struct.new(:account_id)
def perform
acct = Account.find(account_id)
acct.api_update
end
end
end

Related

Start method just once for addition / removal of association elements

I have a Composition model which has a has_and_belongs_to_many :authors.
I need to fire a method after a composition changed its authors, although, since it involves the creation of a PDF file (with the name of the authors), I want to call this method only once, regardless of the number of authors added / removed.
Of course I can add / remove existing authors from the composition, so a before_save / after_save won't work here (somehow it recognizes new authors added to the composition, but not existing ones).
So I tried using after_add / after_remove, but the callbacks specified here will be invoked for every author item added to / removed from the composition.
Is there a way to have a method called only once for every "batch action" of adding / removing items from this kind of relationship?
Here's what a service might look like:
class UpdateCompositionAuthorsService
attr_accessor *%w(
args
).freeze
class << self
def call(args={})
new(args).call
end
end # Class Methods
#======================================================================================
# Instance Methods
#======================================================================================
def initialize(args={})
#args = args
assign_args
end
def call
do_stuff_to_update_authors
generate_the_pdf
end
private
def do_stuff_to_update_authors
# do your 'batch' stuff here
end
def generate_the_pdf
# do your one-time logic here
end
def assign_args
args.each do |k,v|
class_eval do
attr_accessor k
end
send("#{k}=",v)
end
end
end
You would call it something like:
UpdateCompositionAuthorsService.call(composition: #composition, authors: #authors)
I got sick of remembering what args to send to my service classes, so I created a module called ActsAs::CallingServices. When included in a class that wants to call services, the module provides a method called call_service that lets me do something like:
class FooClass
include ActsAs::CallingServices
def bar
call_service UpdateCompositionAuthorsService
end
end
Then, in the service class, I include some additional class-level data, like this:
class UpdateCompositionAuthorsService
SERVICE_DETAILS = [
:composition,
:authors
].freeze
...
def call
do_stuff_to_update_authors
generate_the_pdf
end
...
end
The calling class (FooClass, in this case) uses UpdateCompositionAuthorsService::SERVICE_DETAILS to build the appropriate arguments hash (detail omitted).
I also have a method called good_to_go? (detail omitted) that is included in my service classes, so my call method typically looks like:
class UpdateCompositionAuthorsService
...
def call
raise unless good_to_go?
do_stuff_to_update_authors
generate_the_pdf
end
...
end
So, if the argument set is bad, I know right away instead of bumping into a nil error somewhere in the middle of my service.

How to use ActionView::Helpers::NumberHelpers "number_with_delimeter" in script

I'm writing a script for my rails application and I'm trying to format the numbers with delimeters so they're easier to read. But I have a problem in calling the number_with_delimeter method from ActionView::Helpers::NumberHelpers
I tried
class MyClass < ActiveRecord::base
extend ActiveView::Helpers::NumberHelper
def self.run
puts "#{number_with_delimeter(1234567)}"
end
end
MyClass.run
but it just doesn't work. I always get undefined method errors. I tried it with include instead of extend and some other variations. None of them worked. I don't know how to proceed.
Is there any way to call this method in a script?
*Note: * I call the script with rails r script/my_script.rb
An elegant solution consists in delegation:
def self.run
puts "#{helper.number_with_delimiter(1234567)}"
end
def self.helper
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::NumberHelper
end
Sidenotes:
including modules overloads your class
including the helpers didn't help because you were working at the class level.
formatting should not be model's job, you should extract this kind of logic within presenters.

How to make a method available to all controllers? And how to all models?

I am new to Rails and have written a method to_csv which I have put it in products_controller.rb, but I want it to available to all other controllers too. What is the preferred way to do that? Is it in application.rb?
Similarly, if I am writing a method in some model.rb, how to share that method between all the models?
application_controller will be the place. If for model, maybe you can write in a module, then
include in your model which you want to use.
1) Try ActiveRecord::Base monkeypatching.
The initializer directory is a best place to collect all those little task
So, try /config/initializers/active_record_extension.rb,
class ActiveRecord::Base
def self.export(parameters)
#your csv logic goes here
end
end
or
2) create master class, which is used to inherit by all active_record model
for example /models/your_class.rb
class YourClass < ActiveRecord::Base
def self.export(parameters)
#your csv logic goes here
end
end
class CsvDB < YourClass
end
You can also create a separate model without inheriting from ActiveRecord::Base and define your csv method in that particular model. And from any controller just call
model_name.method_name(parameters)
For example, in the model CsvDB:
class CsvDB
def export(parameters)
# your csv logic goes here
end
end
From any controller just call
CsvDB.export(parameters)

Where does import code belong in Rails?

In my Rails app, I have to import large XML files, that will effect 10 or more models at once.
Now I'm not sure, where the code for this import belongs to. Should I include it in one single model, split it over all the models effected, use modules, or even concerns?
Does anyone have experience with that and can give me some advise?
If the import happens all at once, from one XML file, then just write an import script and put it in /lib/imports then call it from a rake task or something. Unless you need to factor it down into class methods I don't see the point, personally.
We import about 600MB of XML every day via several different import scripts and they're all in /lib/imports and called from rake tasks which in turn are scheduled and run using cron.
I would go with app/models/tasks/somename_importer.rb and encapsulate all your importer stuff there. You might also create a model for the actual data object you are importing.
class ImportedObject
attr_accessor :have, :some, :accessor, :to, :hold, :data
def initialize(data, *opts)
# move data to instance variables
end
def to_object
Object.new(some: mapping)
end
end
class Tasks::SomeNameImporter
def initialize
# maybe setup some logging and stuff
end
def perform
# fetch data from some source via http or file or ftp and iterate over appropriate items
data.each do |item|
imported = ObjectToImport.new(item)
# you can do whatever you want with your imported data
object = imported.to_object
if object.valid?
object.save
else
# do some logging
end
end
end
end
Assuming you could probably need some methods to interact with xml as well, I would go something like this
class XmlBase < ActiveRecord::Base
#import xml files
def method_to_parse_xml
#code
end
end
in your models (which requires xml files)
class User < XmlBase
end
class Project < XmlBase
end
#normal models
class Company < ActiveRecord::Base
end

Ruby on Rails, calling a very big method from a model

I have a very big function in my model and I want to store it somewhere else in order to keep my model dry. I read that storing methods in ApplicationHelper and then calling them from a model is a bad idea. What is a good idea then?
I want to have a separate file with my big methods and call them from a model.
You can create a "plain old ruby object (PORO)" to do your work for you. let's say you had a method that calculates the amount overdue for a user.
So, you can create app/services/calculates_overages.rb
class CalculatesOverages
def initialize(user)
#user = user
end
def calculate
# your method goes here
end
end
Then, you can:
class User < ActiveRecord::Base
def overage_amount
CaluclatesOverage.new(self).calculate
end
end
Or, in a controller you could:
def show
#amount = CaluclatesOverage.new(current_user).calculate
end
The app/services directory could also be app/models, or the lib directory. There's no set convention for this (yet).
Use a Concern. https://gist.github.com/1014971
It's simple. In app/models/concerns create a file your_functionality.rb as follows:
module YourFunctionality
extend ActiveSupport::Concern
def your_fat_method
# insert...
end
end
And in your model simply:
include YourFunctionality

Resources