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

Trailblazer: Making Your Rails Applications Man...

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

Trailblazer: Making Your Rails Applications Manageable

Iowa Ruby Brigade meeting July 2016

Avatar for Zach Taylor

Zach Taylor

July 19, 2016

More Decks by Zach Taylor

Other Decks in Programming

Transcript

  1. DOES YOUR RAILS APP HAVE ANY OF THESE? • Callback

    “hell” • Nested forms • Controllers with logic not relating to HTTP
  2. DOES YOUR RAILS APP HAVE ANY OF THESE? • Callback

    “hell” • Nested forms • Controllers with logic not relating to HTTP • Models with logic not relating to persistence
  3. DOES YOUR RAILS APP HAVE ANY OF THESE? • Callback

    “hell” • Nested forms • Controllers with logic not relating to HTTP • Models with logic not relating to persistence • Views with several nested if statements and partials
  4. BUSINESS REQUIREMENTS class User < ActiveRecord::Base before_validation :set_role, on: :create

    accepts_nested_attributes_for :address, update_only: true, reject_if: :blank? end
  5. BUSINESS REQUIREMENTS def create @todo = Todo.find(params[:todo_id]) @recording = @todo.build_recording(recording_params)

    authorize @recording if @recording.save if @recording.reason != 'Not a weight challenge' if @recording.reason == 'Success' flash.now[:notice] = 'Success: Congrats!' else flash.now[:error] = "You were not awarded credits! Reason: #{@recording.reason}" end render :show else render json: ErrorValue.new(@recording), status: :unprocessable_entity end end end
  6. TRAILBLAZER • “A High-level Architecture For The Web” • A

    collection of framework-agnostic gems that give your app: Structure Encapsulation Inheritance
  7. OPERATION • Acts as an orchestrator of objects that contain

    the business logic • “creating a comment”, “updating a user” • Composable
  8. OPERATION class PostsController < ApplicationController def create run Post::Create do

    |op| return redirect_to posts_path end render :edit end Equivalent to: op = Post::Create.call(params)
  9. OPERATION class Post < ActiveRecord::Base # namespace class Create <

    Trailblazer::Operation def process(params) puts params end end end
  10. FORM • AKA contract • Property: strong parameters • Validation:

    ActiveModel validations (or dry-validation)
  11. FORM class Post < ActiveRecord::Base class Create < Trailblazer::Operation contract

    do property :title property :body validates :title, length: { minimum: 4 } validates :body, presence: true end def process(params) validate(params[:post]) do contract.save end end end end
  12. POLICY • Whether or not you can perform this operation

    • If not, throws Trailblazer::NotAuthorizedError
  13. POLICY class Post::Policy def initialize(user, model) @user, @model = user,

    model end def create? @user.admin == true end end class Post < ActiveRecord::Base class Create < Trailblazer::Operation include Trailblazer::Operation::Policy policy Post::Policy, :create? contract do ... end def process(params) ... end end end
  14. POLICY class PostsController < ApplicationController def create run Post::Create, params:

    params.merge(current_user: current_user) do |op| class PostsController < ApplicationController before_action :add_user_to_params … private def add_user_to_params params[:current_user] = current_user end end
  15. MODEL class Post < ActiveRecord::Base class Create < Trailblazer::Operation include

    Model model Post, :create # or :find or :update def process(params) validate(params[:post]) do contract.save end end end end • :create calls Post.new • :update is the same as :find, they call Post.find(params[:id])
  16. MODEL class Post < ActiveRecord::Base class Update < Trailblazer::Operation include

    Model model Post, :update def process(params) validate(params[:post]) do contract.save end end private def update_model!(params) model_class.find_by(slug: params[:slug]) end end end
  17. CELLS class Post < ActiveRecord::Base module Cell class Show <

    Trailblazer::Cell property :title property :body private def author model.author || AnonymousAuthor.new end def full_name "#{author.first_name} #{author.last_name}" end end end end
  18. CELLS <article> <header> <h1><%= title %></h1> <p>Published: <%= published_at %></p>

    </header> <p><%= body %></p> <footer> <p><small>By: <%= full_name %></small></p> </footer> </article>
  19. DIRECTORY LAYOUT app ├── concepts │ ├── comment │ │

    ├── contract │ │ │ ├── create.rb │ │ │ └── update.rb │ │ ├── cell │ │ │ └── new.rb │ │ ├── operation │ │ │ ├── create.rb │ │ │ └── update.rb │ │ └── view │ │ ├── new.erb │ │ └── show.erb