blix-rest 0.1.30 → 0.7.2
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.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/README.md +154 -28
- data/lib/blix/rest/cache.rb +79 -0
- data/lib/blix/rest/controller.rb +162 -29
- data/lib/blix/rest/cucumber/world.rb +13 -12
- data/lib/blix/rest/format_parser.rb +18 -11
- data/lib/blix/rest/redis_cache.rb +127 -0
- data/lib/blix/rest/request_mapper.rb +22 -6
- data/lib/blix/rest/response.rb +2 -2
- data/lib/blix/rest/server.rb +60 -20
- data/lib/blix/rest/version.rb +1 -1
- data/lib/blix/rest.rb +30 -9
- data/lib/blix/utils/redis_store.rb +2 -2
- metadata +21 -5
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: cf93b3d5836172b6cf1b4ac5df21dfe14e1e998e31e774f9b4fdb4bc5bdec323
         | 
| 4 | 
            +
              data.tar.gz: 622db8bf8d0052fd25f3727656ac6b641bc63a2667e97eb3f93f7dc4884e03ee
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 32516afd36a4a6c36c81650e50f84970cf2436b7cf682a51cae83d1300f736d4dbfe49d04d590ac8ffde841d2dac7f236d0979b3b5c1d5bec230112584eb6e0d
         | 
| 7 | 
            +
              data.tar.gz: 7f106344ec03beb691d5870c5809f379285d578ba17cb9b8edd1ea86c5c64dfa31b0a9747a75adab7730ba514cecca3a508cb36f5560343f147cd64d866ca3d4
         | 
    
        data/LICENSE
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            The MIT License (MIT)
         | 
| 2 2 |  | 
| 3 | 
            -
            Copyright (c) 2017 Clive Andrews
         | 
| 3 | 
            +
            Copyright (c) 2017-2020 Clive Andrews
         | 
| 4 4 |  | 
| 5 5 |  | 
| 6 6 | 
             
            Permission is hereby granted, free of charge, to any person
         | 
| @@ -22,4 +22,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
| 22 22 | 
             
            HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
         | 
| 23 23 | 
             
            WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
         | 
| 24 24 | 
             
            FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
         | 
| 25 | 
            -
            OTHER DEALINGS IN THE SOFTWARE.
         | 
| 25 | 
            +
            OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.md
    CHANGED
    
    | @@ -71,7 +71,7 @@ if there is a more specific path then it will be used first : | |
| 71 71 | 
             
            ### Path options
         | 
| 72 72 |  | 
| 73 73 | 
             
                :accept     : the format or formats to accept eg: :html or [:png, :jpeg]
         | 
| 74 | 
            -
                :default    : default format if not derived through  | 
| 74 | 
            +
                :default    : default format if not derived through other means.
         | 
| 75 75 | 
             
                :force      : force response into the given format
         | 
| 76 76 | 
             
                :query      : derive format from request query (default: false)
         | 
| 77 77 | 
             
                :extension  : derive format from path extension  (default: true)
         | 
| @@ -189,10 +189,19 @@ use the following to accept requests in a special format .. | |
| 189 189 |  | 
| 190 190 | 
             
                get '/custom', :accept=>:xyz, :force=>:raw do
         | 
| 191 191 | 
             
                   add_headers 'Content-Type'=>'text/xyz'
         | 
| 192 | 
            -
             | 
| 193 192 | 
             
                   "xyz"
         | 
| 194 193 | 
             
                end
         | 
| 195 194 |  | 
| 195 | 
            +
             | 
| 196 | 
            +
            Alternatively it is possible to raise a RawResponse:
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                add_headers 'Content-Type'=>'text/xyz'
         | 
| 199 | 
            +
                raise RawResponse, 'xyz'
         | 
| 200 | 
            +
             | 
| 201 | 
            +
            or with status and headers:
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                raise RawResponse.new('xyz', 123, 'Content-Type'=>'text/xyz')
         | 
| 204 | 
            +
             | 
| 196 205 | 
             
            ## FORMATS
         | 
| 197 206 |  | 
| 198 207 | 
             
            the format of a request is derived from
         | 
| @@ -218,32 +227,39 @@ base class for controllers. within your block handling a particular route you | |
| 218 227 | 
             
            have access to a number of methods
         | 
| 219 228 |  | 
| 220 229 |  | 
| 221 | 
            -
                env | 
| 222 | 
            -
                method | 
| 223 | 
            -
                req | 
| 224 | 
            -
                body | 
| 225 | 
            -
                query_params | 
