hanami-cli 0.3.0 → 2.0.0.alpha3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +42 -0
  3. data/.gitignore +4 -2
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +25 -1
  6. data/CHANGELOG.md +30 -1
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/Gemfile +13 -6
  9. data/LICENSE.txt +21 -0
  10. data/README.md +12 -605
  11. data/Rakefile +9 -12
  12. data/bin/console +1 -0
  13. data/exe/hanami +10 -0
  14. data/hanami-cli.gemspec +25 -18
  15. data/lib/hanami/cli/bundler.rb +73 -0
  16. data/lib/hanami/cli/command.rb +16 -355
  17. data/lib/hanami/cli/command_line.rb +17 -0
  18. data/lib/hanami/cli/commands/application.rb +63 -0
  19. data/lib/hanami/cli/commands/db/utils/database.rb +122 -0
  20. data/lib/hanami/cli/commands/db/utils/database_config.rb +48 -0
  21. data/lib/hanami/cli/commands/db/utils/mysql.rb +27 -0
  22. data/lib/hanami/cli/commands/db/utils/postgres.rb +49 -0
  23. data/lib/hanami/cli/commands/db/utils/sqlite.rb +37 -0
  24. data/lib/hanami/cli/commands/gem/new.rb +77 -0
  25. data/lib/hanami/cli/commands/gem/version.rb +18 -0
  26. data/lib/hanami/cli/commands/gem.rb +21 -0
  27. data/lib/hanami/cli/commands/monolith/console.rb +50 -0
  28. data/lib/hanami/cli/commands/monolith/db/create.rb +25 -0
  29. data/lib/hanami/cli/commands/monolith/db/create_migration.rb +29 -0
  30. data/lib/hanami/cli/commands/monolith/db/drop.rb +25 -0
  31. data/lib/hanami/cli/commands/monolith/db/migrate.rb +40 -0
  32. data/lib/hanami/cli/commands/monolith/db/reset.rb +26 -0
  33. data/lib/hanami/cli/commands/monolith/db/rollback.rb +55 -0
  34. data/lib/hanami/cli/commands/monolith/db/sample_data.rb +40 -0
  35. data/lib/hanami/cli/commands/monolith/db/seed.rb +40 -0
  36. data/lib/hanami/cli/commands/monolith/db/setup.rb +24 -0
  37. data/lib/hanami/cli/commands/monolith/db/structure/dump.rb +25 -0
  38. data/lib/hanami/cli/commands/monolith/db/version.rb +26 -0
  39. data/lib/hanami/cli/commands/monolith/generate/action.rb +62 -0
  40. data/lib/hanami/cli/commands/monolith/generate/slice.rb +62 -0
  41. data/lib/hanami/cli/commands/monolith/generate.rb +14 -0
  42. data/lib/hanami/cli/commands/monolith/install.rb +16 -0
  43. data/lib/hanami/cli/commands/monolith/version.rb +18 -0
  44. data/lib/hanami/cli/commands/monolith.rb +55 -0
  45. data/lib/hanami/cli/commands.rb +26 -0
  46. data/lib/hanami/cli/error.rb +8 -0
  47. data/lib/hanami/cli/generators/context.rb +38 -0
  48. data/lib/hanami/cli/generators/gem/application/monolith/action.erb +21 -0
  49. data/lib/hanami/cli/generators/gem/application/monolith/application.erb +8 -0
  50. data/lib/hanami/cli/generators/gem/application/monolith/config_ru.erb +5 -0
  51. data/lib/hanami/cli/generators/gem/application/monolith/entities.erb +9 -0
  52. data/lib/hanami/cli/generators/gem/application/monolith/env.erb +0 -0
  53. data/lib/hanami/cli/generators/gem/application/monolith/functions.erb +13 -0
  54. data/lib/hanami/cli/generators/gem/application/monolith/gemfile.erb +19 -0
  55. data/lib/hanami/cli/generators/gem/application/monolith/keep.erb +0 -0
  56. data/lib/hanami/cli/generators/gem/application/monolith/operation.erb +18 -0
  57. data/lib/hanami/cli/generators/gem/application/monolith/rakefile.erb +3 -0
  58. data/lib/hanami/cli/generators/gem/application/monolith/readme.erb +1 -0
  59. data/lib/hanami/cli/generators/gem/application/monolith/repository.erb +13 -0
  60. data/lib/hanami/cli/generators/gem/application/monolith/routes.erb +4 -0
  61. data/lib/hanami/cli/generators/gem/application/monolith/settings.erb +6 -0
  62. data/lib/hanami/cli/generators/gem/application/monolith/types.erb +10 -0
  63. data/lib/hanami/cli/generators/gem/application/monolith/validation_contract.erb +14 -0
  64. data/lib/hanami/cli/generators/gem/application/monolith/view_context.erb +15 -0
  65. data/lib/hanami/cli/generators/gem/application/monolith.rb +83 -0
  66. data/lib/hanami/cli/generators/gem/application.rb +21 -0
  67. data/lib/hanami/cli/generators/monolith/action/action.erb +13 -0
  68. data/lib/hanami/cli/generators/monolith/action/template.erb +0 -0
  69. data/lib/hanami/cli/generators/monolith/action/template.html.erb +2 -0
  70. data/lib/hanami/cli/generators/monolith/action/view.erb +13 -0
  71. data/lib/hanami/cli/generators/monolith/action.rb +123 -0
  72. data/lib/hanami/cli/generators/monolith/action_context.rb +76 -0
  73. data/lib/hanami/cli/generators/monolith/slice/action.erb +9 -0
  74. data/lib/hanami/cli/generators/monolith/slice/entities.erb +9 -0
  75. data/lib/hanami/cli/generators/monolith/slice/keep.erb +0 -0
  76. data/lib/hanami/cli/generators/monolith/slice/repository.erb +10 -0
  77. data/lib/hanami/cli/generators/monolith/slice/routes.erb +2 -0
  78. data/lib/hanami/cli/generators/monolith/slice/view.erb +9 -0
  79. data/lib/hanami/cli/generators/monolith/slice.rb +56 -0
  80. data/lib/hanami/cli/generators/monolith/slice_context.rb +33 -0
  81. data/lib/hanami/cli/repl/core.rb +55 -0
  82. data/lib/hanami/cli/repl/irb.rb +41 -0
  83. data/lib/hanami/cli/repl/pry.rb +29 -0
  84. data/lib/hanami/cli/system_call.rb +51 -0
  85. data/lib/hanami/cli/url.rb +34 -0
  86. data/lib/hanami/cli/version.rb +4 -3
  87. data/lib/hanami/cli.rb +10 -121
  88. data/lib/hanami/console/context.rb +39 -0
  89. data/lib/hanami/console/plugins/slice_readers.rb +42 -0
  90. data/lib/hanami/rake_tasks.rb +52 -0
  91. metadata +138 -42
  92. data/.circleci/config.yml +0 -141
  93. data/.travis.yml +0 -26
  94. data/lib/hanami/cli/banner.rb +0 -127
  95. data/lib/hanami/cli/command_registry.rb +0 -213
  96. data/lib/hanami/cli/errors.rb +0 -44
  97. data/lib/hanami/cli/option.rb +0 -132
  98. data/lib/hanami/cli/parser.rb +0 -142
  99. data/lib/hanami/cli/program_name.rb +0 -19
  100. data/lib/hanami/cli/registry.rb +0 -328
  101. data/lib/hanami/cli/usage.rb +0 -89
  102. data/script/ci +0 -61
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "shellwords"
5
+ require "hanami/cli/generators/context"
6
+
7
+ module Hanami
8
+ module CLI
9
+ module Generators
10
+ module Gem
11
+ module Application
12
+ class Monolith
13
+ def initialize(fs:, inflector:, command_line:)
14
+ super()
15
+ @fs = fs
16
+ @inflector = inflector
17
+ @command_line = command_line
18
+ end
19
+
20
+ def call(app, slice, slice_url_prefix, context: Context.new(inflector, app), &blk)
21
+ generate_app(app, context)
22
+ blk.call
23
+ generate_slice!(slice, slice_url_prefix)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :fs
29
+
30
+ attr_reader :inflector
31
+
32
+ attr_reader :command_line
33
+
34
+ def generate_app(app, context) # rubocop:disable Metrics/AbcSize
35
+ fs.write(".env", t("env.erb", context))
36
+
37
+ fs.write("README.md", t("readme.erb", context))
38
+ fs.write("Gemfile", t("gemfile.erb", context))
39
+ fs.write("Rakefile", t("rakefile.erb", context))
40
+ fs.write("config.ru", t("config_ru.erb", context))
41
+
42
+ fs.write("config/application.rb", t("application.erb", context))
43
+ fs.write("config/settings.rb", t("settings.erb", context))
44
+ fs.write("config/routes.rb", t("routes.erb", context))
45
+
46
+ fs.write("lib/tasks/.keep", t("keep.erb", context))
47
+
48
+ fs.write("lib/#{app}/entities/.keep", t("keep.erb", context))
49
+ fs.write("lib/#{app}/persistence/relations/.keep", t("keep.erb", context))
50
+ fs.write("lib/#{app}/validation/contract.rb", t("validation_contract.erb", context))
51
+ fs.write("lib/#{app}/view/context.rb", t("view_context.erb", context))
52
+ fs.write("lib/#{app}/action.rb", t("action.erb", context))
53
+ fs.write("lib/#{app}/entities.rb", t("entities.erb", context))
54
+ fs.write("lib/#{app}/functions.rb", t("functions.erb", context))
55
+ fs.write("lib/#{app}/operation.rb", t("operation.erb", context))
56
+ fs.write("lib/#{app}/repository.rb", t("repository.erb", context))
57
+ fs.write("lib/#{app}/types.rb", t("types.erb", context))
58
+ end
59
+
60
+ def generate_slice!(slice_name, slice_url_prefix)
61
+ slice_name = Shellwords.shellescape(slice_name)
62
+ slice_url_prefix = Shellwords.shellescape(slice_url_prefix)
63
+
64
+ command_line.call("hanami generate slice #{slice_name} --url-prefix=#{slice_url_prefix}").tap do |result|
65
+ raise "hanami generate slice #{slice_name} failed\n\n\n#{result.err.inspect}" unless result.successful?
66
+ end
67
+ end
68
+
69
+ def template(path, context)
70
+ require "erb"
71
+
72
+ ERB.new(
73
+ File.read(__dir__ + "/monolith/#{path}")
74
+ ).result(context.ctx)
75
+ end
76
+
77
+ alias_method :t, :template
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Generators
6
+ module Gem
7
+ module Application
8
+ class << self
9
+ def call(architecture, fs, inflector, command_line)
10
+ require_relative "./application/#{architecture}"
11
+
12
+ generator_name = inflector.classify(architecture).to_sym
13
+ const_get(generator_name).new(fs: fs, inflector: inflector, command_line: command_line)
14
+ end
15
+ alias_method :[], :call
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "<%= underscored_slice_name %>/action"
5
+
6
+ module <%= classified_slice_name %>
7
+ module Actions
8
+ <%= module_controller_declaration %>
9
+ <%= module_controller_offset %>class <%= classified_action_name %> < <%= classified_slice_name %>::Action
10
+ <%= module_controller_offset %>end
11
+ <%= module_controller_end %>
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ <h1><%= classified_slice_name %>::Views::<%= classified_controller_name %>::<%= classified_action_name %></h1>
2
+ <h2><%= template_path %></h2>
@@ -0,0 +1,13 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "<%= underscored_slice_name %>/view"
5
+
6
+ module <%= classified_slice_name %>
7
+ module Views
8
+ <%= module_controller_declaration %>
9
+ <%= module_controller_offset %>class <%= classified_action_name %> < <%= classified_slice_name %>::View
10
+ <%= module_controller_offset %>end
11
+ <%= module_controller_end %>
12
+ end
13
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "dry/files"
5
+ require "hanami/cli/generators/monolith/action_context"
6
+ require "hanami/cli/url"
7
+
8
+ module Hanami
9
+ module CLI
10
+ module Generators
11
+ module Monolith
12
+ class Action
13
+ def initialize(fs:, inflector:)
14
+ @fs = fs
15
+ @inflector = inflector
16
+ end
17
+
18
+ # rubocop:disable Metrics/AbcSize
19
+ # rubocop:disable Metrics/ParameterLists
20
+ # rubocop:disable Layout/LineLength
21
+ def call(slice, controller, action, url, http, format, skip_view, context: ActionContext.new(inflector, slice, controller, action))
22
+ slice_directory = fs.join("slices", slice)
23
+ raise ArgumentError.new("slice not found `#{slice}'") unless fs.directory?(slice_directory)
24
+
25
+ fs.inject_line_at_block_bottom(
26
+ fs.join("config", "routes.rb"),
27
+ slice_matcher(slice),
28
+ route(controller, action, url, http)
29
+ )
30
+
31
+ fs.chdir(slice_directory) do
32
+ fs.mkdir(directory = fs.join("actions", controller))
33
+ fs.write(fs.join(directory, "#{action}.rb"), t("action.erb", context))
34
+
35
+ unless skip_view
36
+ fs.mkdir(directory = fs.join("views", controller))
37
+ fs.write(fs.join(directory, "#{action}.rb"), t("view.erb", context))
38
+
39
+ fs.mkdir(directory = fs.join("templates", controller))
40
+ fs.write(fs.join(directory, "#{action}.#{format}.erb"), t(template_format(format), context))
41
+ end
42
+ end
43
+ end
44
+ # rubocop:enable Layout/LineLength
45
+ # rubocop:enable Metrics/ParameterLists
46
+ # rubocop:enable Metrics/AbcSize
47
+
48
+ private
49
+
50
+ ROUTE_HTTP_METHODS = %w[get post delete put patch trace options link unlink].freeze
51
+ private_constant :ROUTE_HTTP_METHODS
52
+
53
+ ROUTE_DEFAULT_HTTP_METHOD = "get"
54
+ private_constant :ROUTE_DEFAULT_HTTP_METHOD
55
+
56
+ ROUTE_RESTFUL_HTTP_METHODS = {
57
+ "create" => "post",
58
+ "update" => "patch",
59
+ "destroy" => "delete"
60
+ }.freeze
61
+ private_constant :ROUTE_RESTFUL_HTTP_METHODS
62
+
63
+ ROUTE_RESTFUL_URL_SUFFIXES = {
64
+ "index" => "",
65
+ "new" => "/new",
66
+ "create" => "",
67
+ "edit" => "/:id/edit",
68
+ "update" => "/:id",
69
+ "show" => "/:id",
70
+ "destroy" => "/:id"
71
+ }.freeze
72
+ private_constant :ROUTE_RESTFUL_URL_SUFFIXES
73
+
74
+ attr_reader :fs
75
+
76
+ attr_reader :inflector
77
+
78
+ def slice_matcher(slice)
79
+ /slice[[:space:]]*:#{slice}/
80
+ end
81
+
82
+ def route(controller, action, url, http)
83
+ %(#{route_http(action,
84
+ http)} "#{route_url(controller, action, url)}", to: "#{controller.join('.')}.#{action}")
85
+ end
86
+
87
+ def template_format(format)
88
+ case format.to_sym
89
+ when :html
90
+ "template.html.erb"
91
+ else
92
+ "template.erb"
93
+ end
94
+ end
95
+
96
+ def template(path, context)
97
+ require "erb"
98
+
99
+ ERB.new(
100
+ File.read(__dir__ + "/action/#{path}")
101
+ ).result(context.ctx)
102
+ end
103
+
104
+ alias_method :t, :template
105
+
106
+ def route_url(controller, action, url)
107
+ CLI::URL.call(url || "/#{controller.join('/')}" + ROUTE_RESTFUL_URL_SUFFIXES.fetch(action))
108
+ end
109
+
110
+ def route_http(action, http)
111
+ result = (http ||= ROUTE_RESTFUL_HTTP_METHODS.fetch(action, ROUTE_DEFAULT_HTTP_METHOD)).downcase
112
+
113
+ unless ROUTE_HTTP_METHODS.include?(result)
114
+ raise ArgumentError.new("unknown HTTP method: `#{http}'")
115
+ end
116
+
117
+ result
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./slice_context"
4
+ require "dry/files/path"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Generators
9
+ module Monolith
10
+ class ActionContext < SliceContext
11
+ def initialize(inflector, slice, controller, action)
12
+ @controller = controller
13
+ @action = action
14
+ super(inflector, nil, slice, nil)
15
+ end
16
+
17
+ def classified_controller_name
18
+ controller.map do |token|
19
+ inflector.camelize(token)
20
+ end.join(NAMESPACE_SEPARATOR)
21
+ end
22
+
23
+ def classified_action_name
24
+ inflector.classify(action)
25
+ end
26
+
27
+ def underscored_controller_name
28
+ controller.map do |token|
29
+ inflector.underscore(token)
30
+ end
31
+ end
32
+
33
+ def underscored_action_name
34
+ inflector.underscore(action)
35
+ end
36
+
37
+ def module_controller_declaration
38
+ controller.each_with_index.map do |token, i|
39
+ "#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
40
+ end.join($/)
41
+ end
42
+
43
+ def module_controller_end
44
+ controller.each_with_index.map do |_, i|
45
+ "#{OFFSET}#{INDENTATION * i}end"
46
+ end.reverse.join($/)
47
+ end
48
+
49
+ def module_controller_offset
50
+ "#{OFFSET}#{INDENTATION * controller.count}"
51
+ end
52
+
53
+ def template_path
54
+ Dry::Files::Path["slices", underscored_slice_name, "templates", *underscored_controller_name,
55
+ "#{underscored_action_name}.html.erb"]
56
+ end
57
+
58
+ private
59
+
60
+ NAMESPACE_SEPARATOR = "::"
61
+ private_constant :NAMESPACE_SEPARATOR
62
+
63
+ INDENTATION = " "
64
+ private_constant :INDENTATION
65
+
66
+ OFFSET = INDENTATION * 2
67
+ private_constant :OFFSET
68
+
69
+ attr_reader :controller
70
+
71
+ attr_reader :action
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "<%= underscored_app_name %>/action"
5
+
6
+ module <%= classified_slice_name %>
7
+ class Action < <%= classified_app_name %>::Action
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ module <%= classified_slice_name %>
5
+ module Entities
6
+ end
7
+ end
8
+
9
+ Dir[File.join(__dir__, "entities", "*.rb")].each(&method(:require))
File without changes
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "<%= underscored_app_name %>/repository"
4
+ require_relative "entities"
5
+
6
+ module <%= classified_slice_name %>
7
+ class Repository < <%= classified_app_name %>::Repository
8
+ struct_namespace Entities
9
+ end
10
+ end
@@ -0,0 +1,2 @@
1
+ slice :<%= underscored_slice_name %>, at: "<%= slice_url_prefix %>" do
2
+ end
@@ -0,0 +1,9 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "<%= underscored_app_name %>/view"
5
+
6
+ module <%= classified_slice_name %>
7
+ class View < <%= classified_app_name %>::View
8
+ end
9
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "dry/files"
5
+ require "hanami/cli/generators/monolith/slice_context"
6
+
7
+ module Hanami
8
+ module CLI
9
+ module Generators
10
+ module Monolith
11
+ class Slice
12
+ def initialize(fs:, inflector:)
13
+ @fs = fs
14
+ @inflector = inflector
15
+ end
16
+
17
+ def call(app, slice, slice_url_prefix, context: SliceContext.new(inflector, app, slice, slice_url_prefix)) # rubocop:disable Metrics/AbcSize
18
+ fs.inject_line_before_last(fs.join("config", "routes.rb"), /end/, t("routes.erb", context).chomp)
19
+
20
+ fs.mkdir(directory = "slices/#{slice}")
21
+
22
+ fs.chdir(directory) do
23
+ fs.write("action.rb", t("action.erb", context))
24
+ fs.write("view.rb", t("view.erb", context))
25
+ fs.write("entities.rb", t("entities.erb", context))
26
+ fs.write("repository.rb", t("repository.erb", context))
27
+
28
+ fs.write("actions/.keep", t("keep.erb", context))
29
+ fs.write("views/.keep", t("keep.erb", context))
30
+ fs.write("templates/.keep", t("keep.erb", context))
31
+ fs.write("templates/layouts/.keep", t("keep.erb", context))
32
+ fs.write("entities/.keep", t("keep.erb", context))
33
+ fs.write("repositories/.keep", t("keep.erb", context))
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :fs
40
+
41
+ attr_reader :inflector
42
+
43
+ def template(path, context)
44
+ require "erb"
45
+
46
+ ERB.new(
47
+ File.read(__dir__ + "/slice/#{path}")
48
+ ).result(context.ctx)
49
+ end
50
+
51
+ alias_method :t, :template
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../context"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ module Monolith
9
+ class SliceContext < Generators::Context
10
+ def initialize(inflector, app, slice, slice_url_prefix)
11
+ @slice = slice
12
+ @slice_url_prefix = slice_url_prefix
13
+ super(inflector, app)
14
+ end
15
+
16
+ def classified_slice_name
17
+ inflector.classify(slice)
18
+ end
19
+
20
+ def underscored_slice_name
21
+ inflector.underscore(slice)
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :slice
27
+
28
+ attr_reader :slice_url_prefix
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/console/context"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Repl
8
+ # @api private
9
+ class Core
10
+ # @api private
11
+ attr_reader :application
12
+
13
+ # @api private
14
+ attr_reader :opts
15
+
16
+ # @api private
17
+ def initialize(application, opts)
18
+ @application = application
19
+ @opts = opts
20
+ end
21
+
22
+ # @api private
23
+ def start
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # @api private
28
+ def context
29
+ @context ||= Hanami::Console::Context.new(application)
30
+ end
31
+
32
+ # @api private
33
+ def prompt
34
+ "#{name}[#{env}]"
35
+ end
36
+
37
+ # @api private
38
+ def name
39
+ (application.container.config.name || inflector.underscore(application.name))
40
+ .split("/")[0]
41
+ end
42
+
43
+ # @api private
44
+ def env
45
+ application.container.env
46
+ end
47
+
48
+ # @api private
49
+ def inflector
50
+ application.inflector
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "irb"
4
+ require_relative "core"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Repl
9
+ # @since 2.0.0
10
+ # @api public
11
+ class Irb < Core
12
+ # @api public
13
+ def start
14
+ ARGV.shift until ARGV.empty?
15
+ TOPLEVEL_BINDING.eval('self').extend(context)
16
+
17
+ IRB.conf[:PROMPT] = {}
18
+
19
+ IRB.conf[:PROMPT][:MY_PROMPT] = {
20
+ :AUTO_INDENT => true,
21
+ :PROMPT_I => ">> ",
22
+ :PROMPT_S => nil,
23
+ :PROMPT_C => nil,
24
+ :RETURN => " ==>%s\n"
25
+ }
26
+
27
+ IRB.conf[:PROMPT_MODE] = :MY_PROMPT
28
+
29
+ IRB.start
30
+ end
31
+
32
+ private
33
+
34
+ # @api private
35
+ def conf
36
+ @conf ||= IRB.conf
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pry"
4
+ require_relative "core"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Repl
9
+ # @since 2.0.0
10
+ # @api public
11
+ class Pry < Core
12
+ # @api private
13
+ class Context
14
+ end
15
+
16
+ # @api public
17
+ def start
18
+ ::Pry.config.prompt = ::Pry::Prompt.new(
19
+ "hanami",
20
+ "my custom prompt",
21
+ [proc { |*| "#{prompt}> " }]
22
+ )
23
+
24
+ ::Pry.start(Context.new.extend(context))
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ class SystemCall
6
+ class Result
7
+ SUCCESSFUL_EXIT_CODE = 0
8
+ private_constant :SUCCESSFUL_EXIT_CODE
9
+
10
+ attr_reader :exit_code, :out, :err
11
+
12
+ def initialize(exit_code:, out:, err:)
13
+ @exit_code = exit_code
14
+ @out = out
15
+ @err = err
16
+ end
17
+
18
+ def successful?
19
+ exit_code == SUCCESSFUL_EXIT_CODE
20
+ end
21
+ end
22
+
23
+ # Adapted from Bundler source code
24
+ #
25
+ # Bundler is released under MIT license
26
+ # https://github.com/bundler/bundler/blob/master/LICENSE.md
27
+ #
28
+ # A special "thank you" goes to Bundler maintainers and contributors.
29
+ #
30
+ # Also adapted from `hanami-devtools` source code
31
+ def call(cmd, env: {})
32
+ exitstatus = nil
33
+ out = nil
34
+ err = nil
35
+
36
+ ::Bundler.with_unbundled_env do
37
+ Open3.popen3(env, cmd) do |stdin, stdout, stderr, wait_thr|
38
+ yield stdin, stdout, wait_thr if block_given?
39
+ stdin.close
40
+
41
+ exitstatus = wait_thr&.value&.exitstatus
42
+ out = Thread.new { stdout.read }.value.strip
43
+ err = Thread.new { stderr.read }.value.strip
44
+ end
45
+ end
46
+
47
+ Result.new(exit_code: exitstatus, out: out, err: err)
48
+ end
49
+ end
50
+ end
51
+ end