views:

99

answers:

1

I'm coming from a .NET background, where it is a practice to not bind domain/entity models directly to the view in not-so-basic CRUD-ish applications where the view does not directly project entity fields as-is.

I'm wondering what's the practice in RoR, where the default persistence mechanism is ActiveRecord. I would assert that presentation-related info should not be leaked to the entities, not sure though if this is how real RoR heads would do it.

If DTOs/model per view is the approach, how will you do it in Rails?

Your thoughts?

EDIT:

Some examples:
- A view shows a list of invoices, with the number of unique items in one column.
- A list of credit card accounts, where possibly fraudulent transactions were executed. For that, the UI needs to show this row in red.

For both scenarios, The lists don't show all of the fields of the entities, just a few to show in the list (like invoice #, transaction date, name of the account, the amount of the transaction)

For the invoice example, The invoice entity doesn't have a field "No. of line items" mapped on it. The database has not been denormalized for perf reasons and it will be computed during query time using aggregate functions.

For the credit card accounts example, surely the card transaction entity doesn't have a "Show-in-red" or "IsFraudulent" invariant. Yes it may be a business rule, but for this example, that is a presentation concern, so I would like to keep it out of my domain model.

+1  A: 

In general I would answer that your AcitveRecord object can contain any fields and you show in views only what you want. There is scaffolding task in rails scripts but it is only to create some setup model, controller and view. When I work with Rails I don't use ./script/generate scaffold at all. Rather I'm generating only model and controller separatly. The view part I add manualy.

ActiveRecord only maps data from database to some nice objects. What you do with it in view is up to you.

According to separation between presentation and business rules I think that fallowing examples would make it clear to you how to handle it in Rails.

For your invoice example I would create a view this way:

<h1>Invoices</h1>
<table>
  <tr>
    <th>Invoice #</th>
    <th>Date</th>
    <th>Name</th>
    <th>No. of line items</th>
    etc
  </tr>
  <% @invoices.each do |invoice| %>
    <tr>
      <td><%= invoice.number %></td>
      <td><%= invoice.date.to_s %></td>
      <td><%= invoice.name %></td>
      <td><%= invoice.line_items.count %></td>
      etc.
    </tr>
  <% end %>
</table>

Or even put a row with invoice data into separate partial and render it in above view. I assume that in your model you have:

# Invoice model
has_many :line_items

Now lets take a look on credit card example. I would do it like this:

# In CreditCard model add method
def fraudulent?
  #put here some logic that returns true or false
end

Then in your view when you render this credit card:

<div <%= @credit_card.fraudulent? ? 'class="show_in_red"' : '' %>
   here you can show whatever you want
</div>

Or even create helper for it:

# credit card helper
def add_show_in_red(credit_card)
  credit_card.fraudulent? ? 'class="show_in_red"' : ''
end

# in Rails 3 or earlier version with plugin that puts `h` method by default 
# your helper should have additional safe_html! call
def add_show_in_red(credit_card)
  (credit_card.fraudulent? ? 'class="show_in_red"' : '').safe_html!
end

and in view:

<div <%= add_show_in_red(@credit_card) %>>
   here you can show whatever you want
</div>
klew
Accessing invoice.line_items.count is OK if data is relatively small. In scenarios where a list of child entities are huge, it will certainly make a huge hit on performance. credit_card.fraudulent is the one I'm trying to prevent, if I have more than one use-case where credit_card entity is used, this forces me to put all presentation-related logic code in my entity, making my entity "dirty" with UI, concerns. Example, in an update credit card use-case, I wouldn't need the Fraudulent flag. This will cause the class to be uncontrollably big since everyone can add invariants to it.
leypascua
To count items you can use `counter_cache` - it will store count in parent object so it won't cause any additional load on database. As I understand this `credit_card.fraudulent` - it isn't presentation. It is some 'state' of credit card, so it belongs to model (as I understand). In Rails ActiveRecord object is always quite big and contains many things that I don't use. As I know Rails 'world' people don't care about it. If you don't need to use `credit_card.fraudulent` in some view, then just ignore it, pretend it's not here.But if you really want to seperate it, than add all logic to helper
klew
But for me it will unnecessarily break MVC
klew
credit_card.fraudulent is an invariant - its value is evaluated everytime it's accessed. In this example, yes it isn't presentation since .fraudulent may be derived from a list of transactions that are "fishy". But what if you're showing a grid, wherein one column shows available actions for a row, and actions vary due to some invariant, then you'll start dirtying up the class. For small teams who have been working for a long time, this may not be an issue. It becomes one when you have new members where control starts to fade away.
leypascua
It's hard for me to understand your problem. Maybe it's because I don't have any experience with e-commerce and I don't 'feel' what you are talking about. Maybe if your examples could be more precise (on example give some data, values and show what you want to get from it) it would be easier. And if you don't want to evaluate `fraudulent` value on every request, you can store it in db - I don't see any other solution here.
klew