Where does import code belong in Rails? - ruby-on-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

Related

how to mixin gem-modules in ruby-on-rails model

I am building a gem in which i have a module OtpGenerator. inside this module i have methods like generate_otp, verify_otp etc. This is just a begining for me and its very simple gem only to generate and verify and save and send, nothing else. Now anyone who uses this gem will have to include this module in their model. for e.g. there is a user model. now what i want is first i will create a instance
user = User.new(params[:user])
now i need to do the operation user.generate_otp, this will assign otp related things in the activerecord instance.
after that user.save, which is also fine.
But i also want a function generate_otp!, which will do all task like generates otp, than save it and sends it. My problem is that i am not getting how to achieve this functionality.
Note: I am very new to ruby. and really getting confused with mixins.
here is my code for otp.rb file
require 'securerandom'
module OtpGenerator
def generate_otp
#do something here
end
def verify_otp(otp)
#do something here
end
def regenerate_otp
#do something here
end
def matches?(generated, otp)
#do something here
end
def expired?(otp_expiry_time)
#do something here
end
end
This code is still in development, i just want to know that how to implement generate_otp! function, which will do all three operation,i.e,
(1) generates otp(user.generate_otp)
(2) saves otp(user.save)
(3) sends otp (i have created the send function, so thats not a problem.)
If this is a mixin in your model, then your model should also have access to it. Here is what I mean:
class User
include OtpGenerator
end
module OtpGenerator
...
def generate_otp!
generate_otp
save
send_generated_otp
end
end
When you call User.find(45).generate_otp!
That would work because of the way inheritances work in Ruby. Once the module is included within a class, it inherits all the methods of the module and the module has access to the context of the included class.
Hope that answers your question

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)

How can I conditionally, or contextually, load an interface of methods into a model from a Rails /lib?

We have a set of reports that run out of /lib. These have grown so voluminous that we now have written many methods that would count as helpers or other 'decorator-style' methods relating specifically to Reporting.
These additional methods live in the report, and look like:
class report
def get_latest_credential_updated_date
credentials.map(&:updated_at).compact.max
end
def initialize
# set up stuff
end
end
Is there a way to load a module, or otherwise inject code to a Model when the reporting lib loads:
class Loan < ActiveRecord::Base
def get_latest_credential_updated_date
credentials.map(&:updated_at).compact.max
end
end
Is there a better pattern to represent this architecture?
Maybe you can create a "inject helper" with class_eval, something like:
def inject_to(class_name, &block)
eval "#{class_name.name}.class_eval &block"
end
inject_to Loan do
def get_latest_credential_updated_date
credentials.map(&:updated_at).compact.max
end
end

Is my thinking about OOP persistence-separation right?

I am reading about OOP and trying to apply it to Rails (inspired by Uncle Bob's Ruby conf talk), and I was wondering, is it right to access the repository from a "logic" model in the way I do below:
class Product
...
def pick_a_random_product
repository.pick_a_random_product
end
...
end
Does this qualify as "logic being separated from persistence" ? Is it O.K to be using the repository as extensively as may be needed, inside a "domain"/"logic" model?
repository is meant to be some class that does the real job of fetching something from the database.
It works but if pick_a_random_product is use for different class means that
Suppose you have class xyz
Class Xyz
...
def pick_a_random_product
Product.pick_a_random_product
# Xyz.pick_a_random_product you can't use this.
end
...
end
you have class Product
class Product
...
def pick_a_random_product
Xyz.pick_a_random_product
end
...
end

Helper Classes for DelayedJob in 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

Resources