Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 169 lines (143 sloc) 4.519 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
require 'dalli'
require 'forwardable'
require 'faraday_middleware/addressable_patch'
require 'digest/md5'
require 'connection_pool'

class Huboard
  class Client
    class ConnectionPool

      def self.options
        @options ||= {
          namespace: "huboard_v1",
          compress: true,
          expires_in: 1.day,
          username: HuboardApplication.cache_config[:username],
          password: HuboardApplication.cache_config[:password]
        }
      end

      def self.connection_pool
        @connection_pool = ::ConnectionPool.new(:size => 20, :timeout => 10) {
          Dalli::Client.new(HuboardApplication.cache_config[:servers].split(","), options)
        }
      end

    end


    class SimpleCache

      def initialize(app, options)
        @options = options
        @app = app
      end

      def cache
        @dalli ||= ConnectionPool.connection_pool
      end

      def clear(key)
        cache.with do |dalli|
          dalli.delete(key)
        end
      end

      def read(key, app, env)

        if cached = get(key)
          cached = Marshal.load(cached)
          etag = cached.headers[:etag]

          env[:request_headers].merge!('If-None-Match' => etag)

          response = app.call(env)

          if response.status == 304
            return cached
          elsif response.status == 200
            write(key, response)
            return response
          end
          response
        else
          data = app.call(env)
          if data.status == 200
            write(key, data)
          end
          data
        end
      end

      def get(key)
        cache.with do |dalli|
          dalli.get(key)
        end
      end

      def write(key, data)
        cache.with do |dalli|
          dalli.set(key, Marshal.dump(data))
          data
        end
      end

      def fetch(key, app, env)
        read(key, app, env)
      end
    end

    class Caching < Faraday::Middleware
      attr_reader :cache

      extend Forwardable
      def_delegators :'Faraday::Utils', :parse_query, :build_query

      # Public: initialize the middleware.
      #
      # cache - An object that responds to read, write and fetch (default: nil).
      # options - An options Hash (default: {}):
      # :ignore_params - String name or Array names of query params
      # that should be ignored when forming the cache
      # key (default: []).
      #
      # Yields if no cache is given. The block should return a cache object.
      def initialize(app, options = {:namespace => "huboard_v1", :compress => true, :expires_in => 1.day,
                     :username => HuboardApplication.cache_config[:username], :password => HuboardApplication.cache_config[:password]})
        super(app)
        @cache = SimpleCache.new app, options
        @options = options
      end

      def call(env)
        if :get == env[:method]
          if env[:parallel_manager]
            # callback mode
            cache_on_complete(env)
          else
            # synchronous mode
            response = cache.fetch(cache_key(env), @app, env)
            finalize_response(response, env)
          end
        elsif :patch == env[:method] || :post == env[:method] || :delete == env[:method]
          cache.clear(cache_key(env))
          @app.call(env)
        else
          @app.call(env)
        end
      end

      def cache_key(env)
        url = env[:url].dup
        if url.query && params_to_ignore.any?
          params = parse_query url.query
          params.reject! {|k,| params_to_ignore.include? k }
          url.query = build_query params
        end
        url.normalize!
        Digest::MD5.hexdigest(url.request_uri)
      end

      def params_to_ignore
        @params_to_ignore ||= Array(@options[:ignore_params]).map { |p| p.to_s }
      end

      def cache_on_complete(env)
        key = cache_key(env)
        if cached_response = cache.read(key, @app, env)
          finalize_response(cached_response, env)
        else
          response = @app.call(env)
          response.on_complete { cache.write(key, response) }
        end
      end

      def finalize_response(response, env)
        response = response.dup if response.frozen?
        env[:response] = response
        unless env[:response_headers]
          env.update response.env
          # FIXME: omg hax
          response.instance_variable_set('@env', env)
        end
        response
      end
    end

  end
end
Something went wrong with that request. Please try again.