346 lines
6.9 KiB
Ruby
346 lines
6.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'sinatra/base'
|
|
|
|
module Sinatra
|
|
# = Sinatra::Cookies
|
|
#
|
|
# Easy way to deal with cookies
|
|
#
|
|
# == Usage
|
|
#
|
|
# Allows you to read cookies:
|
|
#
|
|
# get '/' do
|
|
# "value: #{cookies[:something]}"
|
|
# end
|
|
#
|
|
# And of course to write cookies:
|
|
#
|
|
# get '/set' do
|
|
# cookies[:something] = 'foobar'
|
|
# redirect to('/')
|
|
# end
|
|
#
|
|
# And generally behaves like a hash:
|
|
#
|
|
# get '/demo' do
|
|
# cookies.merge! 'foo' => 'bar', 'bar' => 'baz'
|
|
# cookies.keep_if { |key, value| key.start_with? 'b' }
|
|
# foo, bar = cookies.values_at 'foo', 'bar'
|
|
# "size: #{cookies.length}"
|
|
# end
|
|
#
|
|
# === Classic Application
|
|
#
|
|
# In a classic application simply require the helpers, and start using them:
|
|
#
|
|
# require "sinatra"
|
|
# require "sinatra/cookies"
|
|
#
|
|
# # The rest of your classic application code goes here...
|
|
#
|
|
# === Modular Application
|
|
#
|
|
# In a modular application you need to require the helpers, and then tell
|
|
# the application to use them:
|
|
#
|
|
# require "sinatra/base"
|
|
# require "sinatra/cookies"
|
|
#
|
|
# class MyApp < Sinatra::Base
|
|
# helpers Sinatra::Cookies
|
|
#
|
|
# # The rest of your modular application code goes here...
|
|
# end
|
|
#
|
|
module Cookies
|
|
class Jar
|
|
include Enumerable
|
|
attr_reader :options
|
|
|
|
def initialize(app)
|
|
@response_string = nil
|
|
@response_hash = {}
|
|
@response = app.response
|
|
@request = app.request
|
|
@deleted = []
|
|
|
|
@options = {
|
|
path: @request.script_name.to_s.empty? ? '/' : @request.script_name,
|
|
domain: @request.host == 'localhost' ? nil : @request.host,
|
|
secure: @request.secure?,
|
|
httponly: true
|
|
}
|
|
|
|
return unless app.settings.respond_to? :cookie_options
|
|
|
|
@options.merge! app.settings.cookie_options
|
|
end
|
|
|
|
def ==(other)
|
|
other.respond_to? :to_hash and to_hash == other.to_hash
|
|
end
|
|
|
|
def [](key)
|
|
response_cookies[key.to_s] || request_cookies[key.to_s]
|
|
end
|
|
|
|
def []=(key, value)
|
|
set(key, value: value)
|
|
end
|
|
|
|
if Hash.method_defined? :assoc
|
|
def assoc(key)
|
|
to_hash.assoc(key.to_s)
|
|
end
|
|
end
|
|
|
|
def clear
|
|
each_key { |k| delete(k) }
|
|
end
|
|
|
|
def compare_by_identity?
|
|
false
|
|
end
|
|
|
|
def default
|
|
nil
|
|
end
|
|
|
|
alias default_proc default
|
|
|
|
def delete(key)
|
|
result = self[key]
|
|
@response.delete_cookie(key.to_s, @options)
|
|
result
|
|
end
|
|
|
|
def delete_if
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
each { |k, v| delete(k) if yield(k, v) }
|
|
self
|
|
end
|
|
|
|
def each(&block)
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
to_hash.each(&block)
|
|
end
|
|
|
|
def each_key(&block)
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
to_hash.each_key(&block)
|
|
end
|
|
|
|
alias each_pair each
|
|
|
|
def each_value(&block)
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
to_hash.each_value(&block)
|
|
end
|
|
|
|
def empty?
|
|
to_hash.empty?
|
|
end
|
|
|
|
def fetch(key, &block)
|
|
response_cookies.fetch(key.to_s) do
|
|
request_cookies.fetch(key.to_s, &block)
|
|
end
|
|
end
|
|
|
|
if Hash.method_defined? :flatten
|
|
def flatten
|
|
to_hash.flatten
|
|
end
|
|
end
|
|
|
|
def has_key?(key)
|
|
response_cookies.key? key.to_s or request_cookies.key? key.to_s
|
|
end
|
|
|
|
def has_value?(value)
|
|
response_cookies.value? value or request_cookies.value? value
|
|
end
|
|
|
|
def hash
|
|
to_hash.hash
|
|
end
|
|
|
|
alias include? has_key?
|
|
alias member? has_key?
|
|
|
|
def inspect
|
|
"<##{self.class}: #{to_hash.inspect[1..-2]}>"
|
|
end
|
|
|
|
if Hash.method_defined? :invert
|
|
def invert
|
|
to_hash.invert
|
|
end
|
|
end
|
|
|
|
def keep_if
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
delete_if { |*a| !yield(*a) }
|
|
end
|
|
|
|
def key(value)
|
|
to_hash.key(value)
|
|
end
|
|
|
|
alias key? has_key?
|
|
|
|
def keys
|
|
to_hash.keys
|
|
end
|
|
|
|
def length
|
|
to_hash.length
|
|
end
|
|
|
|
def merge(other, &block)
|
|
to_hash.merge(other, &block)
|
|
end
|
|
|
|
def merge!(other)
|
|
other.each_pair do |key, value|
|
|
self[key] = if block_given? && include?(key)
|
|
yield(key.to_s, self[key], value)
|
|
else
|
|
value
|
|
end
|
|
end
|
|
end
|
|
|
|
def rassoc(value)
|
|
to_hash.rassoc(value)
|
|
end
|
|
|
|
def rehash
|
|
response_cookies.rehash
|
|
request_cookies.rehash
|
|
self
|
|
end
|
|
|
|
def reject(&block)
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
to_hash.reject(&block)
|
|
end
|
|
|
|
alias reject! delete_if
|
|
|
|
def replace(other)
|
|
select! { |k, _v| other.include?(k) or other.include?(k.to_s) }
|
|
merge! other
|
|
end
|
|
|
|
def select(&block)
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
to_hash.select(&block)
|
|
end
|
|
|
|
alias select! keep_if if Hash.method_defined? :select!
|
|
|
|
def set(key, options = {})
|
|
@response.set_cookie key.to_s, @options.merge(options)
|
|
end
|
|
|
|
def shift
|
|
key, value = to_hash.shift
|
|
delete(key)
|
|
[key, value]
|
|
end
|
|
|
|
alias size length
|
|
|
|
if Hash.method_defined? :sort
|
|
def sort(&block)
|
|
to_hash.sort(&block)
|
|
end
|
|
end
|
|
|
|
alias store []=
|
|
|
|
def to_hash
|
|
request_cookies.merge(response_cookies)
|
|
end
|
|
|
|
def to_a
|
|
to_hash.to_a
|
|
end
|
|
|
|
def to_s
|
|
to_hash.to_s
|
|
end
|
|
|
|
alias update merge!
|
|
alias value? has_value?
|
|
|
|
def values
|
|
to_hash.values
|
|
end
|
|
|
|
def values_at(*list)
|
|
list.map { |k| self[k] }
|
|
end
|
|
|
|
private
|
|
|
|
def warn(message)
|
|
super "#{caller.first[/^[^:]:\d+:/]} warning: #{message}"
|
|
end
|
|
|
|
def deleted
|
|
parse_response
|
|
@deleted
|
|
end
|
|
|
|
def response_cookies
|
|
parse_response
|
|
@response_hash
|
|
end
|
|
|
|
def parse_response
|
|
string = @response['Set-Cookie']
|
|
return if @response_string == string
|
|
|
|
hash = {}
|
|
|
|
string.each_line do |line|
|
|
key, value = line.split(';', 2).first.to_s.split('=', 2)
|
|
next if key.nil?
|
|
|
|
key = Rack::Utils.unescape(key)
|
|
if line =~ /expires=Thu, 01[-\s]Jan[-\s]1970/
|
|
@deleted << key
|
|
else
|
|
@deleted.delete key
|
|
hash[key] = value
|
|
end
|
|
end
|
|
|
|
@response_hash.replace hash
|
|
@response_string = string
|
|
end
|
|
|
|
def request_cookies
|
|
@request.cookies.reject { |key, _value| deleted.include? key }
|
|
end
|
|
end
|
|
|
|
def cookies
|
|
@cookies ||= Jar.new(self)
|
|
end
|
|
end
|
|
|
|
helpers Cookies
|
|
end
|