merb 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +105 -95
- data/Rakefile +4 -5
- data/examples/skeleton.tar +0 -0
- data/examples/skeleton/dist/conf/router.rb +5 -2
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +0 -1
- data/lib/merb.rb +10 -3
- data/lib/merb/core_ext.rb +3 -1
- data/lib/merb/core_ext/merb_class.rb +85 -51
- data/lib/merb/core_ext/merb_inflections.rb +112 -0
- data/lib/merb/core_ext/merb_inflector.rb +275 -0
- data/lib/merb/core_ext/merb_object.rb +0 -55
- data/lib/merb/core_ext/merb_string.rb +1 -13
- data/lib/merb/merb_controller.rb +26 -20
- data/lib/merb/merb_dispatcher.rb +10 -3
- data/lib/merb/merb_exceptions.rb +6 -1
- data/lib/merb/merb_handler.rb +1 -1
- data/lib/merb/merb_mailer.rb +3 -2
- data/lib/merb/merb_request.rb +56 -0
- data/lib/merb/merb_router.rb +24 -38
- data/lib/merb/mixins/controller_mixin.rb +34 -5
- data/lib/merb/mixins/render_mixin.rb +7 -7
- data/lib/merb/mixins/responder_mixin.rb +17 -10
- data/lib/merb/mixins/view_context_mixin.rb +13 -2
- data/lib/merb/session/merb_ar_session.rb +4 -4
- data/lib/merb/session/merb_memory_session.rb +4 -4
- data/lib/merb/template/erubis.rb +1 -1
- data/lib/merb/template/haml.rb +20 -14
- data/lib/tasks/merb.rake +11 -2
- metadata +4 -2
    
        data/lib/merb/merb_controller.rb
    CHANGED
    
    | @@ -12,10 +12,18 @@ module Merb | |
| 12 12 | 
             
              # to your controller via params. It also parses the ?query=string and
         | 
| 13 13 | 
             
              # puts that into params as well.
         | 
| 14 14 | 
             
              class Controller
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                 | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 15 | 
            +
                
         | 
| 16 | 
            +
                class_inheritable_accessor :_layout, 
         | 
| 17 | 
            +
                                           :_session_id_key, 
         | 
| 18 | 
            +
                                           :_template_extensions,
         | 
| 19 | 
            +
                                           :_template_root,
         | 
| 20 | 
            +
                                           :_layout_root
         | 
| 21 | 
            +
                self._layout = :application
         | 
| 22 | 
            +
                self._session_id_key = :_session_id
         | 
| 23 | 
            +
                self._template_extensions = { }
         | 
| 24 | 
            +
                self._template_root = File.expand_path(MERB_ROOT / "dist/app/views")
         | 
| 25 | 
            +
                self._layout_root   = File.expand_path(MERB_ROOT / "dist/app/views/layout")
         | 
| 26 | 
            +
                
         | 
| 19 27 | 
             
                include Merb::ControllerMixin
         | 
| 20 28 | 
             
                include Merb::RenderMixin
         | 
| 21 29 | 
             
                include Merb::ResponderMixin
         | 
| @@ -23,22 +31,22 @@ module Merb | |
| 23 31 | 
             
                attr_accessor :status, :body, :request
         | 
| 24 32 |  | 
| 25 33 | 
             
                MULTIPART_REGEXP = /\Amultipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
         | 
| 26 | 
            -
             | 
| 34 | 
            +
                
         | 
| 27 35 | 
             
                # parses the http request into params, headers and cookies
         | 
| 28 36 | 
             
                # that you can use in your controller classes. Also handles
         | 
| 29 37 | 
             
                # file uploads by writing a tempfile and passing a reference 
         | 
| 30 38 | 
             
                # in params.
         | 
| 31 39 | 
             
                def initialize(request, env, args, response)
         | 
| 32 40 | 
             
                  @env = MerbHash[env.to_hash]
         | 