| 226 | 
            -
                path_params | 
| 227 | 
            -
                post_params | 
| 228 | 
            -
                params | 
| 229 | 
            -
                user | 
| 230 | 
            -
                format | 
| 231 | 
            -
                before | 
| 232 | 
            -
                after | 
| 233 | 
            -
                proxy | 
| 234 | 
            -
                session | 
| 235 | 
            -
                redirect | 
| 236 | 
            -
                request_ip | 
| 237 | 
            -
                render_erb | 
| 238 | 
            -
                 | 
| 239 | 
            -
                 | 
| 240 | 
            -
                 | 
| 241 | 
            -
                 | 
| 242 | 
            -
                 | 
| 243 | 
            -
                 | 
| 244 | 
            -
                 | 
| 245 | 
            -
                 | 
| 246 | 
            -
             | 
| 230 | 
            +
                env                : the request environment hash
         | 
| 231 | 
            +
                method             : the request method lowercase( 'get'/'post' ..)
         | 
| 232 | 
            +
                req                : the rack request
         | 
| 233 | 
            +
                body               : the request body as a string
         | 
| 234 | 
            +
                query_params       : a hash of parameters as passed in the url as parameters
         | 
| 235 | 
            +
                path_params        : a hash of parameters constructed from variable parts of the path
         | 
| 236 | 
            +
                post_params        : a hash of parameters passed in the body of the request
         | 
| 237 | 
            +
                params             : all the params combined
         | 
