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

Next-generation Ruby web apps with dry-rb, rom-...

Next-generation Ruby web apps with dry-rb, rom-rb, and Roda

If you’ve ever yearned for more than the Rails way, come along and learn how a small set of tools and techniques can bring joy to your Ruby web app development, from the smallest beginnings through to the most complex of codebases. Discover how concepts like functional programming, immutability, strict typing, dependency injection and object composition can actually be easy and natural in Ruby (yes, really!), and how they will make your web app a pleasure to build, test and extend.

Tim Riley

June 23, 2016
Tweet

More Decks by Tim Riley

Other Decks in Technology

Transcript

  1. “Programmers at work maintaining a Ruby on Rails application” Eero

    Järnefelt, Oil on canvas, 1893 classicprogrammerpaintings.com
  2. # Initialize once create_article = CreateArticle.new(repo) # Reuse many times

    create_article.(title: "Hello World") create_article.(title: "Hello Singapore")
  3. class Index < Blog::View configure do |config| config.template = "articles/index"

    end include Blog::Import["blog.repositories.articles"] def locals(options = {}) super.merge(articles: articles.listing) end end
  4. class Index < Blog::View configure do |config| config.template = "articles/index"

    end include Blog::Import["blog.repositories.articles"] def locals(options = {}) super.merge(articles: articles.listing) end end
  5. Routing & HTTP dry-web & roda Object dependency management dry-component,

    -container & -auto_inject Views dry-view Database queries rom, rom-sql & rom-repository Data modelling dry-types 1 2 3 4 5
  6. class MyApp < Dry::Web::Application route do |r| r.post "articles" do

    r.resolve "operations.create_article" do |create| create.(r[:article]) end end end end
  7. input = { "published" => "1", "published_at" => "2016-05-24 16:30"

    } ArticleSchema.(input).to_h { published: true, published_at: 2016-05-24 16:30:00 +1000 }
  8. ArticleSchema.("published" => "1").messages { :title => [ "is missing", "size

    cannot be less than 5"], :body => ["is missing"], :published_at => ["is missing"] }
  9. module Relations class Articles < ROM::Relation[:sql] schema(:articles) do attribute :id,

    Types::Serial attribute :title, Types::String attribute :body, Types::String attribute :published, Types::Bool attribute :published_at, Types::Time end end end
  10. include Dry::Monads::Either::Mixin include Dry::ResultMatcher.for(:call) def call(input) validation = Validation::ArticleSchema.(input) if

    validation.success? result = articles.create(validation) Right(Article.new(result)) else Left(validation) end end
  11. r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) do |m| m.success

    do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end
  12. r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) do |m| m.success

    do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end
  13. create_article.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure :create

    do |errors| r.view "articles.new", validation: errors end m.failure :notify do |error| # ... end end