get all records via operation and show in view - ruby-on-rails-5

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

Related

Delegate or instantiate additional class?

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?

How Do I Add Methods To ActiveRecord::Base?

I'm trying to create a customized ActiveRecord::Base that includes additional metadata about the connection. I see two ways to go about this:
1.) Inherit from ActiveRecord::Base and add methods & fields in this subclass.
2.) Encapsulate an ActiveRecord::Base object inside my own class
1 has all kinds of problems with the inability to override initialize, weird problems where it doesn't seem to have custom methods I've added, etc.
undefined method `set_profile' for #<Class:0xf041f0>
2 I have not been able to figure out, due to problems with using ActiveRecord::Base.new
I am trying to make an all-purpose ActiveRecord class that I can dynamically establish_connection & set_table_name on, (i.e. not have one underlying table that this ActiveRecord::Base represents) but I can't seem to find a way to accomplish it. Any ideas?
This works:
class MyTable < ActiveRecord::Base
establish_connection $config['custom-db-config'];
set_table_name 'MY_TABLE'
end
but I need a class I can call these things on repeatedly.
Not entirely sure why you'll want that, but maybe you can try this?
module ActiveRecord
class Base
def self.your_method
# implementation goes here
end
end
end
You will need to save this file and put it in config/intializers.
You can also extend the ActiveRecord::Base class in order to add the those methods dynamically which are directly callable by the class inheriting the ActiveRecord::Base...Many acts_as plugins are defined and made according to this practice...

Best way to update elements from an has_many relationship without instantiating full models and triggering the callbacks

This is something I'm trying to do now: A has_many Bs. B has certain callbacks that need to be triggered. Just, when I save from A, I want something to be updated in Bs. Since the Bs can be 10000, I would like not to load them into memory, and still have the benefit of seeing the callbacks triggered. What is the best strategy for this?
And please, no "find_each" or any find by batches variant, I'm aware of them and they will be my last resort in case nothing else works.
When I encountered this problem, I use this solution
define "callback methods" in a class and use they with ids,
define really callbacks in a instance and pass id of record in "class callback"
example of code:
class Post < AR
has_many :comments
after_save do |post|
Post.recalculate_counters(post.comment_ids)
end
end
class Comment < AR
belongs_to :post
after_save :recalculate_couters
def self.recalculate_couters(ids)
... huge and slow update statement ...
end
def recalcuate_couters
self.class.recalculate_couters([id])
end
end
I don't think there's any way you can have the callbacks executed without loading the models. However, if you give up using callback you can use update_all that performs really fast.
You just use:
B.update_all({:timestamp => Time.now}, { :a_id => id })

In RoR 3.0, how can you prune a table down to its 500 most recent entries?

I have a table that I'd like to keep pruned to the 500 most recent rows. What's the most efficient way to do this in rails?
One way to do it:
class MyModel
after_create do
self.class.prune(500)
end
def self.prune(max)
if count > max
order('created_at DESC').offset(max).each do |model|
model.destroy
end
end
end
end
The prune class method could also be added to ActiveRecord::Base if you want to use that on multiple models.
This is definitely one way to do it, although someone may chime in with a more efficient way. Create a method in your controller, for this example I'll call it "prune", and call it after your create action (there may be an after_filter or something similar you can use.) It should look something like this.
def prune
if MyModel.count > 500
#models = MyModel.all(:offset => 500)
#models.each do |m|
m.destroy!
end
end
end
A basic solution would be to use the following script under a scheduling application like whenever https://github.com/javan/whenever to run the following command :
Mould.order('updated_at DESC').offset(20).each {|m| m.destroy }
Substitute Mould with the name of your model. Usage of cron and scheduling has been discussed in detail in following post : A cron job for rails: best practices?

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.