| 238 | 
            +
                user               : the user making this request ( or nil if
         | 
| 239 | 
            +
                format             : the format the response should be in :json or :html
         | 
| 240 | 
            +
                before             : before hook ( opts ) - remember to add 'super' as first line !!!
         | 
| 241 | 
            +
                after              : after hook (opts,response)- remember to add 'super' as first line !!!
         | 
| 242 | 
            +
                proxy              : forward the call to another service (service,path, opts={}) , :include_query=>true/false
         | 
| 243 | 
            +
                session            : req.session
         | 
| 244 | 
            +
                redirect           : (path, status=302) redirect to another url.
         | 
| 245 | 
            +
                request_ip         : the ip of the request
         | 
| 246 | 
            +
                render_erb         : (template_name [,:layout=>name])
         | 
| 247 | 
            +
                server_cache       : get the server cache object
         | 
| 248 | 
            +
                server_cache_get   : retrieve/store value in cache
         | 
| 249 | 
            +
                path_for           : (path) give the correct path for an internal path
         | 
| 250 | 
            +
                url_for            : (path) give the full url for an internal path
         | 
| 251 | 
            +
                h                  : escape html string to avoid XSS
         | 
| 252 | 
            +
                escape_javascript  : escape  a javascript string
         | 
| 253 | 
            +
                server_options     : options as passed to server at create time
         | 
| 254 | 
            +
                logger             : system logger
         | 
| 255 | 
            +
                mode_test?         : test mode ?
         | 
| 256 | 
            +
                mode_production?   : production mode ?
         | 
| 257 | 
            +
                mode_development?  : development mode?
         | 
| 258 | 
            +
                send_data          : send raw data (data, options )
         | 
| 259 | 
            +
                                        [:type=>mimetype]
         | 
| 260 | 
            +
                                        [:filename=>name]
         | 
| 261 | 
            +
                                        [:disposition=>inline|attachment]
         | 
| 262 | 
            +
                                        [:status=>234]
         | 
| 247 263 |  | 
| 248 264 | 
             
                get_session_id(session_name, opts={}) :
         | 
| 249 265 | 
             
                refresh_session_id(session_name, opts={}) :
         | 
| @@ -252,6 +268,45 @@ to accept requests other than json then set `:accept=>[:json,:html]` as options | |
| 252 268 |  | 
| 253 269 | 
             
            eg  `post '/myform' :accept=>[:html]      # this will only accept html requests.`
         | 
| 254 270 |  | 
| 271 | 
            +
            ### Hooks
         | 
| 272 | 
            +
             | 
| 273 | 
            +
            a before or after hook can be defined on a controller. Only define the hook once
         | 
| 274 | 
            +
            for a given controller per source file. A hook included from another source file
         | 
| 275 | 
            +
            is ok though.
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                class MyController < Blix::Rest::Controller
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                  before do
         | 
| 280 | 
            +
                    ...
         | 
| 281 | 
            +
                  end
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                  after do
         | 
| 284 | 
            +
                    ...
         | 
| 285 | 
            +
                  end
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
             | 
| 290 | 
            +
            #### manipulate the route path or options
         | 
| 291 | 
            +
             | 
| 292 | 
            +
            the `before_route` hook can be used to modify the path or options of a route.
         | 
| 293 | 
            +
             | 
| 294 | 
            +
            *NOTE* ! when manipulating the path you have to modify the string object in place.
         | 
| 295 | 
            +
             | 
| 296 | 
            +
            the verb can not be modified
         | 
| 297 | 
            +
             | 
| 298 | 
            +
            example:
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                class MyController < Blix::Rest::Controller
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                  before_route do |verb, path, opts|
         | 
| 303 | 
            +
                    opts[:level] = :visitor unless opts[:level]
         | 
| 304 | 
            +
                    path.prepend('/') unless path[0] == '/'
         | 
| 305 | 
            +
                    path.prepend('/app') unless path[0, 4] == '/app'
         | 
| 306 | 
            +
                  end
         | 
| 307 | 
            +
                  ...
         | 
| 308 | 
            +
                end
         | 
| 309 | 
            +
             | 
| 255 310 | 
             
            ### Sessions
         | 
| 256 311 |  | 
| 257 312 | 
             
            the following methods are available in the controller for managing sessions.
         | 
| @@ -272,7 +327,78 @@ options can include: | |
| 272 327 | 
             
                :samesite =>:lax     # use lax x-site policy
         | 
| 273 328 |  | 
| 274 329 |  | 
| 330 | 
            +
            ## Cache
         | 
| 331 | 
            +
             | 
| 332 | 
            +
             | 
| 333 | 
            +
            the server has a cache which can also be used for storing your own data.
         | 
| 334 | 
            +
             | 
| 335 | 
            +
            within a controller access the controller with `server_cache` which returns the
         | 
| 336 | 
            +
            cache object.
         | 
| 337 | 
            +
             | 
| 338 | 
            +
            cache object methods:
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                get(key)        # return value from the cache or nil
         | 
| 341 | 
            +
                set(key,value)  # set a value in the cache
         | 
| 342 | 
            +
                key?(key)       # is a key present in the cache
         | 
| 343 | 
            +
                delete(key)     # delete a key from the cache
         | 
| 344 | 
            +
                clear           # delete all keys from the cache.
         | 
| 345 | 
            +
             | 
| 346 | 
            +
            there is also a `server_cache_get` method.
         | 
| 347 | 
            +
             | 
| 348 | 
            +
                server_cache_get(key){ action }
         | 
| 349 | 
            +
             | 
| 350 | 
            +
            get the value from the cache. If the key is missing in the cache then perform
         | 
| 351 | 
            +
            the action in the provided block and store the result in the cache.
         | 
| 352 | 
            +
             | 
| 353 | 
            +
            the default cache is just a ruby hash in memory. Pass a custom cache to
         | 
| 354 | 
            +
            when creating a server with the `:cache` parameter.
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                class MyCache < Blix::Rest::Cache
         | 
| 357 | 
            +
                   def get(key)
         | 
| 358 | 
            +
                      ..
         | 
| 359 | 
            +
                   end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                   def set(key,value)
         | 
| 362 | 
            +
                     ..
         | 
| 363 | 
            +
                   end
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                   def key?(key)
         | 
| 366 | 
            +
                    ..
         | 
| 367 | 
            +
                   end
         | 
| 368 | 
            +
             | 
| 369 | 
            +
                   def delete(key)
         | 
| 370 | 
            +
                     ..
         | 
| 371 | 
            +
                   end
         | 
| 372 | 
            +
             | 
| 373 | 
            +
                   def clear
         | 
| 374 | 
            +
                     ..
         | 
| 375 | 
            +
                   end
         | 
| 376 | 
            +
                 end
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                 cache = MyCache.new
         | 
| 379 | 
            +
             | 
| 380 | 
            +
                 app = Blix::Rest::Server.new(:cache=>cache)
         | 
| 381 | 
            +
             | 
| 382 | 
            +
            there is a redis cache already defined:
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                require 'blix/rest/redis_cache'
         | 
| 385 | 
            +
             | 
| 386 | 
            +
                cache = Blix::Rest::RedisCache.new(:expire_secs=>60*60*24) # expire after 1 day
         | 
| 387 | 
            +
                run Blix::Rest::Server.new(:cache=>cache)
         | 
| 388 | 
            +
             | 
| 389 | 
            +
             | 
| 390 | 
            +
            ### automatically cache server responses
         | 
| 391 | 
            +
             | 
| 392 | 
            +
            add  `:cache=>true` to your route options in order to cache this route.
         | 
| 393 | 
            +
             | 
| 394 | 
            +
            add `:cache_reset=>true` to your route options if the cache should be cleared when
         | 
| 395 | 
            +
            calling this route.
         | 
| 396 | 
            +
             | 
| 397 | 
            +
            the cache is not used in development/testmode , only in production mode.
         | 
| 398 | 
            +
             | 
| 399 | 
            +
            ### IMPORTANT - DO NOT CACHE SESSION KEYS AND OTHER SPECIFIC DATA IN CACHE
         | 
| 275 400 |  | 
| 401 | 
            +
            only cache pages with **HEADERS** and **CONTENT** that is not user specific.
         | 
| 276 402 |  | 
| 277 403 | 
             
            ## Views
         | 
| 278 404 |  | 
| @@ -0,0 +1,79 @@ | |
| 1 | 
            +
            module Blix::Rest
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # cache server responses
         | 
| 4 | 
            +
              class Cache
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                attr_reader :params
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(params={})
         | 
| 9 | 
            +
                  @params = params
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def [](key)
         | 
| 13 | 
            +
                  get(key)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def []=(key,data)
         | 
| 17 | 
            +
                  set(key, data)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                #--------------- redefine these methods ..
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # clear all data from the cache
         | 
| 23 | 
            +
                def clear
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # retrieve data from the cache
         | 
| 28 | 
            +
                def get(key)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
               # set data in the cache
         | 
| 33 | 
            +
                def set(key, data)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                #  is key present in the cache
         | 
| 38 | 
            +
                def key?(key)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def delete(key)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                #---------------------------------------------------------------------------
         | 
| 47 | 
            +
             | 
| 48 | 
            +
             | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              # implement cache as a simple ruby hash
         | 
| 52 | 
            +
              class MemoryCache < Cache
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def cache
         | 
| 55 | 
            +
                  @cache ||= {}
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def get(key)
         | 
| 59 | 
            +
                  cache[key]
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def set(key, data)
         | 
| 63 | 
            +
                  cache[key] = data
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def clear
         | 
| 67 | 
            +
                  cache.clear
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                def key?(key)
         | 
| 71 | 
            +
                  cache.key?(key)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                def delete(key)
         | 
| 75 | 
            +
                  cache.delete(key)
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
            end
         | 
    
        data/lib/blix/rest/controller.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ | |
| 3 3 | 
             
            require 'base64'
         | 
| 4 4 | 
             
            require 'erb'
         | 
| 5 5 | 
             
            require 'securerandom'
         | 
| 6 | 
            +
            require 'digest'
         | 
| 6 7 |  | 
| 7 8 | 
             
            module Blix::Rest
         | 
| 8 9 | 
             
              # base class for controllers. within your block handling a particular route you
         | 
| @@ -21,8 +22,19 @@ module Blix::Rest | |
| 21 22 | 
             
              #  to accept requests other thatn json then set :accept=>[:json,:html] as options in the route
         | 
| 22 23 | 
             
              #    eg  post '/myform' :accept=>[:html]              # this will only accept html requests.
         | 
| 23 24 |  | 
| 25 | 
            +
              Context = Struct.new(
         | 
| 26 | 
            +
                :path_params,
         | 
| 27 | 
            +
                :params,
         | 
| 28 | 
            +
                :req,
         | 
| 29 | 
            +
                :format,
         | 
| 30 | 
            +
                :response,
         | 
| 31 | 
            +
                :method,
         | 
| 32 | 
            +
                :server
         | 
| 33 | 
            +
              )
         | 
| 34 | 
            +
             | 
| 24 35 | 
             
              class Controller
         | 
| 25 36 |  | 
| 37 | 
            +
             | 
| 26 38 | 
             
                #--------------------------------------------------------------------------------------------------------
         | 
| 27 39 | 
             
                # convenience methods
         | 
| 28 40 | 
             
                #--------------------------------------------------------------------------------------------------------
         | 
| @@ -35,6 +47,14 @@ module Blix::Rest | |
| 35 47 | 
             
                  @_server_options
         | 
| 36 48 | 
             
                end
         | 
| 37 49 |  | 
| 50 | 
            +
                def server_cache
         | 
| 51 | 
            +
                  @_server_cache
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def server_cache_get(key)
         | 
| 55 | 
            +
                  server_cache[key] ||= yield if block_given?
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 38 58 | 
             
                def logger
         | 
| 39 59 | 
             
                  Blix::Rest.logger
         | 
| 40 60 | 
             
                end
         | 
| @@ -60,8 +80,18 @@ module Blix::Rest | |
| 60 80 | 
             
                  #      env['rack.input'].rewindreq.POST #env["body"]
         | 
| 61 81 | 
             
                end
         | 
| 62 82 |  | 
| 83 | 
            +
                # ovverride the path method to return the internal path.
         | 
| 63 84 | 
             
                def path
         | 
| 64 | 
            -
                  req.path
         | 
| 85 | 
            +
                  p = req.path
         | 
| 86 | 
            +
                  p = '/' + p if p[0, 1] != '/' # ensure a leading slash on path
         | 
| 87 | 
            +
                  idx = RequestMapper.path_root_length
         | 
| 88 | 
            +
                  if idx > 0
         | 
| 89 | 
            +
                    p = p[idx..-1] || '/'
         | 
| 90 | 
            +
                    p = '/' + p if p[0, 1] != '/' # ensure a leading slash on path
         | 
| 91 | 
            +
                    p
         | 
| 92 | 
            +
                  else
         | 
| 93 | 
            +
                    p
         | 
| 94 | 
            +
                  end
         | 
| 65 95 | 
             
                end
         | 
| 66 96 |  | 
| 67 97 | 
             
                def form_hash
         | 
| @@ -113,7 +143,7 @@ module Blix::Rest | |
| 113 143 | 
             
                end
         | 
| 114 144 |  | 
| 115 145 | 
             
                def path_for(path)
         | 
| 116 | 
            -
                  File.join(RequestMapper.path_root, path)
         | 
| 146 | 
            +
                  File.join(RequestMapper.path_root, path || '')
         | 
| 117 147 | 
             
                end
         | 
| 118 148 |  | 
| 119 149 | 
             
                def url_for(path)
         | 
| @@ -128,6 +158,18 @@ module Blix::Rest | |
| 128 158 | 
             
                  @_verb
         | 
| 129 159 | 
             
                end
         | 
| 130 160 |  | 
| 161 | 
            +
                def method
         | 
| 162 | 
            +
                  @_method
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                def route_parameters
         | 
| 166 | 
            +
                  @_parameters
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                def response
         | 
| 170 | 
            +
                  @_response
         | 
| 171 | 
            +
                end
         | 
| 172 | 
            +
             | 
| 131 173 | 
             
                def method
         | 
| 132 174 | 
             
                  env['REQUEST_METHOD'].downcase
         | 
| 133 175 | 
             
                end
         | 
| @@ -147,7 +189,7 @@ module Blix::Rest | |
| 147 189 | 
             
                end
         | 
| 148 190 |  | 
| 149 191 | 
             
                def redirect(path, status = 302)
         | 
| 150 | 
            -
                  raise ServiceError.new(nil, status, ' | 
| 192 | 
            +
                  raise ServiceError.new(nil, status, 'location' => RequestMapper.ensure_full_path(path))
         | 
| 151 193 | 
             
                end
         | 
| 152 194 |  | 
| 153 195 | 
             
                alias redirect_to redirect
         | 
| @@ -191,11 +233,11 @@ module Blix::Rest | |
| 191 233 | 
             
                end
         | 
| 192 234 |  | 
| 193 235 | 
             
                def set_status(value)
         | 
| 194 | 
            -
                  @_response.status = value
         | 
| 236 | 
            +
                  @_response.status = value.to_i
         | 
| 195 237 | 
             
                end
         | 
| 196 238 |  | 
| 197 239 | 
             
                def add_headers(headers)
         | 
| 198 | 
            -
                  @_response.headers.merge!(headers)
         | 
| 240 | 
            +
                  @_response.headers.merge!(headers.map{|k,v| [k.to_s.downcase,v]}.to_h)
         | 
| 199 241 | 
             
                end
         | 
| 200 242 |  | 
| 201 243 | 
             
                # the following is copied from Rack::Utils
         | 
| @@ -231,6 +273,19 @@ module Blix::Rest | |
| 231 273 | 
             
                  raise ServiceError.new(message, status, headers)
         | 
| 232 274 | 
             
                end
         | 
| 233 275 |  | 
| 276 | 
            +
                # send data to browser as attachment
         | 
| 277 | 
            +
                def send_data(data, opts = {})
         | 
| 278 | 
            +
                  add_headers 'content-type'=> opts[:type] || 'application/octet-stream'
         | 
| 279 | 
            +
                  if opts[:filename]
         | 
| 280 | 
            +
                    add_headers 'content-disposition'=>'attachment;filename='+ opts[:filename]
         | 
| 281 | 
            +
                  elsif opts[:disposition] == 'attachment'
         | 
| 282 | 
            +
                    add_headers 'content-disposition'=>'attachment'
         | 
| 283 | 
            +
                  elsif opts[:disposition] == 'inline'
         | 
| 284 | 
            +
                    add_headers 'content-disposition'=>'inline'
         | 
| 285 | 
            +
                  end
         | 
| 286 | 
            +
                  raise RawResponse.new(data, opts[:status] || 200)
         | 
| 287 | 
            +
                end
         | 
| 288 | 
            +
             | 
| 234 289 | 
             
                def auth_error(*params)
         | 
| 235 290 | 
             
                  if params[0].kind_of?(String)
         | 
| 236 291 | 
             
                      message = params[0]
         | 
| @@ -279,7 +334,7 @@ module Blix::Rest | |
| 279 334 | 
             
                  # else
         | 
| 280 335 | 
             
                  #   cookie_header = cookie_text
         | 
| 281 336 | 
             
                  # end
         | 
| 282 | 
            -
                  @_response.headers[' | 
| 337 | 
            +
                  @_response.headers['set-cookie'] = @_cookies.values.join("\n")
         | 
| 283 338 | 
             
                  value
         | 
| 284 339 | 
             
                end
         | 
| 285 340 |  | 
| @@ -315,6 +370,16 @@ module Blix::Rest | |
| 315 370 | 
             
                  store_cookie(session_name, session_id, opts)
         | 
| 316 371 | 
             
                end
         | 
| 317 372 |  | 
| 373 | 
            +
                # perform the before hooks.
         | 
| 374 | 
            +
                def __before(*a)
         | 
| 375 | 
            +
                  self.class._do_before(self, *a)
         | 
| 376 | 
            +
                end
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                # perform the after hooks
         | 
| 379 | 
            +
                def __after(*a)
         | 
| 380 | 
            +
                  self.class._do_after(self, *a)
         | 
| 381 | 
            +
                end
         | 
| 382 | 
            +
             | 
| 318 383 | 
             
                #----------------------------------------------------------------------------------------------------------
         | 
| 319 384 | 
             
                # template methods that can be overwritten
         | 
| 320 385 |  | 
| @@ -329,15 +394,27 @@ module Blix::Rest | |
| 329 394 |  | 
| 330 395 | 
             
                #----------------------------------------------------------------------------------------------------------
         | 
| 331 396 |  | 
| 332 | 
            -
                def  | 
| 333 | 
            -
                  @ | 
| 334 | 
            -
                  @ | 
| 397 | 
            +
                def _setup(context, _verb, _path, _parameters)
         | 
| 398 | 
            +
                  @_context        = context
         | 
| 399 | 
            +
                  @_req            = context.req
         | 
| 400 | 
            +
                  @_env            = req.env
         | 
| 335 401 | 
             
                  @_query_params = StringHash.new(req.GET)
         | 
| 336 | 
            -
                  @_path_params  = StringHash.new(path_params)
         | 
| 337 | 
            -
                  @_format | 
| 338 | 
            -
                  @_verb | 
| 339 | 
            -
                  @_response | 
| 340 | 
            -
                  @_server_options =  | 
| 402 | 
            +
                  @_path_params  = StringHash.new(context.path_params)
         | 
| 403 | 
            +
                  @_format         = context.format
         | 
| 404 | 
            +
                  @_verb           = _verb
         | 
| 405 | 
            +
                  @_response       = context.response
         | 
| 406 | 
            +
                  @_server_options = context.server._options
         | 
| 407 | 
            +
                  @_parameters     = _parameters
         | 
| 408 | 
            +
                  @_server_cache   = context.server._cache
         | 
| 409 | 
            +
                  @_method         = context.method
         | 
| 410 | 
            +
                end
         | 
| 411 | 
            +
             | 
| 412 | 
            +
                def to_s
         | 
| 413 | 
            +
                  "<#{self.class.to_s}:#{object_id}>"
         | 
| 414 | 
            +
                end
         | 
| 415 | 
            +
             | 
| 416 | 
            +
                def inspect
         | 
| 417 | 
            +
                  to_s
         | 
| 341 418 | 
             
                end
         | 
| 342 419 |  | 
| 343 420 | 
             
                # do not cache templates in development mode
         | 
| @@ -376,9 +453,9 @@ module Blix::Rest | |
| 376 453 | 
             
                    path        = opts[:path] || __erb_path || Controller.erb_root
         | 
| 377 454 |  | 
| 378 455 | 
             
                    layout = layout_name && if no_template_cache
         | 
| 379 | 
            -
                                              ERB.new(File.read(File.join(path, layout_name + '.html.erb')) | 
| 456 | 
            +
                                              ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
         | 
| 380 457 | 
             
                                            else
         | 
| 381 | 
            -
                                              erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')) | 
| 458 | 
            +
                                              erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
         | 
| 382 459 | 
             
                    end
         | 
| 383 460 |  | 
| 384 461 | 
             
                    begin
         | 
| @@ -388,8 +465,8 @@ module Blix::Rest | |
| 388 465 | 
             
                        text
         | 
| 389 466 | 
             
                      end
         | 
| 390 467 | 
             
                    rescue Exception
         | 
| 391 | 
            -
                       | 
| 392 | 
            -
                       | 
| 468 | 
            +
                      ::Blix::Rest.logger <<  $!
         | 
| 469 | 
            +
                      ::Blix::Rest.logger <<  $@
         | 
| 393 470 | 
             
                      '*** TEMPLATE ERROR ***'
         | 
| 394 471 | 
             
                    end
         | 
| 395 472 | 
             
                  end
         | 
| @@ -401,15 +478,15 @@ module Blix::Rest | |
| 401 478 | 
             
                    path        = opts[:erb_dir] || __erb_path || Controller.erb_root
         | 
| 402 479 |  | 
| 403 480 | 
             
                    layout = layout_name && if no_template_cache
         | 
| 404 | 
            -
                                              ERB.new(File.read(File.join(path, layout_name + '.html.erb')) | 
| 481 | 
            +
                                              ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
         | 
| 405 482 | 
             
                                            else
         | 
| 406 | 
            -
                                              erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')) | 
| 483 | 
            +
                                              erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
         | 
| 407 484 | 
             
                    end
         | 
| 408 485 |  | 
| 409 486 | 
             
                    erb = if no_template_cache
         | 
| 410 | 
            -
                            ERB.new(File.read(File.join(path, name + '.html.erb')) | 
| 487 | 
            +
                            ERB.new(File.read(File.join(path, name + '.html.erb')),:trim_mode=>'-')
         | 
| 411 488 | 
             
                          else
         | 
| 412 | 
            -
                            erb_templates[name] ||= ERB.new(File.read(File.join(path, name + '.html.erb')) | 
| 489 | 
            +
                            erb_templates[name] ||= ERB.new(File.read(File.join(path, name + '.html.erb')),:trim_mode=>'-')
         | 
| 413 490 | 
             
                    end
         | 
| 414 491 |  | 
| 415 492 | 
             
                    begin
         | 
| @@ -421,8 +498,8 @@ module Blix::Rest | |
| 421 498 | 
             
                        erb.result(bind)
         | 
| 422 499 | 
             
                      end
         | 
| 423 500 | 
             
                    rescue Exception
         | 
| 424 | 
            -
                       | 
| 425 | 
            -
                       | 
| 501 | 
            +
                      ::Blix::Rest.logger <<  $!
         | 
| 502 | 
            +
                      ::Blix::Rest.logger <<  $@
         | 
| 426 503 | 
             
                      '*** TEMPLATE ERROR ***'
         | 
| 427 504 | 
             
                    end
         | 
| 428 505 | 
             
                  end
         | 
| @@ -447,19 +524,32 @@ module Blix::Rest | |
| 447 524 | 
             
                    raise ServiceError, 'invalid format for this request' unless accept.index format
         | 
| 448 525 | 
             
                  end
         | 
| 449 526 |  | 
| 527 | 
            +
                  def _do_route_hook(verb, path, opts)
         | 
| 528 | 
            +
                    if @_route_hook
         | 
| 529 | 
            +
                      superclass._do_route_hook(verb, path, opts) if superclass.respond_to? :_do_route_hook
         | 
| 530 | 
            +
                      @_route_hook.call(verb, path, opts)
         | 
| 531 | 
            +
                    end
         | 
| 532 | 
            +
                  end
         | 
| 533 | 
            +
             | 
| 450 534 | 
             
                  def route(verb, path, opts = {}, &blk)
         | 
| 451 | 
            -
                     | 
| 535 | 
            +
                    path = String.new(path)  # in case frozen.
         | 
| 536 | 
            +
                    _do_route_hook(verb.dup, path, opts)
         | 
| 537 | 
            +
                    proc = lambda do |context|
         | 
| 452 538 | 
             
                      unless opts[:force] && (opts[:accept] == :*)
         | 
| 453 | 
            -
                        check_format(opts[:accept],  | 
| 539 | 
            +
                        check_format(opts[:accept], context.format)
         | 
| 454 540 | 
             
                      end
         | 
| 455 | 
            -
                      app = new | 
| 541 | 
            +
                      app = new
         | 
| 542 | 
            +
                      app._setup(context, verb, path, opts)
         | 
| 456 543 | 
             
                      begin
         | 
| 457 544 | 
             
                        app.before(opts)
         | 
| 458 | 
            -
                         | 
| 545 | 
            +
                        app.__before
         | 
| 546 | 
            +
                        context.response = app.instance_eval( &blk )
         | 
| 459 547 | 
             
                      rescue
         | 
| 460 548 | 
             
                        raise
         | 
| 461 549 | 
             
                      ensure
         | 
| 462 | 
            -
                        app. | 
| 550 | 
            +
                        app.__after
         | 
| 551 | 
            +
                        app.after(opts, context.response)
         | 
| 552 | 
            +
                        context.response
         | 
| 463 553 | 
             
                      end
         | 
| 464 554 | 
             
                    end
         | 
| 465 555 |  | 
| @@ -498,6 +588,49 @@ module Blix::Rest | |
| 498 588 | 
             
                    route 'OPTIONS', *a, &b
         | 
| 499 589 | 
             
                  end
         | 
| 500 590 |  | 
| 591 | 
            +
                  def before_route(&b)
         | 
| 592 | 
            +
                    @_route_hook = b if b
         | 
| 593 | 
            +
                  end
         | 
| 594 | 
            +
             | 
| 595 | 
            +
             | 
| 596 | 
            +
                  def _before_hooks
         | 
| 597 | 
            +
                    @_before_hooks ||= {}
         | 
| 598 | 
            +
                  end
         | 
| 599 | 
            +
             | 
| 600 | 
            +
                  def _after_hooks
         | 
| 601 | 
            +
                    @_after_hooks ||= {}
         | 
| 602 | 
            +
                  end
         | 
| 603 | 
            +
             | 
| 604 | 
            +
                  def _do_before(ctx, *a)
         | 
| 605 | 
            +
                    superclass._do_before(ctx, *a) if superclass.respond_to? :_do_before
         | 
| 606 | 
            +
                    _before_hooks.each_value{ |h| ctx.instance_eval(&h) }
         | 
| 607 | 
            +
                  end
         | 
| 608 | 
            +
             | 
| 609 | 
            +
                  def _do_after(ctx, *a)
         | 
| 610 | 
            +
                    _after_hooks.each_value{ |h| ctx.instance_eval(&h) }
         | 
| 611 | 
            +
                    superclass._do_after(ctx, *a) if superclass.respond_to? :_do_after
         | 
| 612 | 
            +
                  end
         | 
| 613 | 
            +
             | 
| 614 | 
            +
                  # define a before hook for a controller. only one hook can be defined per
         | 
| 615 | 
            +
                  # controller in a single source file.
         | 
| 616 | 
            +
                  def before(&block)
         | 
| 617 | 
            +
                    if block
         | 
| 618 | 
            +
                      file = block.source_location[0]
         | 
| 619 | 
            +
                      warn("warning: before hook already defined in #{file}") if _before_hooks[file]
         | 
| 620 | 
            +
                      _before_hooks[file] = block
         | 
| 621 | 
            +
                    end
         | 
| 622 | 
            +
                  end
         | 
| 623 | 
            +
             | 
| 624 | 
            +
                  # define an after hook for a controller. only one hook can be defined per
         | 
| 625 | 
            +
                  # controller in a single source file.
         | 
| 626 | 
            +
                  def after(&block)
         | 
| 627 | 
            +
                    if block
         | 
| 628 | 
            +
                      file = block.source_location[0]
         | 
| 629 | 
            +
                      warn("warning: after hook already defined in #{file}") if _after_hooks[file]
         | 
| 630 | 
            +
                      _after_hooks[file] = block
         | 
| 631 | 
            +
                    end
         | 
| 632 | 
            +
                  end
         | 
| 633 | 
            +
             | 
| 501 634 | 
             
                end
         | 
| 502 635 |  | 
| 503 636 | 
             
              end
         |