Slide 1

Slide 1 text

Rails Variants

Slide 2

Slide 2 text

O Rails tem content negotiation faz tempo.

Slide 3

Slide 3 text

class TasksController < ApplicationController def index @tasks = Task.all ! respond_to do |format| format.html format.xml { render xml: @tasks } format.json { render json: @tasks } end end end

Slide 4

Slide 4 text

Para renderizar cada formato, basta especificar o parâmetro :format.

Slide 5

Slide 5 text

$ rake routes Prefix Verb URI Pattern Controller#Action root GET / site#index tasks GET /tasks(.:format) tasks#index POST /tasks(.:format) tasks#create new_task GET /tasks/new(.:format) tasks#new edit_task GET /tasks/:id/edit(.:format) tasks#edit task GET /tasks/:id(.:format) tasks#show PATCH /tasks/:id(.:format) tasks#update PUT /tasks/:id(.:format) tasks#update DELETE /tasks/:id(.:format) tasks#destroy

Slide 6

Slide 6 text

O formato pode ser especificado para cada uma das rotas que você definiu.

Slide 7

Slide 7 text

/tasks.json /tasks?format=json

Slide 8

Slide 8 text

À partir do Rails 3 você pode otimizar o processo de content negotiation.

Slide 9

Slide 9 text

class TasksController < ApplicationController respond_to :html, :json, :xml ! def index @tasks = Task.all respond_with(@tasks) end end

Slide 10

Slide 10 text

Você pode acessar e definir explicitamente o formato de uma requisição.

Slide 11

Slide 11 text

request.format request.format = :json

Slide 12

Slide 12 text

Você pode criar responders personalizados que alteram o comportamento padrão do Rails.

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

class TaskCreateResponder < ActionController::Responder delegate :t, :flash, to: :controller def to_html if resource.new_record? flash.notice = t('flash.tasks.create.notice') else flash.alert = resource.errors.full_messages .to_sentence end ! redirect_to navigation_location end end

Slide 16

Slide 16 text

Para usar um responder, especifique a opção :responder.

Slide 17

Slide 17 text

class TasksController < ApplicationController respond_to :html, :json ! def create @task = Task.create(task_params) respond_with @task, location: tasks_path, responder: TaskCreateResponder end end

Slide 18

Slide 18 text

Variants

Slide 19

Slide 19 text

O Rails Variants permite criar uma especialização para o formato que está sendo renderizado.

Slide 20

Slide 20 text

A primeira ideia que vem à cabeça é renderizar um template específico para dispositivos móveis.

Slide 21

Slide 21 text

class ApplicationController < ActionController::Base before_action :set_variant ! private ! def set_variant request.variant = :tablet if browser.tablet? request.variant = :mobile if browser.mobile? end end

Slide 22

Slide 22 text

http://fnando.me/14l

Slide 23

Slide 23 text

Caso uma dessas condições seja satisfeita, o template para aquela variante será usado.

Slide 24

Slide 24 text

application.html.erb application.html+tablet.erb application.html+mobile.erb ! index.html.erb index.html+tablet.erb index.html+mobile.erb

Slide 25

Slide 25 text

Você pode combinar respond_to com variantes.

Slide 26

Slide 26 text

respond_to do |format| format.html do |variant| variant.none(&block) variant.tablet(&block) variant.mobile(&block) end ! format.json end

Slide 27

Slide 27 text

Todos os exemplos de variantes são sempre baseados em detecção de dispositivos.

Slide 28

Slide 28 text

Mas variantes podem ser usadas para abstrair a lógica de renderização.

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

User#security_mode none, card, sms, totp

Slide 31

Slide 31 text

class SecurityModeController < ApplicationController def edit request.variant = SecurityMode.fetch(params[:security_mode]) ! respond_to do |format| format.html do |variant| variant.none variant.card { initialize_card_security_mode } variant.sms { initialize_sms_security_mod } variant.totp { initialize_totp_security_mode } end end end end

Slide 32

Slide 32 text

edit.html+card.erb edit.html+none.erb edit.html+sms.erb edit.html+totp.erb

Slide 33

Slide 33 text

A inicialização dos objetos necessários para renderizar a view pode ser feita dinamicamente.

Slide 34

Slide 34 text

class SecurityModeController < ApplicationController def edit security_mode = SecurityMode.fetch(params[:security_mode]) request.variant = security_mode ! initializer = "initialize_#{security_mode}_security_mode" send(initialize) if respond_to?(initializer, true) end end

Slide 35

Slide 35 text

Instancie apenas um objeto no controller. Sandi Metz, http://fnando.me/14v

Slide 36

Slide 36 text

class Dashboard def initialize(user) @user = user end ! def new_status @new_status ||= Status.new end ! def statuses Status.for(user) end ! def notifications @notifications ||= user.notifications end ! private ! attr_reader :user end

Slide 37

Slide 37 text

class DashboardsController < ApplicationController before_filter :authorize ! def show @dashboard = Dashboard.new(current_user) end end

Slide 38

Slide 38 text

<%= render 'profile' %> <%= render 'groups', groups: @dashboard.group %> ! <%= render 'statuses/form', status: @dashboard.new_status %> <%= render 'statuses', statuses: @dashboard.statuses %>

Slide 39

Slide 39 text

Outros casos de uso Testes A/B Rollout para beta testers Logado vs não logado

Slide 40

Slide 40 text

Evite adicionar tudo como variantes.

Slide 41

Slide 41 text

request.variant = [:logged, :iphone].join('_').to_sym application.html+logged_iphone.erb

Slide 42

Slide 42 text

A quantidade de combinações possíveis pode fugir do seu controle.

Slide 43

Slide 43 text

Tenha bom senso.

Slide 44

Slide 44 text

Obrigado.

Slide 45

Slide 45 text

* * *

Slide 46

Slide 46 text

http://howtocode.com.br