(como cualquier chapiadora)
Viktor Justo Vasquez
You can stalk me out on
twitter/github/instagram
@vjustov
blog.vjustov.me
It's a mee.
class ShoppersController < ApplicationController
def create
@shopper = Shopper.new(shopper_params)
authorize @shopper
@shopper = @shopper.decorate
respond_to do |format|
if @shopper.save
unless params[:timetable_shopper_id].blank?
# Only link with TimetableShopper if needed.
timetable_shopper = TimetableShopper.find(params[:timetable_shopper_id])
timetable_shopper.shopper_id = @shopper.id
timetable_shopper.save
end
flash.now[:notice] = "Shopper #{@shopper.name} was successfully created."
format.html {
redirect_to(
edit_shopper_path(@shopper),
notice: "Shopper #{@shopper.name} was successfully created."
)}
else
timetable_shoppers = TimetableShopper.all
@timetable_shoppers = timetable_shoppers.decorate
format.html { render action: 'new' }
end
end
end
end
/ app/views/users/_form.html.haml
= form_for @user do |f|
- if @user.errors.any?
= render "shared/form_errors", object: @user
.field-box
.col-xs-4.col-lg-4.col-md-3.col-sm-3
= f.hidden_field :external_avatar_url
- if @user.new_record?
#avatar-box.upload-instructions.dashed_border
- else
#avatar-box
= image_tag(@user.avatar.form_thumbnail.url)
.col-xs-6
= f.text_field :email
.field-box
= f.label :role, {class: "required-field"}
.col-md-5
.ui-select
= collection_select @user, :role_ids, Role.all, :id, :name,
{ checked: @user.role_ids.first,
include_blank: "Please select" },
{ disabled: !policy(@user).can_change_role? }
Helpers are usually generic functions that assist in providing some functionality to whichever objects it is "helping". Nothing too fancy here. Due to the fact that we are talking about views, these methods generally handle some sort of data representation.
module BlogsHelper
def display_title_for(blog)
"#{blog.name} - The Blog!"
end
end
# someapp/app/views/something.html.erb
# ...
<% people_without_access_from(company).each do |person| %>
<%= add_person_to_project_check_box(person, company) %>
<% end %>
# ...
class SomethingHelper
def add_person_to_project_check_box(person, company)
content_tag(:label,
check_box_tag("people_ids[]", person.id, false,
{ :class => "company_#{company.id}_person" }) +
" " + person.name
) + tag(:br)
end
end
class ProductDecorator < Draper::Decorator
# ...
def validation_ranged_regular
regular_ranged = h.content_tag(:label, "Regular R: ")
if object[:ranged_regular_from]
regular_ranged << h.number_to_currency(object[:ranged_regular_from])
if object[:ranged_regular_to] do
regular_ranged << " - " << h.number_to_currency(object[:ranged_regular_to])
end
end
html = "" << h.content_tag(:span, regular_ranged.html_safe, class: "first-price-value")
special_ranged = h.content_tag(:label, "Special R: ")
if object[:ranged_special_from]
special_ranged << h.number_to_currency(object[:ranged_special_from])
if object[:ranged_special_to] do
special_ranged << " - " + h.number_to_currency(object[:ranged_special_to])
end
end
html << h.content_tag(:span, special_ranged.html_safe, class: "first-price-value")
html.html_safe
end
# ...
end
class NiñitaPerfecta
def ingredients
['Azucar', 'Flores', 'Muchos Colores']
end
end
class ChicasSuperpoderosas < SimpleDelegator
def ingredients
super << 'Sustancia X'
end
end
class Burger
def price
100
end
end
class BaconCheeseBurger
def initialize(burger)
@burger = burger
end
def price
@burger.price + 30
end
end
class DobleCarneBurger
def initialize(burger)
@burger = burger
end
def price
@burger.price + 50
end
end
DobleCarneBurger.new(
BaconCheeseBurger.new(
Burger.new
)
).price
=> 180
class ProductsController < ApplicationController
def new
@page = Page.find(params[:page_id])
@product = @page.products.new(crop_params).decorate
@brands = Brand.active.pluck(:name, :id)
@units = Unit.active.pluck(:name, :id)
end
end
class ProductsController < ApplicationController
def new
@presenter = NewProductPresenter.new(products_params)
end
end
class CarsController < ApplicationController
def show
car = Car.find(params[:id])
dealer = Dealer.find(params[:dealer_id])
@car = ViewCarPresenter.new(car, dealer)
end
end
class ViewCarPresenter
def initialize(car, dealer)
@car, @dealer = car, dealer
end
def availability
count = dealer.cars_in_stock.select { |c| c == car }.count
"There are #{count} of this car."
end
def price_description
if price > 500_000
"Expensive piece of metal this one is!!"
else
"Damn it's was cheap!!"
end
end
end
#car-view
.availability
= @car.availability
.comments
= @car.price_description
describe ViewCarPresenter do
let(:car) { double(car, price: 2000) }
let(:dealer) { double('dealer', cars_in_stock: [car]) }
let(:presenter) { ViewCarPresenter.new(car ,dealer) }
it 'calculates the availability' do
expect(presenter.availability).to eql('There are 1 of this car.')
end
it "renders the price description" do
expect(presenter.price_description).to eql("Damn it's was cheap!!")
end
end
Wraps a single model instance.
Encapsulates decisions about how to render an object. The tell-tale of an Exhibit is telling an object "render yourself", rather than explicitly rendering a template and passing the object in as an argument.
Brings together a model and a context. Exhibits need a reference to a "context" object—either a controller or a view context—in order to be able to render templates as well as construct URLs for the object or related resources.
class ReadingListExhibit < DisplayCase::Exhibit
def self.applicable_to?(object)
object_is_any_of?(object, 'ReadingList')
end
def to_json(options={})
output = "["
books[0..-2].each do |book|
output << " { \"book\": #{book.to_json}"
output << " },\n"
end
output << " book: { #{books[-1].to_json} } "
output << "]"
end
def to_csv(options={})
output = ""
books.each do |book|
output << "\"#{book.title}\", \"#{book.author}\", \"#{book.publisher}\" \n"
end
output
end
def render_navigation(template)
template.render(
:partial => "shared/reading_list_navigation",
:locals => {
:selected_reading_list => self.name,
:other_reading_lists => ReadingList.without_current(__getobj__)
}
)
end
def render_books(template)
template.render(:partial => "shared/book", :collection => self.books)
end
def render_overview(template)
template.render(:partial => 'shared/reading_list', :object => __getobj__)
end
end
class BookExhibit < DisplayCase::Exhibit
def self.applicable_to?(object)
Rails.logger.debug("app/exhibiit #{object}")
object_is_any_of?(object, 'Book')
end
def render_book(template)
Rails.logger.info(template.class)
template.render(
partial: 'shared/book',
locals: { :book => __getobj__})
end
end
<div class="row-fluid">
<div class="span8">
<% @books.each do |book| %>
<%= book.render_book(self) %>
<% end %>
</div>
<div class="span2 well">
Suggested Reading Lists:
<% @reading_lists.each do |e| %>
<%= e.render_overview(self) %>
<% end %>
</div>
</div>
class ReadingListsController < ...
respond_to :html
def index
@books = exhibit(Book.all)
@reading_lists = exhibit(ReadingList.all)
respond_with(@reading_lists, @books)
end
end
Presenters and exhibits are not mutually exclusive.
We can combine presenters and exhibits to take advantage of both. We use presenters to deal with any view-related logic. We use exhibits to manage rendering the objects.
class ShowAllReadingListsPresenter
attr_reader :books, :reading_list
def initialize(books, reading_list)
@books = BooksExhibit.new(books)
@reading_list = ReadingListExhibit.new(reading_list)
end
end
<div class="row-fluid">
<div class="span8">
<% @presenter.books.each do |book| %>
<%= book.render_book(self) %>
<% end %>
</div>
<div class="span2 well">
Suggested Reading Lists:
<% @presenter.reading_lists.each do |e| %>
<%= e.render_overview(self) %>
<% end %>
</div>
</div>
class ReadingListsController
respond_to :html
def index
@presenter = ViewAllReadingList.new(
Books.all,
ReadingList.all,
self
)
end
end
"Classic" presenters are oriented towards a particular view; they are, in Jay Fields' words, "class versions of your views". They have names like ReportPresenter or OrderCompletionPresenter.
In contrast, this second generation of presenters are oriented primarily towards specific models. They have names like UserPresenter or PicturePostPresenter. They enable a particular model instance to render itself to a page.