Upgrade to Pro — share decks privately, control downloads, hide ads and more …

10 cosas de Rails que deberías saber

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

10 cosas de Rails que deberías saber

Te has preguntado alguna vez ¿Existe vida después de ActiveRecord::Base? si es así, en esta charla vamos descubrir que Rails es algo más que un simple MVC, es un framework repleto de herramientas cuyo conocimiento nos va a facilitar enormemente la vida.

Con estas herramientas vas a poder extender Rails de una forma que no habías imaginado hasta ahora: crearas tus propios validadores, responders y renderers; serás capaz de enviar datos por streaming, de interceptar mails o añadir un nuevo middleware a tu stack.

Queremos mostrar 10 de estas herramientas junto con ejemplos de uso de cada una de ellas, para que las puedas incorporar poco a poco en tu día a día y descubras el mundo de posibilidades que realmente tienes en tus manos.

El conjunto de herramientas que vamos a mostrar es:

ActiveModel::Model
ActiveModel::Validator
ActiveSupport::Concern
ActionSupport::Notifications
ActionController::Renderers
ActionController::Responder
ActionController::Live
ActionView::Resolvers
ActionMailer Interceptors
Rack Middleware

Avatar for Gabriel Ortuño

Gabriel Ortuño

November 21, 2014
Tweet

More Decks by Gabriel Ortuño

Other Decks in Technology

