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

Rails - the dark side

Rails - the dark side

In this talk we will see how to be careful with the framework, and how and what to use according to some situations. Should we always use Fat Models and Skinny? Should we also stick with the MVC model for everything? Should we use Concerns? What about JS+ERB? Use convention over configuration in Rails is always a good thing to do?

Franzé Jr.

November 28, 2019
Tweet

More Decks by Franzé Jr.

Other Decks in Programming

Transcript

  1. A en a 4 I 4 C l 4 P

    s 4 I /P a s n 4 M 4 P s 4 I /P a s n 4 V 4 P s 4 I /P a s n 4 C i
  2. W n ed o ho se xa tl w at

    c lo s nd ow o se he
  3. B t e nd f he al 4 I e

    o k r A s n E u 4 E i c e i a c p 4 M r h "p s" c p s o l m a t
  4. O er im R il b ca e as er

    nd as er o se.
  5. D n t et he ra ew rk ec de

    ve yt in f r s. D k s ?
  6. R il l ts s ak b d n w

    th ut iv ng s ny ar in , a d y he im w r al ze t he ha ge w n ed o ak a e os ly. 1 T l R d e
  7. "w r al y ee t m ve as t

    m ke ur ar et ex y ar s w a e ed ci g <s me es gn ct vi y>".
  8. class SomeController < ApplicationController def blah @model = ... @model.perform_side_effects

    render ... end end class Model < ActiveRecord::Base def perform_side_effects self.some_association.perform_side_effects end end class SomeAssociation < ActiveRecord::Base def perform_side_effects do_the_actual_work end private def do_the_actual_work update_attributes(...) end end e f : h ://j e .c /p /r -v -p -m /
  9. class SomeController < ApplicationController def blah @model = ... @model.perform_side_effects

    render ... end end class Model < ActiveRecord::Base def perform_side_effects self.some_association.perform_side_effects end end class SomeAssociation < ActiveRecord::Base def perform_side_effects do_the_actual_work end private def do_the_actual_work update_attributes(...) end end e f : h ://j e .c /p /r -v -p -m /
  10. class SomeController < ApplicationController def blah @model = ... @model.perform_side_effects

    render ... end end class Model < ActiveRecord::Base def perform_side_effects self.some_association.perform_side_effects end end class SomeAssociation < ActiveRecord::Base def perform_side_effects do_the_actual_work end private def do_the_actual_work update_attributes(...) end end e f : h ://j e .c /p /r -v -p -m /
  11. class SomeController < ApplicationController def blah @model = ... @model.perform_side_effects

    render ... end end class Model < ActiveRecord::Base def perform_side_effects self.some_association.perform_side_effects end end class SomeAssociation < ActiveRecord::Base def perform_side_effects do_the_actual_work end private def do_the_actual_work update_attributes(...) end end e f : h ://j e .c /p /r -v -p -m /
  12. class SomeController < ApplicationController def blah @model = ... @model.perform_side_effects

    render ... end end class Model < ActiveRecord::Base def perform_side_effects self.some_association.perform_side_effects end end class SomeAssociation < ActiveRecord::Base def perform_side_effects do_the_actual_work end private def do_the_actual_work update_attributes(...) end end e f : h ://j e .c /p /r -v -p -m /
  13. class SomeController < ApplicationController def blah @model = ... @model.perform_side_effects

    render ... end end class Model < ActiveRecord::Base def perform_side_effects self.some_association.perform_side_effects end end class SomeAssociation < ActiveRecord::Base def perform_side_effects do_the_actual_work end private def do_the_actual_work update_attributes(...) end end e f : h ://j e .c /p /r -v -p -m /
  14. P ob em 4 I o c a h c

    l c 4 C l o r h g d e e 4 M c a m (a t ) n i a d e e 4 H u a 4 H t
  15. class CheckoutsController < ApplicationController ... def checkout cart = Cart.new(params[:cart])

    if current_user.default_payment_method.pay(cart.amount) line_items = cart.line_items line_items = line_items.map do |line_item| amount = line_item.product.price * line_item.quantity LineItem.new(product: line_item.product, quantity: line_item.quantity, amount: amount) end Order.create(user: current_user, line_items: line_items) Invoice.create(user: current_user, line_items: line_items) CheckoutMailer.with(user: current_user, line_items: line_items).deliver_now Metrics.send(user: current_user, cart: cart) flash.now[:notice] = 'You have just paid' render action: 'success' else flash.now[:error] = 'Could not make checkout' render action: 'payment' end end ... end
  16. class CheckoutsController < ApplicationController ... def checkout cart = Cart.new(params[:cart])

    if current_user.default_payment_method.pay(cart.amount) line_items = cart.line_items line_items = line_items.map do |line_item| amount = line_item.product.price * line_item.quantity LineItem.new(product: line_item.product, quantity: line_item.quantity, amount: amount) end Order.create(user: current_user, line_items: line_items) Invoice.create(user: current_user, line_items: line_items) CheckoutMailer.with(user: current_user, line_items: line_items).deliver_now Metrics.send(user: current_user, cart: cart) flash.now[:notice] = 'You have just paid' render action: 'success' else flash.now[:error] = 'Could not make checkout' render action: 'payment' end end ... end
  17. class CheckoutsController < ApplicationController ... def checkout cart = Cart.new(params[:cart])

    if current_user.default_payment_method.pay(cart.amount) line_items = cart.line_items line_items = line_items.map do |line_item| amount = line_item.product.price * line_item.quantity LineItem.new(product: line_item.product, quantity: line_item.quantity, amount: amount) end Order.create(user: current_user, line_items: line_items) Invoice.create(user: current_user, line_items: line_items) CheckoutMailer.with(user: current_user, line_items: line_items).deliver_now Metrics.send(user: current_user, cart: cart) flash.now[:notice] = 'You have just paid' render action: 'success' else flash.now[:error] = 'Could not make checkout' render action: 'payment' end end ... end
  18. Y u ig t on id r nc rp ra

    e om o t es i ea
  19. H na i S mp e ub f am wo

    k 4 T u h "R b " 4 T o l R
  20. I r/L S R Y o M t H E

    J é V P r R R D B p P F r C M D d H L G ---
  21. # apps/web/controllers/home/index.rb module Web module Controllers module Home class Index

    include Web::Action def call(params) end end end end end E a H d n a s c , w m s t
  22. S ng e es on ib li y ri ci

    le A od le ha c mp le a d ri ts r po t. 4 T c c c 4 T f c c O s t c , n n c c. T ey av t o es on ib it es, a d he t ey ho ld e n wo ep ra e la se .
  23. P oe ix VC 4 S y d e 4

    S d e e 4 S e e v h c l
  24. defmodule HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do

    case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end 4 T c l e a c n h h d e e 4 T c l d 't o a o r a n h d
  25. defmodule HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do

    case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end 4 T c l e a c n h h d e e 4 T c l d 't o a o r a n h d
  26. defmodule HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do

    case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end 4 T c l e a c n h h d e e 4 T c l d 't o a o r a n h d
  27. defmodule HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do

    case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end 4 T c l e a c n h h d e e 4 T c l d 't o a o r a n h d
  28. defmodule HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do

    case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end 4 T c l e a c n h h d e e 4 T c l d 't o a o r a n h d
  29. defmodule HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do

    case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end 4 T c l e a c n h h d e e 4 T c l d 't o a o r a n h d
  30. F t od ls, s in y on ro le

    s I c n e oo , b t nt l c rt in oi t.
  31. L ts f es on ab li ie i t

    e od ls. 4 C a b s l m o f n S 4 H t 4 I 's s e s t f h l c k u R a
  32. U . r .m .c e a (0.8 ) S

    "u ".* O "u " O "u "."i " S L $1 [["L ", 1]] => 2
  33. W en hi c ul b a ro le :

    4 I o n d u a t H 4 M u r a c p l r a t 4 T o e s 3 o k t 4 H n m r o g a e? H w o ee t: 4 R 6 a m a i h t l 4 S t e o m a i
  34. Y u an ed b na a ut ha y

    u ot as g ri la ol in t e an na 1 J A n
  35. D ma n ou le t a ar ic la

    p rs st nc m ch ni m P te ti l ro le s 4 G m , a m a e e i n k e i 4 H t 4 E i h m p 4 H m n
  36. R po it ry class ProductsRepo def find(product_id) Product.find(product_id) end

    def all Product.all end def new Product.new end def update(product, params) product.update(params) end def destroy(product) product.destroy end def save(product) product.save end end
  37. U in a ep si or i t e on

    ro le class ProductsController < ApplicationController def index @products = repo.all end ... private def repo @products_repo ||= ProductsRepo.new end end class ProductsRepo def all Product.all end end
  38. U in a ep si or i t e on

    ro le class ProductsController < ApplicationController def index @products = repo.all end ... private def repo @products_repo ||= ProductsRepo.new end end class ProductsRepo def all Product.all end end
  39. U in a ep si or i t e on

    ro le class ProductsController < ApplicationController def index @products = repo.all end ... private def repo @products_repo ||= ProductsRepo.new end end class ProductsRepo def all Product.all end end
  40. U in a ep si or i t e on

    ro le class ProductsController < ApplicationController def index @products = repo.all end ... private def repo @products_repo ||= ProductsRepo.new end end class ProductsRepo def all Product.all end end
  41. U in a ep si or i t e on

    ro le class ProductsController < ApplicationController def index @products = repo.all end ... private def repo @products_repo ||= ProductsRepo.new end end class ProductsRepo def all Product.all end end
  42. E to 4 I t t d e E 4

    D v e n p e
  43. defmodule Hello.User do use Ecto.Schema import Ecto.Changeset alias Hello.User schema

    "users" do field :bio, :string field :email, :string field :name, :string field :number_of_pets, :integer timestamps() end @doc false def changeset(%User{} = user, attrs) do user |> cast(attrs, [:name, :email, :bio, :number_of_pets]) |> unique_constraint(:email) |> validate_required([:name, :email, :bio, :number_of_pets]) end end
  44. defmodule Hello.User do use Ecto.Schema import Ecto.Changeset alias Hello.User schema

    "users" do field :bio, :string field :email, :string field :name, :string field :number_of_pets, :integer timestamps() end @doc false def changeset(%User{} = user, attrs) do user |> cast(attrs, [:name, :email, :bio, :number_of_pets]) |> unique_constraint(:email) |> validate_required([:name, :email, :bio, :number_of_pets]) end end
  45. defmodule Hello.User do use Ecto.Schema import Ecto.Changeset alias Hello.User schema

    "users" do field :bio, :string field :email, :string field :name, :string field :number_of_pets, :integer timestamps() end @doc false def changeset(%User{} = user, attrs) do user |> cast(attrs, [:name, :email, :bio, :number_of_pets]) |> unique_constraint(:email) |> validate_required([:name, :email, :bio, :number_of_pets]) end end
  46. defmodule Hello.User do use Ecto.Schema import Ecto.Changeset alias Hello.User schema

    "users" do field :bio, :string field :email, :string field :name, :string field :number_of_pets, :integer timestamps() end @doc false def changeset(%User{} = user, attrs) do user |> cast(attrs, [:name, :email, :bio, :number_of_pets]) |> unique_constraint(:email) |> validate_required([:name, :email, :bio, :number_of_pets]) end end
  47. C nt xt defmodule Hello.Accounts do import Ecto.Query, warn: false

    alias Hello.Repo alias Hello.Accounts.User def list_users do Repo.all(User) end ... end 4 S a f n h b s l 4 C s r d e m a e n g r f n .
  48. C nt xt defmodule Hello.Accounts do import Ecto.Query, warn: false

    alias Hello.Repo alias Hello.Accounts.User def list_users do Repo.all(User) end ... end 4 S a f n h b s l 4 C s r d e m a e n g r f n .
  49. C nt xt defmodule Hello.Accounts do import Ecto.Query, warn: false

    alias Hello.Repo alias Hello.Accounts.User def list_users do Repo.all(User) end ... end 4 S a f n h b s l 4 C s r d e m a e n g r f n .
  50. 4 C s a e i h c l defmodule

    HelloWeb.UserController do ... def create(conn, %{"user" => user_params}) do case Accounts.create_user(user_params) do {:ok, user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: Routes.user_path(conn, :show, user)) {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) end end ... end
  51. T er i a ot f uz a ou d

    he am s rv ce
  52. N ti n f rc es ra io Y n

    a c t t : - C t S u : - O - P - P - C
  53. T ai bl ze 4 I 's a b s

    l 4 A O a i 4 W t c g i 4 I c a l s a T a
  54. O er ti n 4 R l b s l

    o h c l n m 4 P a s e, s i o o i .
  55. result = Song::Create.({ title: "SVT" }) class Song::Create < Trailblazer::Operation

    extend Contract::DSL contract do property :title validates :title, presence: true end step Model( Song, :new ) step :assign_current_user! step Contract::Build() step Contract::Validate( ) failure :log_error! step Contract::Persist( ) def log_error!(options) # .. end def assign_current_user!(options) options["model"].created_by = options["current_user"] end end
  56. step Model( Song, :new ) step :assign_current_user! step Contract::Build() step

    Contract::Validate( ) failure :log_error! step Contract::Persist( ) Y a a d t a o n u o o . 4 H o t a o r d 4 W o e c
  57. require "dry/transaction" class CreateUser include Dry::Transaction step :validate step :create

    private def validate(input) # returns Success(valid_data) or Failure(validation) end def create(input) # returns Success(user) end end
  58. S mi ar o ra lb az r pe at

    on & d y-r c nt in r
  59. outcome = Square.run(x: 'two point one') outcome.valid? # => nil

    outcome.errors.messages # => {:x=>["is not a valid float"]} outcome = Square.run(x: 2.1) outcome.valid? # => true outcome.result # => 4.41
  60. E ti ie 4 L o (i a s a

    p t ) E : A e s v n e v s V lu O je ts 4 I l o E : L n(l :, l :)
  61. C ea e n nt ty or ou d ma

    n 4 C e s r u b s m 4 S e a h g h d e a u d
  62. class Car < ActiveRecord::Base end class CarEntity attr_reader :id, :properties

    # ...business logic goes here def do_something end def check_something end end 4 D a h b s l o a h "t "
  63. M w en e re al in w th hi

    d-p rt l br ri s nd e on't ee a es on e ac .
  64. class Order < ActiveRecord::Base after_save do |order| update_my_elastic_search end end

    4 U c t , d 't o E s a r u . 4 T r s 4 T m s e t i f n c : 4 I s i , e d c k d t
  65. class OrderRepository def save(params) Order.create(params) update_my_elastic_search end end 4 W

    c w i o u a c k 4 I g w v h m e "c k", u o a b r e t 4 W a s h O d y n o w a d n c k
  66. I c n e oo , b t nt l

    c rt in oi t.
  67. module Emailable include ActiveSupport::Concern def deliver(email) # send email here...

    end end class Document include Emailable def archive @archived = true deliver({to: '[email protected]', subject: 'Document archived', body: @content}) end end
  68. class MyClassUsingConcerns include Emailable include SomethingElseble include SomethingElse2ble include SomethingElse3ble

    include SomethingElse4ble def my_method super() # Which do concer we have the my_method ? method_do_something() # where is this method definition? method_do_other_thing() # where is this method definition? ... end end
  69. class MyClassUsingConcerns include ! include ! include ! include !

    include ! def my_method ... end end I g , r h l n t r h o a p . i h w .
  70. E tr ct og c ro t e on ro

    le s nd od ls E tr ct c l a d m w rk og th r
  71. Y u an se ha ev r ou an I

    i n t y ur os t t lk ll bo t hi 4 S O (a I t O ) 4 V O 4 F O 4 Q O 4 V O (S z /P e ) 4 P O 4 D o
  72. R nd m S+E B f un o t e

    nt rn t // app/views/articles/create.js.erb $("<%= escape_javascript(render @article) %>").appendTo("#article"); $("#header .article-count").html(<%= Article.size %>); $("#footer .info").html("Last updated by"); T er a e th r ay t d i !
  73. T i : 4 H m n 4 H r

    /u a 4 I k i u u c W c v c n : 4 T h c n
  74. 4 D e n $1 . 4 C w a

    i $1 b a r a . 4 H /H d r s , r n i m . 4 H a 1 m b h a . 4 D e a m b h p t v / r . 4 D e e J +E . T d e r w ?
  75. C ie t ot un ed 4 C l h

    d e , b h d e a r s n i h r t h r t 4 T d e w i l ! 4 H /S k J +E s a o l h m /c l a a c d . ! 4 N e v t o v h a d J i 4 H /S g s r n 4 P o n n f 4 E e a h
  76. D n't ru t y ud em nt G o

    t, s ar h nd ry t y ou se f.
  77. L h ://g s .c /b /2 /0 /1 /s

    s-i - d -d -d -d / h ://w .y .c /w ?v=Y _2 h ://j e .c /p /r -v -p -m / h ://w .t f .c /o -a -r - v -d -m / h ://t b .c /b /s -c l - s -m