| 33 | 
            -
                  @status, @method, @response, @headers = 200, (env[ | 
| 34 | 
            -
                       | 
| 35 | 
            -
                  cookies = query_parse(@env[ | 
| 36 | 
            -
                  querystring = query_parse(@env[ | 
| 41 | 
            +
                  @status, @method, @response, @headers = 200, (env[Mongrel::Const::REQUEST_METHOD]||Mongrel::Const::GET).downcase.to_sym, response,
         | 
| 42 | 
            +
                      Mongrel::Const::CONTENT_TYPE_TEXT_HTML_HASH
         | 
| 43 | 
            +
                  cookies = query_parse(@env[Mongrel::Const::HTTP_COOKIE], ';,')
         | 
| 44 | 
            +
                  querystring = query_parse(@env[Mongrel::Const::QUERY_STRING])
         | 
| 37 45 |  | 
| 38 | 
            -
                  if MULTIPART_REGEXP =~ @env[ | 
| 46 | 
            +
                  if MULTIPART_REGEXP =~ @env[Mongrel::Const::UPCASE_CONTENT_TYPE] && @method == :post
         | 
| 39 47 | 
             
                    querystring.update(parse_multipart(request, $1))
         | 
| 40 48 | 
             
                  elsif @method == :post
         | 
| 41 | 
            -
                    if [ | 
| 49 | 
            +
                    if [Mongrel::Const::APPLICATION_JSON, Mongrel::Const::TEXT_JSON].include?(@env[Mongrel::Const::UPCASE_CONTENT_TYPE])
         | 
| 42 50 | 
             
                      MERB_LOGGER.info("JSON Request")
         | 
| 43 51 | 
             
                      json = JSON.parse(request.read || "") || {}
         | 
| 44 52 | 
             
                      json = MerbHash.new(json) if json.is_a? Hash
         | 
| @@ -49,7 +57,7 @@ module Merb | |
| 49 57 | 
             
                  end
         | 
| 50 58 |  | 
| 51 59 | 
             
                  @cookies, @params = cookies, querystring.update(args)
         | 
| 52 | 
            -
                  @cookies[ | 
| 60 | 
            +
                  @cookies[_session_id_key] = @params[_session_id_key] if @params.key?(_session_id_key)
         | 
| 53 61 |  | 
| 54 62 | 
             
                  allow = [:post, :put, :delete]
         | 
| 55 63 | 
             
                  allow << :get if MERB_ENV == 'development'
         | 
| @@ -81,7 +89,7 @@ module Merb | |
| 81 89 | 
             
                  end
         | 
| 82 90 | 
             
                  call_filters(after_filters) 
         | 
| 83 91 | 
             
                  finalize_session if respond_to?:finalize_session
         | 
| 84 | 
            -
                  MERB_LOGGER.info("Time spent in #{action} action: #{Time.now - start} seconds")
         | 
| 92 | 
            +
                  MERB_LOGGER.info("Time spent in #{self.class}##{action} action: #{Time.now - start} seconds")
         | 
| 85 93 | 
             
                end
         | 
| 86 94 |  | 
| 87 95 | 
             
                # override this method on your controller classes to specialize
         | 
| @@ -126,15 +134,13 @@ module Merb | |
| 126 134 | 
             
                  @response
         | 
| 127 135 | 
             
                end
         | 
| 128 136 |  | 
| 129 | 
            -
             | 
| 130 | 
            -
                trait :template_root => File.expand_path(MERB_ROOT / "dist/app/views")
         | 
| 131 | 
            -
                trait :layout_root   => File.expand_path(MERB_ROOT / "dist/app/views/layout")
         | 
| 137 | 
            +
             | 
| 132 138 | 
             
                # lookup the trait[:template_extensions] for the extname of the filename
         | 
| 133 139 | 
             
                # you pass. Answers with the engine that matches the extension, Template::Erubis
         | 
| 134 140 | 
             
                # is used if none matches.
         | 
| 135 141 | 
             
                def engine_for(file)
         | 
| 136 142 | 
             
                  extension = File.extname(file)[1..-1]
         | 
| 137 | 
            -
                   | 
| 143 | 
            +
                  _template_extensions[extension]
         | 
| 138 144 | 
             
                rescue
         | 
| 139 145 | 
             
                  ::Merb::Template::Erubis
         | 
| 140 146 | 
             
                end
         | 
| @@ -143,7 +149,7 @@ module Merb | |
| 143 149 | 
             
                # a list of extensions that will be looked up on #render of an action.
         | 
| 144 150 | 
             
                def self.register_engine(engine, *extensions)
         | 
| 145 151 | 
             
                  [extensions].flatten.uniq.each do |ext|
         | 
| 146 | 
            -
                     | 
| 152 | 
            +
                    _template_extensions[ext] = engine
         | 
| 147 153 | 
             
                  end  
         | 
| 148 154 | 
             
                end
         | 
| 149 155 |  | 
| @@ -154,8 +160,8 @@ module Merb | |
| 154 160 | 
             
                # from its superclasses. Since @@class variables are almost
         | 
| 155 161 | 
             
                # global vars within an inheritance tree, we use 
         | 
| 156 162 | 
             
                # @class_instance_variables instead
         | 
| 157 | 
            -
                 | 
| 158 | 
            -
                 | 
| 163 | 
            +
                class_inheritable_accessor :before_filters
         | 
| 164 | 
            +
                class_inheritable_accessor :after_filters
         | 
| 159 165 |  | 
| 160 166 | 
             
                # calls a filter chain according to rules.
         | 
| 161 167 | 
             
                def call_filters(filter_set)
         | 
    
        data/lib/merb/merb_dispatcher.rb
    CHANGED
    
    | @@ -5,6 +5,10 @@ module Merb | |
| 5 5 |  | 
| 6 6 | 
             
                  attr_accessor :path_prefix
         | 
| 7 7 |  | 
| 8 | 
            +
                  def use_mutex=(val)
         | 
| 9 | 
            +
                    @@use_mutex = val
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                  
         | 
| 8 12 | 
             
                  @@mutex = Mutex.new
         | 
| 9 13 | 
             
                  @@use_mutex = ::Merb::Server.use_mutex
         | 
| 10 14 | 
             
                  # This is where we grab the incoming request PATH_INFO
         | 
| @@ -45,7 +49,7 @@ module Merb | |
| 45 49 | 
             
                  def route_path(path)
         | 
| 46 50 | 
             
                    path = path.sub(/\/+/, '/').sub(/\?.*$/, '')
         | 
| 47 51 | 
             
                    path = path[0..-2] if (path[-1] == ?/) && path.size > 1
         | 
| 48 | 
            -
                    Merb::RouteMatcher. | 
| 52 | 
            +
                    Merb::RouteMatcher.route_request(path)
         | 
| 49 53 | 
             
                  end
         | 
| 50 54 |  | 
| 51 55 | 
             
                  # take a controller class name string and reload or require
         | 
| @@ -57,14 +61,17 @@ module Merb | |
| 57 61 | 
             
                      raise "Bad controller! #{controller_name.snake_case}"
         | 
| 58 62 | 
             
                    end unless $TESTING
         | 
| 59 63 | 
             
                    begin
         | 
| 60 | 
            -
                       | 
| 64 | 
            +
                      unless MERB_ENV == 'production'
         | 
| 65 | 
            +
                        Object.send(:remove_const, controller_name.camel_case.intern) rescue nil
         | 
| 66 | 
            +
                        load(controller_name.snake_case + '.rb')
         | 
| 67 | 
            +
                      end
         | 
| 61 68 | 
             
                      return Object.const_get( controller_name.camel_case ).new(req, env, params, res)
         | 
| 62 69 | 
             
                    rescue RuntimeError
         | 
| 63 70 | 
             
                      warn "Error getting instance of '#{controller_name.camel_case}': #{$!}"
         | 
| 64 71 | 
             
                      raise $!
         | 
| 65 72 | 
             
                    end
         | 
| 66 73 | 
             
                  end
         | 
| 67 | 
            -
             | 
| 74 | 
            +
             | 
| 68 75 | 
             
                end # end class << self
         | 
| 69 76 |  | 
| 70 77 | 
             
              end
         | 
    
        data/lib/merb/merb_exceptions.rb
    CHANGED
    
    | @@ -43,7 +43,12 @@ module Merb | |
| 43 43 |  | 
| 44 44 | 
             
                  backtrace.map! do |line|
         | 
| 45 45 | 
             
                    file, lineno, meth = line.scan(/(.*?):(\d+)(?::in `(.*?)')?/).first
         | 
| 46 | 
            -
                     | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                    # When a backtrace entry doesn't have a filepath as the first field 
         | 
| 48 | 
            +
                    # (e.g., "(haml):12" or "(eval):41:in `_haml_render'"), it'll cause 
         | 
| 49 | 
            +
                    # a second exception to be raised, masking the first.  This traps it.  
         | 
| 50 | 
            +
                    lines = (__caller_lines__(file, lineno, 5) rescue [])
         | 
| 51 | 
            +
                    
         | 
| 47 52 | 
             
                    [ lines, lines.object_id.abs, file, lineno, meth ]
         | 
| 48 53 | 
             
                  end
         | 
| 49 54 |  | 
    
        data/lib/merb/merb_handler.rb
    CHANGED
    
    | @@ -62,7 +62,7 @@ class MerbHandler < Mongrel::HttpHandler | |
| 62 62 | 
             
                  @files.process(request,response)
         | 
| 63 63 | 
             
                else
         | 
| 64 64 | 
             
                  begin
         | 
| 65 | 
            -
                    #  | 
| 65 | 
            +
                    # Let Merb:Dispatcher find the route and call the filter chain and action
         | 
| 66 66 | 
             
                    controller = nil
         | 
| 67 67 | 
             
                    controller, action = Merb::Dispatcher.handle(request, response)
         | 
| 68 68 |  | 
    
        data/lib/merb/merb_mailer.rb
    CHANGED
    
    | @@ -1,7 +1,8 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
             | 
| 2 2 |  | 
| 3 3 | 
             
            begin
         | 
| 4 4 | 
             
              require 'mailfactory'
         | 
| 5 | 
            +
              require 'net/smtp'
         | 
| 5 6 | 
             
            rescue LoadError
         | 
| 6 7 | 
             
              puts "You need to install the mailfactory gem to use Merb::Mailer"
         | 
| 7 8 | 
             
              MERB_LOGGER.warn "You need to install the mailfactory gem to use Merb::Mailer"
         | 
| @@ -10,7 +11,7 @@ end | |
| 10 11 | 
             
            module Merb
         | 
| 11 12 | 
             
              class Mailer
         | 
| 12 13 |  | 
| 13 | 
            -
                 | 
| 14 | 
            +
                class_inheritable_accessor :config
         | 
| 14 15 |  | 
| 15 16 | 
             
                def sendmail
         | 
| 16 17 | 
             
                  sendmail = IO.popen("sendmail #{@mail.to}", 'w+')
         | 
    
        data/lib/merb/merb_request.rb
    CHANGED
    
    | @@ -50,6 +50,62 @@ module Merb | |
| 50 50 | 
             
                  @env['REQUEST_URI']
         | 
| 51 51 | 
             
                end
         | 
| 52 52 |  | 
| 53 | 
            +
                def user_agent
         | 
| 54 | 
            +
                  @env['HTTP_USER_AGENT']
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def server_name
         | 
| 58 | 
            +
                  @env['SERVER_NAME']
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def accept_encoding
         | 
| 62 | 
            +
                  @env['HTTP_ACCEPT_ENCODING']
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def script_name
         | 
| 66 | 
            +
                  @env['SCRIPT_NAME']
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def cache_control
         | 
| 70 | 
            +
                  @env['HTTP_CACHE_CONTROL']
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def accept_language
         | 
| 74 | 
            +
                  @env['HTTP_ACCEPT_LANGUAGE']
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def host
         | 
| 78 | 
            +
                  @env['HTTP_HOST']
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def server_software
         | 
| 82 | 
            +
                  @env['SERVER_SOFTWARE']
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def keep_alive
         | 
| 86 | 
            +
                  @env['HTTP_KEEP_ALIVE']
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def accept_charset
         | 
| 90 | 
            +
                  @env['HTTP_ACCEPT_CHARSET']
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def version
         | 
| 94 | 
            +
                  @env['HTTP_VERSION']
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def gateway
         | 
| 98 | 
            +
                  @env['GATEWAY_INTERFACE']
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def accept
         | 
| 102 | 
            +
                  @env['HTTP_ACCEPT']
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def connection
         | 
| 106 | 
            +
                  @env['HTTP_CONNECTION']
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 53 109 | 
             
                def query_string
         | 
| 54 110 | 
             
                  @env['QUERY_STRING']  
         | 
| 55 111 | 
             
                end
         | 
    
        data/lib/merb/merb_router.rb
    CHANGED
    
    | @@ -1,9 +1,5 @@ | |
| 1 1 | 
             
            module Merb
         | 
| 2 | 
            -
             | 
| 3 | 
            -
                require 'active_support'
         | 
| 4 | 
            -
              rescue 
         | 
| 5 | 
            -
                MERB_LOGGER.warn "You must have ActiveSupport installed to use merb restful routing\nNormal routing works fine without"
         | 
| 6 | 
            -
              end
         | 
| 2 | 
            +
             | 
| 7 3 | 
             
              # Merb::RouteMatcher is the request routing mapper for the merb framework.
         | 
| 8 4 | 
             
              # You can define placeholder parts of the url with the :symbol notation.
         | 
| 9 5 | 
             
              # so r.add '/foo/:bar/baz/:id', :class => 'Bar', :method => 'foo'
         | 
| @@ -28,17 +24,7 @@ module Merb | |
| 28 24 | 
             
                  yield self
         | 
| 29 25 | 
             
                  compile_router
         | 
| 30 26 | 
             
                end  
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                # init @sections for route segment recognition
         | 
| 33 | 
            -
                def initialize
         | 
| 34 | 
            -
                  @sections = Hash.new
         | 
| 35 | 
            -
                end  
         | 
| 36 | 
            -
                
         | 
| 37 | 
            -
                # all defined routes in their raw form.
         | 
| 38 | 
            -
                def routes
         | 
| 39 | 
            -
                  @@routes
         | 
| 40 | 
            -
                end  
         | 
| 41 | 
            -
                
         | 
| 27 | 
            +
             | 
| 42 28 | 
             
                # the final compiled lambda that gets used
         | 
| 43 29 | 
             
                # as the body of the route_request method.
         | 
| 44 30 | 
             
                def self.compiled_statement
         | 
| @@ -63,28 +49,20 @@ module Merb | |
| 63 49 | 
             
                # against each of the compiled routes in turn.
         | 
| 64 50 | 
             
                # first route that matches wins.
         | 
| 65 51 | 
             
                def self.compile_router
         | 
| 66 | 
            -
                  router_lambda = @@routes.inject("lambda{|path| \n  case path\n") { |m,r|
         | 
| 52 | 
            +
                  router_lambda = @@routes.inject("lambda{|path| \n  sections={}\n  case path\n") { |m,r|
         | 
| 67 53 | 
             
                    m << compile(r)
         | 
| 68 54 | 
             
                  } <<"  else\n    return {:controller=>'Noroutefound', :action=>'noroute'}\n  end\n}"
         | 
| 69 55 | 
             
                  @@compiled_statement = router_lambda
         | 
| 70 | 
            -
                   | 
| 56 | 
            +
                  meta_def(:route_request, &eval(router_lambda))
         | 
| 71 57 | 
             
                end  
         | 
| 72 58 |  | 
| 73 59 | 
             
                # compile each individual route into a when /.../
         | 
| 74 60 | 
             
                # component of the case statement. Takes /:sections
         | 
| 75 61 | 
             
                # of the route def that start with : and turns them
         | 
| 76 62 | 
             
                # into placeholders for whatever urls match against
         | 
| 77 | 
            -
                # the route in question. | 
| 78 | 
            -
                # /:controller/:action/:id route.
         | 
| 63 | 
            +
                # the route in question.
         | 
| 79 64 | 
             
                def self.compile(route)
         | 
| 80 65 | 
             
                  raise ArgumentError unless String === route[0]
         | 
| 81 | 
            -
                  if route[0] == '/:controller/:action/:id'
         | 
| 82 | 
            -
                    return '  when /\A\/([^\/;.,?]+)(?:\/?\Z|\/([^\/;.,?]+)\/?)(?:\/?\Z|\/([^\/;.,?]+)\/?)\Z/
         | 
| 83 | 
            -
                  @sections[:controller] = $1
         | 
| 84 | 
            -
                  @sections[:action] = $2 || \'index\'
         | 
| 85 | 
            -
                  @sections[:id] = $3 if $3
         | 
| 86 | 
            -
                  return @sections'<<"\n"
         | 
| 87 | 
            -
                  end
         | 
| 88 66 | 
             
                  code, count = '', 0
         | 
| 89 67 | 
             
                  while route[0] =~ @@section_regexp
         | 
| 90 68 | 
             
                    route[0] = route[0].dup
         | 
| @@ -96,13 +74,13 @@ module Merb | |
| 96 74 | 
             
                    else  
         | 
| 97 75 | 
             
                      route[0].sub!(@@section_regexp, "([^\/,?]+)")
         | 
| 98 76 | 
             
                    end
         | 
| 99 | 
            -
                    code << "     | 
| 77 | 
            +
                    code << "    sections[:#{name}] = $#{count}\n"
         | 
| 100 78 | 
             
                  end
         | 
| 101 79 | 
             
                  @@compiled_regexen << Regexp.new(route[0])
         | 
| 102 80 | 
             
                  index = @@compiled_regexen.size - 1
         | 
| 103 81 | 
             
                  condition = "  when @@compiled_regexen[#{index}] "
         | 
| 104 82 | 
             
                  statement = "#{condition}\n#{code}"
         | 
| 105 | 
            -
                  statement << "    return #{route[1].inspect}.update( | 
| 83 | 
            +
                  statement << "    return #{route[1].inspect}.update(sections)\n"
         | 
| 106 84 | 
             
                  statement
         | 
| 107 85 | 
             
                end
         | 
| 108 86 |  | 
| @@ -145,7 +123,7 @@ module Merb | |
| 145 123 | 
             
                  if block_given?
         | 
| 146 124 | 
             
                    procs = []
         | 
| 147 125 | 
             
                    yield Resource.new(res, procs, opts)
         | 
| 148 | 
            -
                    procs.reverse.each  | 
| 126 | 
            +
                    procs.reverse.each &:call
         | 
| 149 127 | 
             
                  else
         | 
| 150 128 | 
             
                    generate_resources_routes(res,opts)
         | 
| 151 129 | 
             
                  end
         | 
| @@ -157,7 +135,7 @@ module Merb | |
| 157 135 | 
             
                  if block_given?
         | 
| 158 136 | 
             
                    procs = []
         | 
| 159 137 | 
             
                    yield Resource.new(res, procs, opts)
         | 
| 160 | 
            -
                    procs.reverse.each  | 
| 138 | 
            +
                    procs.reverse.each &:call
         | 
| 161 139 | 
             
                  else
         | 
| 162 140 | 
             
                    generate_singleton_routes(res,opts)
         | 
| 163 141 | 
             
                  end
         | 
| @@ -165,19 +143,19 @@ module Merb | |
| 165 143 |  | 
| 166 144 | 
             
                def self.generate_resources_routes(res,opt)
         | 
| 167 145 | 
             
                  with_options :controller => res.to_s, :rest => true do |r|
         | 
| 168 | 
            -
                    r.add "#{opt[:prefix]}/#{res}/:id[ | 
| 169 | 
            -
                    r.add "#{opt[:prefix]}/#{res}/new[ | 
| 146 | 
            +
                    r.add "#{opt[:prefix]}/#{res}/:id[;/]+edit", :allowed => {:get => 'edit'}
         | 
| 147 | 
            +
                    r.add "#{opt[:prefix]}/#{res}/new[;/]+:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'}
         | 
| 170 148 | 
             
                    r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
         | 
| 171 149 | 
             
                    if mem = opt[:member]
         | 
| 172 150 | 
             
                      mem.keys.sort_by{|x| "#{x}"}.each {|action|
         | 
| 173 | 
            -
                        allowed = mem[action]. | 
| 174 | 
            -
                        r.add "#{opt[:prefix]}/#{res}/:id[ | 
| 151 | 
            +
                        allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
         | 
| 152 | 
            +
                        r.add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed
         | 
| 175 153 | 
             
                      }
         | 
| 176 154 | 
             
                    end
         | 
| 177 155 | 
             
                    if coll = opt[:collection]
         | 
| 178 156 | 
             
                      coll.keys.sort_by{|x| "#{x}"}.each {|action|
         | 
| 179 | 
            -
                        allowed = coll[action]. | 
| 180 | 
            -
                        r.add "#{opt[:prefix]}/#{res}[ | 
| 157 | 
            +
                        allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
         | 
| 158 | 
            +
                        r.add "#{opt[:prefix]}/#{res}[;/]+#{action}", :allowed => allowed
         | 
| 181 159 | 
             
                      }
         | 
| 182 160 | 
             
                    end  
         | 
| 183 161 | 
             
                    r.add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
         | 
| @@ -189,13 +167,21 @@ module Merb | |
| 189 167 |  | 
| 190 168 | 
             
                def self.generate_singleton_routes(res,opt)
         | 
| 191 169 | 
             
                  with_options :controller => res.to_s, :rest => true do |r|
         | 
| 192 | 
            -
                    r.add "#{opt[:prefix]}/#{res}[ | 
| 170 | 
            +
                    r.add "#{opt[:prefix]}/#{res}[;/]+edit", :allowed => {:get => 'edit'}
         | 
| 193 171 | 
             
                    r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'}
         | 
| 194 172 | 
             
                    r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
         | 
| 195 173 | 
             
                    r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'}
         | 
| 196 174 | 
             
                  end
         | 
| 197 175 | 
             
                end
         | 
| 198 176 |  | 
| 177 | 
            +
                def self.default_routes
         | 
| 178 | 
            +
                  add "/:controller/:action/:id\\.:format"
         | 
| 179 | 
            +
                  add "/:controller/:action/:id"
         | 
| 180 | 
            +
                  add "/:controller/:action\\.:format"
         | 
| 181 | 
            +
                  add "/:controller/:action"
         | 
| 182 | 
            +
                  add "/:controller\\.:format", :action => 'index'
         | 
| 183 | 
            +
                  add "/:controller", :action => 'index'
         | 
| 184 | 
            +
                end
         | 
| 199 185 | 
             
              end
         | 
| 200 186 |  | 
| 201 187 | 
             
            end
         | 
| @@ -11,12 +11,13 @@ module Merb | |
| 11 11 | 
             
                  buf = ""
         | 
| 12 12 | 
             
                  content_length = @env['CONTENT_LENGTH'].to_i
         | 
| 13 13 | 
             
                  input = request
         | 
| 14 | 
            +
                  input.binmode if defined? input.binmode
         | 
| 14 15 | 
             
                  boundary_size = boundary.size + EOL.size
         | 
| 15 16 | 
             
                  bufsize = 16384
         | 
| 16 17 | 
             
                  content_length -= boundary_size
         | 
| 17 18 | 
             
                  status = input.read(boundary_size)
         | 
| 18 19 | 
             
                  raise EOFError, "bad content body"  unless status == boundary + EOL
         | 
| 19 | 
            -
                  rx = /(?:#{EOL})?#{Regexp.quote(boundary)}(#{EOL}|--)/
         | 
| 20 | 
            +
                  rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
         | 
| 20 21 |  | 
| 21 22 | 
             
                  loop {
         | 
| 22 23 | 
             
                    head = nil
         | 
| @@ -32,7 +33,7 @@ module Merb | |
| 32 33 | 
             
                        content_type = head[CONTENT_TYPE_REGEX, 1]
         | 
| 33 34 | 
             
                        name = head[NAME_REGEX, 1]
         | 
| 34 35 |  | 
| 35 | 
            -
                        if filename      
         | 
| 36 | 
            +
                        if filename && !filename.empty?      
         | 
| 36 37 | 
             
                          body = Tempfile.new(:Merb)
         | 
| 37 38 | 
             
                          body.binmode if defined? body.binmode
         | 
| 38 39 | 
             
                        end
         | 
| @@ -58,9 +59,14 @@ module Merb | |
| 58 59 | 
             
                      content_length = -1  if $1 == "--"
         | 
| 59 60 | 
             
                    end
         | 
| 60 61 |  | 
| 61 | 
            -
                    if filename
         | 
| 62 | 
            +
                    if filename && !filename.empty?   
         | 
| 62 63 | 
             
                      body.rewind
         | 
| 63 | 
            -
                      data = { | 
| 64 | 
            +
                      data = { 
         | 
| 65 | 
            +
                     	  :filename => File.basename(filename),  
         | 
| 66 | 
            +
                     	  :content_type => content_type,  
         | 
| 67 | 
            +
                     	  :tempfile => body, 
         | 
| 68 | 
            +
                     	  :size => File.size(body) 
         | 
| 69 | 
            +
                     	}
         | 
| 64 70 | 
             
                    else
         | 
| 65 71 | 
             
                      data = body
         | 
| 66 72 | 
             
                    end
         | 
| @@ -90,7 +96,7 @@ module Merb | |
| 90 96 | 
             
                # request into the params hash. So for example:
         | 
| 91 97 | 
             
                # /foo?bar=nik&post[title]=heya&post[body]=whatever
         | 
| 92 98 | 
             
                # parses into:
         | 
| 93 | 
            -
                # {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
         | 
| 99 | 
            +
                # {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever'}}
         | 
| 94 100 | 
             
                def query_parse(qs, d = '&;')
         | 
| 95 101 | 
             
                  m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
         | 
| 96 102 | 
             
                  (qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p| 
         | 
| @@ -161,6 +167,29 @@ module Merb | |
| 161 167 | 
             
                  return
         | 
| 162 168 | 
             
                end
         | 
| 163 169 |  | 
| 170 | 
            +
                # stream_file( { :filename => file_name, 
         | 
| 171 | 
            +
                #                :type => content_type,
         | 
| 172 | 
            +
                #                :content_length => content_length }) do
         | 
| 173 | 
            +
                #   @response.send_status(opts[:content_length])
         | 
| 174 | 
            +
                #   @response.send_header
         | 
| 175 | 
            +
                #   AWS::S3::S3Object.stream(user.folder_name + "-" + user_file.unique_id, bucket_name) do |chunk|
         | 
| 176 | 
            +
                #       @response.write chunk
         | 
| 177 | 
            +
                #   end
         | 
| 178 | 
            +
                # end
         | 
| 179 | 
            +
                def stream_file(opts={}, &stream)
         | 
| 180 | 
            +
                  opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
         | 
| 181 | 
            +
                  disposition = opts[:disposition].dup || 'attachment'
         | 
| 182 | 
            +
                  disposition << %(; filename="#{opts[:filename]}")
         | 
| 183 | 
            +
                  @response.headers.update(
         | 
| 184 | 
            +
                    'Content-Type'              => opts[:type].strip,  # fixes a problem with extra '\r' with some browsers
         | 
| 185 | 
            +
                    'Content-Disposition'       => disposition,
         | 
| 186 | 
            +
                    'Content-Transfer-Encoding' => 'binary',
         | 
| 187 | 
            +
                    'CONTENT-LENGTH'            => opts[:content_length]
         | 
| 188 | 
            +
                  )
         | 
| 189 | 
            +
                  stream
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
                
         | 
| 192 | 
            +
             | 
| 164 193 | 
             
                # This uses nginx X-Accel-Redirect header to send
         | 
| 165 194 | 
             
                # a file directly from nginx. See the nginx wiki:
         | 
| 166 195 | 
             
                # http://wiki.codemongers.com/NginxXSendfile
         |