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.

5e47e75af9d584936f6b4be3520c75d7?s=128

Bob Maerten

April 06, 2017
Tweet

Transcript

  1. 4.
  2. 5.
  3. 6.
  4. 7.
  5. 8.
  6. 9.
  7. 10.
  8. 11.
  9. 12.
  10. 13.
  11. 14.
  12. 15.
  13. 16.
  14. 17.
  15. 18.
  16. 19.
  17. 20.
  18. 21.
  19. 22.
  20. 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
  21. 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
  22. 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
  23. 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
  24. 28.

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

    def self.wrap(collection) collection.map { |e| new(e) } end <!‑‑ view ‑‑> <ul> <% PersonDecorator.each(Person.registered) do |decorated_person| %> <li><%= decorated_person.full_name %></li> <% end %> </ul> # controller @decorated_people = PersonDecorator.wrap(Person.registered) # view <%= safe_join @decorated_people.map(&:full_name), tag(:br) %> 28 / 41
  25. 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
  26. 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
  27. 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
  28. 32.
  29. 33.
  30. 34.
  31. 35.
  32. 36.
  33. 37.
  34. 40.