1

I am working on a scholarship application, where people can make donations to support persons for different programs that they want to participate in. I need some help with Rubocop refactoring in rails.

I have the following issues;

  1. Controller action only calls one model method other than an initial find or new. Make custom .new or .update methods in the model with all necessary.
  2. Assignment Branch Condition size for index is too high
  3. Method has too many lines

I have tried to refactor the code, but I am still facing the same issues with the code.

My codes are;

Dashboard Controller (Initial*)

class DashboardController < ApplicationController
  def index
    #Paid Donations in Chart
    @paid_donations = Donation.where(payment: true).count
    #Unpaid Donations in Chart
    @unpaid_donations = Donation.where(payment: false).count
    #Total Donations Sum
    @total_donations_sum = Donation.where(payment: true).sum(:amount)
    #Deployed Donations
    @deployed_donations = Donation.where(deployment: true).sum(:amount)
    #Not Deployed Donations
    @not_deployed_donations = Donation.where(deployment: false,  payment: true).sum(:amount)
    #Deployed Donations Percentage
    @deployed_donations_percentage = (@deployed_donations.to_f / @total_donations_sum.to_f) * 100
    #Not Deployed Donations Percentage
    @not_deployed_donations_percentage = (@not_deployed_donations.to_f / @total_donations_sum.to_f) * 100

    #Total Donations
    @total_donations = Donation.count
    #Paid Donations
    @paid_donations = Donation.where(payment: true).count
    #Unpaid Donations
    @unpaid_donations = Donation.where(payment: false).count

    #All Programs
    @programs = Program.all
  end
end

Dashboard Controller (Refactored)

class DashboardController < ApplicationController    
  def index
    # Paid Donations in Chart
    @paid_donations = Donation.paid_count
    # Unpaid Donations in Chart
    @unpaid_donations = Donation.unpaid_count
    # Total Donations Sum
    @total_donations_sum = Donation.paid_sum
    # Deployed Donations
    @deployed_donations = Donation.deployed_sum
    # Not Deployed Donations
    @not_deployed_donations = Donation.not_deployed_sum
    # Deployed Donations Percentage
    @deployed_donations_percentage = percentage(@deployed_donations, @total_donations_sum)
    # Not Deployed Donations Percentage
    @not_deployed_donations_percentage = (@not_deployed_donations.to_f / @total_donations_sum.to_f) * 100

    # Total Donations
    @total_donations = Donation.count
    # Paid Donations
    @paid_donations = Donation.paid_count
    # Unpaid Donations
    @unpaid_donations = Donation.unpaid_count

    # All Programs
    @programs = Program.all
  end
end

Donation Model (Initial)

class Donation < ApplicationRecord
  belongs_to :program
end

Donations Model (Refactored)

Class Donation < ApplicationRecord
  belongs_to :program
  scope :paid_count, -> { where(payment: true).count }
  scope :unpaid_count, -> { where(payment: false).count }
  scope :paid_sum, -> { where(payment: true).sum(:amount) }
  scope :paid_sum, -> { where(payment: true).sum(:amount) }
  scope :deployed_sum, -> { where(deployment: true).sum(:amount) }
  scope :not_deployed_sum, -> { where(deployment: false).sum(:amount) }

  def percentage(donate, total)
    (donate.to_f / total.to_f) * 100
  end
end

I need some assistance on the rails best practices to resolve these issues, following the skinny models and skinny controllers rails principle.

halfer
  • 19,824
  • 17
  • 99
  • 186
Promise Preston
  • 24,334
  • 12
  • 145
  • 143

1 Answers1

0

I think in this case you can create method in model Donation to return all value to show in 1 hash.

Class Donation < ApplicationRecord
  belongs_to :program
  scope :paid_count, -> { where(payment: true).count }
  scope :unpaid_count, -> { where(payment: false).count }
  scope :paid_sum, -> { where(payment: true).sum(:amount) }
  scope :deployed_sum, -> { where(deployment: true).sum(:amount) }
  scope :not_deployed_sum, -> { where(deployment: false).sum(:amount) }

  def self.deployed_donations_percentage
    (deployed_sum / size) * 100
  end

  def self.not_deployed_donations_percentage
    (not_deployed_sum / size) * 100
  end

  def self.info
    {}.tap do |info|
      info[:paid_donations] = paid_count
      info[:unpaid_donations] = unpaid_count
      info[:total_donations_sum] = paid_count
      info[:deployed_donations_percentage] = deployed_donations_percentage
      info[:not_deployed_donations_percentage] = not_deployed_donations_percentage
      #...anything you want to show
    end

  end
end

in your controller

class DashboardController < ApplicationController    
  def index
    # donations info
    @donations_info = Donation.info
    # All Programs
    @programs = Program.all
  end
end

and in your view you can access value with

<%= @donations_info[:paid_donations] %>
  • This is so cool. My apologies for not replying early enough, I didn't get the notification early on. How do I also refactor these other items on the controller? **# Deployed Donations Percentage** `@deployed_donations_percentage = percentage(@deployed_donations, @total_donations_sum)`, **# Not Deployed Donations Percentage** `@not_deployed_donations_percentage = (@not_deployed_donations.to_f / @total_donations_sum.to_f) * 100`. I used `def percentage(donate, total) (donate.to_f / total.to_f) * 100 end` on the model, but don't know how to implement on the controller – Promise Preston Jun 13 '19 at 07:55
  • @PromisePreston you're wellcome, i've updated my answer – Văn Trọng Trần Jun 14 '19 at 08:41
  • Thank you so much. I just noticed that you used `def self.deployed_donations_percentage (deployed_sum / size) * 100 end`. I understand that using self.a_method during method definition makes the method a class method, but I want to ask **why you had to use a class method there instead of just an instance method** and then call the method on the controller. Also, I would like to know why you used size, cause I really don't understand its best usage **(when and how to use the .size method)**. Please bear with me. My questions seem much. I am not yet an expert in ruby on rails. Thanks. – Promise Preston Jun 14 '19 at 09:37
  • @PromisePreston use class method when we don't care about object or instance value. You can see deployed_sum and size not are attribute of any one instance, also deployed_donations_percentage too, so this is class method. - `count` will perform an SQL COUNT query - `length` will calculate the length of the resulting array - `size` will try to pick the most appropriate of the two to avoid excessive queries – Văn Trọng Trần Jun 16 '19 at 15:30
  • Thank you Van for your explicit explanation. I have tried out your solution, but I seem to hit a roadblock. When I try to view the dashboard, no information for the Donations is displayed. I think the reason is that we moved the business logic to the **Donations Model** and then we are calling all those logic on the **Dashboard Controller**, which does not seem to be able to access the data from the **Donations Model**. **Please how do I expose the business logic that we defined on the Donations Model to the Dashboard Controller, since they are different controller and models?** Thank you. – Promise Preston Jun 17 '19 at 11:38
  • @PromisePreston you can't see here: https://stackoverflow.com/questions/18563229/mvc-where-to-put-business-logic if it still not work, can you show me your code? – Văn Trọng Trần Jun 18 '19 at 02:19
  • Thank you for your support. I read a few things about **Service Objects** from the link that you shared. It advises that I moved the business logic from the model to the services folder and then do a call from each service that I want to the controller. **I am still not certain as to how I can implement that in the code below.** If you can help out with that, I will greatly appreciate and then learn how to implement it from this example. **Thank you so much** – Promise Preston Jun 18 '19 at 16:05