├── .gitignore ├── test ├── views │ ├── fringe.haml │ └── foo.haml ├── watchr.rb ├── helper.rb ├── no_block_test.rb ├── scope_defined_test.rb └── controller_test.rb ├── Gemfile ├── lib ├── sinatra-controllers │ └── version.rb └── sinatra-controllers.rb ├── Rakefile ├── CHANGELOG ├── Gemfile.lock ├── sinatra-controllers.gemspec └── README.rdoc /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | *.gem 3 | .bundle 4 | -------------------------------------------------------------------------------- /test/views/fringe.haml: -------------------------------------------------------------------------------- 1 | bishop 2 | = @fringe 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :gemcutter 2 | 3 | # Specify your gem's dependencies in sinatra-controllers.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/sinatra-controllers/version.rb: -------------------------------------------------------------------------------- 1 | module Sinatra 2 | module Controllers 3 | VERSION = "0.2.3" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/watchr.rb: -------------------------------------------------------------------------------- 1 | watch('test/.*_test.*') {|md| system "rake test"} 2 | watch('test/*') {|md| system "rake test"} 3 | watch('lib/*') {|md| system "rake test"} 4 | 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 'rake/testtask' 3 | Bundler::GemHelper.install_tasks 4 | 5 | Rake::TestTask.new :test do |spec| 6 | spec.test_files = FileList["test/*_test.rb"] 7 | end 8 | -------------------------------------------------------------------------------- /test/views/foo.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta(http-equiv="content-type" content="text/html; charset=utf-8") 5 | 6 | %title test 7 | %body 8 | test 9 | =@blah 10 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | * 0.2.2 3 | - fix bug with class with scope '/'. 4 | - adding of Sinatra::Controllers.routes and Sinatra::Controllers.show_routes. 5 | - adding example application. 6 | * 0.2.1 7 | - fix bug where omittance of leading slash and no defined scope leads to 8 | TypeError 9 | * 0.2.0 add scoping 10 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'rack/test' 4 | require 'test/unit' 5 | require 'minitest/autorun' 6 | require 'awesome_print' 7 | require 'sinatra' 8 | require 'sinatra-controllers' 9 | 10 | class Pride 11 | def print o 12 | case o 13 | when /0 errors/i 14 | Kernel.print o.green 15 | when /(e|f)/i 16 | Kernel.print o.red 17 | else 18 | Kernel.print o.green 19 | end 20 | end 21 | def puts o = '' 22 | if o=~ /([1-9]+ failures)/ 23 | o.sub!(/([1-9]+ failures)/, '\1'.red) 24 | end 25 | Kernel.puts o 26 | end 27 | end 28 | MiniTest::Unit.output = Pride.new 29 | 30 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | sinatra-controllers (0.2.2) 5 | sinatra (>= 1.0.0) 6 | 7 | GEM 8 | remote: http://rubygems.org/ 9 | specs: 10 | awesome_print (0.3.1) 11 | haml (3.0.25) 12 | minitest (2.0.2) 13 | rack (1.2.2) 14 | rack-test (0.5.7) 15 | rack (>= 1.0) 16 | sinatra (1.2.1) 17 | rack (~> 1.1) 18 | tilt (>= 1.2.2, < 2.0) 19 | tilt (1.2.2) 20 | watchr (0.7) 21 | 22 | PLATFORMS 23 | ruby 24 | 25 | DEPENDENCIES 26 | awesome_print 27 | bundler (>= 1.0.0) 28 | haml 29 | minitest (~> 2.0.0) 30 | rack-test (~> 0.5.7) 31 | sinatra (>= 1.0.0) 32 | sinatra-controllers! 33 | watchr 34 | -------------------------------------------------------------------------------- /test/no_block_test.rb: -------------------------------------------------------------------------------- 1 | require './test/helper' 2 | require 'minitest/spec' 3 | 4 | class Welcome < Sinatra::Controller 5 | def get_index 6 | 'route test' 7 | end 8 | def put_update 9 | 'walternet' 10 | end 11 | def post_peter 12 | 'amber' 13 | end 14 | def delete_universe 15 | 'destroy' 16 | end 17 | end 18 | Sinatra::Controllers.register(Welcome) 19 | 20 | describe "without defined route block" do 21 | include Rack::Test::Methods 22 | def app 23 | @app = Sinatra::Application 24 | @app.set :environment, :test 25 | @app 26 | end 27 | it "should respond to get" do 28 | get '/welcome/index' 29 | last_response.status.must_equal 200 30 | last_response.body.must_match /route test/ 31 | end 32 | it "should respond to put" do 33 | put '/welcome/update' 34 | last_response.status.must_equal 200 35 | last_response.body.must_match /walternet/ 36 | end 37 | it "should respond to post" do 38 | post '/welcome/peter' 39 | last_response.status.must_equal 200 40 | last_response.body.must_match /amber/ 41 | end 42 | it "should respond to delete" do 43 | delete '/welcome/universe' 44 | last_response.status.must_equal 200 45 | last_response.body.must_match /destroy/ 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /sinatra-controllers.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path("../lib/sinatra-controllers/version", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "sinatra-controllers" 6 | s.version = Sinatra::Controllers::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ['Daniel Bretoi'] 9 | s.email = [] 10 | s.homepage = "https://github.com/danielb2/sinatra-controllers" 11 | s.summary = "Adds controllers to Sinatra" 12 | s.description = "Adds controllers to Sinatra" 13 | 14 | s.required_rubygems_version = ">= 1.3.6" 15 | s.rubyforge_project = "sinatra-controllers" 16 | 17 | s.add_dependency "sinatra", ">= 1.0.0" 18 | s.add_development_dependency "bundler", ">= 1.0.0" 19 | s.add_development_dependency "minitest", "~> 2.0.0" 20 | s.add_development_dependency "watchr" 21 | s.add_development_dependency "rack-test", "~> 0.5.7" 22 | s.add_development_dependency "awesome_print" 23 | s.add_development_dependency "haml" 24 | 25 | 26 | s.files = `git ls-files`.split("\n") 27 | s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact 28 | s.require_path = 'lib' 29 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 30 | end 31 | -------------------------------------------------------------------------------- /test/scope_defined_test.rb: -------------------------------------------------------------------------------- 1 | require './test/helper' 2 | require 'minitest/spec' 3 | 4 | class Show70 < Sinatra::Controller 5 | def get_kelso 6 | 'michael' 7 | end 8 | end 9 | 10 | class Seinfeld < Sinatra::Controller 11 | def get_cosmo 12 | 'kramer' 13 | end 14 | def george 15 | 'bald' 16 | end 17 | end 18 | 19 | class Leader < Sinatra::Controller 20 | def omit 21 | 'slash' 22 | end 23 | end 24 | 25 | class Sliders < Sinatra::Controller 26 | def get_quinn 27 | 'mallory' 28 | end 29 | end 30 | Sinatra::Controllers.register(Sliders, :scope => '/') 31 | 32 | Sinatra::Controllers.register(Show70, :scope => '/70s_show') 33 | # make sure we don't need leading slash 34 | Sinatra::Controllers.register(Seinfeld, :scope => 'seinfeld') do 35 | get 'costanza', :george 36 | end 37 | 38 | describe "with defined scope" do 39 | include Rack::Test::Methods 40 | def app 41 | @app = Sinatra::Application 42 | @app.set :environment, :test 43 | @app 44 | end 45 | 46 | it "should not bail on empty scope without preceding slash" do 47 | Sinatra::Controllers.register(Leader) do 48 | get 'foo', :omit 49 | end 50 | end 51 | 52 | it "should get a scope of slash correctly for a class" do 53 | get '/quinn' 54 | last_response.status.must_equal 200 55 | last_response.body.must_equal 'mallory' 56 | end 57 | 58 | it "should use correct scope" do 59 | get '/70s_show/kelso' 60 | last_response.status.must_equal 200 61 | last_response.body.must_equal 'michael' 62 | end 63 | it "should use correct scope without leading slash using no block" do 64 | get '/seinfeld/cosmo' 65 | last_response.status.must_equal 200 66 | last_response.body.must_equal 'kramer' 67 | end 68 | it "should use correct scope without leading slash for block" do 69 | get '/seinfeld/costanza' 70 | last_response.status.must_equal 200 71 | last_response.body.must_equal 'bald' 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Sinatra-controllers 2 | 3 | Sinatra routing done differently. Allows for clean organization across 4 | multiple sinatra routes files. 5 | 6 | * this has only been tested with ruby 1.9.2 7 | * redirect doesn't work.. yet 8 | 9 | 10 | == Examples 11 | Also see the fixtures folders for what's used in the tests. 12 | 13 | views/index.haml 14 | 15 | %html 16 | %head 17 | %meta(http-equiv="content-type" content="text/html; charset=utf-8") 18 | 19 | %title test page 20 | %body 21 | fly away with sinatra controllers 22 | 23 | You can divide your routes cleanly between as many classes as you please. At 24 | any time, you can also simply revert back to the plain vanilla sinatra syntax 25 | on use get at the top level. 26 | 27 | sinatra\_controller.rb 28 | #!/usr/bin/env ruby 29 | 30 | require 'sinatra' 31 | require 'sinatra-controllers' 32 | 33 | class Welcome < Sinatra::Controller 34 | def index 35 | haml :index 36 | end 37 | 38 | # this create a route corresponding to `get '/welcome/about'` 39 | # making it a shortcut for common routes. It doesn't handle arguments 40 | # unlike the block syntax. 41 | def get_about 42 | 'making controllers in sinatra easy' 43 | end 44 | end 45 | 46 | Sinatra::Controllers.register Welcome do 47 | get '/', :index 48 | end 49 | 50 | This is a trick for common requests that don't require arguments. It creates a 51 | route corresponding to `get '/welcome/about'` in the flavor of Test::Unit where 52 | it uses the prefix before the underscore for the route type. The block to 53 | register is optional here. 54 | 55 | class Welcome < Sinatra::Controller 56 | def get_about 57 | 'making controllers in sinatra easy' 58 | end 59 | end 60 | 61 | You can also use scopes for your own defined path, either because you're 62 | unhappy with your classname, or because you just want to: 63 | 64 | class Fringe < Sinatra::Controller 65 | def get_walternet 66 | 'scopes' 67 | end 68 | def peter 69 | 'more scopes' 70 | end 71 | def index 72 | 'index still if you want' 73 | end 74 | end 75 | 76 | Sinatra::Controllers.register(Fringe, :scope => 'fringe') do 77 | get 'peter', :peter 78 | get '/', :index 79 | end 80 | 81 | this enables the following routes 82 | 83 | get '/fringe/walternet' 84 | get '/fringe/peter' 85 | get '/' 86 | -------------------------------------------------------------------------------- /lib/sinatra-controllers.rb: -------------------------------------------------------------------------------- 1 | module Sinatra 2 | class Controller 3 | attr_accessor :params, :request, :template_cache 4 | include Sinatra::Templates 5 | def initialize(params,request) 6 | @params = params 7 | @request = request 8 | @template_cache = Tilt::Cache.new rescue 'sinatra < 1.0' 9 | @templates = {} 10 | end 11 | class << self 12 | def views 13 | end 14 | def templates 15 | {} 16 | end 17 | end 18 | end 19 | module Controllers 20 | class Mapping 21 | @@routes = [] 22 | attr_accessor :klass, :opts 23 | def initialize(klass,opts={}) 24 | @klass = klass 25 | @opts = opts 26 | end 27 | 28 | def self.routes 29 | @@routes 30 | end 31 | 32 | def route(verb, path, action) 33 | aklass = klass # need this so it will stay in scope for closure 34 | block = proc { aklass.new(params, request).send action } 35 | if path[0] != '/' 36 | path = '/' + File.join(opts[:scope] || '', path) 37 | end 38 | @@routes.push [verb.to_sym, path, "#{aklass}\##{action}"] 39 | path.gsub!(%r{^/+},'/') 40 | Sinatra::Base.send verb, path, &block 41 | end 42 | 43 | def parse 44 | klass.instance_methods.each do |meth| 45 | verb,name = meth.to_s.scan(/^(get|post|delete|put)_(.*)$/).flatten 46 | next unless verb 47 | scope = '/' + (opts[:scope] || klass.to_s.downcase) 48 | scope.gsub!(%r{^/+},'/') 49 | route verb,"#{scope}/#{name.downcase}", meth 50 | end 51 | end 52 | 53 | # why are these even defined? 54 | undef_method :get, :post, :put, :delete 55 | def method_missing(method,*a) 56 | case method.to_s 57 | when /(get|post|delete|put)/ 58 | route(method, *a) 59 | else 60 | super 61 | end 62 | end 63 | end 64 | 65 | class << self 66 | def register(klass, opts={}, &block) 67 | map = Mapping.new(klass,opts) 68 | map.instance_eval(&block) if block 69 | map.parse 70 | end 71 | def routes 72 | Sinatra::Controllers::Mapping.routes 73 | end 74 | def show_routes 75 | routes.each do |verb, path, action| 76 | puts 77 | puts "#{verb.to_s.ljust(8)}#{path.ljust(35)}#{action.ljust(25)}" 78 | puts 79 | end 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /test/controller_test.rb: -------------------------------------------------------------------------------- 1 | require './test/helper' 2 | 3 | class Blah < Sinatra::Controller 4 | def help 5 | @blah = 'message' + params.inspect 6 | haml :foo 7 | end 8 | def test 9 | params.inspect 10 | end 11 | 12 | def flames 13 | 'heat' 14 | end 15 | 16 | def requested 17 | request.inspect 18 | end 19 | 20 | def racket 21 | 'posted' 22 | end 23 | 24 | def everything 25 | 'wiped' 26 | end 27 | def fringe 28 | @fringe = "Anna Torv" 29 | haml :fringe 30 | end 31 | def take 32 | params[:arg] 33 | end 34 | end 35 | 36 | class Help < Sinatra::Controller 37 | def help 38 | 'another help' 39 | end 40 | def get_index 41 | 'help found' 42 | end 43 | end 44 | 45 | Sinatra::Controllers.register(Help) do 46 | get '/help', :help 47 | end 48 | 49 | Sinatra::Controllers.register(Blah) do 50 | get '/request', :requested 51 | get '/', :help 52 | get '/test/:id', :test 53 | post '/racket', :racket 54 | put '/flame', :flames 55 | delete '/all', :everything 56 | get '/fringe', :fringe 57 | get '/take/:arg', :take 58 | end 59 | 60 | 61 | 62 | # regular path should work too 63 | get '/regular' do 64 | 'flames' 65 | end 66 | 67 | class ClassicMappingTest < MiniTest::Unit::TestCase 68 | include Rack::Test::Methods 69 | 70 | def app 71 | @app = Sinatra::Application 72 | @app.set :environment, :test 73 | @app 74 | end 75 | 76 | def test_render 77 | get '/' 78 | assert_equal 200, last_response.status 79 | assert_equal true, (last_response.body =~ /message/) != nil 80 | end 81 | 82 | def test_fringe 83 | get '/fringe' 84 | assert_equal 200, last_response.status 85 | assert_equal true, (last_response.body =~ /bishop/) != nil 86 | assert_equal true, (last_response.body =~ /Anna Torv/) != nil 87 | end 88 | 89 | def test_param_pass 90 | get '/test/232' 91 | assert_equal 200, last_response.status 92 | assert_equal true, (last_response.body =~ /232/) != nil 93 | end 94 | 95 | def test_help_route 96 | get '/help' 97 | assert_equal 200, last_response.status 98 | end 99 | def test_help_index 100 | get '/help/index' 101 | assert_equal 200, last_response.status 102 | assert_equal true, (last_response.body =~ /help found/) != nil 103 | end 104 | 105 | def test_regular 106 | get '/regular' 107 | assert_equal 200, last_response.status 108 | assert_equal true, (last_response.body =~ /flames/) != nil 109 | end 110 | 111 | def test_all 112 | delete '/all' 113 | assert_equal 200, last_response.status 114 | assert_equal true, (last_response.body =~ /wiped/) != nil 115 | end 116 | 117 | def test_flame 118 | put '/flame' 119 | assert_equal 200, last_response.status 120 | assert_equal true, (last_response.body =~ /heat/) != nil 121 | end 122 | 123 | def test_request 124 | get '/request' 125 | assert_equal 200, last_response.status 126 | assert_equal true, (last_response.body =~ /SCRIPT_NAME/) != nil 127 | end 128 | 129 | def test_post 130 | post '/racket' 131 | assert_equal 200, last_response.status 132 | assert_equal true, (last_response.body =~ /posted/) != nil 133 | end 134 | def test_arguments 135 | get '/take/123' 136 | assert_equal 200, last_response.status 137 | assert_equal true, (last_response.body =~ /123/) != nil 138 | end 139 | end 140 | --------------------------------------------------------------------------------