jets 1.0.18 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +10 -10
- data/README/testing.md +5 -2
- data/lib/jets.rb +2 -2
- data/lib/jets/application.rb +69 -40
- data/lib/jets/booter.rb +17 -20
- data/lib/jets/builders/code_builder.rb +7 -8
- data/lib/jets/cfn/ship.rb +0 -6
- data/lib/jets/commands/build.rb +0 -5
- data/lib/jets/commands/deploy.rb +0 -4
- data/lib/jets/commands/main.rb +31 -4
- data/lib/jets/commands/templates/skeleton/{.env → .env.tt} +1 -0
- data/lib/jets/commands/templates/skeleton/config.ru +1 -0
- data/lib/jets/commands/upgrade/v1.rb +12 -0
- data/lib/jets/controller.rb +5 -0
- data/lib/jets/controller/base.rb +43 -21
- data/lib/jets/controller/cookies.rb +40 -0
- data/lib/jets/controller/cookies/jar.rb +269 -0
- data/lib/jets/controller/middleware.rb +4 -0
- data/lib/jets/controller/middleware/local.rb +119 -0
- data/lib/jets/{server/lambda_aws_proxy.rb → controller/middleware/local/api_gateway.rb} +11 -49
- data/lib/jets/controller/middleware/local/mimic_aws_call.rb +38 -0
- data/lib/jets/{server → controller/middleware/local}/route_matcher.rb +4 -4
- data/lib/jets/controller/middleware/main.rb +46 -0
- data/lib/jets/{server → controller/middleware}/webpacker_setup.rb +0 -1
- data/lib/jets/controller/params.rb +2 -1
- data/lib/jets/controller/rack.rb +5 -0
- data/lib/jets/controller/rack/adapter.rb +60 -0
- data/lib/jets/controller/rack/env.rb +96 -0
- data/lib/jets/controller/redirection.rb +1 -1
- data/lib/jets/controller/renderers.rb +1 -1
- data/lib/jets/controller/renderers/base_renderer.rb +0 -4
- data/lib/jets/controller/renderers/{aws_proxy_renderer.rb → rack_renderer.rb} +7 -19
- data/lib/jets/controller/renderers/template_renderer.rb +1 -1
- data/lib/jets/controller/request.rb +14 -44
- data/lib/jets/controller/response.rb +55 -7
- data/lib/jets/internal/app/controllers/jets/rack_controller.rb +13 -3
- data/lib/jets/mega.rb +7 -0
- data/lib/jets/{rack → mega}/hash_converter.rb +1 -1
- data/lib/jets/{rack → mega}/request.rb +17 -4
- data/lib/jets/middleware.rb +38 -0
- data/lib/jets/middleware/configurator.rb +84 -0
- data/lib/jets/middleware/default_stack.rb +44 -0
- data/lib/jets/middleware/layer.rb +34 -0
- data/lib/jets/middleware/stack.rb +77 -0
- data/lib/jets/resource/function.rb +1 -1
- data/lib/jets/ruby_server.rb +1 -1
- data/lib/jets/server.rb +48 -13
- data/lib/jets/version.rb +1 -1
- metadata +24 -17
- data/lib/jets/application/middleware.rb +0 -23
- data/lib/jets/default/application.rb +0 -23
- data/lib/jets/rack.rb +0 -7
- data/lib/jets/rack/server.rb +0 -47
- data/lib/jets/server/api_gateway.rb +0 -39
- data/lib/jets/server/timing_middleware.rb +0 -33
- data/lib/jets/timing.rb +0 -65
- data/lib/jets/timing/report.rb +0 -82
data/lib/jets/commands/build.rb
CHANGED
@@ -2,7 +2,6 @@ require 'digest'
|
|
2
2
|
|
3
3
|
module Jets::Commands
|
4
4
|
class Build
|
5
|
-
include Jets::Timing
|
6
5
|
include StackInfo
|
7
6
|
|
8
7
|
def initialize(options)
|
@@ -17,18 +16,15 @@ module Jets::Commands
|
|
17
16
|
@options.merge!(stack_type: stack_type, s3_bucket: s3_bucket)
|
18
17
|
build
|
19
18
|
end
|
20
|
-
time :run
|
21
19
|
|
22
20
|
def build
|
23
21
|
build_code unless @options[:templates]
|
24
22
|
build_templates
|
25
23
|
end
|
26
|
-
time :build
|
27
24
|
|
28
25
|
def build_code
|
29
26
|
Jets::Builders::CodeBuilder.new.build unless @options[:noop]
|
30
27
|
end
|
31
|
-
time :build_code
|
32
28
|
|
33
29
|
def build_templates
|
34
30
|
puts "Building CloudFormation templates."
|
@@ -36,7 +32,6 @@ module Jets::Commands
|
|
36
32
|
build_minimal_template
|
37
33
|
build_all_templates if full?
|
38
34
|
end
|
39
|
-
time :build_templates
|
40
35
|
|
41
36
|
def full?
|
42
37
|
@options[:templates] || @options[:stack_type] == :full
|
data/lib/jets/commands/deploy.rb
CHANGED
@@ -2,7 +2,6 @@ module Jets::Commands
|
|
2
2
|
class Deploy
|
3
3
|
extend Memoist
|
4
4
|
include StackInfo
|
5
|
-
include Jets::Timing
|
6
5
|
|
7
6
|
def initialize(options)
|
8
7
|
@options = options
|
@@ -32,7 +31,6 @@ module Jets::Commands
|
|
32
31
|
# deploy full nested stack when stack already exists
|
33
32
|
ship(stack_type: :full, s3_bucket: s3_bucket)
|
34
33
|
end
|
35
|
-
time :run
|
36
34
|
|
37
35
|
def delete_minimal_stack
|
38
36
|
puts "Existing stack is in ROLLBACK_COMPLETE state from a previous failed minimal deploy. Deleting stack and continuing."
|
@@ -51,7 +49,6 @@ module Jets::Commands
|
|
51
49
|
def build_code
|
52
50
|
Jets::Commands::Build.new(@options).build_code
|
53
51
|
end
|
54
|
-
time :build_code
|
55
52
|
|
56
53
|
# Checks that all routes are validate and have corresponding lambda functions
|
57
54
|
def validate_routes!
|
@@ -70,7 +67,6 @@ module Jets::Commands
|
|
70
67
|
Jets::Commands::Build.new(options).build_templates
|
71
68
|
Jets::Cfn::Ship.new(options).run
|
72
69
|
end
|
73
|
-
time :ship
|
74
70
|
|
75
71
|
def status
|
76
72
|
Jets::Cfn::Status.new(stack_name)
|
data/lib/jets/commands/main.rb
CHANGED
@@ -18,9 +18,7 @@ module Jets::Commands
|
|
18
18
|
# environment parameter. It is not actually set here. It is set earlier
|
19
19
|
# in cli.rb: set_jets_env_for_deploy_command!
|
20
20
|
def deploy(environment=nil)
|
21
|
-
Jets::Timing.clear # must happen outside Deploy#run
|
22
21
|
Deploy.new(options).run
|
23
|
-
Jets::Timing.report
|
24
22
|
end
|
25
23
|
|
26
24
|
desc "delete", "Delete the Jets project and all its resources"
|
@@ -35,13 +33,16 @@ module Jets::Commands
|
|
35
33
|
long_desc Help.text(:server)
|
36
34
|
option :port, default: "8888", desc: "use PORT"
|
37
35
|
option :host, default: "127.0.0.1", desc: "listen on HOST"
|
36
|
+
option :reload, type: :boolean, default: true, desc: "Enables hot-reloading for development"
|
38
37
|
def server
|
39
38
|
# shell out to shotgun for automatic reloading
|
40
39
|
o = options
|
41
|
-
|
40
|
+
server_command = o[:reload] ? "shotgun" : "rackup"
|
41
|
+
command = "bundle exec #{server_command} --port #{o[:port]} --host #{o[:host]}"
|
42
42
|
puts "=> #{command}".colorize(:green)
|
43
43
|
puts Jets::Booter.message
|
44
|
-
Jets::
|
44
|
+
Jets::Booter.check_config_ru!
|
45
|
+
Jets::Server.start(options) unless ENV['JETS_RACK'] == '0' # rack server runs in background by default
|
45
46
|
system(command)
|
46
47
|
end
|
47
48
|
|
@@ -80,6 +81,16 @@ module Jets::Commands
|
|
80
81
|
option :guess, type: :boolean, default: true, desc: "Enables guess mode. Uses inference to allows use of all dashes to specify functions. Smart mode verifies that the function exists in the code base."
|
81
82
|
option :local, type: :boolean, desc: "Enables local mode. Instead of invoke the AWS Lambda function, the method gets called locally with current app code. With local mode guess mode is always used."
|
82
83
|
def call(function_name, payload='')
|
84
|
+
# Printing to stdout can mangle up the response when piping
|
85
|
+
# the value to jq. For example:
|
86
|
+
#
|
87
|
+
# `jets call --local .. | jq`
|
88
|
+
#
|
89
|
+
# By redirecting stderr we can use jq safely.
|
90
|
+
#
|
91
|
+
$stdout.sync = true
|
92
|
+
$stderr.sync = true
|
93
|
+
$stdout = $stderr # jets call operation
|
83
94
|
Call.new(function_name, payload, options).run
|
84
95
|
end
|
85
96
|
|
@@ -101,6 +112,22 @@ module Jets::Commands
|
|
101
112
|
Jets::Commands::Url.new(options).display
|
102
113
|
end
|
103
114
|
|
115
|
+
desc "secret", "Generates secret"
|
116
|
+
long_desc Help.text(:secret)
|
117
|
+
def secret
|
118
|
+
puts SecureRandom.hex(64)
|
119
|
+
end
|
120
|
+
|
121
|
+
desc "middleware", "Prints list of middleware"
|
122
|
+
long_desc Help.text(:middleware)
|
123
|
+
def middleware
|
124
|
+
stack = Jets.application.middlewares
|
125
|
+
stack.middlewares.each do |middleware|
|
126
|
+
puts "use #{middleware.name}"
|
127
|
+
end
|
128
|
+
puts "run #{Jets.application.endpoint}"
|
129
|
+
end
|
130
|
+
|
104
131
|
desc "version", "Prints Jets version"
|
105
132
|
long_desc Help.text(:version)
|
106
133
|
def version
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
|
3
|
+
# This class tries to be idempotent, so users should be able to run it multiple times safely.
|
3
4
|
class Jets::Commands::Upgrade
|
4
5
|
class V1
|
5
6
|
def initialize(options)
|
@@ -11,6 +12,7 @@ class Jets::Commands::Upgrade
|
|
11
12
|
environment_configs
|
12
13
|
update_routes
|
13
14
|
update_mode_setting
|
15
|
+
update_config_ru
|
14
16
|
puts "Upgrade complete."
|
15
17
|
end
|
16
18
|
|
@@ -67,5 +69,15 @@ class Jets::Commands::Upgrade
|
|
67
69
|
content = lines.join
|
68
70
|
IO.write(application_file, content)
|
69
71
|
end
|
72
|
+
|
73
|
+
def update_config_ru
|
74
|
+
config_ru = File.read("#{Jets.root}config.ru")
|
75
|
+
return if config_ru.include?("Jets.boot")
|
76
|
+
|
77
|
+
src = File.expand_path("../templates/skeleton/config.ru", File.dirname(__FILE__))
|
78
|
+
dest = "#{Jets.root}config.ru"
|
79
|
+
puts "Update: config.ru"
|
80
|
+
FileUtils.cp(src, dest)
|
81
|
+
end
|
70
82
|
end
|
71
83
|
end
|
data/lib/jets/controller.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
class Jets::Controller
|
2
|
+
DEFAULT_CONTENT_TYPE = "text/html; charset=utf-8"
|
3
|
+
|
2
4
|
autoload :Base, "jets/controller/base"
|
3
5
|
autoload :Callbacks, "jets/controller/callbacks"
|
6
|
+
autoload :Cookies, "jets/controller/cookies"
|
4
7
|
autoload :Layout, "jets/controller/layout"
|
8
|
+
autoload :Middleware, "jets/controller/middleware"
|
5
9
|
autoload :Params, "jets/controller/params"
|
10
|
+
autoload :Rack, "jets/controller/rack"
|
6
11
|
autoload :Redirection, "jets/controller/redirection"
|
7
12
|
autoload :Renderers, "jets/controller/renderers"
|
8
13
|
autoload :Rendering, "jets/controller/rendering"
|
data/lib/jets/controller/base.rb
CHANGED
@@ -6,38 +6,60 @@ require "rack/utils" # Rack::Utils.parse_nested_query
|
|
6
6
|
# Controller public methods get turned into Lambda functions.
|
7
7
|
class Jets::Controller
|
8
8
|
class Base < Jets::Lambda::Functions
|
9
|
-
include Layout
|
10
9
|
include Callbacks
|
11
|
-
include
|
10
|
+
include Cookies
|
11
|
+
include Layout
|
12
12
|
include Params
|
13
|
+
include Rendering
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
delegate :headers, to: :request
|
16
|
+
delegate :set_header, to: :response
|
17
|
+
attr_reader :request, :response
|
18
|
+
attr_accessor :session
|
19
|
+
def initialize(event, context={}, meth)
|
20
|
+
super
|
21
|
+
@request = Request.new(event, context)
|
22
|
+
@response = Response.new
|
23
|
+
end
|
17
24
|
|
18
|
-
|
25
|
+
def process!
|
26
|
+
adapter = Jets::Controller::Rack::Adapter.new(event, context, meth)
|
27
|
+
adapter.rack_vars(
|
28
|
+
'jets.controller' => self,
|
29
|
+
'lambda.context' => context,
|
30
|
+
'lambda.event' => event,
|
31
|
+
'lambda.meth' => meth,
|
32
|
+
)
|
33
|
+
# adapter.process ultimately calls app controller action at the very last
|
34
|
+
# middleware stack.
|
35
|
+
adapter.process # Returns API Gateway hash structure
|
36
|
+
end
|
19
37
|
|
20
|
-
|
21
|
-
|
38
|
+
def dispatch!
|
39
|
+
t1 = Time.now
|
40
|
+
Jets.logger.info "Processing by #{self.class.name}##{@meth}"
|
41
|
+
Jets.logger.info " Event: #{@event.inspect}"
|
42
|
+
Jets.logger.info " Parameters: #{params(raw: true).to_h.inspect}"
|
22
43
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
44
|
+
run_before_actions
|
45
|
+
send(@meth)
|
46
|
+
triplet = ensure_render
|
47
|
+
run_after_actions
|
27
48
|
|
28
49
|
took = Time.now - t1
|
29
|
-
|
50
|
+
status = triplet[0]
|
51
|
+
Jets.logger.info "Completed Status Code #{status} in #{took}s"
|
30
52
|
|
31
|
-
|
53
|
+
triplet # status, headers, body
|
32
54
|
end
|
33
55
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
56
|
+
def self.process(event, context={}, meth)
|
57
|
+
controller = new(event, context, meth)
|
58
|
+
# Using send because process! is private method in Jets::RackController so
|
59
|
+
# it doesnt create a lambda function. It's doesnt matter what scope process!
|
60
|
+
# is in Controller::Base because Jets lambda functions inheritance doesnt
|
61
|
+
# include methods in Controller::Base.
|
62
|
+
controller.send(:process!)
|
41
63
|
end
|
42
64
|
|
43
65
|
class_attribute :internal_controller
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Based on sinatra/cookies.rb
|
2
|
+
# https://github.com/sinatra/sinatra/blob/master/sinatra-contrib/lib/sinatra/cookies.rb
|
3
|
+
class Jets::Controller
|
4
|
+
# = Jets::Controller::Cookies
|
5
|
+
#
|
6
|
+
# Easy way to deal with cookies
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
#
|
10
|
+
# Allows you to read cookies:
|
11
|
+
#
|
12
|
+
# def index
|
13
|
+
# "value: #{cookies[:something]}"
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# And of course to write cookies:
|
17
|
+
#
|
18
|
+
# def show
|
19
|
+
# cookies[:something] = 'foobar'
|
20
|
+
# render json: cookies
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# And generally behaves like a hash:
|
24
|
+
#
|
25
|
+
# def index
|
26
|
+
# cookies.merge! 'foo' => 'bar', 'bar' => 'baz'
|
27
|
+
# cookies.keep_if { |key, value| key.start_with? 'b' }
|
28
|
+
# foo, bar = cookies.values_at 'foo', 'bar'
|
29
|
+
# puts "size: #{cookies.length}"
|
30
|
+
# render json: cookies
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
module Cookies
|
34
|
+
autoload :Jar, "jets/controller/cookies/jar"
|
35
|
+
|
36
|
+
def cookies
|
37
|
+
@cookies ||= Jar.new(self)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
# Based on sinatra/cookies.rb
|
2
|
+
# https://github.com/sinatra/sinatra/blob/master/sinatra-contrib/lib/sinatra/cookies.rb
|
3
|
+
module Jets::Controller::Cookies
|
4
|
+
class Jar
|
5
|
+
include Enumerable
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(controller)
|
9
|
+
@response_string = nil
|
10
|
+
@response_hash = {}
|
11
|
+
@response = controller.response
|
12
|
+
@request = controller.request
|
13
|
+
@deleted = []
|
14
|
+
|
15
|
+
@options = {
|
16
|
+
path: @request.script_name.to_s.empty? ? '/' : @request.script_name,
|
17
|
+
domain: @request.host == 'localhost' ? nil : @request.host,
|
18
|
+
secure: @request.ssl?,
|
19
|
+
httponly: true
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
other.respond_to? :to_hash and to_hash == other.to_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
response_cookies[key.to_s] || request_cookies[key.to_s]
|
29
|
+
end
|
30
|
+
|
31
|
+
def []=(key, value)
|
32
|
+
set(key, value: value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def assoc(key)
|
36
|
+
to_hash.assoc(key.to_s)
|
37
|
+
end if Hash.method_defined? :assoc
|
38
|
+
|
39
|
+
def clear
|
40
|
+
each_key { |k| delete(k) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def compare_by_identity?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def default
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
alias default_proc default
|
52
|
+
|
53
|
+
def delete(key)
|
54
|
+
result = self[key]
|
55
|
+
@response.delete_cookie(key.to_s, @options)
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete_if
|
60
|
+
return enum_for(__method__) unless block_given?
|
61
|
+
each { |k, v| delete(k) if yield(k, v) }
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def each(&block)
|
66
|
+
return enum_for(__method__) unless block_given?
|
67
|
+
to_hash.each(&block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def each_key(&block)
|
71
|
+
return enum_for(__method__) unless block_given?
|
72
|
+
to_hash.each_key(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
alias each_pair each
|
76
|
+
|
77
|
+
def each_value(&block)
|
78
|
+
return enum_for(__method__) unless block_given?
|
79
|
+
to_hash.each_value(&block)
|
80
|
+
end
|
81
|
+
|
82
|
+
def empty?
|
83
|
+
to_hash.empty?
|
84
|
+
end
|
85
|
+
|
86
|
+
def fetch(key, &block)
|
87
|
+
response_cookies.fetch(key.to_s) do
|
88
|
+
request_cookies.fetch(key.to_s, &block)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def flatten
|
93
|
+
to_hash.flatten
|
94
|
+
end if Hash.method_defined? :flatten
|
95
|
+
|
96
|
+
def has_key?(key)
|
97
|
+
response_cookies.has_key? key.to_s or request_cookies.has_key? key.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def has_value?(value)
|
101
|
+
response_cookies.has_value? value or request_cookies.has_value? value
|
102
|
+
end
|
103
|
+
|
104
|
+
def hash
|
105
|
+
to_hash.hash
|
106
|
+
end
|
107
|
+
|
108
|
+
alias include? has_key?
|
109
|
+
alias member? has_key?
|
110
|
+
|
111
|
+
def index(value)
|
112
|
+
warn "Hash#index is deprecated; use Hash#key"
|
113
|
+
key(value)
|
114
|
+
end
|
115
|
+
|
116
|
+
def inspect
|
117
|
+
"<##{self.class}: #{to_hash.inspect[1..-2]}>"
|
118
|
+
end
|
119
|
+
|
120
|
+
def invert
|
121
|
+
to_hash.invert
|
122
|
+
end if Hash.method_defined? :invert
|
123
|
+
|
124
|
+
def keep_if
|
125
|
+
return enum_for(__method__) unless block_given?
|
126
|
+
delete_if { |*a| not yield(*a) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def key(value)
|
130
|
+
to_hash.key(value)
|
131
|
+
end
|
132
|
+
|
133
|
+
alias key? has_key?
|
134
|
+
|
135
|
+
def keys
|
136
|
+
to_hash.keys
|
137
|
+
end
|
138
|
+
|
139
|
+
def length
|
140
|
+
to_hash.length
|
141
|
+
end
|
142
|
+
|
143
|
+
def merge(other, &block)
|
144
|
+
to_hash.merge(other, &block)
|
145
|
+
end
|
146
|
+
|
147
|
+
def merge!(other)
|
148
|
+
other.each_pair do |key, value|
|
149
|
+
if block_given? and include? key
|
150
|
+
self[key] = yield(key.to_s, self[key], value)
|
151
|
+
else
|
152
|
+
self[key] = value
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def rassoc(value)
|
158
|
+
to_hash.rassoc(value)
|
159
|
+
end
|
160
|
+
|
161
|
+
def rehash
|
162
|
+
response_cookies.rehash
|
163
|
+
request_cookies.rehash
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
def reject(&block)
|
168
|
+
return enum_for(__method__) unless block_given?
|
169
|
+
to_hash.reject(&block)
|
170
|
+
end
|
171
|
+
|
172
|
+
alias reject! delete_if
|
173
|
+
|
174
|
+
def replace(other)
|
175
|
+
select! { |k, v| other.include?(k) or other.include?(k.to_s) }
|
176
|
+
merge! other
|
177
|
+
end
|
178
|
+
|
179
|
+
def select(&block)
|
180
|
+
return enum_for(__method__) unless block_given?
|
181
|
+
to_hash.select(&block)
|
182
|
+
end
|
183
|
+
|
184
|
+
alias select! keep_if if Hash.method_defined? :select!
|
185
|
+
|
186
|
+
def set(key, options = {})
|
187
|
+
@response.set_cookie key.to_s, @options.merge(options)
|
188
|
+
end
|
189
|
+
|
190
|
+
def shift
|
191
|
+
key, value = to_hash.shift
|
192
|
+
delete(key)
|
193
|
+
[key, value]
|
194
|
+
end
|
195
|
+
|
196
|
+
alias size length
|
197
|
+
|
198
|
+
def sort(&block)
|
199
|
+
to_hash.sort(&block)
|
200
|
+
end if Hash.method_defined? :sort
|
201
|
+
|
202
|
+
alias store []=
|
203
|
+
|
204
|
+
def to_hash
|
205
|
+
request_cookies.merge(response_cookies)
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_a
|
209
|
+
to_hash.to_a
|
210
|
+
end
|
211
|
+
|
212
|
+
def to_s
|
213
|
+
to_hash.to_s
|
214
|
+
end
|
215
|
+
|
216
|
+
alias update merge!
|
217
|
+
alias value? has_value?
|
218
|
+
|
219
|
+
def values
|
220
|
+
to_hash.values
|
221
|
+
end
|
222
|
+
|
223
|
+
def values_at(*list)
|
224
|
+
list.map { |k| self[k] }
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def warn(message)
|
230
|
+
super "#{caller.first[/^[^:]:\d+:/]} warning: #{message}"
|
231
|
+
end
|
232
|
+
|
233
|
+
def deleted
|
234
|
+
parse_response
|
235
|
+
@deleted
|
236
|
+
end
|
237
|
+
|
238
|
+
def response_cookies
|
239
|
+
parse_response
|
240
|
+
@response_hash
|
241
|
+
end
|
242
|
+
|
243
|
+
def parse_response
|
244
|
+
string = @response.headers['Set-Cookie']
|
245
|
+
return if @response_string == string
|
246
|
+
|
247
|
+
hash = {}
|
248
|
+
|
249
|
+
string.each_line do |line|
|
250
|
+
key, value = line.split(';', 2).first.to_s.split('=', 2)
|
251
|
+
next if key.nil?
|
252
|
+
key = Rack::Utils.unescape(key)
|
253
|
+
if line =~ /expires=Thu, 01[-\s]Jan[-\s]1970/
|
254
|
+
@deleted << key
|
255
|
+
else
|
256
|
+
@deleted.delete key
|
257
|
+
hash[key] = value
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
@response_hash.replace hash
|
262
|
+
@response_string = string
|
263
|
+
end
|
264
|
+
|
265
|
+
def request_cookies
|
266
|
+
@request.cookies.reject { |key, value| deleted.include? key }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|