Changeset 169

Show
Ignore:
Timestamp:
01/24/07 21:17:16 (2 years ago)
Author:
e.@brainspl.at
Message:

Merb just got RESTFULL. You can now use r.resource :posts in yoru router definition to get full rest style dispatching.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/README

    r160 r169  
    66Little bitty lightweight ruby app server. For when you really need performance 
    77for simple dynamic pages. 
    8  
    9 **Sample APP included** 
    108 
    119** Dependencies ** 
     
    3735*RouteMatcher and route compiler*  
    3836Reads your route definition and compiles 
    39 a method on the fly that will match the request path against each route and do the  
    40 right thing. So the following routes: 
     37a method on the fly that will match the request path against each route and do the right thing. 
     38 
     39*** NEW RESTFULL ROUTES *** 
     40 
     41note the r.resource :posts macro. That makes it possible to use a restfull crud style controller for the posts resource 
    4142 
    4243Merb::RouteMatcher.prepare do |r| 
    43   r.add '/foo/:bar/baz/:id', :controller => 'Test', :action => 'foo' 
     44  r.resource :posts 
    4445  r.add '/:controller/:action/:id' 
    45   r.add '/bar/:*rest', :controller => 'Test', :action => 'glob
     46  r.add '/', :controller => 'files', :action => 'index
    4647end 
    4748 
     
    5051lambda{|path|  
    5152  case path 
    52   when Regexp.new('/foo/([^/;.,?]+)/baz/([^/;.,?]+)') 
    53     @sections[:bar] = $1 
    54     @sections[:id] = $2 
    55     return {:controller=>"Test", :action=>"foo"}.merge(@sections) 
     53  when Regexp.new('/posts/([^/,?]+)[;]edit') 
     54    @sections[:id] = $1 
     55    return {:rest=>true, :controller=>"posts", :allowed=>{:get=>"edit"}}.update(@sections) 
     56  when Regexp.new('/posts/new[;]([^/,?]+)') 
     57    @sections[:action] = $1 
     58    return {:rest=>true, :controller=>"posts", :allowed=>{:post=>"new", :get=>"new", :delete=>"new", :put=>"new"}}.update(@sections) 
     59  when Regexp.new('/posts/new') 
     60    return {:rest=>true, :controller=>"posts", :allowed=>{:get=>"new"}}.update(@sections) 
     61  when Regexp.new('/posts/([^/,?]+)\.([^/,?]+)') 
     62    @sections[:id] = $1 
     63    @sections[:format] = $2 
     64    return {:rest=>true, :controller=>"posts", :allowed=>{:get=>"show", :delete=>"destroy", :put=>"update"}}.update(@sections) 
     65  when Regexp.new('/posts\.([^/,?]+)') 
     66    @sections[:format] = $1 
     67    return {:rest=>true, :controller=>"posts", :allowed=>{:post=>"create", :get=>"index"}}.update(@sections) 
     68  when Regexp.new('/posts/([^/,?]+)') 
     69    @sections[:id] = $1 
     70    return {:rest=>true, :controller=>"posts", :allowed=>{:get=>"show", :delete=>"destroy", :put=>"update"}}.update(@sections) 
     71  when Regexp.new('/posts/?') 
     72    return {:rest=>true, :controller=>"posts", :allowed=>{:post=>"create", :get=>"index"}}.update(@sections) 
    5673  when /\A\/([^\/;.,?]+)(?:\/?\Z|\/([^\/;.,?]+)\/?)(?:\/?\Z|\/([^\/;.,?]+)\/?)\Z/ 
    5774      @sections[:controller] = $1 
    5875      @sections[:action] = $2 || 'index' 
    59       @sections[:id] = $3 || nil 
     76      @sections[:id] = $3 if $3 
    6077      return @sections 
    61   when Regexp.new('/bar/([^;.,?]+)') 
    62     @sections[:rest] = $1 
    63     return {:controller=>"Test", :action=>"glob"}.merge(@sections) 
     78  when Regexp.new('/') 
     79    return {:controller=>"files", :action=>"index"}.update(@sections) 
    6480  else 
    6581    return {:controller=>'Noroutefound', :action=>'noroute'} 
    6682  end 
    6783} 
     84 
     85 
     86*Restful Controller* 
     87 
     88restful controllers use a different dispatch system based on the request method verbs. 
     89 
     90class Posts < Merb::Controller 
     91  # GET /posts 
     92  # GET /posts.xml 
     93  def index 
     94  end 
     95 
     96  # GET /posts/1 
     97  # GET /posts/1.xml 
     98  def show 
     99  end 
     100 
     101  # GET /posts/new 
     102  def new 
     103  end 
     104 
     105  # GET /posts/1;edit 
     106  def edit 
     107  end 
     108 
     109  # POST /posts 
     110  # POST /posts.xml 
     111  def create 
     112  end 
     113 
     114  # PUT /posts/1 
     115  # PUT /posts/1.xml 
     116  def update 
     117  end 
     118 
     119  # DELETE /posts/1 
     120  # DELETE /posts/1.xml 
     121  def destroy 
     122  end 
     123end 
     124 
    68125 
    69126*Simple Controllers* 
  • trunk/lib/merb.rb

    r167 r169  
    4848MERB_ROOT = Merb::Server.merb_root || Dir.pwd 
    4949DIST_ROOT = Merb::Server.dist_root || Dir.pwd+'/dist' 
     50MERB_ENV  = Merb::Server.config[:environment] 
    5051 
    5152logpath = $TESTING ? "/tmp/merb_test.log" : "#{MERB_ROOT}/log/merb.#{Merb::Server.port}.log" 
  • trunk/lib/merb/core_ext/merb_object.rb

    r142 r169  
    1111    self.class.class_eval { define_method name, &blk } 
    1212  end    
     13   
    1314  def blank? 
    1415    if    respond_to? :empty? then empty? 
     
    1819  end 
    1920 
     21  def with_options(options) 
     22    yield Merb::OptionMerger.new(self, options) 
     23  end 
    2024end 
     25 
     26module Merb 
     27  class OptionMerger #:nodoc: 
     28    instance_methods.each do |method|  
     29      undef_method(method) if method !~ /^(__|instance_eval|class)/ 
     30    end 
     31     
     32    def initialize(context, options) 
     33      @context, @options = context, options 
     34    end 
     35     
     36    private 
     37      def method_missing(method, *arguments, &block) 
     38        merge_argument_options! arguments 
     39        @context.send(method, *arguments, &block) 
     40      end 
     41       
     42      def merge_argument_options!(arguments) 
     43        arguments << if arguments.last.respond_to? :to_hash 
     44          @options.merge(arguments.pop) 
     45        else 
     46          @options.dup 
     47        end   
     48      end 
     49  end 
     50end 
  • trunk/lib/merb/merb_controller.rb

    r167 r169  
    2020    include Merb::ResponderMixin 
    2121     
    22     attr_accessor :status, :body 
     22    attr_accessor :status, :body, :request 
    2323     
    2424    MULTIPART_REGEXP = /\Amultipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze 
     
    8585        end   
    8686      end 
     87       
    8788      @cookies, @params = cookies.dup, querystring.dup.merge(args) 
    88       @cookies.merge!(:_session_id => @params[:_session_id]) if @params.has_key?(:_session_id) 
    89       @method = @params.delete(:_method).downcase.to_sym if @params.has_key?(:_method) 
     89      @cookies.merge!(session_id_key => @params[session_id_key]) if @params.key?(session_id_key) 
     90       
     91      allow = [:post, :put, :delete] 
     92      allow << :get if MERB_ENV == 'development' 
     93      if @params.key?(:_method) && [:post, :put, :delete].include?(@method) 
     94        @method = @params.delete(:_method).downcase.to_sym  
     95      end 
    9096      @request = Request.new(@env, @method) 
     97       
    9198      MERB_LOGGER.info("Params: #{params.inspect}") 
    9299    end 
  • trunk/lib/merb/merb_exceptions.rb

    r149 r169  
    99  class MissingControllerFile < MerbError; end 
    1010  class MerbControllerError < MerbError; end 
    11    
     11  class RestfulMethodNotAllowed < MerbError; end 
    1212  # format exception message for browser display   
    1313  def self.html_exception(e) 
  • trunk/lib/merb/merb_handler.rb

    r167 r169  
    147147  # [controller, action] 
    148148  def handle(request, response) 
    149     path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/') 
     149    path = request.params[Mongrel::Const::REQUEST_URI].sub(/\/+/, '/') 
    150150    path = path[0..-2] if (path[-1] == ?/) && path.size > 1 
     151     
    151152    route = Merb::RouteMatcher.new.route_request(path) 
    152     [ instantiate_controller(route[:controller], request.body, request.params, route, response),  
    153       route[:action] ] 
     153     
     154    allowed = route.delete(:allowed) 
     155    rest = route.delete(:rest) 
     156     
     157    controller =  instantiate_controller(route[:controller], request.body, request.params, route, response) 
     158     
     159    if rest 
     160      method = controller.request.method 
     161      if allowed.keys.include?(method) 
     162        controller.params[:action] = allowed[method] 
     163        [controller, allowed[method]] 
     164      else 
     165        raise Merb::RestfulMethodNotAllowed 
     166      end   
     167    else   
     168      [controller, route[:action] ] 
     169    end 
    154170  end 
    155171   
     
    160176  def instantiate_controller(controller_name, req, env, params, res)  
    161177    if !File.exist?(DIST_ROOT+"/app/controllers/#{controller_name.snake_case}.rb") 
    162       raise Merb::MissingControllerFile 
     178      raise "Bad controller! #{controller_name.snake_case}" 
    163179    end 
    164180    begin 
  • trunk/lib/merb/merb_request.rb

    r106 r169  
    7979    # returns the REQUEST_METHOD 
    8080    def method 
    81       @method ||= @env['REQUEST_METHOD'] 
     81      @method ||= @env['REQUEST_METHOD'].downcase.intern 
    8282    end   
    8383     
  • trunk/lib/merb/merb_router.rb

    r168 r169  
    3737    # the final compiled lambda that gets used 
    3838    # as the body of the route_request method. 
     39    def self.compiled_statement 
     40      @@compiled_statement 
     41    end 
     42     
    3943    def compiled_statement 
    4044      @@compiled_statement 
     
    9195      statement 
    9296    end 
    93            
     97     
     98    # add a resource to be compiled for rest style dispatch 
     99    def self.resource(res) 
     100      with_options :controller => res.to_s, :rest => true do |r| 
     101        r.add "/#{res}/:id[;]edit", :allowed => {:get => 'edit'} 
     102        r.add "/#{res}/new[;]:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'} 
     103        r.add "/#{res}/new" , :allowed => {:get => 'new'} 
     104        # r.add "/#{res}/:id[;]+:action$", :allowed => {:get => '?', :post => '?', :put => '?', :delete => '?'} 
     105        r.add "/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'} 
     106        r.add "/#{res}\\.:format", :allowed => {:get => 'index', :post => 'create'} 
     107        r.add "/#{res}/:id", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'} 
     108        r.add "/#{res}/?", :allowed => {:get => 'index', :post => 'create'} 
     109      end 
     110    end 
     111     
    94112  end 
    95113 
    96 end  
     114end