Delegate or instantiate additional class? - ruby-on-rails-3

Let's say I have an Account class and an AccountReport class. In accounts#show I want to show a report of an account. Both Account and AccountReport have a number of public methods. Which technique of the following techniques is better?
1) Instantiate an Account and an AccountReport, initializing the AccountReport with the account data.
class AccountsController < ActionController::Base
def show
#account = current_user.account
#account_report = AccountReport.new(#account.orders)
respond_with(#account)
end
# ...
end
2) Allow an instance of Account to instantiate AccountReport and delegate method calls
class Account < ActiveRecord::Base
attr_reader :account_report
delegate :method_a, :method_b, :method_c, :method_d, :to => :account_report
after_initialize :setup_account_report
def setup_account_report
#account_report = AccountReport.new(orders)
end
# ...
end
Option 2 seems to be a cleaner approach to me but loading up Account with lots of methods makes it feel like a God class.

Well, i think you have to make a mix of both option.
The first one is good, if you only use reports on show.
The second one is good, if you use all the time reports for your account.
With the second one, all the time your report will be instantiate and it could reduce performances.
You should perhaps try something like this:
class Account < ActiveRecord::Base
# ...
#report = nil
def report
if #report.nil?
#report = AccountReport.new(self.orders)
end
end
# ...
end
The good thing of this solution is that report is loaded only if needed, but will not be loaded every time.
The bad thing of this solution is that if you add some orders your report will not be up to date.
UPDATE:
To improve this, you could replace the condition with this one
if #report.nil || self.created_at_changed?

I like the first option because it keeps coupling low. The second option ties Account and AccountReport together in a way that is probably unnecessary. What happens whenever you get another type of report? You'll probably need to change a bunch of things in Account, which is sad because they are seemingly unrelated.
You can keep the logic / verbosity low in the controller by combining these two things in a service object, and handing that off to your views. An AccountReporting service can handle the logic behind combining these two classes together, e.g.:
class AccountReporting
def initialize(account)
#account = account
end
def report
AccountReport.new(account.orders)
end
end
Then, to use it in the controller:
AccountReporting.new(current_user.account)
Does this make sense?

Related

Should I use a presenter or a decorator?

I'm kinda lost with these terms since I'm a beginner in the Rails world.
I have some code in my header, not related to the model. I just want to show it or not depending on the page user is.
I know it's kinda simple and helpers would do it pretty well too, but is it possible to fit the code in a presenter or a decorator?
When do I have to use them? I really don't understand it yet.
tl;dr:
Use a Presenter in this scenario
Elaboration:
A Decorator is a structural design pattern that wraps other objects and adds new functionality without the need to extend the class you are decorating.
A Presenter on the other hand should use methods of the object you are presenting to format data in a way you want to show them. Eg. you have a User model:
class User < ActiveRecord:Base
# it has first_name and last_name columns
end
and you want to present the full name without much logic in the views. You can create a UserPresenter class like this:
class UserPresenter
def initialize(user)
#user = user
end
def full_name
"#{#user.last_name} #{#user.first_name}"
end
end
So instead of calling both attributes separately, you just do it with the presenter
user = User.new(first_name: "John", last_name: "Doe")
user_presenter = UserPresenter.new(user)
user_presenter.full_name #=> "Doe John"

get all records via operation and show in view

I have trouble with trailblazer when setting up a simple show all Things view.
operation
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
include Model
model Thing, :all #why :all is not working here?
def process
end
end
end
controller
class PageController < ApplicationController
def index
run Word::ShowAll
end
end
why is :all not working for getting all Things from the db but :find works to get one via its id?
The best place to ask TRB questions is actually on Github channel.
I'm not sure where you found that example, as it is not supposed to work AFAIK, :find is a shortcut I believe, I've never actually used it.
All your logic should be defined inside the process method. http://trailblazer.to/gems/operation/1.1/api.html#process
Having said that, trying to get all records without some kind of pagination is a really bad idea, unless you are 100% sure that your table won't grow beyond several dozen records. Unless you know you don't have a large load. So to define that kind of shortcut is dangerous.
Calling Trailblazer::Model#model as you're doing there is just a shortcut for overriding the TrailBlazer::Operaration#model! method. So what you seem to want to do is:
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
def model!(params)
Thing.all # add any filtering or pagination here
end
end
end
And in your controller call present instead of run so that it sets up the model but doesn't call the process method of the operation.
class PageController < ApplicationController
def index
present Word::ShowAll
end
end

