$30 off During Our Annual Pro Sale. View Details »

Retour sur un processus de dé-gem-ification sur une appli Rails

Retour sur un processus de dé-gem-ification sur une appli Rails

L'attachement à de nombreux•ses gems dans un projet Rails peut s'avérer complexe lors des montées en version. Une dépendance non maintenue peut mettre en péril l'évolution de l'application.

Cette présentation déroule le processus qui nous a amené à remplacer un(e) gem par un code maison pour s'abstraire de cette dépendance qui ne nous permettait pas de mettre à jours nos applications.

Bob Maerten

April 06, 2017
Tweet

More Decks by Bob Maerten

Other Decks in Technology

Transcript

  1. Apéro Ruby Lille - Avril 2017
    1 / 41

    View Slide

  2. https://bobmaerten.eu
    2 / 41

    View Slide

  3. https://levups.com/fr/blog
    3 / 41

    View Slide

  4. 4 / 41

    View Slide

  5. 5 / 41

    View Slide

  6. 6 / 41

    View Slide

  7. 7 / 41

    View Slide

  8. 8 / 41

    View Slide

  9. 9 / 41

    View Slide

  10. 10 / 41

    View Slide

  11. 11 / 41

    View Slide

  12. 12 / 41

    View Slide

  13. 13 / 41

    View Slide

  14. 14 / 41

    View Slide

  15. 15 / 41

    View Slide

  16. 16 / 41

    View Slide

  17. 17 / 41

    View Slide

  18. 18 / 41

    View Slide

  19. 19 / 41

    View Slide

  20. 20 / 41

    View Slide

  21. 21 / 41

    View Slide

  22. 22 / 41

    View Slide

  23. Et puis c'est gentil tes images mais on veut voir du code !
    23 / 41

    View Slide

  24. # model
    class Article < ApplicationRecord
    end
    # decorator
    class ArticleDecorator < Draper::Decorator
    delegate_all
    def published_date
    object.published_at.strftime("%A, %B %e")
    end
    end
    # controller
    @article = Article.find(params[:id]).decorate
    # view
    = @article.published_date
    24 / 41

    View Slide

  25. # model
    class Person
    attr_reader :first_name, :last_name
    def initialize(first_name, last_name)
    @first_name, @last_name = first_name, last_name
    end
    end
    # decorator
    class PersonDecorator < SimpleDelegator
    def full_name
    "#{first_name} #{last_name}"
    end
    end
    # code
    person = Person.new('Gérard', 'Menvussa')
    person.first_name #=> 'Gérard'
    person.last_name #=> 'Menvussa'
    decorated_person = PersonDecorator.new(person)
    decorated_person.full_name #=> 'Gérard Menvussa'
    25 / 41

    View Slide

  26. # app/decorators/application_decorator.rb
    class ApplicationDecorator < SimpleDelegator
    attr_reader :view_context
    alias_method :h, :view_context
    DEFAULT_VIEW_CONTEXT = ApplicationController.new.view_context
    def self.each(collection, &block)
    collection.each { |e| block.call new(e) }
    end
    def self.wrap(collection)
    collection.map { |e| new(e) }
    end
    def initialize(model, view_context = nil)
    super(model)
    @view_context = view_context || DEFAULT_VIEW_CONTEXT
    end
    end
    26 / 41

    View Slide

  27. attr_reader :view_context
    view_context est un objet ActionController / ActionMailer
    contient les informations de la requête (host, controller, action, etc.)
    permet d'accéder aux helpers (rails helpers, routes helpers, custom
    helpers, etc.)
    alias_method :h, :view_context
    alias pour réduire la largueur du code source
    La plupart des implémentations de decorators utilisent h en raccourci
    27 / 41

    View Slide

  28. def self.each(collection, &block)
    collection.each { |e| block.call new(e) }
    end
    def self.wrap(collection)
    collection.map { |e| new(e) }
    end


    <% PersonDecorator.each(Person.registered) do |decorated_person| %>
    <%= decorated_person.full_name %>
    <% end %>

    # controller
    @decorated_people = PersonDecorator.wrap(Person.registered)
    # view
    <%= safe_join @decorated_people.map(&:full_name), tag(:br) %>
    28 / 41

    View Slide

  29. class PersonDecorator < ApplicationDecorator
    def register_link
    h.content_tag(:p) do
    h.link_to h.register_person_path(person_id: id) do
    h.fa_icon('chevron‑down', text: 'Enregistrer cette personne')
    end
    end
    end
    end
    29 / 41

    View Slide

  30. require "test_helper"
    class PersonDecoratorTest < ActionView::TestCase
    setup do
    view.extend FontAwesome::Rails::IconHelper
    @person = people(:person_1)
    @decorated_person = PersonDecorator.new(@decorated_person, view)
    end
    test "register_link" do
    assert_includes @decorated_person.register_link, @person.id
    assert_includes @decorated_person.register_link, 'Enregister'
    end
    end
    30 / 41

    View Slide

  31. DEFAULT_VIEW_CONTEXT = ApplicationController.new.view_context
    def initialize(model, view_context = nil)
    super(model)
    @view_context = view_context || DEFAULT_VIEW_CONTEXT
    end
    Fallback sur un view_context par défaut pour accéder à nos helpers
    view_context n'était pas défini sur certains tests mailers/controllers
    class UserDecoratorTest < ActionView::TestCase
    setup do
    @person = Person.new('Gérard', 'Menvussa')
    @decorated_person = PersonDecorator.new @person, view
    end
    test "fullname" do
    assert_includes @decorated_person.fullname, @person.first_name
    assert_includes @decorated_person.fullname, @person.last_name
    end
    end
    31 / 41

    View Slide

  32. 32 / 41

    View Slide

  33. 33 / 41

    View Slide

  34. 34 / 41

    View Slide

  35. 35 / 41

    View Slide

  36. 36 / 41

    View Slide

  37. 37 / 41

    View Slide

  38. https://bibwild.wordpress.com/2012/12/19/the-simplest-rails-decorator-
    implementation-that-just-might-work
    https://hashrocket.com/blog/posts/using-simpledelegator-for-your-
    decorators
    https://robots.thoughtbot.com/decorating-activerecord
    38 / 41

    View Slide

  39. Générique de la série MadMen (AMC)
    MemeSuper.com
    GitHub.com
    39 / 41

    View Slide

  40. 40 / 41

    View Slide

  41. https://github.com/amatsuda/active_decorator
    41 / 41

    View Slide