Last active
December 18, 2022 06:59
-
-
Save johnholdun/bafbb4131f1812bb2ae8 to your computer and use it in GitHub Desktop.
Using ActionController inside a Sinatra App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'sinatra/base' | |
# ActionPack is a gem that is part of Rails. It includes ActionController and ActionView | |
require 'action_pack' | |
# Rack::Test is not used in this file but ActionController will yell if we don't require it | |
require 'rack/test' | |
# This is part of the ActionPack gem | |
require 'action_controller' | |
module RailsIntegrationHelpers | |
# Connect a Sinatra endpoint to an ActionController action | |
# @param [Symbol] method The HTTP method to match | |
# @param [String] path A Sinatra-compatibile path to match | |
# @param [Hash] options Configuration for the route. | |
# Requires at least one key (:to), whose value must either be a String of the form 'controller_name#action_name' | |
# or a hash with keys :controller and :action. | |
# In either case, controller_name should be a snake case representation of the name of the controller class, | |
# without the `_controller` suffix. | |
# For example, { to: 'hello_world#test' } maps to HelloWorldController#test | |
# All other options will be passed as usual to the Sinatra processor. | |
def rails_route(method, path, options) | |
options = options.with_indifferent_access | |
to = options.delete(:to) | |
controller_name, action_name = to.is_a?(String) ? to.split('#') : [to[:controller], to[:action]] | |
controller_klass = "#{ controller_name.camelize }Controller".constantize | |
route method.to_s.upcase, path, options do | |
# Make sure that our parsed URL params are where Rack (and ActionDispatch) expect them | |
action_request = ActionDispatch::Request.new \ | |
request.env.merge 'rack.request.query_hash' => params | |
controller_klass.new.dispatch action_name, action_request | |
end | |
end | |
end | |
# A real Rails controller! | |
class TestController < ActionController::Base | |
# A dumb filter to prove that our dispatcher is working | |
before_filter :test | |
def hello | |
render text: "<h1>Hello from #{ @framework }</h1>" | |
end | |
def greeting | |
render text: "<h1>Hello from #{ params[:name] } and #{ @framework }</h1>" | |
end | |
private | |
def test | |
# This isn't TECHNICALLY Rails, so use scare quotes | |
@framework = '"Rails"' | |
end | |
end | |
# A Sinatra app; the status quo | |
class SomeApp < Sinatra::Base | |
extend RailsIntegrationHelpers | |
# Here it is: a proposal for tying Sinatra::Base and ActionController::Base together | |
# This syntax is similar to what the Rails router uses; let's get some practice in. | |
# This will direct requests for /hello to TestController#hello | |
rails_route :get, '/hello', to: 'test#hello' | |
# And this will direct requests for /hello/:name to TestController#greeting, | |
# passing params[:name] as expected | |
rails_route :get, '/hello/:name', to: 'test#greeting' | |
# This is just how you run a modular Sinatra app with `ruby file.rb` | |
run! if app_file == $0 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment