简体   繁体   中英

Rails: Cleaning up large methods in controllers

Still learning how to refactor some of my controllers and would like some advice (I left some notes in the code block as to what's going on).

My current implementation works OK, but I'm wondering if there is a better & easier way to approach this; something about adding this many instance variables in the method to pull off such a trivial thing.

class JobsController < ApplicationController

  def index
    ## records created through app that have been approved (published)
    @paid_jobs = Job.published

    ## records fetched from RSS feed
    @fetched_jobs = JobEntry.all

    ## creates an array of paid_jobs and fetched_jobs, and what is considered the 'feed'. would usually be order("published_at DESC") but you can't call order on an array
    @job = (@paid_jobs + @fetched_jobs).sort_by(&:published_at).reverse

    ## you can't show pagination links for a static array directly. you can, however, first paginate it
    @jobs = @job.paginate(:page => params[:page], :per_page => 10)

    ## this is the actual variable I call in the Job#index view lol
    @published_jobs = @jobs.group_by { |job| job.published_at.to_date }
  end

You could use the rails feature single table inheritance (STI). This lets you store similar models in one table,so you could store the regular jobs and the fetched job in one table. Selection and pagination would now be very simple and removes all needs for any custom logic in ruby. This might also speed up your performance significantly (depending on the number of database records).

See the official docs: http://guides.rubyonrails.org/association_basics.html#single-table-inheritance

So you would create the parent class:

class Job < ActiveRecord::Base
  scope :published, -> { where(published: true) } # is inherited by all children
  scope :latest, -> { order(published_at: :desc) } # shortcut for ordering
  # you can add more scopes to enhance readability in controller

  # Job related logic inherited by all children
end

It is important that the table for this AR has the column type (string).

Then you derive both job types from that parent class:

class InternalJob < Job
  # InternalJob related logic
end

class FetchedJob < Job
  # FetchedJob related logic
end

Now you can fetch all the jobs to your hearts desire and paginate on it:

InternalJob.published # returns all published internal Jobs
FetchedJob.published # returns all published internal Jobs
Job.published # returns all Jobs

pagination and sorting is easy:

Job.published.sort_by(&:published_at).reverse.paginate(:page => params[:page], :per_page => 10)

This would scale very well as the database does all the filtering and sorting.

This also makes your controller code very slim:

def index
  @published_jobs = Job.published.latest.paginate(:page => params[:page], :per_page => 10).group_by do |job|
    job.published_at.to_date
  end
end

You can try below methods to clean your code

  1. Go for ActiveJob for fetching the records in background
  2. Use ActiveSupport::Concern to keep helper methods for controller in another file. http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
  3. Or you can keep cron job using Active Job and store result in json format

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM