Coder Social home page Coder Social logo

router's Introduction

Lotus::Router

Rack compatible, lightweight and fast HTTP Router for Ruby and Lotus.

Status

Gem Version Build Status Coverage Code Climate Dependencies Inline docs

Contact

Rubies

Lotus::Router supports Ruby (MRI) 2+, JRuby 9k+

Installation

Add this line to your application's Gemfile:

gem 'lotus-router'

And then execute:

$ bundle

Or install it yourself as:

$ gem install lotus-router

Getting Started

require 'lotus/router'

app = Lotus::Router.new do
  get '/', to: ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
end

Rack::Server.start app: app, Port: 2300

Usage

Lotus::Router is designed to work as a standalone framework or within a context of a Lotus application.

For the standalone usage, it supports neat features:

A Beautiful DSL:

Lotus::Router.new do
  get '/', to: ->(env) { [200, {}, ['Hi!']] }
  get '/dashboard',   to: Dashboard::Index
  get '/rack-app',    to: RackApp.new
  get '/flowers',     to: 'flowers#index'
  get '/flowers/:id', to: 'flowers#show'

  redirect '/legacy', to: '/'

  mount Api::App, at: '/api'

  namespace 'admin' do
    get '/users', to: Users::Index
  end

  resource 'identity' do
    member do
      get '/avatar'
    end

    collection do
      get '/api_keys'
    end
  end

  resources 'robots' do
    member do
      patch '/activate'
    end

    collection do
      get '/search'
    end
  end
end

Fixed string matching:

router = Lotus::Router.new
router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }

String matching with variables:

router = Lotus::Router.new
router.get '/flowers/:id', to: ->(env) { [200, {}, ["Hello from Flower no. #{ env['router.params'][:id] }!"]] }

Variables Constraints:

router = Lotus::Router.new
router.get '/flowers/:id', id: /\d+/, to: ->(env) { [200, {}, [":id must be a number!"]] }

String matching with globbing:

router = Lotus::Router.new
router.get '/*', to: ->(env) { [200, {}, ["This is catch all: #{ env['router.params'].inspect }!"]] }

String matching with optional tokens:

router = Lotus::Router.new
router.get '/lotus(.:format)' to: ->(env) { [200, {}, ["You've requested #{ env['router.params'][:format] }!"]] }

Support for the most common HTTP methods:

router   = Lotus::Router.new
endpoint = ->(env) { [200, {}, ['Hello from Lotus!']] }

router.get    '/lotus', to: endpoint
router.post   '/lotus', to: endpoint
router.put    '/lotus', to: endpoint
router.patch  '/lotus', to: endpoint
router.delete '/lotus', to: endpoint
router.trace  '/lotus', to: endpoint

Redirect:

router = Lotus::Router.new
router.get '/redirect_destination', to: ->(env) { [200, {}, ['Redirect destination!']] }
router.redirect '/legacy', to: '/redirect_destination'

Named routes:

router = Lotus::Router.new(scheme: 'https', host: 'lotusrb.org')
router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }, as: :lotus

router.path(:lotus) # => "/lotus"
router.url(:lotus)  # => "https://lotusrb.org/lotus"

Namespaced routes:

router = Lotus::Router.new
router.namespace 'animals' do
  namespace 'mammals' do
    get '/cats', to: ->(env) { [200, {}, ['Meow!']] }, as: :cats
  end
end

# and it generates:

router.path(:animals_mammals_cats) # => "/animals/mammals/cats"

Mount Rack applications:

Lotus::Router.new do
  mount RackOne,                             at: '/rack1'
  mount RackTwo,                             at: '/rack2'
  mount RackThree.new,                       at: '/rack3'
  mount ->(env) {[200, {}, ['Rack Four']]},  at: '/rack4'
  mount 'dashboard#index',                   at: '/dashboard'
end
  1. RackOne is used as it is (class), because it respond to .call
  2. RackTwo is initialized, because it respond to #call
  3. RackThree is used as it is (object), because it respond to #call
  4. That Proc is used as it is, because it respond to #call
  5. That string is resolved as Dashboard::Index (Lotus::Controller integration)

Duck typed endpoints:

Everything that responds to #call is invoked as it is:

router = Lotus::Router.new
router.get '/lotus',      to: ->(env) { [200, {}, ['Hello from Lotus!']] }
router.get '/middleware', to: Middleware
router.get '/rack-app',   to: RackApp.new
router.get '/method',     to: ActionControllerSubclass.action(:new)

If it's a string, it tries to instantiate a class from it:

class RackApp
  def call(env)
    # ...
  end
end

router = Lotus::Router.new
router.get '/lotus', to: 'rack_app' # it will map to RackApp.new

It also supports Controller + Action syntax:

module Flowers
  class Index
    def call(env)
      # ...
    end
  end
end

router = Lotus::Router.new
router.get '/flowers', to: 'flowers#index' # it will map to Flowers::Index.new

Implicit Not Found (404):

router = Lotus::Router.new
router.call(Rack::MockRequest.env_for('/unknown')).status # => 404

Controllers:

Lotus::Router has a special convention for controllers naming. It allows to declare an action as an endpoint, with a special syntax: <controller>#<action>.

Lotus::Router.new do
  get '/', to: 'welcome#index'
end

In the example above, the router will look for the Welcome::Index action.

Namespaces