Running multiple SQL queries in Rails without closing ActiveRecord.connection

So I have a rails module called DbsfnpService.
Within this, I have a class
class DbnsfpAccess < ActiveRecord::Base
establish_connection :dbnsfp
end
I then have many methods within DbsfnpService similar to this.
def get_tables
sql = "select * from dbnsfp limit 1"
results = DbnsfpAccess.connection.execute(sql)
return results
end
When I call these methods from another class by including DbsnfpService, I would like to only establish one connection to :dbnsfp and handle all my queries and then close that connection. I believe how it is now, every method I call that contains DbsnfpAccess.connection.execute(sql) is making a separate connection(?). What is the best way to achieve this? Passing in a connection object into these functions? Thanks in advance!
If I'm understanding your question correctly, you want to execute calls on the database without knowing the table names and therefore unable to create rails ActiveRecord table models ahead of time.
Instead of executing sql you can use ActiveRecord::Base.connection.tables
(see this link How to list of all the tables defined for the database when using active record?)
To use these table names what you will need is to turn DbnsfpAccess into an abstract class.
class DbnsfpAccess < ActiveRecord::Base
self.abstract_class = true
establish_connection :dbnsfp
end
Then you can create the models you want to connect to DbnsfpAccess like this:
class DynamicallyGenratedDbnsfpModel << DbnsfpAccess

Rails 3, Model Methods / Calculated Attributes

I am dealing with numerous calculations to bring various values within a model to a simple TRUE or FALSE. Problem is, these calculations are pretty intense and not something I want to create a long, hard to follow SQL statement for. I'd rather just have the entire calculation within a method that the model could check for when returning records.
I've tried numerous ways to accomplish this, and when looking up other similar feats, others push newbs like me to SQL which might serve most purposes but will not serve mine as the calculations being done are somewhat external to the model.
Model:
class Quality < ActiveRecord::Base
...
def passed_inspection
[code that calculates based on values in model]
end
Controller:
#records = Quality.where('passed_inspection = true')
View:
Did pass inspection?: <%= record.passed_inspection %>
It sounds like the solution to your problem would be to use a Scope with a Class Method to help clean up your model. Essentially you would set up your model like this:
class Quality < ActiveRecord::Base
def self.passed_inspection
# Code that does your calculations
end
scope :passed, passed_inspection() # This needs to be below the function above
end
Then you could get this data by calling it like this
#records = Quality.passed
There is a rails cast about this problem if you need any more information: RailsCast #215 Advanced Queries
Edit: Fixed some terrible grammar

In a migration: how to loop through Models and execute a private method in Model

I have a Post, to which a migration adds a new attribute and table column short_url. This attribute is either provided by the user, or, if left blank, automatically created:
class Post < ActiveRecord::Base
before_create :create_short_url
private
def create_short_url
if short_url.blank? || already_exists?(short_url)
write_attribute :short_url, random_string(6)
end
end
def random_string(length)
#innards are irrelevant for this question
end
end
In the migration, I want to run through all posts and have the short_url created and saved.
problem: Post.find(:all).each {|post| post.create_short_url} in the self.up is not possible, due to the private scope of the create_short_url method.
problem: Looping through posts and update!-ing them does not invoke the before_create :create_short_url, because it is not before create. Once migrated, I prefer to not have any before_update hooks in place: I don't need to change anything on update.
How would you tackle this? Copy over the random_string() and associated methods to the migration? Add specific migration helper methods to the Post?
Just use the Object method send (it doesn't check protected/private).
Post.all.each do |post|
post.send :create_short_url
post.save!
end
An alternative would be (but that could interfere with other migrations running in the same Ruby-process after that):
Post.before_save :create_short_url
Post.all.each(&:save!)
Visibility tip: Most of the time what you really mean is protected (see here). I recommend to use protected instead of private in this case.