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
| @@ -4,7 +4,7 @@ | |
| 4 4 | 
             
            class RestWorld
         | 
| 5 5 | 
             
              # the entry point to the rack application to be tested
         | 
| 6 6 | 
             
              def self.app
         | 
| 7 | 
            -
                @_app ||= Rack::Builder.parse_file('config.ru') | 
| 7 | 
            +
                @_app ||= Rack::Builder.parse_file('config.ru')
         | 
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 10 | 
             
              # a dummy request to sent to the server
         | 
| @@ -16,15 +16,16 @@ class RestWorld | |
| 16 16 | 
             
              class Response
         | 
| 17 17 | 
             
                def initialize(resp)
         | 
| 18 18 | 
             
                  @resp = resp
         | 
| 19 | 
            -
                   | 
| 19 | 
            +
                  content_type = @resp.headers['Content-Type'] || @resp.headers['content-type']
         | 
| 20 | 
            +
                  if content_type == 'application/json'
         | 
| 20 21 | 
             
                    begin
         | 
| 21 | 
            -
                      @h = MultiJson.load( | 
| 22 | 
            +
                      @h = MultiJson.load(body) || {}
         | 
| 22 23 | 
             
                    rescue Exception => e
         | 
| 23 | 
            -
                       | 
| 24 | 
            +
                      log 'INVALID RESPONSE BODY=>' + body
         | 
| 24 25 | 
             
                      raise
         | 
| 25 26 | 
             
                    end
         | 
| 26 27 | 
             
                  else
         | 
| 27 | 
            -
                    @h = { 'html' =>  | 
| 28 | 
            +
                    @h = { 'html' => body }
         | 
| 28 29 | 
             
                  end
         | 
| 29 30 |  | 
| 30 31 | 
             
                  # get_ids_from_hash
         | 
| @@ -35,7 +36,7 @@ class RestWorld | |
| 35 36 | 
             
                end
         | 
| 36 37 |  | 
| 37 38 | 
             
                def body
         | 
| 38 | 
            -
                  @resp.body
         | 
| 39 | 
            +
                  [@resp.body].flatten.join('')
         | 
| 39 40 | 
             
                end
         | 
| 40 41 |  | 
| 41 42 | 
             
                def data
         | 
| @@ -51,7 +52,7 @@ class RestWorld | |
| 51 52 | 
             
                end
         | 
| 52 53 |  | 
| 53 54 | 
             
                def header
         | 
| 54 | 
            -
                  @resp. | 
| 55 | 
            +
                  @resp.headers || {}
         | 
| 55 56 | 
             
                end
         | 
| 56 57 |  | 
| 57 58 | 
             
                def content_type
         | 
| @@ -93,10 +94,10 @@ class RestWorld | |
| 93 94 | 
             
              end
         | 
| 94 95 |  | 
| 95 96 | 
             
              def explain
         | 
| 96 | 
            -
                 | 
| 97 | 
            -
                 | 
| 98 | 
            -
                 | 
| 99 | 
            -
                 | 
| 97 | 
            +
                log "request ==> #{@_verb} #{@_request}"
         | 
| 98 | 
            +
                log "cookies ==> #{cookies.join('; ')}" if cookies.length > 0
         | 
| 99 | 
            +
                log "body ==> #{@_body}" if @_body
         | 
| 100 | 
            +
                log "response ==> #{@_response.inspect}"
         | 
| 100 101 | 
             
              end
         | 
| 101 102 |  | 
| 102 103 | 
             
              def before_parse_path(path); end
         | 
| @@ -217,7 +218,7 @@ class RestWorld | |
| 217 218 | 
             
                @_response = Response.new(raw_response)
         | 
| 218 219 | 
             
                # add cookies to the cookie jar.
         | 
| 219 220 | 
             
                #unless @_current_user=="guest"
         | 
| 220 | 
            -
                if cookie = @_response.header["Set-Cookie"]
         | 
| 221 | 
            +
                if cookie = @_response.header["Set-Cookie"] || @_response.header["set-cookie"]
         | 
| 221 222 | 
             
                  parts = cookie.split(';')
         | 
| 222 223 | 
             
                  cookies << parts[0].strip
         | 
| 223 224 | 
             
                end
         | 
| @@ -72,16 +72,16 @@ module Blix::Rest | |
| 72 72 | 
             
                def format_response(value, response)
         | 
| 73 73 | 
             
                  if value.is_a?(RawJsonString)
         | 
| 74 74 | 
             
                    response.content = if _options[:nodata]
         | 
| 75 | 
            -
                                         value.to_s
         | 
| 75 | 
            +
                                         [value.to_s]
         | 
| 76 76 | 
             
                                       else
         | 
| 77 | 
            -
                                         "{\"data\":#{value}}"
         | 
| 77 | 
            +
                                         ["{\"data\":#{value}}"]
         | 
| 78 78 | 
             
                                       end
         | 
| 79 79 | 
             
                  else
         | 
| 80 80 | 
             
                    begin
         | 
| 81 81 | 
             
                      response.content = if _options[:nodata]
         | 
| 82 | 
            -
                                           MultiJson.dump(value)
         | 
| 82 | 
            +
                                           [MultiJson.dump(value)]
         | 
| 83 83 | 
             
                                         else
         | 
| 84 | 
            -
                                           MultiJson.dump('data' => value)
         | 
| 84 | 
            +
                                           [MultiJson.dump('data' => value)]
         | 
| 85 85 | 
             
                                       end
         | 
| 86 86 | 
             
                    rescue Exception => e
         | 
| 87 87 | 
             
                      ::Blix::Rest.logger << e.to_s
         | 
| @@ -107,7 +107,8 @@ module Blix::Rest | |
| 107 107 | 
             
                end
         | 
| 108 108 |  | 
| 109 109 | 
             
                def format_response(value, response)
         | 
| 110 | 
            -
                   | 
| 110 | 
            +
                  value = [value.to_s] unless value.respond_to?(:each) ||  value.respond_to?(:call)
         | 
| 111 | 
            +
                  response.content = value
         | 
| 111 112 | 
             
                end
         | 
| 112 113 |  | 
| 113 114 | 
             
              end
         | 
| @@ -121,8 +122,8 @@ module Blix::Rest | |
| 121 122 |  | 
| 122 123 | 
             
                def set_default_headers(headers)
         | 
| 123 124 | 
             
                  headers[CACHE_CONTROL] = CACHE_NO_STORE
         | 
| 124 | 
            -
                  headers[PRAGMA] | 
| 125 | 
            -
                  headers[CONTENT_TYPE] | 
| 125 | 
            +
                  headers[PRAGMA]        = NO_CACHE
         | 
| 126 | 
            +
                  headers[CONTENT_TYPE]  = CONTENT_TYPE_XML
         | 
| 126 127 | 
             
                end
         | 
| 127 128 |  | 
| 128 129 | 
             
                def format_error(message)
         | 
| @@ -130,7 +131,7 @@ module Blix::Rest | |
| 130 131 | 
             
                end
         | 
| 131 132 |  | 
| 132 133 | 
             
                def format_response(value, response)
         | 
| 133 | 
            -
                  response.content = value.to_s | 
| 134 | 
            +
                  response.content = [value.to_s]
         | 
| 134 135 | 
             
                end
         | 
| 135 136 |  | 
| 136 137 | 
             
              end
         | 
| @@ -144,8 +145,14 @@ module Blix::Rest | |
| 144 145 |  | 
| 145 146 | 
             
                def set_default_headers(headers)
         | 
| 146 147 | 
             
                  headers[CACHE_CONTROL] = CACHE_NO_STORE
         | 
| 147 | 
            -
                  headers[PRAGMA] | 
| 148 | 
            -
                  headers[CONTENT_TYPE] | 
| 148 | 
            +
                  headers[PRAGMA]        = NO_CACHE
         | 
| 149 | 
            +
                  headers[CONTENT_TYPE]  = CONTENT_TYPE_HTML
         | 
| 150 | 
            +
                  # headers['X-Frame-Options']        =  'SAMEORIGIN'
         | 
| 151 | 
            +
                  # headers['X-XSS-Protection']       =  '1; mode=block'
         | 
| 152 | 
            +
                  # headers['X-Content-Type-Options'] =  'nosniff'
         | 
| 153 | 
            +
                  # headers['X-Download-Options']     =  'noopen'
         | 
| 154 | 
            +
                  # headers['X-Permitted-Cross-Domain-Policies'] =  'none'
         | 
| 155 | 
            +
                  # headers['Referrer-Policy']        =  'strict-origin-when-cross-origin'
         | 
| 149 156 | 
             
                end
         | 
| 150 157 |  | 
| 151 158 | 
             
                def format_error(message)
         | 
| @@ -160,7 +167,7 @@ module Blix::Rest | |
| 160 167 | 
             
                end
         | 
| 161 168 |  | 
| 162 169 | 
             
                def format_response(value, response)
         | 
| 163 | 
            -
                  response.content = value.to_s
         | 
| 170 | 
            +
                  response.content = [value.to_s]
         | 
| 164 171 | 
             
                end
         | 
| 165 172 |  | 
| 166 173 | 
             
              end
         | 
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'redis'
         | 
| 4 | 
            +
            require_relative 'cache'
         | 
| 5 | 
            +
            require_relative 'string_hash'
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            #  options:
         | 
| 8 | 
            +
            #     :expire_secs           - how long store should save data.
         | 
| 9 | 
            +
            #     :reset_expire_on_get   - start the expire timer again on read.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            module Blix
         | 
| 12 | 
            +
              module Rest
         | 
| 13 | 
            +
                class RedisCache < Cache
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  STORE_PREFIX = 'blixcache'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  #---------------------------------------------------------------------------
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  # clear all data from the cache
         | 
| 20 | 
            +
                  def clear
         | 
| 21 | 
            +
                    reset
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  # retrieve data from the cache
         | 
| 25 | 
            +
                  def get(id)
         | 
| 26 | 
            +
                    key = _key(id)
         | 
| 27 | 
            +
                    str = redis.get(key)
         | 
| 28 | 
            +
                    data = str && begin
         | 
| 29 | 
            +
                                    _decode(str)
         | 
| 30 | 
            +
                                  rescue StandardError
         | 
| 31 | 
            +
                                     redis.del( key)
         | 
| 32 | 
            +
                                     nil
         | 
| 33 | 
            +
                                  end
         | 
| 34 | 
            +
                    redis.expire(key, _opts[:expire_secs]) if data && _opts[:reset_expire_on_get] && _opts.key?(:expire_secs)
         | 
| 35 | 
            +
                    data
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # set data in the cache
         | 
| 39 | 
            +
                  def set(id, data)
         | 
| 40 | 
            +
                    params = {}
         | 
| 41 | 
            +
                    params[:ex] = _opts[:expire_secs] if _opts.key?(:expire_secs)
         | 
| 42 | 
            +
                    redis.set(_key(id), _encode(data), **params)
         | 
| 43 | 
            +
                    data
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  #  is key present in the cache
         | 
| 47 | 
            +
                  def key?(id)
         | 
| 48 | 
            +
                    redis.get(_key(id)) != nil
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def delete(id)
         | 
| 52 | 
            +
                    redis.del(_key(id)) > 0
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  #---------------------------------------------------------------------------
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def _key(name)
         | 
| 58 | 
            +
                    _prefix + name
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def _opts
         | 
| 62 | 
            +
                    @_opts ||= begin
         | 
| 63 | 
            +
                      o = ::Blix::Rest::StringHash.new
         | 
| 64 | 
            +
                      o[:prefix] = STORE_PREFIX
         | 
| 65 | 
            +
                      o.merge!(params)
         | 
| 66 | 
            +
                      o
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  def _prefix
         | 
| 71 | 
            +
                    @_prefix ||= _opts[:prefix]
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # remove all sessions from the store
         | 
| 75 | 
            +
                  def reset(name = nil)
         | 
| 76 | 
            +
                    keys = _all_keys(name)
         | 
| 77 | 
            +
                    redis.del(*keys) unless keys.empty?
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def _all_keys(name = nil)
         | 
| 81 | 
            +
                    redis.keys("#{_prefix}#{name}*") || []
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  # the redis session store
         | 
| 85 | 
            +
                  def redis
         | 
| 86 | 
            +
                    @redis ||= begin
         | 
| 87 | 
            +
                      r = Redis.new
         | 
| 88 | 
            +
                      begin
         | 
| 89 | 
            +
                        r.ping
         | 
| 90 | 
            +
                      rescue Exception => e
         | 
| 91 | 
            +
                        Blix::Rest.logger.error "cannot reach redis server:#{e}"
         | 
| 92 | 
            +
                        raise
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
                      r
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  # the number of sessions in the store
         | 
| 99 | 
            +
                  def length
         | 
| 100 | 
            +
                    _all_keys.length
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  # delete expired sessions from the store. this should be handled
         | 
| 104 | 
            +
                  # automatically by redis if the ttl is set on save correctly
         | 
| 105 | 
            +
                  def cleanup(opts = nil); end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  def _encode(data)
         | 
| 108 | 
            +
                    Marshal.dump(data)
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  def _decode(msg)
         | 
| 112 | 
            +
                    Marshal.load(msg)
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  def get_expiry_time
         | 
| 116 | 
            +
                    if expire = _opts[:expire_secs] || _opts['expire_secs']
         | 
| 117 | 
            +
                      Time.now - expire
         | 
| 118 | 
            +
                    end
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  def get_expiry_secs
         | 
| 122 | 
            +
                    _opts[:expire_secs] || _opts['expire_secs']
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
            end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            +
            require 'thread'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Blix::Rest
         | 
| 4 5 | 
             
              class RequestMapperError < StandardError; end
         | 
| @@ -7,6 +8,8 @@ module Blix::Rest | |
| 7 8 | 
             
              # these routes and return an associated block and parameters.
         | 
| 8 9 | 
             
              class RequestMapper
         | 
| 9 10 |  | 
| 11 | 
            +
                @@mutex = Mutex.new
         | 
| 12 | 
            +
             | 
| 10 13 | 
             
                WILD_PLACEHOLDER = '/'
         | 
| 11 14 | 
             
                PATH_SEP         = '/'
         | 
| 12 15 | 
             
                STAR_PLACEHOLDER = '*'
         | 
| @@ -52,6 +55,7 @@ module Blix::Rest | |
| 52 55 |  | 
| 53 56 | 
             
                class << self
         | 
| 54 57 |  | 
| 58 | 
            +
                  # the root always starts with '/' and finishes with '/'
         | 
| 55 59 | 
             
                  def set_path_root(root)
         | 
| 56 60 | 
             
                    root = root.to_s
         | 
| 57 61 | 
             
                    root = '/' + root if root[0, 1] != '/'
         | 
| @@ -64,19 +68,30 @@ module Blix::Rest | |
| 64 68 | 
             
                    @path_root || '/'
         | 
| 65 69 | 
             
                  end
         | 
| 66 70 |  | 
| 67 | 
            -
                   | 
| 71 | 
            +
                  def path_root_length
         | 
| 72 | 
            +
                    @path_root_length.to_i
         | 
| 73 | 
            +
                  end
         | 
| 68 74 |  | 
| 69 75 | 
             
                  def full_path(path)
         | 
| 70 76 | 
             
                    path = path[1..-1] if path[0, 1] == '/'
         | 
| 71 77 | 
             
                    path_root + path
         | 
| 72 78 | 
             
                  end
         | 
| 73 79 |  | 
| 80 | 
            +
                  # ensure that absolute path is the  full path
         | 
| 81 | 
            +
                  def ensure_full_path(path)
         | 
| 82 | 
            +
                    if path[0, 1] == '/' && (path_root_length>0) && path[0, path_root_length] != path_root[0, path_root_length]
         | 
| 83 | 
            +
                      path = path_root + path[1..-1]
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                    path
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 74 88 | 
             
                  def locations
         | 
| 75 89 | 
             
                    @locations ||= Hash.new { |h, k| h[k] = [] }
         | 
| 76 90 | 
             
                  end
         | 
| 77 91 |  | 
| 78 92 | 
             
                  def table
         | 
| 79 | 
            -
                     | 
| 93 | 
            +
                    # compile the table in one thread only.
         | 
| 94 | 
            +
                    @table ||= @@mutex.synchronize{@table ||= compile}
         | 
| 80 95 | 
             
                  end
         | 
| 81 96 |  | 
| 82 97 | 
             
                  def dump
         | 
| @@ -147,6 +162,7 @@ module Blix::Rest | |
| 147 162 | 
             
                    path = path[1..-1] if path[0, 1] == PATH_SEP
         | 
| 148 163 | 
             
                    RequestMapper.locations[verb] << [verb, path, opts, blk]
         | 
| 149 164 | 
             
                    @table = nil # force recompile
         | 
| 165 | 
            +
                    true
         | 
| 150 166 | 
             
                  end
         | 
| 151 167 |  | 
| 152 168 | 
             
                  # match a given path to  declared route.
         | 
| @@ -212,12 +228,12 @@ module Blix::Rest | |
| 212 228 | 
             
                        # .. if there is a wildpath foloowing then fine ..
         | 
| 213 229 | 
             
                        # .. otherwise an error !
         | 
| 214 230 |  | 
| 215 | 
            -
                        if idx == limit # the last section of  | 
| 231 | 
            +
                        if idx == limit # the last section of path
         | 
| 216 232 | 
             
                          if current.blk
         | 
| 217 233 | 
             
                            return [current.blk, parameters, current.opts]
         | 
| 218 234 | 
             
                          elsif (havewild = current[STAR_PLACEHOLDER])
         | 
| 219 235 | 
             
                            parameters[havewild.parameter.to_s] = '/'
         | 
| 220 | 
            -
                            return [havewild.blk, parameters, havewild.opts]
         | 
| 236 | 
            +
                            return [havewild.blk, parameters, havewild.opts, true]
         | 
| 221 237 | 
             
                          else
         | 
| 222 238 | 
             
                            return [nil, {}, nil]
         | 
| 223 239 | 
             
                          end
         | 
| @@ -250,7 +266,7 @@ module Blix::Rest | |
| 250 266 | 
             
                              return [current.blk, parameters, current.opts]
         | 
| 251 267 | 
             
                            elsif (havewild = current[STAR_PLACEHOLDER])
         | 
| 252 268 | 
             
                              parameters[havewild.parameter.to_s] = '/'
         | 
| 253 | 
            -
                              return [havewild.blk, parameters, havewild.opts]
         | 
| 269 | 
            +
                              return [havewild.blk, parameters, havewild.opts, true]
         | 
| 254 270 | 
             
                            else
         | 
| 255 271 | 
             
                              return [nil, {}, nil]
         | 
| 256 272 | 
             
                            end
         | 
| @@ -268,7 +284,7 @@ module Blix::Rest | |
| 268 284 | 
             
                              parameters['format'] = wildformat[1..-1].to_sym
         | 
| 269 285 | 
             
                            end
         | 
| 270 286 | 
             
                            parameters[current.parameter.to_s] = wildpath
         | 
| 271 | 
            -
                            return [current.blk, parameters, current.opts]
         | 
| 287 | 
            +
                            return [current.blk, parameters, current.opts, true]
         | 
| 272 288 | 
             
                          else
         | 
| 273 289 | 
             
                            return [nil, {}, nil]
         | 
| 274 290 | 
             
                          end
         | 
    
        data/lib/blix/rest/response.rb
    CHANGED
    
    | @@ -12,13 +12,13 @@ module Blix::Rest | |
| 12 12 |  | 
| 13 13 | 
             
                def initialize
         | 
| 14 14 | 
             
                  @status  = 200
         | 
| 15 | 
            -
                  @headers  =  | 
| 15 | 
            +
                  @headers  = Rack::Headers.new
         | 
| 16 16 | 
             
                  @content = nil
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 19 | 
             
                def set(status,content=nil,headers=nil)
         | 
| 20 20 | 
             
                  @status = status if status
         | 
| 21 | 
            -
                  @content = String.new(content) if content
         | 
| 21 | 
            +
                  @content = [String.new(content)] if content
         | 
| 22 22 | 
             
                  @headers.merge!(headers) if headers
         | 
| 23 23 | 
             
                end
         | 
| 24 24 |  | 
    
        data/lib/blix/rest/server.rb
    CHANGED
    
    | @@ -17,8 +17,23 @@ module Blix::Rest | |
| 17 17 | 
             
                  @_options = opts
         | 
| 18 18 | 
             
                end
         | 
| 19 19 |  | 
| 20 | 
            +
                # options passed to the server
         | 
| 21 | 
            +
                def _options
         | 
| 22 | 
            +
                  @_options
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
                # the object serving as a cache
         | 
| 20 27 | 
             
                def _cache
         | 
| 21 | 
            -
                  @_cache ||=  | 
| 28 | 
            +
                  @_cache ||= begin
         | 
| 29 | 
            +
                    obj = _options[:cache] || _options['cache']
         | 
| 30 | 
            +
                    if obj
         | 
| 31 | 
            +
                      raise "cache must be a subclass of Blix::Rest::Cache" unless obj.is_a?(Cache)
         | 
| 32 | 
            +
                      obj
         | 
| 33 | 
            +
                    else
         | 
| 34 | 
            +
                      MemoryCache.new
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 22 37 | 
             
                end
         | 
| 23 38 |  | 
| 24 39 | 
             
                def extract_parsers_from_options(opts)
         | 
| @@ -34,7 +49,6 @@ module Blix::Rest | |
| 34 49 | 
             
                def set_custom_headers(format, headers)
         | 
| 35 50 | 
             
                  parser = get_parser(format)
         | 
| 36 51 | 
             
                  raise "parser not found for custom headers format=>#{format}" unless parser
         | 
| 37 | 
            -
             | 
| 38 52 | 
             
                  parser.__custom_headers = headers
         | 
| 39 53 | 
             
                end
         | 
| 40 54 |  | 
| @@ -54,6 +68,7 @@ module Blix::Rest | |
| 54 68 | 
             
                  parser._types.each { |t| @_mime_types[t.downcase] = parser } # register each of the mime types
         | 
| 55 69 | 
             
                end
         | 
| 56 70 |  | 
| 71 | 
            +
                # retrieve parameters from the http request
         | 
| 57 72 | 
             
                def retrieve_params(env)
         | 
| 58 73 | 
             
                  post_params = {}
         | 
| 59 74 | 
             
                  body        = ''
         | 
| @@ -104,6 +119,7 @@ module Blix::Rest | |
| 104 119 | 
             
                  end
         | 
| 105 120 | 
             
                end
         | 
| 106 121 |  | 
| 122 | 
            +
                # determine standard format from http mime type
         | 
| 107 123 | 
             
                def get_format_from_mime(mime)
         | 
| 108 124 | 
             
                  case mime
         | 
| 109 125 | 
             
                  when 'application/json' then :json
         | 
| @@ -138,20 +154,24 @@ module Blix::Rest | |
| 138 154 | 
             
                  parser
         | 
| 139 155 | 
             
                end
         | 
| 140 156 |  | 
| 157 | 
            +
                # the call function is the interface with the rack framework
         | 
| 141 158 | 
             
                def call(env)
         | 
| 142 159 | 
             
                  req = Rack::Request.new(env)
         | 
| 143 160 |  | 
| 144 | 
            -
                  verb | 
| 145 | 
            -
                  path | 
| 161 | 
            +
                  verb  = env['REQUEST_METHOD']
         | 
| 162 | 
            +
                  path  = req.path
         | 
| 163 | 
            +
                  path  = CGI.unescape(path).gsub('+',' ') unless _options[:unescape] == false
         | 
| 146 164 |  | 
| 147 | 
            -
                  blk, path_params, options = RequestMapper.match(verb, path)
         | 
| 165 | 
            +
                  blk, path_params, options, is_wild = RequestMapper.match(verb, path)
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  match_all = RequestMapper.match('ALL', path) unless blk && !is_wild
         | 
| 168 | 
            +
                  blk, path_params, options = match_all if match_all && match_all[0] # override
         | 
| 148 169 |  | 
| 149 | 
            -
                  blk, path_params, options = RequestMapper.match('ALL', path) unless blk
         | 
| 150 170 |  | 
| 151 171 | 
             
                  default_format = options && options[:default] && options[:default].to_sym
         | 
| 152 172 | 
             
                  force_format = options && options[:force] && options[:force].to_sym
         | 
| 153 | 
            -
                  do_cache | 
| 154 | 
            -
                  clear_cache | 
| 173 | 
            +
                  do_cache     = options && options[:cache] && !Blix::Rest.cache_disabled
         | 
| 174 | 
            +
                  clear_cache  = options && options[:cache_reset]
         | 
| 155 175 |  | 
| 156 176 | 
             
                  query_format = options && options[:query] && req.GET['format'] && req.GET['format'].to_sym
         | 
| 157 177 |  | 
| @@ -159,39 +179,49 @@ module Blix::Rest | |
| 159 179 |  | 
| 160 180 | 
             
                  parser = get_parser(force_format || format)
         | 
| 161 181 |  | 
| 162 | 
            -
                   | 
| 182 | 
            +
                  unless parser
         | 
| 183 | 
            +
                    if blk
         | 
| 184 | 
            +
                      return [406, {}, ["Invalid Format: #{format}"]]
         | 
| 185 | 
            +
                    else
         | 
| 186 | 
            +
                      return [404, {}, ["Invalid Url"]]
         | 
| 187 | 
            +
                    end
         | 
| 188 | 
            +
                  end
         | 
| 163 189 |  | 
| 164 190 | 
             
                  parser._options = options
         | 
| 165 191 |  | 
| 166 192 | 
             
                  # check for cached response end return with cached response if found.
         | 
| 167 193 | 
             
                  #
         | 
| 168 | 
            -
                  if do_cache && _cache["#{verb}|#{format}|#{path}"]
         | 
| 169 | 
            -
                    response  | 
| 170 | 
            -
                    return [response.status, response.headers.merge('X-Blix-Cache' => 'cached'), [response.content]]
         | 
| 194 | 
            +
                  if do_cache && ( response = _cache["#{verb}|#{format}|#{path}"] )
         | 
| 195 | 
            +
                    return [response.status, response.headers.merge('x-blix-cache' => 'cached'), response.content]
         | 
| 171 196 | 
             
                  end
         | 
| 172 197 |  | 
| 173 198 | 
             
                  response = Response.new
         | 
| 174 199 |  | 
| 175 | 
            -
                  if parser.__custom_headers
         | 
| 176 | 
            -
                    response.headers.merge! parser.__custom_headers
         | 
| 177 | 
            -
                  else
         | 
| 178 | 
            -
                    parser.set_default_headers(response.headers)
         | 
| 179 | 
            -
                  end
         | 
| 180 | 
            -
             | 
| 181 200 | 
             
                  if blk
         | 
| 182 201 |  | 
| 183 202 | 
             
                    begin
         | 
| 184 203 | 
             
                      params = env['params']
         | 
| 185 | 
            -
                       | 
| 204 | 
            +
                      context = Context.new(path_params, params, req, format, response, verb, self)
         | 
| 205 | 
            +
                      value  = blk.call(context)
         | 
| 186 206 | 
             
                    rescue ServiceError => e
         | 
| 207 | 
            +
                      set_default_headers(parser,response)
         | 
| 187 208 | 
             
                      response.set(e.status, parser.format_error(e.message), e.headers)
         | 
| 209 | 
            +
                    rescue RawResponse => e
         | 
| 210 | 
            +
                      value = e.content
         | 
| 211 | 
            +
                      value = [value.to_s] unless value.respond_to?(:each) ||  value.respond_to?(:call)
         | 
| 212 | 
            +
                      response.status  = e.status if e.status
         | 
| 213 | 
            +
                      response.content = value
         | 
| 214 | 
            +
                      response.headers.merge!(e.headers) if e.headers
         | 
| 188 215 | 
             
                    rescue AuthorizationError => e
         | 
| 216 | 
            +
                      set_default_headers(parser,response)
         | 
| 189 217 | 
             
                      response.set(401, parser.format_error(e.message), AUTH_HEADER => "#{e.type} realm=\"#{e.realm}\", charset=\"UTF-8\"")
         | 
| 190 218 | 
             
                    rescue Exception => e
         | 
| 219 | 
            +
                      set_default_headers(parser,response)
         | 
| 191 220 | 
             
                      response.set(500, parser.format_error('internal error'))
         | 
| 192 221 | 
             
                      ::Blix::Rest.logger <<  "----------------------------\n#{$!}\n----------------------------"
         | 
| 193 222 | 
             
                      ::Blix::Rest.logger <<  "----------------------------\n#{$@}\n----------------------------"
         | 
| 194 223 | 
             
                    else # no error
         | 
| 224 | 
            +
                      set_default_headers(parser,response)
         | 
| 195 225 | 
             
                      parser.format_response(value, response)
         | 
| 196 226 | 
             
                      # cache response if requested
         | 
| 197 227 | 
             
                      _cache.clear if clear_cache
         | 
| @@ -199,9 +229,19 @@ module Blix::Rest | |
| 199 229 | 
             
                    end
         | 
| 200 230 |  | 
| 201 231 | 
             
                  else
         | 
| 232 | 
            +
                    set_default_headers(parser,response)
         | 
| 202 233 | 
             
                    response.set(404, parser.format_error('Invalid Url'))
         | 
| 203 234 | 
             
                  end
         | 
| 204 | 
            -
             | 
| 235 | 
            +
             | 
| 236 | 
            +
                  [response.status.to_i, response.headers, response.content]
         | 
| 237 | 
            +
                end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                def set_default_headers(parser,response)
         | 
| 240 | 
            +
                  if parser.__custom_headers
         | 
| 241 | 
            +
                    response.headers.merge! parser.__custom_headers
         | 
| 242 | 
            +
                  else
         | 
| 243 | 
            +
                    parser.set_default_headers(response.headers)
         | 
| 244 | 
            +
                  end
         | 
| 205 245 | 
             
                end
         | 
| 206 246 |  | 
| 207 247 | 
             
              end
         | 
    
        data/lib/blix/rest/version.rb
    CHANGED
    
    
    
        data/lib/blix/rest.rb
    CHANGED
    
    | @@ -1,6 +1,8 @@ | |
| 1 1 | 
             
            require 'base64'
         | 
| 2 2 | 
             
            require 'logger'
         | 
| 3 3 | 
             
            require 'time'
         | 
| 4 | 
            +
            require 'cgi'
         | 
| 5 | 
            +
            require 'rack'
         | 
| 4 6 |  | 
| 5 7 | 
             
            module Blix
         | 
| 6 8 | 
             
              module Rest
         | 
| @@ -8,14 +10,14 @@ module Blix | |
| 8 10 | 
             
                # EXPIRED_TOKEN_MESSAGE = 'token expired'
         | 
| 9 11 | 
             
                # INVALID_TOKEN_MESSAGE = 'invalid token'
         | 
| 10 12 |  | 
| 11 | 
            -
                CONTENT_TYPE      = ' | 
| 13 | 
            +
                CONTENT_TYPE      = 'content-type'.freeze
         | 
| 12 14 | 
             
                CONTENT_TYPE_JSON = 'application/json'.freeze
         | 
| 13 15 | 
             
                CONTENT_TYPE_HTML = 'text/html; charset=utf-8'.freeze
         | 
| 14 16 | 
             
                CONTENT_TYPE_XML  = 'application/xml'.freeze
         | 
| 15 | 
            -
                AUTH_HEADER       = ' | 
| 16 | 
            -
                CACHE_CONTROL     = ' | 
| 17 | 
            +
                AUTH_HEADER       = 'www-authenticate'.freeze
         | 
| 18 | 
            +
                CACHE_CONTROL     = 'cache-control'.freeze
         | 
| 17 19 | 
             
                CACHE_NO_STORE    = 'no-store'.freeze
         | 
| 18 | 
            -
                PRAGMA            = ' | 
| 20 | 
            +
                PRAGMA            = 'pragma'.freeze
         | 
| 19 21 | 
             
                NO_CACHE          = 'no-cache'.freeze
         | 
| 20 22 | 
             
                URL_ENCODED       = %r{^application/x-www-form-urlencoded}.freeze
         | 
| 21 23 | 
             
                JSON_ENCODED      = %r{^application/json}.freeze # NOTE: "text/json" and "text/javascript" are deprecated forms
         | 
| @@ -43,6 +45,14 @@ module Blix | |
| 43 45 | 
             
                  @_logger = val
         | 
| 44 46 | 
             
                end
         | 
| 45 47 |  | 
| 48 | 
            +
                def self.cache_disabled
         | 
| 49 | 
            +
                  @_cache_disabled
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def self.disable_cache
         | 
| 53 | 
            +
                  @_cache_disabled = true
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 46 56 | 
             
                def self.logger
         | 
| 47 57 | 
             
                  @_logger ||= begin
         | 
| 48 58 | 
             
                    l = Logger.new(STDOUT)
         | 
| @@ -67,13 +77,10 @@ module Blix | |
| 67 77 | 
             
                end
         | 
| 68 78 |  | 
| 69 79 | 
             
                # interpret payload string as json
         | 
| 70 | 
            -
                class RawJsonString
         | 
| 71 | 
            -
                  def initialize(str)
         | 
| 72 | 
            -
                    @str = str
         | 
| 73 | 
            -
                  end
         | 
| 80 | 
            +
                class RawJsonString < String
         | 
| 74 81 |  | 
| 75 82 | 
             
                  def as_json(*_a)
         | 
| 76 | 
            -
                     | 
| 83 | 
            +
                    self
         | 
| 77 84 | 
             
                  end
         | 
| 78 85 |  | 
| 79 86 | 
             
                  def to_json(*a)
         | 
| @@ -94,6 +101,19 @@ module Blix | |
| 94 101 | 
             
                  end
         | 
| 95 102 | 
             
                end
         | 
| 96 103 |  | 
| 104 | 
            +
                class RawResponse < StandardError
         | 
| 105 | 
            +
                  attr_reader :status
         | 
| 106 | 
            +
                  attr_reader :headers
         | 
| 107 | 
            +
                  attr_reader :content
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  def initialize(message, status = nil, headers = nil)
         | 
| 110 | 
            +
                    super(message || "")
         | 
| 111 | 
            +
                    @status  = status
         | 
| 112 | 
            +
                    @headers = headers
         | 
| 113 | 
            +
                    @content = message
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 97 117 | 
             
                class AuthorizationError < StandardError
         | 
| 98 118 | 
             
                  attr_reader :realm, :type
         | 
| 99 119 | 
             
                  def initialize(message=nil, realm=nil, type=nil)
         | 
| @@ -128,6 +148,7 @@ require 'rack' | |
| 128 148 | 
             
            require 'blix/rest/response'
         | 
| 129 149 | 
             
            require 'blix/rest/format_parser'
         | 
| 130 150 | 
             
            require 'blix/rest/request_mapper'
         | 
| 151 | 
            +
            require 'blix/rest/cache'
         | 
| 131 152 | 
             
            require 'blix/rest/server'
         | 
| 132 153 | 
             
            # require 'blix/rest/provider'
         | 
| 133 154 | 
             
            require 'blix/rest/controller'
         | 
| @@ -36,7 +36,7 @@ module Blix | |
| 36 36 | 
             
                def store_data(id, data)
         | 
| 37 37 | 
             
                  params = {}
         | 
| 38 38 | 
             
                  params[:ex] = _opts[:expire_secs] if _opts.key?(:expire_secs)
         | 
| 39 | 
            -
                  redis.set(_key(id), data, params)
         | 
| 39 | 
            +
                  redis.set(_key(id), data, **params)
         | 
| 40 40 | 
             
                  data
         | 
| 41 41 | 
             
                end
         | 
| 42 42 |  | 
| @@ -89,7 +89,7 @@ module Blix | |
| 89 89 | 
             
                  params[:ex] = _opts[:expire_secs] if _opts.key?(:expire_secs)
         | 
| 90 90 | 
             
                  hash ||= {}
         | 
| 91 91 | 
             
                  hash['_last_access'] = Time.now
         | 
| 92 | 
            -
                  redis.set(_key(id), _encode(hash), params)
         | 
| 92 | 
            +
                  redis.set(_key(id), _encode(hash), **params)
         | 
| 93 93 | 
             
                  hash
         | 
| 94 94 | 
             
                end
         | 
| 95 95 |  |