In applications where for maintainability or technical reasons, this convention can't work, Lotus::Router can accept a :namespace option, which defines the Ruby namespace where to look for actions.

For instance, given a Lotus full stack application called Bookshelf, the controllers are available under Bookshelf::Controllers.

Lotus::Router.new(namespace: Bookshelf::Controllers) do
  get '/', to: 'welcome#index'
end

In the example above, the router will look for the Bookshelf::Controllers::Welcome::Index action.

RESTful Resource:

router = Lotus::Router.new
router.resource 'identity'

It will map:

Verb Path Action Name Named Route
GET /identity Identity::Show :show :identity
GET /identity/new Identity::New :new :new_identity
POST /identity Identity::Create :create :identity
GET /identity/edit Identity::Edit :edit :edit_identity
PATCH /identity Identity::Update :update :identity
DELETE /identity Identity::Destroy :destroy :identity

If you don't need all the default endpoints, just do:

router = Lotus::Router.new
router.resource 'identity', only: [:edit, :update]

#### which is equivalent to:

router.resource 'identity', except: [:show, :new, :create, :destroy]

If you need extra endpoints:

router = Lotus::Router.new
router.resource 'identity' do
  member do
    get 'avatar'           # maps to Identity::Avatar
  end

  collection do
    get 'authorizations'   # maps to Identity::Authorizations
  end
end

router.path(:avatar_identity)         # => /identity/avatar
router.path(:authorizations_identity) # => /identity/authorizations

Configure controller:

router = Lotus::Router.new
router.resource 'profile', controller: 'identity'

router.path(:profile) # => /profile # Will route to Identity::Show

Nested Resources

We can nest resource(s):

router = Lotus::Router.new
router.resource :identity do
  resource  :avatar
  resources :api_keys
end

router.path(:identity_avatar)       # => /identity/avatar
router.path(:new_identity_avatar)   # => /identity/avatar/new
router.path(:edit_identity_avatar)  # => /identity/avatar/new

router.path(:identity_api_keys)     # => /identity/api_keys
router.path(:identity_api_key)      # => /identity/api_keys/:id
router.path(:new_identity_api_key)  # => /identity/api_keys/new
router.path(:edit_identity_api_key) # => /identity/api_keys/:id/edit

RESTful Resources:

router = Lotus::Router.new
router.resources 'flowers'

It will map:

Verb Path Action Name Named Route
GET /flowers Flowers::Index :index :flowers
GET /flowers/:id Flowers::Show :show :flower
GET /flowers/new Flowers::New :new :new_flower
POST /flowers Flowers::Create :create :flowers
GET /flowers/:id/edit Flowers::Edit :edit :edit_flower
PATCH /flowers/:id Flowers::Update :update :flower
DELETE /flowers/:id Flowers::Destroy :destroy :flower
router.path(:flowers)             # => /flowers
router.path(:flower, id: 23)      # => /flowers/23
router.path(:edit_flower, id: 23) # => /flowers/23/edit

If you don't need all the default endpoints, just do:

router = Lotus::Router.new
router.resources 'flowers', only: [:new, :create, :show]

#### which is equivalent to:

router.resources 'flowers', except: [:index, :edit, :update, :destroy]

If you need extra endpoints:

router = Lotus::Router.new
router.resources 'flowers' do
  member do
    get 'toggle' # maps to Flowers::Toggle
  end

  collection do
    get 'search' # maps to Flowers::Search
  end
end

router.path(:toggle_flower, id: 23)  # => /flowers/23/toggle
router.path(:search_flowers)         # => /flowers/search

Configure controller:

router = Lotus::Router.new
router.resources 'blossoms', controller: 'flowers'

router.path(:blossom, id: 23) # => /blossoms/23 # Will route to Flowers::Show

Nested Resources

We can nest resource(s):

router = Lotus::Router.new
router.resources :users do
  resource  :avatar
  resources :favorites
end

router.path(:user_avatar,      user_id: 1)  # => /users/1/avatar
router.path(:new_user_avatar,  user_id: 1)  # => /users/1/avatar/new
router.path(:edit_user_avatar, user_id: 1)  # => /users/1/avatar/edit

router.path(:user_favorites, user_id: 1)             # => /users/1/favorites
router.path(:user_favorite, user_id: 1, id: 2)       # => /users/1/favorites/2
router.path(:new_user_favorites, user_id: 1)         # => /users/1/favorites/new
router.path(:edit_user_favorites, user_id: 1, id: 2) # => /users/1/favorites/2/edit

Testing

require 'lotus/router'
require 'rack/request'

router = Lotus::Router.new do
  get '/', to: ->(env) { [200, {}, ['Hi!']] }
end

app = Rack::MockRequest.new(router)
app.get('/') # => #<Rack::MockResponse:0x007fc4540dc238 ...>

Versioning

Lotus::Router uses Semantic Versioning 2.0.0

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Acknowledgements

Thanks to Joshua Hull (@joshbuddy) for his http_router.

Copyright

Copyright ยฉ 2014-2015 Luca Guidi โ€“ Released under MIT License

router's People

Contributors

alfonsouceda avatar arthurgeek avatar fredwu avatar gzigzigzeo avatar janko avatar javierav avatar jc00ke avatar jeremyf avatar jodosha avatar karlfreeman avatar marcosdsanchez avatar pragtob avatar rrrene avatar stravid avatar tak1n avatar triplepointfive avatar vyper avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.