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

Building_Rails_with_Trailblazer_slides.pdf

 Building_Rails_with_Trailblazer_slides.pdf

RubyConf TW 2019

WenchuanLin

July 27, 2019
Tweet

Other Decks in Programming

Transcript

  1. Why This Talk? Share some experience Collect insights from people

    here Make some friends Free conference tickets
  2. 1 Experience with Ruby?
 3 Form Object?
 Service Object? 2

    Experience with Rails? 4 Trailblazer(TRB)?
  3. Controller def create run Song::Create do |result| redirect_to songs_path(result["model"]) end

    render :new end def create authorize! :create, Song @song = Song.new(params[:song]) if @song.save current_user.increment_song_counter redirect_to @song else render :new end end Rails Rails With TRB
  4. Model class Song < ActiveRecord::Base has_many :authors end class Song

    < ActiveRecord::Base has_many :authors validates :name, :author, presence: true after_save :notify_author end Rails Rails With TRB
  5. TRB Operation class Song::Create < Trailblazer::Operation step Model( Song, :new

    ) step Policy::Pundit( Policy, :create? ) step Contract::Build( constant: Song::Create ) step Contract::Validate() step Contract::Persist() fail Notifier::DBError step :update_song_count! step :notify_author def update_song_count!(options, current_user:, **) current_user.increment_song_counter end def notify_author!(options, **) #… end end class Song::Create < Reform::Form property :name validate :name, presence: true end Reform
  6. The Decision ✅ Great Fit to Our Scenario ✅ Integration

    with Rails (Validation / I18n) ✅ Online Documentation / Book ✅ Incrementally Adoptable
  7. 2016 Q3 - Kickoff / Initial commit 2017 Q1 -

    Only 3 Operations Created... 2017 Q2 - 2 Core Business Concepts Migrated 2017 Q4 - EmailMessage 2018 Q2 - OnlineAppForm Now... TimeLine
  8. ::call !"" ::build_operation # !"" #initialize # # !"" #setup!

    # # # !"" #assign_params! # # # # # !"" #params! # # # !"" #setup_params! # # # !"" #build_model! # # # # !"" #assign_model! # # # # # !"" #model! # # # # !"" #setup_model! # !"" #run # # !"" #process Callstack in Operation Too many….
  9. You need more than Book / Documentation But Sometimes the

    source code is difficult to trace - Trailblazer - Trailblazer-operation - Trailblazer-rails - Trailblazer-loader - Reform / Reform-Rails - Disposable / Twin - Cell - ……
  10. *main process of Reform object
 **alias method for #valid?
 #valid?

    
 Method (A) #validate Method (B) #validate_with Method(C) Reform ✅ * ✅ ❎ ActiveModel ✅ ** ✅ ✅ Validation Layer
  11. Populate In Reform songs: [ {name: "Midnight Rendezvous"}, {name: "Information

    Error"} ] class Song < ActiveRecord::Base belongs_to :album end class Album < ActiveRecord::Base has_many :songs end class AlbumForm < Reform::Form collection :songs, populate_if_empty: Song do property :name end end
  12. def update fail 'Invalid submit value' if !params[:complete].present? && !params[:update].present?

    obj = resource_instance obj.assign_attributes(items_completion_params) if params[:update] ? obj.save : obj.complete_items respond_to do |format| format.html { redirect_to obj, notice: "#{params[:commit]} successfully completed." } format.json { render json: {status: :ok} } end else if params[:update] respond_to do |format| format.html { redirect_to obj, alert: obj.errors.full_messages.join(', ') } format.json { render json: {status: :bad_request, msg: 'Failed to update remark'} } end else flash.now[:alert] = 'Failed to complete' obj.items.select(&:_complete).each { |i| i.status = Dealing::STATUS_DEALING } render :edit end end end Revisit, Refactor, Rewrite def update result = Dealing::Save.call(params) if result.success? # ... elsif result['complete.failure?'] flash.now[:alert] = 'Failed to complete' render :edit else # ... end end class Dealing::Save < Trailblazer::Operation step Model( Song, :find ) step Nested { :build! } step Contract::Build( constant: Dealing::Update ) step Contract::Validate() step Contract::Persist() #=> save failure :error! def build!(options, params:, **) if params[:complete].present? Dealing::Complete elsif params[:update].present? self.class else fail 'Invalid submit value' end end end class Dealing::Complete < Trailblazer::Operation step Contract::Build( constant: Dealing::Complete ) step Contract::Validate() step Contract::Persist() #=> complete_items failure :error! failure :revert_status! def revert_status!(options, **) #... end end Save Complete
  13. Cost • Another framework / DSL to learn • Sometimes

    it is not so friendly to Rails • The part of document is incomplete • Debug is harder than before