henrique-ft / ruby-roda-cookbook

Grouping Roda docs and content in a "action oriented way" with some more nice examples.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

📖 Ruby Roda Cookbook


Routing

Roda suport get and post verbs initially

class App < Roda    
  route do |r|   
    r.get do              # GET
      r.on "a" do         # GET /a branch
        r.on "b" do       # GET /a/b branch
          r.is "c" do end # GET /a/b/c request
          r.is "d" do end # GET /a/b/d request
        end
      end
    end

    r.post do             # POST
      r.on "a" do         # POST /a branch
        r.on "b" do       # POST /a/b branch
          r.is "c" do end # POST /a/b/c request
          r.is "e" do end # POST /a/b/e request
        end
      end
    end
  end
end

For delete, put and patch, we must use all_verbsplugin

class App < Roda  
  plugin :all_verbs
  
  route do |r|   
    route do |r|
      r.delete do
        # Handle DELETE
      end
      r.put do
        # Handle PUT
      end
      r.patch do
        # Handle PATCH
      end
    end
  end
end

Getting values from url

class App < Roda  
  # GET /post/2011/02/16/hello
  r.get "post", Integer, Integer, Integer, String do |year, month, day, slug|
    "#{year}-#{month}-#{day} #{slug}" #=> "2011-02-16 hello"
  end
end

More info:

Rendering views

Create a directory for views named views in the root of your project

$ mkdir views

Create a layout file inside this directory

$ touch views/layout.erb
<%= yield %>

Create the view file

$ touch views/foo/index.erb
<h1> Hello, <%= @name %> </h1>
Age: <%= age %>

Add :render plugin and view 'foo/index'

class App < Roda                   
  plugin :render # import render             
                                 
  route do |r|
    @name = 'Henrique' # will be available in the erb file
                   
    r.root do                     
      view 'foo/index', locals: { age: 26 } # this maps to views/food/index.erb  with layout       
    end   

    r.get 'without-layout' do                     
      render 'foo/index', locals: { age: 26 } # this maps to views/food/index.erb without layout       
    end   
  end
end

More info:

Serving static content

class App < Roda
  plugin :public # Will serve any file / folder in <your_app_root>/public

  route do |r|
    r.public
  end
end

More info:

Compiling assets

class App < Roda
  # some_file.scss is the scss root, the same for some_file.js for javascript
  # it will find assets/css/some_file.scss and assets/js/some_file.js
  plugin :assets, css: 'some_file.scss', js: 'some_file.js' # Add this plugin

  route do |r|
    r.assets # Add this call

    r.get { |r| view 'index' }
  end
end

Create the layout file:

$ mkdir views && touch views/layout.erb

<html>                 
  <head>               
    # The link to files
    <%= assets(:css) %>
    <%= assets(:js) %> 
  </head>              
                       
  <body>               
    <%= yield %>       
  </body>              
</html>                

Create the view file:

$ touch views/index.erb

<h1> Hello </h1>

Create some scss files:

$ mkdir assets/css && touch assets/css/some_file.scss

@import 'global.scss';

$ touch assets/css/_global.scss

body {                    
  background-color: blue;
}                         

Create the js file:

$ mkdir assets/js && touch assets/js/some_file.js

console.log("hello")

More info:

Returning JSON

class App < Roda                   
  plugin :json # import json             

  route do |r|                   
    r.root do                     
      [{
        name: 'Banana',
        nutrients: [
          {
            name: 'B vitamin',
            quantity: 3,
            quantity_unit: 'mg'
          }
        ]
      }] # automatic conversion to json
    end   
  end
end

More info:

Returning specific status code

class App < Roda                       
  route do |r|                   
    r.root do                     
      response.status = 201 # Set status in global response object

      "" # return something                  
    end   
  end
end

More info:

Halting requests

class App < Roda
  plugin :halt # load the halt plugin
                       
  route do |r|                   
    r.root do                     
      r.halt(403)              
    end   

    r.get "example2" do
      r.halt('body')
    end

    r.get "example3" do
      r.halt(403, 'body')
    end

    r.get "example4" do
      r.halt(403, {'Content-Type'=>'text/csv'}, 'body')
    end

    r.get "example5" do
      r.halt([403, {'Content-Type'=>'text/csv'}, ['body']])
    end
  end
end

More info:

CORS

Use rack-cors gem:

gem 'rack-cors' # Gemfile

in config.ru:

require "rack/cors"

require_relative "app" # require roda app

use Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :patch, :put]
  end
end

run App.freeze.app

More info:

Auto reload

Use rerun gem:

$ gem install rerun

and then:

$ rerun 'rackup'

More info:

Accessing through many devices

Run Roda with this command

rackup --host 0.0.0.0 --port 9292

and then, access your IP address in port 9292

CSRF

"CSRF" section on Roda README

Cross Site Scripting

"Cross Site Scripting (XSS)" section on Roda README

Creating Roda plugins

Using multiple route files

Option 1

routes/foo.rb

module Routes
  class Foo < Roda
    route do |r|
      r.get do
        "hello foo"
      end
    end
  end
end

routes/bar.rb

module Routes
  class Bar < Roda
    route do |r|
      r.get do
        "hello bar"
      end
    end
  end
end

config.ru

require "roda"

Dir['routes/*.rb'].each { |f| require_relative f }

class App < Roda
  plugin :multi_run # Allow to group many "r.run" in one call

  run 'foo', Routes::Foo
  run 'bar', Routes::Bar

  # Same as
  # route do |r| 
  #   r.multi_run
  # end
  route(&:multi_run)
end

run App.freeze.app

Option 2

routes/foo.rb

module Routes
  module Foo
    def self.included(app)   
      app.class_eval do      
        route('foo') do |r|
          r.get do
            "hello foo, #{@shared_value}"
          end
        end
      end
    end
  end
end

routes/bar.rb

module Routes
  module Bar
    def self.included(app)   
      app.class_eval do      
        route('bar') do |r|
          r.get do
            "hello bar, #{@shared_value}"
          end
        end
      end
    end
  end
end

config.ru

require "roda"

Dir['routes/*.rb'].each { |f| require_relative f }

class App < Roda
  plugin :multi_route

  include Routes::Foo
  include Routes::Bar

  # route(&:multi_route) 
  route do |r| 
     # with this option, keep in mind that all routes will share the same class scope even in separated files
     # functions defined outside the 'route' block in routes modules may be dangerous
     @shared_value = 'keep focus'
     r.multi_route
  end
end

run App.freeze.app

More info:

File uploads

Install and use Shrine (https://shrinerb.com/)

Contributing

Help us grow this Roda cookbook, open a PR with your contribution!

About

Grouping Roda docs and content in a "action oriented way" with some more nice examples.