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,315 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "forwardable"
5
+ require "optparse"
6
+
7
+ require "pakyow/support/deep_freeze"
8
+ require "pakyow/support/cli/style"
9
+
10
+ require "pakyow/cli"
11
+
12
+ module Pakyow
13
+ # Base task class that extends rake with additional functionality.
14
+ #
15
+ class Task
16
+ include Rake::DSL
17
+
18
+ extend Forwardable
19
+ def_delegators :@rake, :name
20
+
21
+ extend Support::DeepFreeze
22
+ unfreezable :rake
23
+
24
+ attr_reader :description
25
+
26
+ def initialize(namespace: [], description: nil, arguments: {}, options: {}, flags: {}, task_args: [], global: false, &block)
27
+ @description, @arguments, @options, @flags, @global = description, arguments, options, flags, global
28
+ @short_names = determine_short_names
29
+
30
+ if namespace.any?
31
+ send(:namespace, namespace.join(":")) do
32
+ define_task(task_args, block)
33
+ end
34
+ else
35
+ define_task(task_args, block)
36
+ end
37
+ end
38
+
39
+ def call(options = {}, argv = [])
40
+ parse_options(argv, options)
41
+ parse_arguments(argv, options)
42
+ check_options(options)
43
+
44
+ @rake.invoke(*args.map { |arg|
45
+ options[arg]
46
+ })
47
+ end
48
+
49
+ def help(describe: true)
50
+ required_arguments = sorted_arguments.select { |_, argument|
51
+ argument[:required]
52
+ }.map { |key, _|
53
+ "[#{key.to_s.upcase}]"
54
+ }.join(" ")
55
+
56
+ required_options = sorted_options.select { |_, option|
57
+ option[:required]
58
+ }.map { |key, _|
59
+ "--#{key}=#{key}"
60
+ }.join(" ")
61
+
62
+ text = String.new
63
+
64
+ if describe
65
+ text << Support::CLI.style.blue.bold(@description) + "\n"
66
+ end
67
+
68
+ text += <<~HELP
69
+
70
+ #{Support::CLI.style.bold("USAGE")}
71
+ $ pakyow #{[name, required_arguments, required_options].reject(&:empty?).join(" ")}
72
+ HELP
73
+
74
+ if @arguments.any?
75
+ text += <<~HELP
76
+
77
+ #{Support::CLI.style.bold("ARGUMENTS")}
78
+ HELP
79
+
80
+ longest_length = @arguments.keys.map(&:to_s).max_by(&:length).length
81
+ sorted_arguments.each do |key, argument|
82
+ description = Support::CLI.style.yellow(argument[:description])
83
+ if argument[:required]
84
+ description += Support::CLI.style.red(" (required)")
85
+ end
86
+ text += " #{key.upcase}".ljust(longest_length + 4) + description + "\n"
87
+ end
88
+ end
89
+
90
+ if @options.any?
91
+ text += <<~HELP
92
+
93
+ #{Support::CLI.style.bold("OPTIONS")}
94
+ HELP
95
+
96
+ longest_length = (@options.keys + @flags.keys).map(&:to_s).max_by(&:length).length
97
+ sorted_options_and_flags.each do |key, option|
98
+ description = Support::CLI.style.yellow(option[:description])
99
+
100
+ if option[:required]
101
+ description += Support::CLI.style.red(" (required)")
102
+ end
103
+
104
+ prefix = if @flags.key?(key)
105
+ " --#{key}"
106
+ else
107
+ if @short_names.key?(key)
108
+ " -#{key.to_s[0]}, --#{key}=#{key}"
109
+ else
110
+ " --#{key}=#{key}"
111
+ end
112
+ end
113
+
114
+ text += prefix.ljust(longest_length * 2 + 11) + description + "\n"
115
+ end
116
+ end
117
+
118
+ text
119
+ end
120
+
121
+ def app?
122
+ args.include?(:app)
123
+ end
124
+
125
+ def global?
126
+ @global == true
127
+ end
128
+
129
+ private
130
+
131
+ def sorted_arguments
132
+ @arguments.sort_by { |key, _| args.index(key) }
133
+ end
134
+
135
+ def sorted_options
136
+ @options.sort
137
+ end
138
+
139
+ def sorted_options_and_flags
140
+ @options.merge(@flags).sort
141
+ end
142
+
143
+ def define_task(task_args, task_block)
144
+ @rake = task(*task_args) { |task, args|
145
+ instance_exec(task, args, &task_block)
146
+ }
147
+ end
148
+
149
+ def args
150
+ @args ||= @rake.arg_names.map(&:to_sym)
151
+ end
152
+
153
+ def parse_options(argv, options)
154
+ unparsed = Array.new
155
+ OptionParser.new { |opts|
156
+ @flags.keys.each do |flag|
157
+ opts.on("--#{flag}") do |v|
158
+ options[flag] = v
159
+ end
160
+ end
161
+
162
+ @options.keys.each do |option|
163
+ match = ["--#{option}=VAL"]
164
+ if @short_names.key?(option)
165
+ match.unshift("-#{@short_names[option]}VAL")
166
+ end
167
+
168
+ opts.on(*match) do |v|
169
+ options[option] = v
170
+ end
171
+ end
172
+ }.order!(argv) do |arg|
173
+ unparsed << arg
174
+ end
175
+
176
+ argv.concat(unparsed)
177
+ rescue OptionParser::InvalidOption => error
178
+ raise CLI::InvalidInput, "`#{error.args.first}' is not a supported option"
179
+ end
180
+
181
+ def parse_arguments(argv, options)
182
+ sorted_arguments.each do |key, argument|
183
+ if argv.any?
184
+ options[key] = argv.shift
185
+ elsif argument[:required]
186
+ raise CLI::InvalidInput, "`#{key}' is a required argument"
187
+ end
188
+ end
189
+
190
+ if argv.any?
191
+ raise CLI::InvalidInput, "`#{argv.shift}' is not a supported argument"
192
+ end
193
+ end
194
+
195
+ def check_options(options)
196
+ @options.each do |key, option|
197
+ if option[:required] && !options.key?(key)
198
+ raise CLI::InvalidInput, "`#{key}' is a required option"
199
+ end
200
+ end
201
+
202
+ options.keys.each do |key|
203
+ unless global_options.key?(key) || args.include?(key)
204
+ raise CLI::InvalidInput, "`#{key}' is not a supported option"
205
+ end
206
+ end
207
+ end
208
+
209
+ def global_options
210
+ Hash[@options.select { |_, option|
211
+ option[:global]
212
+ }]
213
+ end
214
+
215
+ UNAVAILABLE_SHORT_NAMES = %w(a e h).freeze
216
+ def determine_short_names
217
+ short_names = {
218
+ env: "e"
219
+ }
220
+
221
+ @options.each do |name, opts|
222
+ if short = opts[:short]
223
+ short = name[0] if short == :default
224
+ unless short_names.value?(short) || UNAVAILABLE_SHORT_NAMES.include?(short)
225
+ short_names[name] = short
226
+ end
227
+ end
228
+ end
229
+
230
+ @flags.each do |name, opts|
231
+ if short = opts[:short]
232
+ unless short_names.value?(short) || UNAVAILABLE_SHORT_NAMES.include?(short)
233
+ short_names[name] = short
234
+ end
235
+ end
236
+ end
237
+
238
+ short_names
239
+ end
240
+
241
+ class Loader
242
+ attr_reader :__namespace, :__description, :__arguments, :__options, :__flags, :__tasks, :__global
243
+
244
+ def initialize(path)
245
+ @__namespace = []
246
+ @__description = nil
247
+ @__arguments = {}
248
+ @__options = {}
249
+ @__flags = {}
250
+ @__tasks = []
251
+ @__global = false
252
+
253
+ eval(File.read(path), binding, path)
254
+ end
255
+
256
+ def namespace(name, &block)
257
+ @__namespace << name.to_sym
258
+ instance_exec(&block)
259
+ @__namespace.pop
260
+ end
261
+
262
+ def describe(description)
263
+ @__description = description
264
+ end
265
+ alias :desc :describe
266
+
267
+ def argument(name, description, required: false)
268
+ @__arguments[name.to_sym] = {
269
+ description: description,
270
+ required: required
271
+ }
272
+ end
273
+
274
+ def option(name, description, required: false, short: :default)
275
+ @__options[name.to_sym] = {
276
+ description: description,
277
+ required: required,
278
+ short: short
279
+ }
280
+ end
281
+
282
+ def flag(name, description, short: nil)
283
+ @__flags[name.to_sym] = {
284
+ description: description,
285
+ short: short
286
+ }
287
+ end
288
+
289
+ def task(*args, &block)
290
+ @__tasks << Task.new(
291
+ namespace: @__namespace,
292
+ description: @__description,
293
+ arguments: @__arguments,
294
+ options: CLI::GLOBAL_OPTIONS.select { |key, _|
295
+ key == :env || args[1].to_a.include?(key)
296
+ }.merge(@__options),
297
+ flags: @__flags,
298
+ task_args: args,
299
+ global: @__global,
300
+ &block
301
+ )
302
+
303
+ @__description = nil
304
+ @__arguments = {}
305
+ @__options = {}
306
+ @__global = false
307
+ end
308
+
309
+ def global_task(*args, &block)
310
+ @__global = true
311
+ task(*args, &block)
312
+ end
313
+ end
314
+ end
315
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Boot the environment"
4
+ option :host, "The host the server runs on (default: #{Pakyow.config.server.host})"
5
+ option :port, "The port the server runs on (default: #{Pakyow.config.server.port})"
6
+ flag :standalone, "Disable automatic reloading of changes"
7
+ task :boot, [:host, :port, :standalone] do |_, args|
8
+ if host = args[:host]
9
+ Pakyow.config.server.host = host
10
+ end
11
+
12
+ if port = args[:port]
13
+ Pakyow.config.server.port = port
14
+ end
15
+
16
+ if args[:standalone]
17
+ Pakyow.config.server.proxy = false
18
+ end
19
+
20
+ Pakyow.run
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Create a new project"
4
+ argument :path, "Where to create the project", required: true
5
+ global_task :create, [:path] do |_, args|
6
+ require "pakyow/support/inflector"
7
+
8
+ project_name = Pakyow::Support.inflector.underscore(
9
+ File.basename(args[:path]).downcase
10
+ )
11
+
12
+ project_name.gsub!(" ", " ")
13
+ project_name.gsub!(" ", "_")
14
+
15
+ human_project_name = Pakyow::Support.inflector.humanize(project_name)
16
+
17
+ require "pakyow/generators/project"
18
+ Pakyow::Generators::Project.new(
19
+ File.expand_path("../../generators/project/default", __FILE__)
20
+ ).generate(args[:path], project_name: project_name, human_project_name: human_project_name)
21
+
22
+ require "pakyow/support/cli/style"
23
+ puts <<~OUTPUT
24
+
25
+ #{Pakyow::Support::CLI.style.bold "You're all set! Go to your new project:"}
26
+ $ cd #{args[:path]}
27
+
28
+ #{Pakyow::Support::CLI.style.bold "Then boot it up:"}
29
+ $ pakyow boot
30
+
31
+ OUTPUT
32
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Get help for the command line interface"
4
+ argument :command, "The command to get help for"
5
+ task :help, [:command] do |_, args|
6
+ # FIXME: Without this condition the command runs infinitely. The reason isn't
7
+ # obvious and needs to be revisited at some point in the future.
8
+ #
9
+ unless defined?($helping) && $helping
10
+ $helping = true
11
+ if args.key?(:command)
12
+ Pakyow::CLI.new([args[:command], "-h"])
13
+ else
14
+ Pakyow::CLI.new(["-h"])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/cli/style"
4
+ require "pakyow/support/dependencies"
5
+
6
+ desc "Show defined endpoints for an app"
7
+ namespace :info do
8
+ task :endpoints, [:app] do |_, args|
9
+ endpoint_methods = args[:app].endpoints.map { |endpoint|
10
+ endpoint.method.to_s.upcase
11
+ }
12
+
13
+ longest_endpoint_method = endpoint_methods.max { |a, b|
14
+ a.length <=> b.length
15
+ }
16
+
17
+ endpoint_names = args[:app].endpoints.map { |endpoint|
18
+ endpoint.name
19
+ }
20
+
21
+ longest_endpoint_name = endpoint_names.max { |a, b|
22
+ a.to_s.length <=> b.to_s.length
23
+ }
24
+
25
+ endpoint_paths = args[:app].endpoints.map { |endpoint|
26
+ endpoint.path
27
+ }
28
+
29
+ longest_endpoint_path = endpoint_paths.max { |a, b|
30
+ a.to_s.length <=> b.to_s.length
31
+ }
32
+
33
+ endpoint_sources = args[:app].endpoints.map { |endpoint|
34
+ source_location = endpoint.source_location.join(":")
35
+ if source_location.empty?
36
+ "unknown"
37
+ else
38
+ Pakyow::Support::Dependencies.library_name(source_location) ||
39
+ Pakyow::Support::Dependencies.strip_path_prefix(source_location)
40
+ end
41
+ }
42
+
43
+ longest_endpoint_source = endpoint_sources.max { |a, b|
44
+ a.to_s.length <=> b.to_s.length
45
+ }
46
+
47
+ puts endpoint_names.map(&:to_s).each_with_index.map { |endpoint_name, i|
48
+ if endpoint_name.start_with?("@") || endpoint_name.end_with?("_default")
49
+ nil
50
+ else
51
+ source = endpoint_sources[i]
52
+
53
+ source = if source.start_with?("pakyow-")
54
+ Pakyow::Support::CLI.style.blue(
55
+ "pakyow/#{source.split("pakyow-", 2)[1]}"
56
+ )
57
+ elsif source == "unknown"
58
+ Pakyow::Support::CLI.style.italic(source)
59
+ else
60
+ source
61
+ end
62
+
63
+ [
64
+ ":#{endpoint_name.ljust(longest_endpoint_name.length)}",
65
+ endpoint_methods[i].ljust(longest_endpoint_method.length),
66
+ endpoint_paths[i].ljust(longest_endpoint_path.length),
67
+ source.ljust(longest_endpoint_source.length)
68
+ ].join(" ")
69
+ end
70
+ }.compact.sort
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/info"
4
+
5
+ require "pakyow/support/inflector"
6
+ require "pakyow/support/cli/style"
7
+
8
+ desc "Show details about the current project"
9
+ task :info do
10
+ info = Pakyow.info
11
+ longest_key_length = info.values.flat_map { |value|
12
+ value.is_a?(Hash) ? value.keys : value
13
+ }.max_by(&:length).length
14
+
15
+ puts Pakyow::Support::CLI.style.bold("Library Versions".upcase)
16
+ info[:versions].each do |key, value|
17
+ puts " #{Pakyow::Support.inflector.humanize(key).ljust(longest_key_length + 8)}#{value}"
18
+ end
19
+
20
+ info[:apps].each do |app|
21
+ header = app[:class]
22
+ header += " [#{app[:reference]}]" if app.key?(:reference)
23
+ puts
24
+ puts Pakyow::Support::CLI.style.bold(header)
25
+ app.delete(:class)
26
+ app.delete(:reference)
27
+ app.each do |key, value|
28
+ puts " #{Pakyow::Support.inflector.humanize(key).ljust(longest_key_length + 8)}#{value}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Start an interactive session"
4
+ task :irb do
5
+ Pakyow.boot
6
+
7
+ require "#{Pakyow.config.cli.repl.to_s.downcase}"
8
+
9
+ ARGV.clear
10
+ Pakyow.config.cli.repl.start
11
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Run the prelaunch tasks"
4
+ task :prelaunch do
5
+ def find_task(task_name)
6
+ Pakyow.tasks.find { |task|
7
+ task.name == task_name.to_s
8
+ } || raise("#{Pakyow::Support::CLI.style.blue(task_name)} is not a prelaunch task")
9
+ end
10
+
11
+ def run_task(task_name, task_options)
12
+ task_options[:env] = Pakyow.env
13
+ task = find_task(task_name)
14
+
15
+ Pakyow.logger.info "[prelaunch] running: #{task_name}, #{task_options}"
16
+ task.call(task_options)
17
+ end
18
+
19
+ Pakyow.boot(unsafe: true)
20
+
21
+ # Run prelaunch tasks registered with the environment.
22
+ #
23
+ Pakyow.config.tasks.prelaunch.each do |task_name, task_options = {}|
24
+ run_task(task_name, task_options)
25
+ end
26
+
27
+ # Run prelaunch tasks registered with each pakyow app.
28
+ #
29
+ Pakyow.apps.each do |app|
30
+ if app.is_a?(Pakyow::App)
31
+ app.config.tasks.prelaunch.each do |task_name, task_options = {}|
32
+ task_options[:app] = app
33
+ run_task(task_name, task_options)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-types"
4
+
5
+ module Pakyow
6
+ module Types
7
+ include Dry.Types(default: :nominal)
8
+
9
+ MAPPING = {
10
+ string: Coercible::String,
11
+ boolean: Params::Bool,
12
+ date: Params::Date,
13
+ time: Params::Time,
14
+ datetime: Params::Time,
15
+ integer: Params::Integer,
16
+ float: Params::Float,
17
+ decimal: Params::Decimal
18
+ }.freeze
19
+
20
+ def self.type_for(type)
21
+ if type.is_a?(Dry::Types::Type)
22
+ type
23
+ else
24
+ MAPPING.fetch(type.to_sym)
25
+ end
26
+ rescue KeyError => error
27
+ raise UnknownType.build(error, type: type, context: {
28
+ type: type, types: MAPPING.keys
29
+ })
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/core_refinements/array/ensurable"
4
+
5
+ require "pakyow/validator"
6
+
7
+ module Pakyow
8
+ module Validations
9
+ # Ensures that the given value matches an acceptance value. By default, the value must equal
10
+ # +true+. Use the `accepts` keyword argument to pass one or more comparison values.
11
+ #
12
+ # @example
13
+ # validate :acceptance
14
+ #
15
+ # @example
16
+ # validate :acceptance, accepts: ["yes", "y"]
17
+ #
18
+ # @api public
19
+ module Acceptance
20
+ using Support::Refinements::Array::Ensurable
21
+
22
+ def self.name
23
+ :acceptance
24
+ end
25
+
26
+ def self.message(**)
27
+ "must be accepted"
28
+ end
29
+
30
+ def self.valid?(value, accepts: true, **)
31
+ Array.ensure(accepts).include?(value)
32
+ end
33
+ end
34
+
35
+ Validator.register_validation(Acceptance)
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/validator"
4
+
5
+ module Pakyow
6
+ module Validations
7
+ # Validates that the value is a valid email address.
8
+ #
9
+ # @api public
10
+ module Email
11
+ REGEX = /\A[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\z/i
12
+
13
+ def self.name
14
+ :email
15
+ end
16
+
17
+ def self.message(**)
18
+ "must be a valid email address"
19
+ end
20
+
21
+ def self.valid?(value, **)
22
+ value.to_s.match?(REGEX)
23
+ end
24
+ end
25
+
26
+ Validator.register_validation(Email)
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Pakyow
6
+ module Validations
7
+ # Wraps an inline validation proc so that we know its given name.
8
+ #
9
+ # @api private
10
+ class Inline
11
+ extend Forwardable
12
+
13
+ attr_reader :name
14
+
15
+ def initialize(name, block)
16
+ @name, @block = name, block
17
+ end
18
+
19
+ def message(**)
20
+ "is invalid"
21
+ end
22
+
23
+ def valid?(value, context: nil, **)
24
+ if context
25
+ context.instance_exec(value, &@block)
26
+ else
27
+ @block.call(value)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end