Transcript

  1. MADRID · NOV 21-22 · 2014 10 cosas de Rails

    que deberías saber Carlos Sánchez & Gabriel Ortuño
  2. MADRID · NOV 21-22 · 2014 Carlos Sánchez Pérez Person.new(

    name: "Carlos Sánchez Pérez", job: "ASPgems", twitter: "carlossanchezp", github: "carlossanchezp", Blog: carlossanchezperez.wordpress.com")
  3. MADRID · NOV 21-22 · 2014 Gabriel Ortuño Person.new( name:

    "Gabriel Ortuño", job: "jobandtalent", web: "arctarus.com", twitter: "arctarus", github: "arctarus")
  4. MADRID · NOV 21-22 · 2014 Madrid.rb Group.new( name: "Madrid.rb",

    google_group: "madrid-rb", twitter: "madridrb", vimeo: "madridrb") ¡El último jueves de cada mes en el Irish Rover!
  5. MADRID · NOV 21-22 · 2014 Hoja de Ruta 1.

    ActiveSupport::Concern 2. ActiveModel::Validator 3. ActiveModel::Model 4. ActiveSupport::Notifications 5. ActionController::Renderer 6. ActionController::Responder 7. ActionController::Live 8. ActionView::Resolver 9. ActionMailer::Interceptor 10. RackMiddleware
  6. MADRID · NOV 21-22 · 2014 1. ActiveSupport::Concern ¿Qué es?

    ➔ Simple Syntactic Sugar sobre modulos de Ruby ➔ Forma estándar de extender clases desde Rails 4.0 ¿Para qué se usa? ➔ Agrupa métodos que definen una responsabilidad ➔ Mejora cohesión ➔ Permite reutilización
  7. MADRID · NOV 21-22 · 2014 # app/models/message.rb class Message

    < ActiveRecord::Base default_scope -> { where(trashed: false) } scope :trashed, -> { where(trashed: true) } def trash update_attribute :trashed, true end end
  8. MADRID · NOV 21-22 · 2014 # app/models/concerns/trashable.rb module Trashable

    extend ActiveSupport::Concern included do default_scope -> { where(trashed: false) } scope :trashed, -> { where(trashed: true) } end def trash update_attribute :trashed, true end end
  9. MADRID · NOV 21-22 · 2014 # app/models/message.rb class Message

    < ActiveRecord::Base include Trashable, Subscribable, Commentable end # app/models/post.rb class Post < ActiveRecord::Base include Trashable, Commentable end
  10. MADRID · NOV 21-22 · 2014 2. ActiveModel::Validator ¿Qué es?

    ➔ Es una clase base para implementar validadores que pueden ser usados por ActiveModel ¿Para qué sirve? ➔ Permite extraer lógica de validación de una forma sencilla y reutilizable
  11. MADRID · NOV 21-22 · 2014 # app/models/user.rb class User

    < ActiveRecord::Base validates_format_of :email, with: \A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/ end
  12. MADRID · NOV 21-22 · 2014 # lib/email_validator.rb class EmailValidator

    < ActiveModel::EachValidator def validate_each(record, attribute, value) message = options.fetch :message, I18n.t("validators.email") unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i record.errors[attribute] << message end end end
  13. MADRID · NOV 21-22 · 2014 class Person < ActiveRecord::Base

    validates :email, presence: true, email: true end
  14. MADRID · NOV 21-22 · 2014 person = Person.new(email: '[email protected]')

    person.valid? # ActiveModel ejecuta algo como.... validator = EmailValidator.new validator.validate_each(person, :email, '[email protected]')
  15. MADRID · NOV 21-22 · 2014 3. ActiveModel::Model ¿Qué es?

    ➔ Incluir funcionalidades de ActiveModel::Model sobre clases de Ruby ➔ Naming, Translation, Validation, Conversions. ¿Para qué se usa? ➔ Crear una clase con validaciones ➔ Utilizarla en las vistas
  16. MADRID · NOV 21-22 · 2014 class Contact include ActiveModel::Model

    attr_accessor :name, :email, :message validates :name, presence: true validates :email, presence: true, email: true validates :message, presence: true end
  17. MADRID · NOV 21-22 · 2014 class ContactsController < ApplicationController

    def new... def create @contact = Contact.new(params[:contact]) if @contact.valid? ContactMailer.new_contact(@contact).deliver redirect_to root_path else render :new end end end
  18. MADRID · NOV 21-22 · 2014 <h1>Contact Us</h1> <%= form_for

    @contact do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :message %> <%= f.text_area :message %> <%= f.submit %> <% end %>
  19. MADRID · NOV 21-22 · 2014 4. ActiveSupport::Notifications ¿Qué es?

    ➔ Envio de notificaciones cuando se producen acciones estándar. ¿Para qué se usa? ➔ Imagina que nos preguntamos “¿Por qué nuestra página es tan lenta?" ➔ Cómo podemos utilizar las notificaciones para obtener las métricas.
  20. MADRID · NOV 21-22 · 2014 # config/initializers/notification.rb ActiveSupport::Notifications.subscribe "process_action.

    action_controller" do|name,start,finish,id,payload| PageRequest.create! do |page_request| page_request.path = payload[:path] page_request.page_duration = (finish - start) * 1000 page_request.view_duration = payload[:view_runtime] page_request.db_duration = payload[:db_runtime] end end
  21. MADRID · NOV 21-22 · 2014 class Product < ActiveRecord::Base

    def self.search_by_name(text) ActiveSupport::Notifications.instrument( "products.search_by_name", search: text) where("name LIKE ?", "%#{text}%") end end
  22. MADRID · NOV 21-22 · 2014 # config/initializers/notification.rb ActiveSupport::Notifications.subscribe "products.search_by_name"

    do |name, start, finish, id, payload| Rails.logger.debug "SEARCH: #{payload[:search]}" end
  23. MADRID · NOV 21-22 · 2014 5. ActionController::Renderer ¿Qué es?

    ➔ Un hook que expone el método render para personalizar su comportamiento. ¿Para qué se usa? ➔ Nos permite devolver respuestas con formatos personalizados como csv, pdf, zip, etc.
  24. MADRID · NOV 21-22 · 2014 # app/controllers/csvable_controller.rb def show

    @csvable = Csvable.find(params[:id]) respond_to do |format| format.html format.csv { render csv: @csvable, filename: @csvable.name } end end
  25. MADRID · NOV 21-22 · 2014 # lib/renderers/csv_renderer.rb ActionController::Renderers.add :csv

    do |obj, options| filename = options.fetch(:filename, 'data') str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s send_data str, type: Mime::CSV, disposition: "attachment; filename=#{filename}.csv" end # config/initializer/mime_types.rb Mime::Type.register "text/csv", :csv
  26. MADRID · NOV 21-22 · 2014 6. ActionController::Responder ¿Qué es?

    ➔ Abstrae como funciona los controllers según: ◆ Tipo de request: HTML, XML, JSON ◆ Verbo http usando: GET, POST, PUT, PATCH, DELETE ◆ Estado del recurso: válido, con errores ¿Para qué se usa? ➔ Evita que repitamos lógica entre controladores ➔ FlashResponder, HttpCacheResponder...
  27. MADRID · NOV 21-22 · 2014 def create @user =

    User.new(params[:user]) respond_to do |format| if @user.save flash[:notice] = 'User was successfully created.' format.html { redirect_to @user } format.xml { render xml: @user, status: :created, location: @user } else format.html { render action: "new" } format.xml { render xml: @user.errors, status: :unprocessable_entity } end end end
  28. MADRID · NOV 21-22 · 2014 class UsersController < ApplicationController

    respond_to :html, :xml def create @user = User.new(params[:user]) flash[:notice] = 'User was successfully created.' if @user.save respond_with @user end end
  29. MADRID · NOV 21-22 · 2014 # lib/flash_responder.rb class FlashResponder

    < ActionController::Responder def to_html set_flash_message! unless get? super end private def set_flash_message! status = has_errors? ? :alert : :notice return if controller.flash[status].present? message = i18n_lookup(status) controller.flash[status] = message if message.present? end end
  30. MADRID · NOV 21-22 · 2014 # app/controllers/application_controller.rb class ApplicationController

    < ActionController::Base self.responder = FlashResponder respond_to :html end
  31. MADRID · NOV 21-22 · 2014 7. ActionController::Live ¿Qué es?

    ➔ Módulo que permite el envío datos en streaming. ➔ Requiere el uso un servidor como de Puma o Thin ➔ ActionController::Live::SSE (Server Side Events) ¿Para qué se usa? ➔ Envio de datos al clientes sin necesidad de iniciar una nueva request. ActionController::Live::SS
  32. MADRID · NOV 21-22 · 2014 class MessagesController < ApplicationController

    include ActionController::Live def events 3.times do |n| response.stream.write "#{n}...\n\n" sleep 2 end ensure response.stream.close end end
  33. MADRID · NOV 21-22 · 2014 class MessagesController < ApplicationController

    include ActionController::Live def create attributes = params.require(:message).permit(:content, :name) @message = Message.create!(attributes) $redis.publish('messages.create', @message.to_json) end end
  34. MADRID · NOV 21-22 · 2014 # app/controllers/messages_controller.rb def events

    response.headers['Content-Type'] = 'text/event-stream' sse = SSE.new(response.stream, event: "live.messages") $redis.psubscribe('messages.create') do |on| on.pmessage do |pattern, event, data| sse.write(data, event: event, retry: 500) end end ensure sse.close end
  35. MADRID · NOV 21-22 · 2014 # app/assets/javascript/messages.js.coffee source =

    new EventSource('/messages/events') source.addEventListener 'live.messages', (e) -> message = $.parseJSON(e.data).message $('#chat').append($('<li>'). text("#{message.name}: #{message.content}"))
  36. MADRID · NOV 21-22 · 2014 8. ActionView::Resolver ¿Qué es?

    ➔ Es el responsable de encontrar el template que debe ser renderizado. ¿Para qué se usa? ➔ Obtener templates directamente de la base de datos. ➔ Modificar el directorio de vistas a renderizar en caso de no existir para el controlador actual. ActionController::Live::SS
  37. MADRID · NOV 21-22 · 2014 # lib/admin/resolver.rb class Admin::Resolver

    < ::ActionView::FileSystemResolver def initialize super("app/views") end def find_templates(name, prefix, partial, details) super(name, "admin/defaults", partial, details) end end
  38. MADRID · NOV 21-22 · 2014 class SqlResolver < ActionView::Resolver

    def find_templates(name, prefix, partial, details) conditions = { path: normalize_path(name, prefix, partial), locale: normalize_array(details[:locale]).first, format: normalize_array(details[:formats]).first, handler: normalize_array(details[:handlers]), partial: partial || false } ::SqlTemplate.where(conditions).map do |record| initialize_template(record) end end end
  39. MADRID · NOV 21-22 · 2014 9. ActionMailer::Interceptor ¿Qué es?

    ➔ Son hooks en el proceso de enviar emails ➔ Podríamos decir que ActionMailer ofrece una forma de manipular los atributos de nuestros emails justo antes de su envío. ¿Para qué se usa? ➔ Un ejemplo sería si tenemos correos electrónicos de usuarios reales, modificar el destinatario y el asunto de un email de desarrollo.
  40. MADRID · NOV 21-22 · 2014 class EnvironmentInterceptor def self.delivering_email(message)

    message.to = "[email protected]" message.subject = "#{Rails.env} #{message.subject}" end end
  41. MADRID · NOV 21-22 · 2014 # Ahora registramos nuestro

    interceptor: # config/initializers/mail.rb require 'environment_interceptor' unless Rails.env.production? ActionMailer::Base.register_interceptor(EnvironmentInterceptor) end
  42. MADRID · NOV 21-22 · 2014 10. Rack Middleware ¿Qué

    es? ➔ Clase ruby que intercepta una request dentro de una Rack app y la procesa para acabar devolviendo una response. ➔ Es una implementación de Pipeline Design Pattern ¿Para que se usa? ➔ Modificar cabeceras de la respuesta, caching, manage exceptions, almacenar cookies, etc
  43. MADRID · NOV 21-22 · 2014 Rack Rack proporciona una

    interfaz mínima entre servidores web y frameworks Ruby
  44. MADRID · NOV 21-22 · 2014 $ bin/rake middleware use

    Rack::Lock use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActiveRecord::Migration::CheckPending use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache ...
  45. MADRID · NOV 21-22 · 2014 class RackMiddleware def initialize(app)

    @app = app # Rack app end def call(env) status, headers, response = @app.call(env) ... [status, headers, response] end end Simplest Rack Middleware
  46. MADRID · NOV 21-22 · 2014 require 'digest/md5' module Rack

    # Automatically sets the ETag header on all String bodies class ETag def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if !headers.has_key?('ETag') parts = [] body.each { |part| parts << part.to_s } headers['ETag'] = %("#{Digest::MD5.hexdigest(parts.join(""))}") [status, headers, parts] else [status, headers, body] end end end end
  47. MADRID · NOV 21-22 · 2014 # Rack::Etag def call(env)

    status, headers, body = @app.call(env) if !headers.has_key?('ETag') parts = [] body.each { |part| parts << part.to_s } etag = %("#{Digest::MD5.hexdigest(parts.join(""))}") headers['ETag'] = etag [status, headers, parts] else [status, headers, body] end end
  48. MADRID · NOV 21-22 · 2014 # config/application.rb # Push

    Rack::ETag at the bottom config.middleware.use Rack::ETag # Add Rack::ETag after ActiveRecord::QueryCache. config.middleware.insert_after ActiveRecord::QueryCache, Rack::ETag
  49. MADRID · NOV 21-22 · 2014 ActiveSupport::Concern • Put chubby

    models on a diet with concerns : https://signalvnoise.com/posts/3372-put-chubby- models-on-a-diet-with-concerns ActiveModel::Validator • Seven useful validator: http://viget.com/extend/seven-useful-activemodel-validators ActiveModel::Model • ActiveModel::Model [Rails 4 Countdown to 2013]: http://blog.remarkablelabs. com/2012/12/activemodel-model-rails-4-countdown-to-2013 ActionController::Responder • http://blog.plataformatec.com.br/2009/08/embracing-rest-with-mind-body-and-soul/ • https://github.com/plataformatec/responders
  50. MADRID · NOV 21-22 · 2014 ActionView::Resolver • Plataformatec: http://blog.plataformatec.com.br/2011/04/default-views-in-rails-3-0-with-custom-

    resolvers/ ActionController::Live • Railscast #401: http://railscasts.com/episodes/401-actioncontroller-live • Tenderlove, Is it live?: http://tenderlovemaking.com/2012/07/30/is-it-live.html Rack Middlewares • Railscast #151: http://railscasts.com/episodes/151-rack-middleware • Stackoverflow: http://stackoverflow.com/questions/2256569/what-is-rack-middleware • Amberit: https://www.amberbit.com/blog/2011/07/13/introduction-to-rack-middleware/