pakyow-core 0.11.3 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +0 -0
- data/LICENSE +4 -0
- data/{pakyow-core/README.md → README.md} +3 -4
- data/commands/pakyow +13 -0
- data/lib/pakyow/app/connection/behavior/session.rb +33 -0
- data/lib/pakyow/app/connection/behavior/values.rb +42 -0
- data/lib/pakyow/app/connection/behavior/verifier.rb +34 -0
- data/lib/pakyow/app/connection/session/abstract.rb +24 -0
- data/lib/pakyow/app/connection/session/cookie.rb +56 -0
- data/lib/pakyow/app/connection.rb +57 -0
- data/lib/pakyow/app.rb +219 -0
- data/lib/pakyow/behavior/aspects.rb +45 -0
- data/lib/pakyow/behavior/config.rb +72 -0
- data/lib/pakyow/behavior/endpoints.rb +41 -0
- data/lib/pakyow/behavior/frameworks.rb +45 -0
- data/lib/pakyow/behavior/helpers.rb +107 -0
- data/lib/pakyow/behavior/initializers.rb +19 -0
- data/lib/pakyow/behavior/isolating.rb +69 -0
- data/lib/pakyow/behavior/operations.rb +46 -0
- data/lib/pakyow/behavior/pipeline.rb +58 -0
- data/lib/pakyow/behavior/plugins.rb +148 -0
- data/lib/pakyow/behavior/rescuing.rb +51 -0
- data/lib/pakyow/behavior/restarting.rb +68 -0
- data/lib/pakyow/behavior/sessions.rb +34 -0
- data/lib/pakyow/behavior/verification.rb +47 -0
- data/lib/pakyow/cli.rb +199 -0
- data/lib/pakyow/connection/multipart_input.rb +24 -0
- data/lib/pakyow/connection/multipart_parser.rb +112 -0
- data/lib/pakyow/connection/params.rb +29 -0
- data/lib/pakyow/connection/query_parser.rb +174 -0
- data/lib/pakyow/connection/statuses.rb +119 -0
- data/lib/pakyow/connection.rb +568 -0
- data/lib/pakyow/endpoints.rb +94 -0
- data/lib/pakyow/environment/actions/dispatch.rb +37 -0
- data/lib/pakyow/environment/actions/input_parser.rb +21 -0
- data/lib/pakyow/environment/actions/logger.rb +33 -0
- data/lib/pakyow/environment/actions/normalizer.rb +73 -0
- data/lib/pakyow/environment/behavior/config.rb +166 -0
- data/lib/pakyow/environment/behavior/initializers.rb +21 -0
- data/lib/pakyow/environment/behavior/input_parsing.rb +54 -0
- data/lib/pakyow/environment/behavior/plugins.rb +23 -0
- data/lib/pakyow/environment/behavior/restarting.rb +54 -0
- data/lib/pakyow/environment/behavior/running.rb +129 -0
- data/lib/pakyow/environment/behavior/silencing.rb +23 -0
- data/lib/pakyow/environment/behavior/timezone.rb +21 -0
- data/lib/pakyow/environment/behavior/watching.rb +75 -0
- data/lib/pakyow/environment.rb +381 -0
- data/lib/pakyow/error.rb +273 -0
- data/lib/pakyow/errors.rb +97 -0
- data/lib/pakyow/framework.rb +39 -0
- data/lib/pakyow/generator.rb +138 -0
- data/lib/pakyow/generators/project/default/%dot%env.erb +1 -0
- data/lib/pakyow/generators/project/default/%dot%gitignore +11 -0
- data/lib/pakyow/generators/project/default/%dot%ruby-version.erb +1 -0
- data/lib/pakyow/generators/project/default/Gemfile.erb +28 -0
- data/lib/pakyow/generators/project/default/README.md +14 -0
- data/lib/pakyow/generators/project/default/config/application.rb.erb +17 -0
- data/lib/pakyow/generators/project/default/config/environment.rb +11 -0
- data/lib/pakyow/generators/project/default/config/initializers/application/keep +0 -0
- data/lib/pakyow/generators/project/default/config/initializers/environment/keep +0 -0
- data/lib/pakyow/generators/project/default/database/keep +0 -0
- data/lib/pakyow/generators/project/default/frontend/assets/images/keep +0 -0
- data/lib/pakyow/generators/project/default/frontend/assets/packs/keep +0 -0
- data/lib/pakyow/generators/project/default/frontend/assets/styles/keep +0 -0
- data/lib/pakyow/generators/project/default/frontend/includes/keep +0 -0
- data/lib/pakyow/generators/project/default/frontend/layouts/default.html.erb +19 -0
- data/lib/pakyow/generators/project/default/frontend/pages/keep +0 -0
- data/lib/pakyow/generators/project/default/public/favicon.ico +0 -0
- data/lib/pakyow/generators/project/default/public/robots.txt +1 -0
- data/lib/pakyow/generators/project.rb +29 -0
- data/lib/pakyow/helpers/app.rb +28 -0
- data/lib/pakyow/helpers/connection.rb +45 -0
- data/lib/pakyow/info.rb +26 -0
- data/lib/pakyow/integrations/bootsnap.rb +18 -0
- data/lib/pakyow/integrations/bundler/require.rb +7 -0
- data/lib/pakyow/integrations/bundler/setup.rb +7 -0
- data/lib/pakyow/integrations/dotenv.rb +9 -0
- data/lib/pakyow/loader.rb +31 -0
- data/lib/pakyow/logger/colorizer.rb +38 -0
- data/lib/pakyow/logger/destination.rb +17 -0
- data/lib/pakyow/logger/formatter.rb +22 -0
- data/lib/pakyow/logger/formatters/human.rb +101 -0
- data/lib/pakyow/logger/formatters/json.rb +99 -0
- data/lib/pakyow/logger/formatters/logfmt.rb +50 -0
- data/lib/pakyow/logger/multiplexed.rb +19 -0
- data/lib/pakyow/logger/thread_local.rb +29 -0
- data/lib/pakyow/logger/timekeeper.rb +64 -0
- data/lib/pakyow/logger.rb +125 -0
- data/lib/pakyow/operation.rb +71 -0
- data/lib/pakyow/plugin/helper_caller.rb +20 -0
- data/lib/pakyow/plugin/lookup.rb +33 -0
- data/lib/pakyow/plugin/state.rb +68 -0
- data/lib/pakyow/plugin.rb +367 -0
- data/lib/pakyow/process_manager.rb +53 -0
- data/lib/pakyow/processes/proxy.rb +67 -0
- data/lib/pakyow/processes/server.rb +32 -0
- data/lib/pakyow/rack/compatibility.rb +68 -0
- data/lib/pakyow/task.rb +315 -0
- data/lib/pakyow/tasks/boot.rake +21 -0
- data/lib/pakyow/tasks/create.rake +32 -0
- data/lib/pakyow/tasks/help.rake +17 -0
- data/lib/pakyow/tasks/info/endpoints.rake +72 -0
- data/lib/pakyow/tasks/info.rake +31 -0
- data/lib/pakyow/tasks/irb.rake +11 -0
- data/lib/pakyow/tasks/prelaunch.rake +37 -0
- data/lib/pakyow/types.rb +32 -0
- data/lib/pakyow/validations/acceptance.rb +37 -0
- data/lib/pakyow/validations/email.rb +28 -0
- data/lib/pakyow/validations/inline.rb +32 -0
- data/lib/pakyow/validations/length.rb +44 -0
- data/lib/pakyow/validations/presence.rb +41 -0
- data/lib/pakyow/validations.rb +8 -0
- data/lib/pakyow/validator.rb +81 -0
- data/lib/pakyow/verifier.rb +177 -0
- data/lib/pakyow.rb +5 -0
- metadata +267 -65
- data/pakyow-core/CHANGELOG.md +0 -128
- data/pakyow-core/LICENSE +0 -20
- data/pakyow-core/lib/pakyow/core/app.rb +0 -82
- data/pakyow-core/lib/pakyow/core/app_context.rb +0 -10
- data/pakyow-core/lib/pakyow/core/base.rb +0 -61
- data/pakyow-core/lib/pakyow/core/call_context.rb +0 -171
- data/pakyow-core/lib/pakyow/core/config/app.rb +0 -49
- data/pakyow-core/lib/pakyow/core/config/cookies.rb +0 -4
- data/pakyow-core/lib/pakyow/core/config/logger.rb +0 -34
- data/pakyow-core/lib/pakyow/core/config/reloader.rb +0 -10
- data/pakyow-core/lib/pakyow/core/config/server.rb +0 -10
- data/pakyow-core/lib/pakyow/core/config/session.rb +0 -41
- data/pakyow-core/lib/pakyow/core/config.rb +0 -95
- data/pakyow-core/lib/pakyow/core/errors.rb +0 -16
- data/pakyow-core/lib/pakyow/core/helpers/configuring.rb +0 -142
- data/pakyow-core/lib/pakyow/core/helpers/hooks.rb +0 -106
- data/pakyow-core/lib/pakyow/core/helpers/running.rb +0 -124
- data/pakyow-core/lib/pakyow/core/helpers.rb +0 -61
- data/pakyow-core/lib/pakyow/core/loader.rb +0 -32
- data/pakyow-core/lib/pakyow/core/middleware/logger.rb +0 -146
- data/pakyow-core/lib/pakyow/core/middleware/override.rb +0 -3
- data/pakyow-core/lib/pakyow/core/middleware/reloader.rb +0 -23
- data/pakyow-core/lib/pakyow/core/middleware/req_path_normalizer.rb +0 -49
- data/pakyow-core/lib/pakyow/core/middleware/session.rb +0 -5
- data/pakyow-core/lib/pakyow/core/middleware/static.rb +0 -76
- data/pakyow-core/lib/pakyow/core/multilog.rb +0 -19
- data/pakyow-core/lib/pakyow/core/request.rb +0 -119
- data/pakyow-core/lib/pakyow/core/response.rb +0 -135
- data/pakyow-core/lib/pakyow/core/route_eval.rb +0 -254
- data/pakyow-core/lib/pakyow/core/route_expansion_eval.rb +0 -115
- data/pakyow-core/lib/pakyow/core/route_lookup.rb +0 -41
- data/pakyow-core/lib/pakyow/core/route_merger.rb +0 -76
- data/pakyow-core/lib/pakyow/core/route_module.rb +0 -21
- data/pakyow-core/lib/pakyow/core/route_set.rb +0 -74
- data/pakyow-core/lib/pakyow/core/route_template_defaults.rb +0 -43
- data/pakyow-core/lib/pakyow/core/route_template_eval.rb +0 -72
- data/pakyow-core/lib/pakyow/core/router.rb +0 -181
- data/pakyow-core/lib/pakyow/core.rb +0 -8
- data/pakyow-core/lib/pakyow-core.rb +0 -1
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
class Logger
|
5
|
+
class Formatter
|
6
|
+
attr_reader :output
|
7
|
+
|
8
|
+
def initialize(output)
|
9
|
+
@output = output
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(event, **options)
|
13
|
+
event = yield if block_given?
|
14
|
+
format(event, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def verbose!(value)
|
18
|
+
@verbose = value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/connection"
|
4
|
+
require "pakyow/error"
|
5
|
+
|
6
|
+
require "pakyow/logger/colorizer"
|
7
|
+
require "pakyow/logger/formatter"
|
8
|
+
require "pakyow/logger/timekeeper"
|
9
|
+
|
10
|
+
require "pakyow/connection/statuses"
|
11
|
+
|
12
|
+
module Pakyow
|
13
|
+
class Logger
|
14
|
+
module Formatters
|
15
|
+
# Formats log messages for humans.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# 19.00μs http.c730cb72 | GET / (for 127.0.0.1 at 2016-06-20 10:00:49 -0500)
|
19
|
+
# 1.97ms http.c730cb72 | hello 2016-06-20 10:00:49 -0500
|
20
|
+
# 3.78ms http.c730cb72 | 200 (OK)
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
class Human < Formatter
|
24
|
+
private
|
25
|
+
|
26
|
+
def format(event, **options)
|
27
|
+
entry = String.new
|
28
|
+
|
29
|
+
case event
|
30
|
+
when Hash
|
31
|
+
if event.key?("logger") && event.key?("message")
|
32
|
+
format_logger_message(event, entry)
|
33
|
+
else
|
34
|
+
entry << event.to_s
|
35
|
+
end
|
36
|
+
else
|
37
|
+
entry << event.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
@output.call(Colorizer.colorize(entry, options[:severity]) << "\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_logger_message(logger_message, entry)
|
44
|
+
logger = logger_message["logger"]
|
45
|
+
message = logger_message["message"]
|
46
|
+
|
47
|
+
format_info(entry, id: logger.id, type: logger.type, elapsed: logger.elapsed)
|
48
|
+
|
49
|
+
case message
|
50
|
+
when Hash
|
51
|
+
if connection = message["prologue"]
|
52
|
+
format_prologue(connection, entry)
|
53
|
+
elsif connection = message["epilogue"]
|
54
|
+
format_epilogue(connection, entry)
|
55
|
+
elsif error = message["error"]
|
56
|
+
format_error(error, entry)
|
57
|
+
else
|
58
|
+
format_message(message, entry)
|
59
|
+
end
|
60
|
+
when Exception
|
61
|
+
format_error(message, entry)
|
62
|
+
else
|
63
|
+
format_message(message, entry)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_prologue(connection, entry)
|
68
|
+
entry << connection.request_method << " " << connection.path
|
69
|
+
entry << " (for " << connection.ip << " at " << connection.timestamp.to_s << ")"
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_epilogue(connection, entry)
|
73
|
+
entry << connection.status.to_s << " (" << Connection::Statuses.describe(connection.status) << ")"
|
74
|
+
end
|
75
|
+
|
76
|
+
def format_info(entry, id:, type:, elapsed:)
|
77
|
+
entry << Timekeeper.format_elapsed_time(elapsed).rjust(8, " ")
|
78
|
+
entry << " " << type.to_s << "." << id << " | "
|
79
|
+
end
|
80
|
+
|
81
|
+
def format_message(message, entry)
|
82
|
+
message.to_s.each_line.with_index do |line, i|
|
83
|
+
if i == 0
|
84
|
+
entry << line.rstrip
|
85
|
+
else
|
86
|
+
entry << "\n | " << line.rstrip
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def format_error(error, entry)
|
92
|
+
unless error.is_a?(Error)
|
93
|
+
error = Error.build(error)
|
94
|
+
end
|
95
|
+
|
96
|
+
format_message(Error::CLIFormatter.new(error), entry)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
require "pakyow/logger"
|
6
|
+
require "pakyow/logger/timekeeper"
|
7
|
+
|
8
|
+
module Pakyow
|
9
|
+
class Logger
|
10
|
+
module Formatters
|
11
|
+
# Formats log messages as json.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# {"severity":"info","timestamp":"2016-06-20 10:07:30 -0500","id":"c8af6a8b","type":"http","elapsed":"0.01ms","method":"GET","path":"/","ip":"127.0.0.1"}
|
15
|
+
# {"severity":"info","timestamp":"2016-06-20 10:07:30 -0500","id":"c8af6a8b","type":"http","elapsed":"1.24ms","message":"hello 2016-06-20 10:07:30 -0500"}
|
16
|
+
# {"severity":"info","timestamp":"2016-06-20 10:07:30 -0500","id":"c8af6a8b","type":"http","elapsed":"3.08ms","status":200}
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
class JSON < Formatter
|
20
|
+
private
|
21
|
+
|
22
|
+
def format(event, **options)
|
23
|
+
entry = {
|
24
|
+
"severity" => options[:severity],
|
25
|
+
"timestamp" => Time.now
|
26
|
+
}
|
27
|
+
|
28
|
+
case event
|
29
|
+
when Hash
|
30
|
+
if event.key?("logger") && event.key?("message")
|
31
|
+
format_logger_message(event, entry)
|
32
|
+
else
|
33
|
+
entry.merge!(event)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
entry["message"] = event.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
serialize(entry)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def format_logger_message(logger_message, entry)
|
45
|
+
logger = logger_message["logger"]
|
46
|
+
message = logger_message["message"]
|
47
|
+
|
48
|
+
format_entry(
|
49
|
+
entry, id: logger.id, type: logger.type, elapsed: logger.elapsed
|
50
|
+
)
|
51
|
+
|
52
|
+
case message
|
53
|
+
when Hash
|
54
|
+
if connection = message.delete("prologue")
|
55
|
+
format_prologue(connection, entry)
|
56
|
+
elsif connection = message.delete("epilogue")
|
57
|
+
format_epilogue(connection, entry)
|
58
|
+
elsif error = message.delete("error")
|
59
|
+
format_error(error, entry)
|
60
|
+
else
|
61
|
+
entry.update(message)
|
62
|
+
end
|
63
|
+
when Exception
|
64
|
+
format_error(message, entry)
|
65
|
+
else
|
66
|
+
entry["message"] = message.to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_prologue(connection, entry)
|
71
|
+
entry["method"] = connection.request_method
|
72
|
+
entry["uri"] = connection.path
|
73
|
+
entry["ip"] = connection.ip
|
74
|
+
end
|
75
|
+
|
76
|
+
def format_epilogue(connection, entry)
|
77
|
+
entry["status"] = connection.status
|
78
|
+
end
|
79
|
+
|
80
|
+
def format_error(error, entry)
|
81
|
+
entry["exception"] = error.class
|
82
|
+
entry["message"] = error.to_s
|
83
|
+
entry["backtrace"] = error.backtrace
|
84
|
+
end
|
85
|
+
|
86
|
+
def format_entry(entry, id:, type:, elapsed:)
|
87
|
+
entry["id"] = id
|
88
|
+
entry["type"] = type
|
89
|
+
entry["elapsed"] = Timekeeper.format_elapsed_time_in_milliseconds(elapsed)
|
90
|
+
entry
|
91
|
+
end
|
92
|
+
|
93
|
+
def serialize(entry)
|
94
|
+
@output.call(entry.to_json << "\n")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/logger/formatters/json"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
class Logger
|
7
|
+
module Formatters
|
8
|
+
# Formats log messages as logfmt.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# severity=INFO timestamp="2016-06-20 10:08:29 -0500" id=678cf582 type=http elapsed=0.01ms method=GET uri=/ ip=127.0.0.1
|
12
|
+
# severity=INFO timestamp="2016-06-20 10:08:29 -0500" id=678cf582 type=http elapsed=1.56ms message="hello 2016-06-20 10:08:29 -0500"
|
13
|
+
# severity=INFO timestamp="2016-06-20 10:08:29 -0500" id=678cf582 type=http elapsed=3.37ms status=200
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
class Logfmt < Pakyow::Logger::Formatters::JSON
|
17
|
+
private
|
18
|
+
|
19
|
+
UNESCAPED_STRING = /\A[\w\.\-\+\%\,\:\;\/]*\z/i
|
20
|
+
|
21
|
+
def serialize(message)
|
22
|
+
first = true
|
23
|
+
message.each_pair do |key, value|
|
24
|
+
value = case value
|
25
|
+
when Array
|
26
|
+
value.join(",")
|
27
|
+
else
|
28
|
+
value.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
unless value.match?(UNESCAPED_STRING)
|
32
|
+
value = value.dump
|
33
|
+
end
|
34
|
+
|
35
|
+
unless first
|
36
|
+
@output.call(" ")
|
37
|
+
end
|
38
|
+
|
39
|
+
@output.call(key)
|
40
|
+
@output.call("=")
|
41
|
+
@output.call(value)
|
42
|
+
first = false
|
43
|
+
end
|
44
|
+
|
45
|
+
@output.call("\n")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
class Logger
|
5
|
+
class Multiplexed
|
6
|
+
attr_reader :destinations
|
7
|
+
|
8
|
+
def initialize(*destinations)
|
9
|
+
@destinations = destinations
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(entry)
|
13
|
+
@destinations.each do |destination|
|
14
|
+
destination.call(entry)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
require "pakyow/support/deep_freeze"
|
6
|
+
|
7
|
+
require "pakyow/logger"
|
8
|
+
|
9
|
+
module Pakyow
|
10
|
+
class Logger
|
11
|
+
# Determines at log time what logger to use, based on a thread-local context.
|
12
|
+
#
|
13
|
+
class ThreadLocal
|
14
|
+
extend Support::DeepFreeze
|
15
|
+
unfreezable :default
|
16
|
+
|
17
|
+
extend Forwardable
|
18
|
+
def_delegators :target, *(Logger.instance_methods - Object.instance_methods)
|
19
|
+
|
20
|
+
def initialize(default_logger)
|
21
|
+
@default = default_logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def target
|
25
|
+
Thread.current[:pakyow_logger] || @default
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
class Logger
|
5
|
+
# Helpers for formatting elapsed time in logs.
|
6
|
+
#
|
7
|
+
module Timekeeper
|
8
|
+
# Accepts elapsed time and formats it to be more human-readable.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Pakyow::Logger::Timekeeper.format_elapsed_time(60)
|
12
|
+
# => 1.00m
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# Pakyow::Logger::Timekeeper.format_elapsed_time(15)
|
16
|
+
# => 15.00s
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# Pakyow::Logger::Timekeeper.format_elapsed_time(0.1)
|
20
|
+
# => 100.00ms
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# Pakyow::Logger::Timekeeper.format_elapsed_time(0.00001)
|
24
|
+
# => 10.00μs
|
25
|
+
#
|
26
|
+
# @param time [Fixnum, Float] the elapsed time (in seconds)
|
27
|
+
#
|
28
|
+
# @return [String] elapsed time, rounded to two decimal places
|
29
|
+
# with the proper units
|
30
|
+
#
|
31
|
+
def self.format_elapsed_time(time)
|
32
|
+
if time >= 60
|
33
|
+
format_elapsed_time_in_minutes(time)
|
34
|
+
elsif time >= 1
|
35
|
+
format_elapsed_time_in_seconds(time)
|
36
|
+
elsif time >= 0.001
|
37
|
+
format_elapsed_time_in_milliseconds(time)
|
38
|
+
else
|
39
|
+
format_elapsed_time_in_microseconds(time)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.format_elapsed_time_in_minutes(time)
|
44
|
+
round_elapsed_time(time / 60).to_s << "m "
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.format_elapsed_time_in_seconds(time)
|
48
|
+
round_elapsed_time(time).to_s << "s "
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.format_elapsed_time_in_milliseconds(time)
|
52
|
+
round_elapsed_time(time * 1_000).to_s << "ms"
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.format_elapsed_time_in_microseconds(time)
|
56
|
+
round_elapsed_time(time * 1_000_000).to_s << "μs"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.round_elapsed_time(time)
|
60
|
+
"%.2f" % time
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
require "console/filter"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
# Logs messages throughout the lifetime of an environment, connection, etc.
|
9
|
+
#
|
10
|
+
# In addition to logging standard messages, this class provides a way to log a `prologue` and
|
11
|
+
# `epilogue` for a connection, as well as a `houston` method for logging errors.
|
12
|
+
#
|
13
|
+
class Logger < Console::Filter[internal: 0, debug: 1, info: 2, warn: 3, error: 4, fatal: 5, unknown: 6]
|
14
|
+
require "pakyow/logger/colorizer"
|
15
|
+
require "pakyow/logger/timekeeper"
|
16
|
+
|
17
|
+
# @!attribute [r] id
|
18
|
+
# @return [String] the unique id of the logger instance
|
19
|
+
attr_reader :id
|
20
|
+
|
21
|
+
# @!attribute [r] started_at
|
22
|
+
# @return [Time] the time when logging started
|
23
|
+
attr_reader :started_at
|
24
|
+
|
25
|
+
# @!attribute [r] type
|
26
|
+
# @return [Symbol] the type of logger
|
27
|
+
attr_reader :type
|
28
|
+
|
29
|
+
# @param type [Symbol] the type of logging being done (e.g. :http, :sock)
|
30
|
+
# @param started_at [Time] when the logging began
|
31
|
+
# @param output [Object] the object that will perform the logging
|
32
|
+
# @param id [String] a unique id used to identify the request
|
33
|
+
def initialize(type, started_at: Time.now, id: SecureRandom.hex(4), output:, level:)
|
34
|
+
@type, @started_at, @id = type, started_at, id
|
35
|
+
|
36
|
+
level = case level
|
37
|
+
when :all
|
38
|
+
0
|
39
|
+
when :off
|
40
|
+
7
|
41
|
+
when Symbol
|
42
|
+
self.class.const_get(:LEVELS)[level]
|
43
|
+
else
|
44
|
+
level
|
45
|
+
end
|
46
|
+
|
47
|
+
super(output, level: level)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Temporarily silences logs, up to +temporary_level+.
|
51
|
+
#
|
52
|
+
def silence(temporary_level = :error)
|
53
|
+
original_level = @level
|
54
|
+
self.level = self.class.const_get(:LEVELS)[temporary_level]
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
self.level = original_level
|
58
|
+
end
|
59
|
+
|
60
|
+
LEVELS.keys.each do |method|
|
61
|
+
class_eval <<~CODE, __FILE__, __LINE__ + 1
|
62
|
+
def #{method}(message = nil, &block)
|
63
|
+
super(message) { decorate(message, &block) }
|
64
|
+
end
|
65
|
+
CODE
|
66
|
+
end
|
67
|
+
|
68
|
+
def <<(message)
|
69
|
+
add(:unknown, message)
|
70
|
+
end
|
71
|
+
|
72
|
+
def add(level, message = nil, &block)
|
73
|
+
public_send(level, message, &block)
|
74
|
+
end
|
75
|
+
alias log add
|
76
|
+
|
77
|
+
# Logs the beginning of a request, including the time, request method,
|
78
|
+
# request uri, and originating ip address.
|
79
|
+
#
|
80
|
+
# @param env [Hash] the rack env for the request
|
81
|
+
#
|
82
|
+
def prologue(connection)
|
83
|
+
info { formatted_prologue(connection) }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Logs the conclusion of a request, including the response status.
|
87
|
+
#
|
88
|
+
# @param res [Array] the rack response array
|
89
|
+
#
|
90
|
+
def epilogue(connection)
|
91
|
+
info { formatted_epilogue(connection) }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Logs an error raised when processing the request.
|
95
|
+
#
|
96
|
+
# @param error [Object] the error object
|
97
|
+
#
|
98
|
+
def houston(error)
|
99
|
+
error { formatted_error(error) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def elapsed
|
103
|
+
(Time.now - @started_at)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def decorate(message = nil)
|
109
|
+
message = yield if block_given?
|
110
|
+
{ "logger" => self, "message" => message }
|
111
|
+
end
|
112
|
+
|
113
|
+
def formatted_prologue(connection)
|
114
|
+
{ "prologue" => connection }
|
115
|
+
end
|
116
|
+
|
117
|
+
def formatted_epilogue(connection)
|
118
|
+
{ "epilogue" => connection }
|
119
|
+
end
|
120
|
+
|
121
|
+
def formatted_error(error)
|
122
|
+
{ "error" => error }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/class_state"
|
4
|
+
require "pakyow/support/makeable"
|
5
|
+
require "pakyow/support/pipeline"
|
6
|
+
require "pakyow/support/pipeline/object"
|
7
|
+
|
8
|
+
require "pakyow/behavior/verification"
|
9
|
+
|
10
|
+
module Pakyow
|
11
|
+
class Operation
|
12
|
+
extend Support::ClassState
|
13
|
+
class_state :__handlers, default: {}, inheritable: true
|
14
|
+
|
15
|
+
extend Support::Makeable
|
16
|
+
|
17
|
+
include Support::Pipeline
|
18
|
+
include Support::Pipeline::Object
|
19
|
+
|
20
|
+
attr_reader :app, :values
|
21
|
+
|
22
|
+
include Behavior::Verification
|
23
|
+
verifies :values
|
24
|
+
|
25
|
+
def initialize(app:, **values)
|
26
|
+
@app, @values = app, values
|
27
|
+
end
|
28
|
+
|
29
|
+
def perform
|
30
|
+
call(self)
|
31
|
+
rescue => error
|
32
|
+
if handler = self.class.__handlers[error.class] || self.class.__handlers[:global]
|
33
|
+
instance_exec(&handler); self
|
34
|
+
else
|
35
|
+
raise error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(name, *args, &block)
|
40
|
+
name = name.to_sym
|
41
|
+
if @values.key?(name)
|
42
|
+
@values[name.to_sym]
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to_missing?(name, include_private = false)
|
49
|
+
@values.key?(name.to_sym) || super
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
# Perform input verification before performing the operation
|
54
|
+
#
|
55
|
+
# @see Pakyow::Verifier
|
56
|
+
#
|
57
|
+
# @api public
|
58
|
+
def verify(&block)
|
59
|
+
define_method :__verify do
|
60
|
+
verify(&block)
|
61
|
+
end
|
62
|
+
|
63
|
+
action :__verify
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle(error = nil, &block)
|
67
|
+
@__handlers[error || :global] = block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
class Plugin
|
5
|
+
# @api private
|
6
|
+
class HelperCaller
|
7
|
+
def initialize(connection:, helpers:, plugin:)
|
8
|
+
@connection, @helpers, @plugin = connection, helpers, plugin
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method_name, *args, &block)
|
12
|
+
@helpers.public_send(method_name, *args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond_to_missing?(method_name, include_private = false)
|
16
|
+
@helpers.respond_to?(method_name) || super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
class Plugin
|
7
|
+
# @api private
|
8
|
+
class Lookup
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
def_delegators :@plugs, :each, :<<
|
13
|
+
|
14
|
+
def initialize(plugs)
|
15
|
+
@plugs = plugs
|
16
|
+
end
|
17
|
+
|
18
|
+
def finalize
|
19
|
+
@plugs.map { |plug|
|
20
|
+
plug.class.plugin_name
|
21
|
+
}.uniq.each do |plugin_name|
|
22
|
+
define_singleton_method plugin_name do |plugin_instance_name = :default|
|
23
|
+
plugin_instance_name = plugin_instance_name.to_sym
|
24
|
+
|
25
|
+
@plugs.find { |plug|
|
26
|
+
plug.class.plugin_name == plugin_name && plug.__object_name.namespace.parts.last == plugin_instance_name
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|