Slide 1

Slide 1 text

OBJECTS ON RAILS Lailson Bandeira

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Conteúdo avançado

Slide 4

Slide 4 text

WHOAMI Lailson Bandeira Co-founder of Guava Software Co-founder Frevo on Rails MS.c. in Machine Learning at CIn/UFPE Member of the VIISAR Research Group Sun Certified Java Programmer (SCJP)

Slide 5

Slide 5 text

M V C

Slide 6

Slide 6 text

M C V

Slide 7

Slide 7 text

Fat controllers

Slide 8

Slide 8 text

Skinny controllers, fat models

Slide 9

Slide 9 text

Bullshit!

Slide 10

Slide 10 text

You’ll talk about concerns, right?

Slide 11

Slide 11 text

Noooooo!

Slide 12

Slide 12 text

PRESENTER aka Exhibit aka View Model

Slide 13

Slide 13 text

PRESENTER DEFINITION Presenters objects are a type of decorator specialized for presenting models to an end user. — Avdi Grimm M C V

Slide 14

Slide 14 text

PRESENTER LONG DEFINITION If the Model is concerned with storing and manipulating business data, and the View is concerned with displaying it, you can think of the Exhibit as standing between them deciding which data to show, and in what order. It may also provide some extra presentation-specific information (such as the specific URLs for related re- sources) which the business model has no knowledge of by itself. — Avdi Grimm

Slide 15

Slide 15 text

PRESENTER WHEN TO USE IT Use it when you have complex presentation rules.

Slide 16

Slide 16 text

PRESENTER WHEN TO USE IT Presenters are the ideal place to: • format complex data for user display • define commonly-used representations of an object • mark up attributes with a little semantic HTML — Draper

Slide 17

Slide 17 text

PRESENTER EXAMPLE # app/helpers/articles_helper.rb def publication_status(article) if article.published? "Published at #{article.published_at.strftime('%A, %B %e')}" else "Unpublished" end end

Slide 18

Slide 18 text

PRESENTER # app/decorators/article_decorator.rb class ArticleDecorator < Draper::Decorator delegate_all ! def publication_status if published? "Published at #{published_at}" else "Unpublished" end end ! def published_at object.published_at.strftime("%A, %B %e") end end EXAMPLE

Slide 19

Slide 19 text

PRESENTER # app/controllers/articles_controller.rb def show @article = Article.find(params[:id]).decorate end ! # view <%= @article.publication_status %> EXAMPLE

Slide 20

Slide 20 text

PRESENTER LIBRARIES • draper https://github.com/drapergem/draper ! • cells https://github.com/apotonick/cells • DIY with Forwardables

Slide 21

Slide 21 text

FORM OBJECT

Slide 22

Slide 22 text

FORM OBJECT DEFINITION Form objects decouple your models from forms. They usually perform validations, setup of nested models, rendering of form helpers and coordination of object relations. — Nick Sutterer M C V

Slide 23

Slide 23 text

FORM OBJECT WHEN TO USE IT Use it when when you have a form which doesn’t match your models.

Slide 24

Slide 24 text

FORM OBJECT EXAMPLE # models ! class User < ActiveRecord::Base has_one :profile end ! class Profile < ActiveRecord::Base belongs_to :user end

Slide 25

Slide 25 text

FORM OBJECT EXAMPLE # form class UserForm < Reform::Form include DSL include Reform::Form::ActiveModel ! properties [:first_name, :last_name], on: :user properties [:email, :locale, :age], on: :profile ! model :user, on: :user ! validates :first_name, :last_name, :email, presence: true end

Slide 26

Slide 26 text

FORM OBJECT EXAMPLE class UsersController < ApplicationController def new @form = Forms::UserForm.new(user: User.new, profile: Profile.new) end ! def create if form.validate(params) form.save do |data, map| user = User.create(map[:user]) map[:user_id] = user.id Profile.create(map[:profile]) end else render action: :new end end end

Slide 27

Slide 27 text

FORM OBJECT • reform https://github.com/apotonick/reform ! • bureaucrat https://github.com/tizoc/bureaucrat • DIY with virtus
 https://github.com/solnic/virtus LIBRARIES

Slide 28

Slide 28 text

VALIDATOR

Slide 29

Slide 29 text

VALIDATOR DEFINITION Validator is an object that validates one or more objects. — Unknown author M C V

Slide 30

Slide 30 text

WHEN TO USE IT Use it when you have complex validation rules, possible spanning multiple objects or when you have contextual validation. VALIDATOR

Slide 31

Slide 31 text

EXAMPLE # models ! class Article < ActiveRecord::Base # attrs title, body, status end VALIDATOR

Slide 32

Slide 32 text

EXAMPLE class Edit < Scrivener attr_accessor :title attr_accessor :body ! def validate assert_present :title assert_present :body end end ! edit = Edit.new(title: title, body: body) edit.valid? #=> true ! article = Article.new(edit.attributes) article.save VALIDATOR

Slide 33

Slide 33 text

EXAMPLE class Publish < Scrivener attr_accessor :status ! def validate assert_format :status, /^(published|draft)$/ end end ! publish = Publish.new(status: "published") publish.valid? #=> true ! article.update_attributes(publish.attributes) VALIDATOR

Slide 34

