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,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/loader"
4
+
5
+ module Pakyow
6
+ class Plugin
7
+ # @api private
8
+ class State
9
+ def initialize(plugin, path: plugin.class.plugin_path)
10
+ @plugin, @path = plugin, path
11
+ end
12
+
13
+ def backend_path(aspect)
14
+ File.join(@path, "backend", aspect.to_s)
15
+ end
16
+
17
+ def frontend_path
18
+ File.join(@path, "frontend")
19
+ end
20
+
21
+ def load_frontend
22
+ @plugin.state(:templates) << Presenter::Templates.new(
23
+ @plugin.config.name,
24
+ frontend_path,
25
+ config: {
26
+ prefix: @plugin.class.mount_path
27
+ },
28
+ processor: Presenter::ProcessorCaller.new(
29
+ @plugin.parent.state(:processor)
30
+ )
31
+ ).tap do |plugin_templates|
32
+ if app_templates = @plugin.parent.state(:templates).find { |templates| templates.name == :default }
33
+ plugin_templates.paths.each do |path|
34
+ plugin_info = plugin_templates.info(path)
35
+
36
+ # Use the app's layout, if available.
37
+ #
38
+ if app_templates.layouts.include?(plugin_info[:page].info(:layout))
39
+ plugin_info[:layout] = app_templates.layout(plugin_info[:page].info(:layout).to_sym)
40
+ plugin_info[:partials].merge!(app_templates.includes)
41
+ end
42
+
43
+ if app_info = app_templates.info(path)
44
+ # Define the plugin view as the `plug` partial so that it can be included.
45
+ #
46
+ plugin_info[:partials][:plug] = Presenter::Partial.from_object(
47
+ :plug, plugin_info[:page].object
48
+ )
49
+
50
+ # Set the layout for the page.
51
+ #
52
+ plugin_info[:layout] = app_templates.layout(app_info[:page].info(:layout).to_sym)
53
+
54
+ # Include the app partials, since we're using a page from the app that might include them.
55
+ #
56
+ plugin_info[:partials].merge!(app_info[:partials])
57
+
58
+ # Finally, override the page with the one from the app.
59
+ #
60
+ plugin_info[:page] = app_info[:page]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,367 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/class_state"
4
+ require "pakyow/support/configurable"
5
+ require "pakyow/support/definable"
6
+ require "pakyow/support/hookable"
7
+ require "pakyow/support/pipeline"
8
+
9
+ require "pakyow/behavior/aspects"
10
+ require "pakyow/behavior/endpoints"
11
+ require "pakyow/behavior/frameworks"
12
+ require "pakyow/behavior/helpers"
13
+ require "pakyow/behavior/isolating"
14
+ require "pakyow/behavior/operations"
15
+ require "pakyow/behavior/pipeline"
16
+ require "pakyow/behavior/rescuing"
17
+ require "pakyow/behavior/restarting"
18
+
19
+ require "pakyow/app"
20
+ require "pakyow/endpoints"
21
+
22
+ require "pakyow/plugin/helper_caller"
23
+
24
+ module Pakyow
25
+ # Base plugin class.
26
+ #
27
+ class Plugin
28
+ require "pakyow/plugin/state"
29
+
30
+ extend Support::ClassState
31
+ class_state :__enabled_features, default: []
32
+ class_state :__disabled_features, default: []
33
+
34
+ include Support::Configurable
35
+
36
+ setting :name
37
+
38
+ setting :root do
39
+ plugin_path
40
+ end
41
+
42
+ setting :src do
43
+ File.join(config.root, "backend")
44
+ end
45
+
46
+ setting :lib do
47
+ File.join(config.src, "lib")
48
+ end
49
+
50
+ configurable :tasks do
51
+ setting :prelaunch, []
52
+ end
53
+
54
+ include Support::Definable
55
+ include Support::Hookable
56
+ include Support::Pipeline
57
+
58
+ # Use the same events as app.
59
+ #
60
+ events(*App.events)
61
+
62
+ # Include behavior so that plugin behaves like an app.
63
+ #
64
+ include Behavior::Aspects
65
+ include Behavior::Endpoints
66
+ include Behavior::Frameworks
67
+ include Behavior::Helpers
68
+ include Behavior::Isolating
69
+ include Behavior::Operations
70
+ include Behavior::Pipeline
71
+ include Behavior::Rescuing
72
+ include Behavior::Restarting
73
+
74
+ attr_reader :parent
75
+
76
+ def initialize(parent, &block)
77
+ super()
78
+
79
+ @parent = parent
80
+ @state = []
81
+ @endpoints = Endpoints.new
82
+ @features = self.class.features
83
+ @key = build_key
84
+
85
+ performing :configure do
86
+ configure!(@parent.environment)
87
+ end
88
+
89
+ performing :initialize do
90
+ if block_given?
91
+ instance_exec(&block)
92
+ end
93
+
94
+ # Load state prior to calling the load hooks so that helpers are available.
95
+ #
96
+ load_state
97
+
98
+ # We still want to call the load hooks so that behavior works properly.
99
+ #
100
+ performing :load do; end
101
+
102
+ defined!
103
+ end
104
+
105
+ create_helper_contexts
106
+
107
+ if respond_to?(:boot)
108
+ boot
109
+ end
110
+ end
111
+
112
+ def booted
113
+ call_hooks :after, :boot
114
+ end
115
+
116
+ def feature?(name)
117
+ name = name.to_sym
118
+ @features.any? { |feature|
119
+ feature[:name] == name
120
+ }
121
+ end
122
+
123
+ def call(connection)
124
+ super(isolated(:Connection).from_connection(connection, :@app => self))
125
+ end
126
+
127
+ def method_missing(method_name, *args, &block)
128
+ if @parent.respond_to?(method_name)
129
+ @parent.public_send(method_name, *args, &block)
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ def respond_to_missing?(method_name, include_private = false)
136
+ @parent.respond_to?(method_name) || super
137
+ end
138
+
139
+ def helpers(connection)
140
+ @helper_class.new(self, connection)
141
+ end
142
+
143
+ def __object_name
144
+ self.class.__object_name
145
+ end
146
+
147
+ def plugin_path
148
+ self.class.plugin_path
149
+ end
150
+
151
+ def helper_caller(helper_context, connection, call_context)
152
+ connection = connection.class.from_connection(connection, :@app => self)
153
+
154
+ HelperCaller.new(
155
+ plugin: self,
156
+ connection: connection,
157
+ helpers: @helper_contexts[helper_context.to_sym].new(connection, call_context)
158
+ )
159
+ end
160
+
161
+ def load_frontend
162
+ @state.each(&:load_frontend)
163
+ end
164
+
165
+ def exposed_value_name(name)
166
+ prefix = if self.class.__object_name.name == :default
167
+ self.class.plugin_name
168
+ else
169
+ "#{self.class.plugin_name}(#{self.class.__object_name.name})"
170
+ end
171
+
172
+ :"__#{prefix}.#{name}"
173
+ end
174
+
175
+ def _dump(_)
176
+ Marshal.dump(
177
+ {
178
+ parent: {
179
+ name: @parent.config.name
180
+ },
181
+
182
+ plugin_name: self.class.plugin_name,
183
+ plugin_path: self.class.plugin_path,
184
+ mount_path: self.class.mount_path
185
+ }
186
+ )
187
+ end
188
+
189
+ def self._load(state)
190
+ state = Marshal.load(state)
191
+ Pakyow.app(state[:parent][:name]).plugs.find { |plug|
192
+ plug.class.plugin_name == state[:plugin_name] &&
193
+ plug.class.plugin_path == state[:plugin_path] &&
194
+ plug.class.mount_path == state[:mount_path]
195
+ }
196
+ end
197
+
198
+ def frontend_key(name = nil)
199
+ if name
200
+ :"@#{@key}.#{name}"
201
+ else
202
+ @key
203
+ end
204
+ end
205
+
206
+ private
207
+
208
+ def build_key
209
+ namespace = self.class.__object_name.namespace.parts.last
210
+ @key = case namespace
211
+ when :default
212
+ :"#{self.class.plugin_name}"
213
+ else
214
+ :"#{self.class.plugin_name}.#{namespace}"
215
+ end
216
+ end
217
+
218
+ def load_aspect(aspect)
219
+ @state.each do |state|
220
+ super(aspect, path: state.backend_path(aspect), target: self)
221
+ end
222
+ end
223
+
224
+ def load_state
225
+ load_global_state
226
+ load_feature_state
227
+ end
228
+
229
+ def load_global_state
230
+ @state << State.new(self)
231
+ end
232
+
233
+ def load_feature_state
234
+ @features.each do |feature|
235
+ @state << State.new(self, path: feature[:path])
236
+
237
+ initializer = File.join(feature[:path], "initializer.rb")
238
+ if File.exist?(initializer)
239
+ instance_eval(File.read(initializer), initializer)
240
+ end
241
+ end
242
+ end
243
+
244
+ def load_endpoints
245
+ state.each_with_object(@endpoints) do |(_, state_object), endpoints|
246
+ state_object.instances.each do |state_instance|
247
+ endpoints.load(state_instance)
248
+ end
249
+ end
250
+
251
+ define_app_endpoints
252
+ end
253
+
254
+ def define_app_endpoints
255
+ @endpoints.each do |endpoint|
256
+ # Register endpoints accessible for backend path building.
257
+ #
258
+ @parent.endpoints << Endpoint.new(
259
+ name: [config.name.to_s, endpoint.name].join("_"),
260
+ method: :get,
261
+ builder: endpoint.builder
262
+ )
263
+
264
+ # Register endpoints accessible for frontend path building.
265
+ #
266
+ namespace = self.class.__object_name.namespace.parts.last
267
+
268
+ endpoint_name = if namespace == :default
269
+ :"@#{self.class.plugin_name}.#{endpoint.name}"
270
+ else
271
+ :"@#{self.class.plugin_name}(#{namespace}).#{endpoint.name}"
272
+ end
273
+
274
+ @parent.endpoints << Endpoint.new(
275
+ name: endpoint_name,
276
+ method: :get,
277
+ builder: endpoint.builder
278
+ )
279
+ end
280
+ end
281
+
282
+ def create_helper_contexts
283
+ @helper_contexts = %i(global passive active).each_with_object({}) { |context, helper_contexts|
284
+ helper_class = Class.new do
285
+ def initialize(connection, context)
286
+ @connection, @context = connection, context
287
+ end
288
+
289
+ def method_missing(method_name, *args, &block)
290
+ if @context.respond_to?(method_name)
291
+ @context.public_send(method_name, *args, &block)
292
+ else
293
+ super
294
+ end
295
+ end
296
+
297
+ def respond_to_missing?(method_name, include_private = false)
298
+ @context.respond_to?(method_name, include_private) || super
299
+ end
300
+ end
301
+
302
+ self.class.include_helpers(context, helper_class)
303
+ helper_contexts[context] = helper_class
304
+ }
305
+ end
306
+
307
+ class << self
308
+ attr_reader :plugin_name, :plugin_path, :mount_path
309
+
310
+ # rubocop:disable Naming/MethodName
311
+ def Plugin(name, path)
312
+ Class.new(self) do
313
+ @plugin_name = name
314
+ @plugin_path = path
315
+ end
316
+ end
317
+ # rubocop:enabled Naming/MethodName
318
+
319
+ def inherited(plugin_class)
320
+ super
321
+
322
+ if instance_variable_defined?(:@plugin_name)
323
+ plugin_class.instance_variable_set(:@plugin_name, instance_variable_get(:@plugin_name))
324
+ plugin_class.instance_variable_set(:@plugin_path, instance_variable_get(:@plugin_path))
325
+
326
+ unless Pakyow.plugins.include?(@plugin_name)
327
+ Pakyow.register_plugin(@plugin_name, plugin_class)
328
+ end
329
+ end
330
+ end
331
+
332
+ def enable(*features)
333
+ @__enabled_features.concat(features)
334
+ end
335
+
336
+ def disable(*features)
337
+ @__disabled_features.concat(features)
338
+ end
339
+
340
+ def features
341
+ Dir.glob(File.join(plugin_path, "features", "*")).map { |feature_path|
342
+ {
343
+ name: File.basename(feature_path).to_sym,
344
+ path: feature_path
345
+ }
346
+ }.tap do |features|
347
+ features.delete_if do |feature|
348
+ @__disabled_features.include?(feature[:name])
349
+ end
350
+
351
+ if @__enabled_features.any?
352
+ features.keep_if do |feature|
353
+ @__enabled_features.include?(feature[:name])
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ context = self
361
+ Pakyow.singleton_class.class_eval do
362
+ define_method :Plugin do |name, path|
363
+ context.Plugin(name, path)
364
+ end
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "process/group"
4
+
5
+ require "pakyow/support/inflector"
6
+
7
+ module Pakyow
8
+ class ProcessManager
9
+ def initialize
10
+ @group, @processes, @stopped = Process::Group.new, [], false
11
+ end
12
+
13
+ def add(process)
14
+ process = process.dup
15
+ run_process(process)
16
+ @processes << process
17
+ end
18
+
19
+ def wait
20
+ @group.wait
21
+ end
22
+
23
+ def stop(signal = :INT)
24
+ @stopped = true
25
+ @group.kill(signal)
26
+ end
27
+
28
+ def restart
29
+ @group.running.each do |pid, forked|
30
+ if forked.instance_variable_get(:@options)[:restartable]
31
+ Process.kill(:INT, pid)
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def run_process(process)
39
+ Fiber.new {
40
+ until @stopped
41
+ status = @group.fork(process) {
42
+ begin
43
+ Async::Reactor.run(&process[:block])
44
+ rescue Interrupt
45
+ end
46
+ }
47
+
48
+ break unless status.success?
49
+ end
50
+ }.resume
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Processes
5
+ class Proxy
6
+ class << self
7
+ def find_local_port
8
+ server = TCPServer.new("127.0.0.1", 0)
9
+ port = server.addr[1]
10
+ server.close
11
+ port
12
+ end
13
+ end
14
+
15
+ def initialize(host:, port:, proxy_port:)
16
+ @host, @port, @proxy_port = host, port, proxy_port
17
+ end
18
+
19
+ def run
20
+ endpoint = Async::HTTP::Endpoint.parse(
21
+ "http://#{@host}:#{@port}"
22
+ )
23
+
24
+ server = Server.new(
25
+ host: @host, port: @proxy_port, forwarded: "#{@host}:#{@port}"
26
+ )
27
+
28
+ Async::Reactor.run do
29
+ Async::HTTP::Server.new(server, endpoint).run
30
+
31
+ if !ENV.key?("PW_RESPAWN")
32
+ Pakyow.logger << Pakyow::Processes::Server.running_text(
33
+ scheme: "http", host: @host, port: @port
34
+ )
35
+ end
36
+ end
37
+ end
38
+
39
+ class Server
40
+ def initialize(port:, host:, forwarded:)
41
+ @port, @host, @forwarded = port, host, forwarded
42
+ @destination = "#{@host}:#{@port}"
43
+ @client = Async::HTTP::Client.new(
44
+ Async::HTTP::Endpoint.parse(
45
+ File.join("http://#{@destination}")
46
+ )
47
+ )
48
+ end
49
+
50
+ def call(request, total_waits = 0)
51
+ @client.call(request)
52
+ rescue
53
+ if total_waits == 30
54
+ Async::HTTP::Protocol::Response.new(
55
+ nil, 404, [], Async::HTTP::Body::Buffered.wrap(
56
+ StringIO.new("app did not respond")
57
+ )
58
+ )
59
+ else
60
+ Async::Task.current.sleep 0.5
61
+ call(request, total_waits + 1)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async/http/server"
4
+
5
+ require "pakyow/support/extension"
6
+
7
+ module Pakyow
8
+ module Processes
9
+ class Server
10
+ class << self
11
+ def running_text(scheme:, host:, port:)
12
+ text = String.new("Pakyow › #{Pakyow.env.capitalize}")
13
+ text << " › #{scheme}://#{host}:#{port}"
14
+
15
+ Support::CLI.style.blue.bold(
16
+ text
17
+ ) + Support::CLI.style.italic("\nUse Ctrl-C to shut down the environment.")
18
+ end
19
+ end
20
+
21
+ def initialize(endpoint:, protocol:, scheme:)
22
+ @endpoint, @protocol, @scheme = endpoint, protocol, scheme
23
+ end
24
+
25
+ def run
26
+ Async::HTTP::Server.new(
27
+ Pakyow.boot, @endpoint, @protocol, @scheme
28
+ ).run
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "protocol/http/headers"
4
+ require "rack/request"
5
+
6
+ require "pakyow/environment"
7
+
8
+ module Pakyow
9
+ module Rack
10
+ class Connection < ::Pakyow::Connection
11
+ def initialize(rack_env)
12
+ super(::Rack::Request.new(rack_env))
13
+ end
14
+
15
+ def query
16
+ @request.query_string
17
+ end
18
+
19
+ def fullpath
20
+ @request.fullpath
21
+ end
22
+
23
+ def request_header(key)
24
+ normalize_header_value(key, @request.get_header(normalize_header(key)))
25
+ end
26
+
27
+ def request_header?(key)
28
+ @request.has_header?(normalize_header(key))
29
+ end
30
+
31
+ def ip
32
+ @request.ip
33
+ end
34
+
35
+ def hijack?
36
+ @request.env["rack.hijack?"]
37
+ end
38
+
39
+ def hijack!
40
+ @request.env["rack.hijack"].call
41
+ end
42
+
43
+ # @api private
44
+ def request_method
45
+ @request.request_method
46
+ end
47
+
48
+ # @api private
49
+ def request_path
50
+ @request.fullpath
51
+ end
52
+
53
+ private
54
+
55
+ def normalize_header(key)
56
+ key.to_s.upcase.gsub("-", "_")
57
+ end
58
+
59
+ def normalize_header_value(key, value)
60
+ if value && policy = Protocol::HTTP::Headers::MERGE_POLICY[key.to_s.downcase.gsub("_", "-")]
61
+ policy.new(value.to_s)
62
+ else
63
+ value
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end