Home > Software design >  Rails 7 how to decorate non-ActiveRecord objects
Rails 7 how to decorate non-ActiveRecord objects

Time:09-02

In my Rails 7 app I've got data table which I want to decorate. The data comes from the API response so in fact it's an array of hashes. Like below:

# transactions_controller.rb

class TransactionsController < ApplicationController
  def index
    response = client.transactions.list(platform_id: current_user.platform_id, page: 1, per_page: 100)
    @transactions = response.body['data']
  end

  private

  def client
    @client ||= TestAPI::Client.new
  end
end

Now inside the transactions/index.html.erb I've got a table with @transactions data which I want to decorate:

#views/transactions/index.html.erb

<table >
  <thead>
    <tr>
      <b>
        <tr>
          <th>Date</th>
          <th>Amount</th>
        </tr>
      </b>
    </tr>
  </thead>

  <tbody>
    <% @transactions.map do |transaction| %>
      <tr>
        <td>
          <%= transaction['created_at'] %>
        </td>
        <td>
          <%= transaction['amount_cents'] %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

I know I could inject that logic inside of view file to be like:

 (...)
        <td>
          <%= Date.parse(transaction['created_at']).strftime("%d.%m.%Y") %>
        </td>
        <td>
          <%= "#{ transactions_data.last['amount_cents']/100}" "#{ transactions_data.last['currency']}" %>
        </td>
(...)

But I want to get rid of that logic from the view since I'll have more and more logic in the future here.

CodePudding user response:

Kudos for wanting to remove logic from the view.

You need a new object, it could be called TransactionPresenter or whatever you choose. It will implement the view logic. So in your TransactionsController:

def index
    response = client.
                 transactions.
                 list(platform_id: current_user.platform_id, page: 1, per_page: 100).
                 map{|t| TransactionPresenter.new(t)}
    @transactions = response.body['data']
  end

and the TransactionPresenter model could be something like this:

class TransactionPresenter
  def initialize(transaction)
    # capture the fields of interest as variables
  end

  def amount
    "$#{amount_cents.to_f/100}" # for example, whatever makes sense in your context
  end
end

so all logic is removed from the view:

<table>
<% @transactions.each do |transaction| %>
  <tr><%= transaction.amount %></tr>
<% end %>
</table>

  • Related