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.
Files changed (156) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +0 -0
  3. data/LICENSE +4 -0
  4. data/{pakyow-core/README.md → README.md} +3 -4
  5. data/commands/pakyow +13 -0
  6. data/lib/pakyow/app/connection/behavior/session.rb +33 -0
  7. data/lib/pakyow/app/connection/behavior/values.rb +42 -0
  8. data/lib/pakyow/app/connection/behavior/verifier.rb +34 -0
  9. data/lib/pakyow/app/connection/session/abstract.rb +24 -0
  10. data/lib/pakyow/app/connection/session/cookie.rb +56 -0
  11. data/lib/pakyow/app/connection.rb +57 -0
  12. data/lib/pakyow/app.rb +219 -0
  13. data/lib/pakyow/behavior/aspects.rb +45 -0
  14. data/lib/pakyow/behavior/config.rb +72 -0
  15. data/lib/pakyow/behavior/endpoints.rb +41 -0
  16. data/lib/pakyow/behavior/frameworks.rb +45 -0
  17. data/lib/pakyow/behavior/helpers.rb +107 -0
  18. data/lib/pakyow/behavior/initializers.rb +19 -0
  19. data/lib/pakyow/behavior/isolating.rb +69 -0
  20. data/lib/pakyow/behavior/operations.rb +46 -0
  21. data/lib/pakyow/behavior/pipeline.rb +58 -0
  22. data/lib/pakyow/behavior/plugins.rb +148 -0
  23. data/lib/pakyow/behavior/rescuing.rb +51 -0
  24. data/lib/pakyow/behavior/restarting.rb +68 -0
  25. data/lib/pakyow/behavior/sessions.rb +34 -0
  26. data/lib/pakyow/behavior/verification.rb +47 -0
  27. data/lib/pakyow/cli.rb +199 -0
  28. data/lib/pakyow/connection/multipart_input.rb +24 -0
  29. data/lib/pakyow/connection/multipart_parser.rb +112 -0
  30. data/lib/pakyow/connection/params.rb +29 -0
  31. data/lib/pakyow/connection/query_parser.rb +174 -0
  32. data/lib/pakyow/connection/statuses.rb +119 -0
  33. data/lib/pakyow/connection.rb +568 -0
  34. data/lib/pakyow/endpoints.rb +94 -0
  35. data/lib/pakyow/environment/actions/dispatch.rb +37 -0
  36. data/lib/pakyow/environment/actions/input_parser.rb +21 -0
  37. data/lib/pakyow/environment/actions/logger.rb +33 -0
  38. data/lib/pakyow/environment/actions/normalizer.rb +73 -0
  39. data/lib/pakyow/environment/behavior/config.rb +166 -0
  40. data/lib/pakyow/environment/behavior/initializers.rb +21 -0
  41. data/lib/pakyow/environment/behavior/input_parsing.rb +54 -0
  42. data/lib/pakyow/environment/behavior/plugins.rb +23 -0
  43. data/lib/pakyow/environment/behavior/restarting.rb +54 -0
  44. data/lib/pakyow/environment/behavior/running.rb +129 -0
  45. data/lib/pakyow/environment/behavior/silencing.rb +23 -0
  46. data/lib/pakyow/environment/behavior/timezone.rb +21 -0
  47. data/lib/pakyow/environment/behavior/watching.rb +75 -0
  48. data/lib/pakyow/environment.rb +381 -0
  49. data/lib/pakyow/error.rb +273 -0
  50. data/lib/pakyow/errors.rb +97 -0
  51. data/lib/pakyow/framework.rb +39 -0
  52. data/lib/pakyow/generator.rb +138 -0
  53. data/lib/pakyow/generators/project/default/%dot%env.erb +1 -0
  54. data/lib/pakyow/generators/project/default/%dot%gitignore +11 -0
  55. data/lib/pakyow/generators/project/default/%dot%ruby-version.erb +1 -0
  56. data/lib/pakyow/generators/project/default/Gemfile.erb +28 -0
  57. data/lib/pakyow/generators/project/default/README.md +14 -0
  58. data/lib/pakyow/generators/project/default/config/application.rb.erb +17 -0
  59. data/lib/pakyow/generators/project/default/config/environment.rb +11 -0
  60. data/lib/pakyow/generators/project/default/config/initializers/application/keep +0 -0
  61. data/lib/pakyow/generators/project/default/config/initializers/environment/keep +0 -0
  62. data/lib/pakyow/generators/project/default/database/keep +0 -0
  63. data/lib/pakyow/generators/project/default/frontend/assets/images/keep +0 -0
  64. data/lib/pakyow/generators/project/default/frontend/assets/packs/keep +0 -0
  65. data/lib/pakyow/generators/project/default/frontend/assets/styles/keep +0 -0
  66. data/lib/pakyow/generators/project/default/frontend/includes/keep +0 -0
  67. data/lib/pakyow/generators/project/default/frontend/layouts/default.html.erb +19 -0
  68. data/lib/pakyow/generators/project/default/frontend/pages/keep +0 -0
  69. data/lib/pakyow/generators/project/default/public/favicon.ico +0 -0
  70. data/lib/pakyow/generators/project/default/public/robots.txt +1 -0
  71. data/lib/pakyow/generators/project.rb +29 -0
  72. data/lib/pakyow/helpers/app.rb +28 -0
  73. data/lib/pakyow/helpers/connection.rb +45 -0
  74. data/lib/pakyow/info.rb +26 -0
  75. data/lib/pakyow/integrations/bootsnap.rb +18 -0
  76. data/lib/pakyow/integrations/bundler/require.rb +7 -0
  77. data/lib/pakyow/integrations/bundler/setup.rb +7 -0
  78. data/lib/pakyow/integrations/dotenv.rb +9 -0
  79. data/lib/pakyow/loader.rb +31 -0
  80. data/lib/pakyow/logger/colorizer.rb +38 -0
  81. data/lib/pakyow/logger/destination.rb +17 -0
  82. data/lib/pakyow/logger/formatter.rb +22 -0
  83. data/lib/pakyow/logger/formatters/human.rb +101 -0
  84. data/lib/pakyow/logger/formatters/json.rb +99 -0
  85. data/lib/pakyow/logger/formatters/logfmt.rb +50 -0
  86. data/lib/pakyow/logger/multiplexed.rb +19 -0
  87. data/lib/pakyow/logger/thread_local.rb +29 -0
  88. data/lib/pakyow/logger/timekeeper.rb +64 -0
  89. data/lib/pakyow/logger.rb +125 -0
  90. data/lib/pakyow/operation.rb +71 -0
  91. data/lib/pakyow/plugin/helper_caller.rb +20 -0
  92. data/lib/pakyow/plugin/lookup.rb +33 -0
  93. data/lib/pakyow/plugin/state.rb +68 -0
  94. data/lib/pakyow/plugin.rb +367 -0
  95. data/lib/pakyow/process_manager.rb +53 -0
  96. data/lib/pakyow/processes/proxy.rb +67 -0
  97. data/lib/pakyow/processes/server.rb +32 -0
  98. data/lib/pakyow/rack/compatibility.rb +68 -0
  99. data/lib/pakyow/task.rb +315 -0
  100. data/lib/pakyow/tasks/boot.rake +21 -0
  101. data/lib/pakyow/tasks/create.rake +32 -0
  102. data/lib/pakyow/tasks/help.rake +17 -0
  103. data/lib/pakyow/tasks/info/endpoints.rake +72 -0
  104. data/lib/pakyow/tasks/info.rake +31 -0
  105. data/lib/pakyow/tasks/irb.rake +11 -0
  106. data/lib/pakyow/tasks/prelaunch.rake +37 -0
  107. data/lib/pakyow/types.rb +32 -0
  108. data/lib/pakyow/validations/acceptance.rb +37 -0
  109. data/lib/pakyow/validations/email.rb +28 -0
  110. data/lib/pakyow/validations/inline.rb +32 -0
  111. data/lib/pakyow/validations/length.rb +44 -0
  112. data/lib/pakyow/validations/presence.rb +41 -0
  113. data/lib/pakyow/validations.rb +8 -0
  114. data/lib/pakyow/validator.rb +81 -0
  115. data/lib/pakyow/verifier.rb +177 -0
  116. data/lib/pakyow.rb +5 -0
  117. metadata +267 -65
  118. data/pakyow-core/CHANGELOG.md +0 -128
  119. data/pakyow-core/LICENSE +0 -20
  120. data/pakyow-core/lib/pakyow/core/app.rb +0 -82
  121. data/pakyow-core/lib/pakyow/core/app_context.rb +0 -10
  122. data/pakyow-core/lib/pakyow/core/base.rb +0 -61
  123. data/pakyow-core/lib/pakyow/core/call_context.rb +0 -171
  124. data/pakyow-core/lib/pakyow/core/config/app.rb +0 -49
  125. data/pakyow-core/lib/pakyow/core/config/cookies.rb +0 -4
  126. data/pakyow-core/lib/pakyow/core/config/logger.rb +0 -34
  127. data/pakyow-core/lib/pakyow/core/config/reloader.rb +0 -10
  128. data/pakyow-core/lib/pakyow/core/config/server.rb +0 -10
  129. data/pakyow-core/lib/pakyow/core/config/session.rb +0 -41
  130. data/pakyow-core/lib/pakyow/core/config.rb +0 -95
  131. data/pakyow-core/lib/pakyow/core/errors.rb +0 -16
  132. data/pakyow-core/lib/pakyow/core/helpers/configuring.rb +0 -142
  133. data/pakyow-core/lib/pakyow/core/helpers/hooks.rb +0 -106
  134. data/pakyow-core/lib/pakyow/core/helpers/running.rb +0 -124
  135. data/pakyow-core/lib/pakyow/core/helpers.rb +0 -61
  136. data/pakyow-core/lib/pakyow/core/loader.rb +0 -32
  137. data/pakyow-core/lib/pakyow/core/middleware/logger.rb +0 -146
  138. data/pakyow-core/lib/pakyow/core/middleware/override.rb +0 -3
  139. data/pakyow-core/lib/pakyow/core/middleware/reloader.rb +0 -23
  140. data/pakyow-core/lib/pakyow/core/middleware/req_path_normalizer.rb +0 -49
  141. data/pakyow-core/lib/pakyow/core/middleware/session.rb +0 -5
  142. data/pakyow-core/lib/pakyow/core/middleware/static.rb +0 -76
  143. data/pakyow-core/lib/pakyow/core/multilog.rb +0 -19
  144. data/pakyow-core/lib/pakyow/core/request.rb +0 -119
  145. data/pakyow-core/lib/pakyow/core/response.rb +0 -135
  146. data/pakyow-core/lib/pakyow/core/route_eval.rb +0 -254
  147. data/pakyow-core/lib/pakyow/core/route_expansion_eval.rb +0 -115
  148. data/pakyow-core/lib/pakyow/core/route_lookup.rb +0 -41
  149. data/pakyow-core/lib/pakyow/core/route_merger.rb +0 -76
  150. data/pakyow-core/lib/pakyow/core/route_module.rb +0 -21
  151. data/pakyow-core/lib/pakyow/core/route_set.rb +0 -74
  152. data/pakyow-core/lib/pakyow/core/route_template_defaults.rb +0 -43
  153. data/pakyow-core/lib/pakyow/core/route_template_eval.rb +0 -72
  154. data/pakyow-core/lib/pakyow/core/router.rb +0 -181
  155. data/pakyow-core/lib/pakyow/core.rb +0 -8
  156. 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