2

I am trying to implement the Draper gem for my Rails project and having trouble getting it to work. I'm getting the error message:

undefined method `exists?' for #Draper::CollectionDecorator:0x000055b625da7a10>

The error seems to be coming up from a partial I'm rendering on my index view. The partial is calling "@accounts.exists?" which is where the error is happening. If I remove the call to the partial my decorator works fine. I have a feeling I need to use a delegate command for this but I'm not sure. I am fairly new to Rails and just starting to explore some of the helpful gems out there. I'm hoping someone could take a look at my code and point me in the right direction to get past this error. Thanks! :)


Model: Account.rb

# An account which will store many transactions and belongs to one user
class Account < ApplicationRecord
  belongs_to :user
  has_many :transactions, dependent: :destroy

  validates :name, presence: true, length: { maximum: 50, minimum: 2 }
  validates :starting_balance, presence: true
  validates :last_four, length: { maximum: 4 }

  # validates_associated :transactions

  after_create :create_initial_transaction

  def create_initial_transaction
    update(current_balance: 0.00)
    # rubocop:disable Metrics/LineLength
    Transaction.create(trx_type: 'credit', trx_date: Time.current, account_id: id, description: 'Starting Balance', amount: starting_balance)
    # rubocop:enable Metrics/LineLength
  end
end

Controller: accounts_controller.rb

class AccountsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_account, only: %i[show edit update destroy activate deactivate]

  # GET /accounts
  # GET /accounts.json
  def index
    @accounts = current_user.accounts.where(active: true).order('created_at ASC')
    @accounts = @accounts.decorate
  end

  def inactive
    @accounts = current_user.accounts.where(active: false).order('created_at ASC')
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_account
    @account = current_user.accounts.find(params[:id])
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def account_params
    params.require(:account).permit(:name, :starting_balance, :current_balance, :last_four, :active, :user_id)
  end
end

Decorator: account_decorator.rb

class AccountDecorator < Draper::Decorator
  decorates_finders
  decorates_association :user
  delegate_all

  def last_four_formatted
    last_four.present? ? ' ( ... ' + last_four.to_s + ')' : nil
  end
end

View: accounts/index.html.erb

<%= render partial: "shared/pagetitle", locals: { page_model: "account", description: "Accounts" } %>

<!-- Render the message if no accounts exist -->
<%= render partial: "accounts/noaccounts", locals: { description: "It looks like you don't have any accounts added. To add an account, click the add account button at the top of the page :)" } %>

<div class="row" id="accounts">
  <%= render partial: "accounts/account", collection: @accounts %>
</div>

View Partial: accounts/_noaccounts.html.erb

<% if !@accounts.exists? then %>
  <div class="row">
    <div class="col s12 m8 offset-m2">
      <div class="card ob-card-gradient ob-account-card">
        <div class="card-content ob-text-primary">
          <span class="card-title">Welcome to mysite!</span>
          <div class="row">
            <br />
            <div class="col s1"><i class="material-icons valign-wrapper">account_balance</i></div>
            <div class="col s11">
            <%= description %></div>
          </div>
        </div>
      </div>
    </div>
  </div>
<% end %>
Kevin Custer
  • 576
  • 1
  • 5
  • 13

1 Answers1

2

I am not familiar with the draper gem, but you are attempting to call exists? on what looks like a collection of objects, not on an ActiveRecord object. And since exists? is defined in ActiveRecord, you're not going to be able to do that.

You can probably safely change this line:

<% if !@accounts.exists? then %>

to:

<% if @accounts.empty? then %>

The .empty? method is defined in multiple places, but the gist of it is if an object like a collection is empty then it will return true.

anothermh
  • 9,815
  • 3
  • 33
  • 52
  • Wow, I changed my code and it works perfectly now. I've been stumped on this for hours, so now I have a follow up question. Before I tried using Draper, that bit of code (!@accounts.exists?) worked fine and behaved as I expected it to. Any idea why? Anyway, much thanks for the answer! – Kevin Custer Oct 12 '18 at 01:57
  • If `@accounts` was an `ActiveRecord::Relation` object (the type of object returned by something like `Account.where(foo: 'bar')`) then it will have the method `exists?` because it's an object created by ActiveRecord. When you call `decorate`, it returns a `Draper::CollectionDecorator` object, which is not created by ActiveRecord, so it does not have the method. – anothermh Oct 12 '18 at 02:03
  • As a debugging tip you can call `object.methods` (e.g., `@accounts.methods`) in the console to list the methods that are available on that object. To skip listing all the standard Ruby methods that are applied to all objects you can call `@accounts.methods - Object.methods` and it will provide a more succinct list of methods. In your case, you'll see that `@accounts` does not have a `.exists?` method, but `Account.all.methods - Object.methods` will include `exists?(*conditions) Account::ActiveRecord_Relation (ActiveRecord::FinderMethods)`, telling you the method exists AND where to find it. – anothermh Oct 12 '18 at 02:09