Slide 34 text

• scrivener https://github.com/soveran/scrivener ! • vanguard https://github.com/mbj/vanguard • DIY with active_model LIBRARIES VALIDATOR

Slide 35

Slide 35 text

POLICY

Slide 36

Slide 36 text

POLICY DEFINITION Policy is an object that encapsulates permission business rules. — Me M C V

Slide 37

Slide 37 text

POLICY WHEN TO USE IT Use it when your application has complex domain/business rules.

Slide 38

Slide 38 text

POLICY EXAMPLE class InvestmentPromotionPolicy attr_reader :investment ! def initialize(investment) @investment = investment end ! def promoted? valid_promotion_date? and owner_promotable? end ! def owner_promotable? investment.owner.active_for_promotion? end ! def promotion_status case when promoted? :promoted when valid_promotion_date? and !owner_promotable? :pending_for_promotion else :not_promoted end end ! def valid_promotion_date? (investment.promotion_starts_at..investment.promotion_ends_at).cover? Time.now end end

Slide 39

Slide 39 text

POLICY EXAMPLE class Investment < ActiveRecord::Base delegate :promoted?, :owner_promotable?, :promotion_status to: :promotion_policy ! # some methods ! private ! def promotion_policy @promotion_policy ||= InvestmentPromotionPolicy.new(self) end end

Slide 40

Slide 40 text

POLICY EXAMPLE 2 class PostPolicy attr_reader :user, :post ! def initialize(user, post) @user = user @post = post end ! def update? user.admin? or not post.published? end end

Slide 41

Slide 41 text

POLICY EXAMPLE 2 # controller def update @post = Post.find(params[:id]) authorize @post if @post.update(post_params) redirect_to @post else render :edit end end

Slide 42

Slide 42 text

POLICY • pundit https://github.com/elabs/pundit ! • DIY with POROs LIBRARIES

Slide 43

Slide 43 text

INTERACTOR aka Service aka Workflow aka Use Case

Slide 44

Slide 44 text

INTERACTOR DEFINITION An interactor is an object with a simple interface and a singular purpose. It provides a common interface for performing complex interactions in a single request and usually represents a business process. — Collective Idea M C V

Slide 45

Slide 45 text

INTERACTOR WHEN TO USE IT Use it when your application complex domain/business process.

Slide 46

Slide 46 text

INTERACTOR EXAMPLE class AuthenticateUser include Interactor ! def perform if user = User.authenticate(context[:email], context[:password]) context[:user] = user else context.fail! end end end

Slide 47

Slide 47 text

INTERACTOR EXAMPLE class SessionsController < ApplicationController def create result = AuthenticateUser.perform(session_params) ! if result.success? redirect_to result.user else render :new end end ! private def session_params params.require(:session).permit(:email, :password) end end

Slide 48

Slide 48 text

INTERACTOR EXAMPLE class PlaceOrder include Interactor::Organizer ! organize [ CheckInventory, CalculateTax, ChargeCard, CreateOrder, DeliverThankYou, DeliverOrderNotification, ScheduleFollowUp ] end

Slide 49

Slide 49 text

INTERACTOR • interactor https://github.com/collectiveidea/interactor ! • mutations https://github.com/cypriss/mutations ! • DIY with POROs LIBRARIES

Slide 50

Slide 50 text

QUERY

Slide 51

Slide 51 text

QUERY DEFINITION Query is an object that implements an interface for querying the database. — Luca Guidi M C V

Slide 52

Slide 52 text

QUERY WHEN TO USE IT Use it when your application business rules requires complex queries.

Slide 53

Slide 53 text

QUERY class MediaQuery attr_reader :start_date, :end_date, :term, :location_name ! def initialize(params={}) @start_date = params[:start_date] @end_date = params[:end_date] @term = params[:term] @location_name = params[:location_name] end ! def generate query = Medium.scoped query = query.gte(created_at: start_date) if start_date query = query.lte(created_at: end_date) if end_date query = query.where(location: location_name) if location_name.present? query = query.text(term) if term.present? query end ! end EXAMPLE

Slide 54

Slide 54 text

QUERY EXAMPLE • arel https://github.com/rails/arel ! • origin https://github.com/mongoid/origin ! • DIY with POROs

Slide 55

Slide 55 text

REFERENCES • 7 Ways to Decompose Fat ActiveRecord Models http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ ! • Structuring Rails Applications http://karolgalanciak.com/blog/2013/10/06/structuring-rails-applications/ ! • Concerns, Decorators, Presenters, Service Objects, Helpers, Help Me Decide! http://confreaks.com/videos/3329-railsconf-concerns-decorators-presenters-service-objects-helpers-help-me-decide ! • Catalog of Patterns of Enterprise Application Architecture http://martinfowler.com/eaaCatalog/

Slide 56

Slide 56 text

That’s all, folks! XXI FREVO ON RAILS MEETUP Slides presented by Lailson Bandeira on July 26, 2014 as part of the 21st Frevo on Rails Meetup, available at https://speakerdeck.com/lailsonbm. Slides created with Keynote with the fonts Monserrat, Source Sans Pro and Inconsolata. LAILSON BANDEIRA http://lailson.me/ http://github.com/lailsonbm http://facebook.com/guavasoftware