hanami-cli 0.2.0 → 2.0.0.alpha2

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 (101) 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 +39 -1
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/Gemfile +13 -6
  9. data/LICENSE.txt +21 -0
  10. data/README.md +13 -488
  11. data/Rakefile +8 -15
  12. data/bin/console +1 -0
  13. data/exe/hanami +10 -0
  14. data/hanami-cli.gemspec +24 -18
  15. data/lib/hanami/cli.rb +10 -121
  16. data/lib/hanami/cli/bundler.rb +73 -0
  17. data/lib/hanami/cli/command.rb +16 -355
  18. data/lib/hanami/cli/command_line.rb +17 -0
  19. data/lib/hanami/cli/commands.rb +26 -0
  20. data/lib/hanami/cli/commands/application.rb +63 -0
  21. data/lib/hanami/cli/commands/db/utils/database.rb +122 -0
  22. data/lib/hanami/cli/commands/db/utils/database_config.rb +48 -0
  23. data/lib/hanami/cli/commands/db/utils/mysql.rb +27 -0
  24. data/lib/hanami/cli/commands/db/utils/postgres.rb +49 -0
  25. data/lib/hanami/cli/commands/db/utils/sqlite.rb +37 -0
  26. data/lib/hanami/cli/commands/gem.rb +21 -0
  27. data/lib/hanami/cli/commands/gem/new.rb +77 -0
  28. data/lib/hanami/cli/commands/gem/version.rb +18 -0
  29. data/lib/hanami/cli/commands/monolith.rb +55 -0
  30. data/lib/hanami/cli/commands/monolith/console.rb +50 -0
  31. data/lib/hanami/cli/commands/monolith/db/create.rb +25 -0
  32. data/lib/hanami/cli/commands/monolith/db/create_migration.rb +29 -0
  33. data/lib/hanami/cli/commands/monolith/db/drop.rb +25 -0
  34. data/lib/hanami/cli/commands/monolith/db/migrate.rb +40 -0
  35. data/lib/hanami/cli/commands/monolith/db/reset.rb +26 -0
  36. data/lib/hanami/cli/commands/monolith/db/rollback.rb +55 -0
  37. data/lib/hanami/cli/commands/monolith/db/sample_data.rb +40 -0
  38. data/lib/hanami/cli/commands/monolith/db/seed.rb +40 -0
  39. data/lib/hanami/cli/commands/monolith/db/setup.rb +24 -0
  40. data/lib/hanami/cli/commands/monolith/db/structure/dump.rb +25 -0
  41. data/lib/hanami/cli/commands/monolith/db/version.rb +26 -0
  42. data/lib/hanami/cli/commands/monolith/generate.rb +14 -0
  43. data/lib/hanami/cli/commands/monolith/generate/action.rb +62 -0
  44. data/lib/hanami/cli/commands/monolith/generate/slice.rb +62 -0
  45. data/lib/hanami/cli/commands/monolith/install.rb +16 -0
  46. data/lib/hanami/cli/commands/monolith/version.rb +18 -0
  47. data/lib/hanami/cli/error.rb +8 -0
  48. data/lib/hanami/cli/generators/context.rb +38 -0
  49. data/lib/hanami/cli/generators/gem/application.rb +21 -0
  50. data/lib/hanami/cli/generators/gem/application/monolith.rb +83 -0
  51. data/lib/hanami/cli/generators/gem/application/monolith/action.erb +21 -0
  52. data/lib/hanami/cli/generators/gem/application/monolith/application.erb +8 -0
  53. data/lib/hanami/cli/generators/gem/application/monolith/config_ru.erb +5 -0
  54. data/lib/hanami/cli/generators/gem/application/monolith/entities.erb +9 -0
  55. data/lib/hanami/cli/generators/gem/application/monolith/env.erb +0 -0
  56. data/lib/hanami/cli/generators/gem/application/monolith/functions.erb +13 -0
  57. data/lib/hanami/cli/generators/gem/application/monolith/gemfile.erb +19 -0
  58. data/lib/hanami/cli/generators/gem/application/monolith/keep.erb +0 -0
  59. data/lib/hanami/cli/generators/gem/application/monolith/operation.erb +18 -0
  60. data/lib/hanami/cli/generators/gem/application/monolith/rakefile.erb +3 -0
  61. data/lib/hanami/cli/generators/gem/application/monolith/readme.erb +1 -0
  62. data/lib/hanami/cli/generators/gem/application/monolith/repository.erb +13 -0
  63. data/lib/hanami/cli/generators/gem/application/monolith/routes.erb +4 -0
  64. data/lib/hanami/cli/generators/gem/application/monolith/settings.erb +6 -0
  65. data/lib/hanami/cli/generators/gem/application/monolith/types.erb +10 -0
  66. data/lib/hanami/cli/generators/gem/application/monolith/validation_contract.erb +14 -0
  67. data/lib/hanami/cli/generators/gem/application/monolith/view_context.erb +15 -0
  68. data/lib/hanami/cli/generators/monolith/action.rb +123 -0
  69. data/lib/hanami/cli/generators/monolith/action/action.erb +13 -0
  70. data/lib/hanami/cli/generators/monolith/action/template.erb +0 -0
  71. data/lib/hanami/cli/generators/monolith/action/template.html.erb +2 -0
  72. data/lib/hanami/cli/generators/monolith/action/view.erb +13 -0
  73. data/lib/hanami/cli/generators/monolith/action_context.rb +76 -0
  74. data/lib/hanami/cli/generators/monolith/slice.rb +56 -0
  75. data/lib/hanami/cli/generators/monolith/slice/action.erb +9 -0
  76. data/lib/hanami/cli/generators/monolith/slice/entities.erb +9 -0
  77. data/lib/hanami/cli/generators/monolith/slice/keep.erb +0 -0
  78. data/lib/hanami/cli/generators/monolith/slice/repository.erb +10 -0
  79. data/lib/hanami/cli/generators/monolith/slice/routes.erb +2 -0
  80. data/lib/hanami/cli/generators/monolith/slice/view.erb +9 -0
  81. data/lib/hanami/cli/generators/monolith/slice_context.rb +33 -0
  82. data/lib/hanami/cli/repl/core.rb +55 -0
  83. data/lib/hanami/cli/repl/irb.rb +41 -0
  84. data/lib/hanami/cli/repl/pry.rb +29 -0
  85. data/lib/hanami/cli/system_call.rb +51 -0
  86. data/lib/hanami/cli/url.rb +34 -0
  87. data/lib/hanami/cli/version.rb +4 -3
  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 -41
  92. data/.travis.yml +0 -16
  93. data/lib/hanami/cli/banner.rb +0 -126
  94. data/lib/hanami/cli/command_registry.rb +0 -213
  95. data/lib/hanami/cli/errors.rb +0 -32
  96. data/lib/hanami/cli/option.rb +0 -125
  97. data/lib/hanami/cli/parser.rb +0 -122
  98. data/lib/hanami/cli/program_name.rb +0 -19
  99. data/lib/hanami/cli/registry.rb +0 -328
  100. data/lib/hanami/cli/usage.rb +0 -88
  101. data/script/ci +0 -51
@@ -0,0 +1,13 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "dry/transformer"
5
+
6
+ module <%= classified_app_name %>
7
+ module Functions
8
+ extend Dry::Transformer::Registry
9
+
10
+ import Dry::Transformer::ArrayTransformations
11
+ import Dry::Transformer::HashTransformations
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake"
6
+
7
+ gem "hanami-router", "<%= hanami_version %>"
8
+ gem "hanami-controller", "<%= hanami_version %>"
9
+ gem "hanami-validations", "<%= hanami_version %>"
10
+ gem "hanami-view", git: "https://github.com/hanami/view.git", branch: "master"
11
+ gem "dry-cli", "~> 0.6", require: false, git: "https://github.com/dry-rb/dry-cli.git", branch: "feature/file-utils-class"
12
+ gem "hanami-cli", git: "https://github.com/hanami/cli.git", branch: "main"
13
+ gem "hanami", require: false, git: "https://github.com/hanami/hanami.git", branch: "feature/hanami-2-cli"
14
+
15
+ gem "puma"
16
+
17
+ group :cli, :development, :test do
18
+ gem "hanami-rspec", git: "https://github.com/hanami/rspec.git", branch: "main"
19
+ end
@@ -0,0 +1,18 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "dry/monads"
5
+ require "dry/matcher/result_matcher"
6
+
7
+ module <%= classified_app_name %>
8
+ class Operation
9
+ include Dry::Monads[:result, :try]
10
+
11
+ class << self
12
+ def inherited(klass)
13
+ klass.include Dry::Monads[:do]
14
+ klass.include Dry::Matcher::ResultMatcher.for(:call)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/application/rake_tasks"
@@ -0,0 +1 @@
1
+ # <%= classified_app_name %>
@@ -0,0 +1,13 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "rom-repository"
5
+ require_relative "entities"
6
+
7
+ module <%= classified_app_name %>
8
+ class Repository < ROM::Repository::Root
9
+ include Deps[container: "persistence.rom"]
10
+
11
+ struct_namespace Entities
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ Hanami.application.routes do
4
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "<%= underscored_app_name %>/types"
4
+
5
+ Hanami.application.settings do
6
+ end
@@ -0,0 +1,10 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "dry/types"
5
+
6
+ module <%= classified_app_name %>
7
+ module Types
8
+ include Dry.Types
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "dry/validation"
5
+ require "dry/schema/messages/i18n"
6
+
7
+ module <%= classified_app_name %>
8
+ module Validation
9
+ class Contract < Dry::Validation::Contract
10
+ config.messages.backend = :i18n
11
+ config.messages.top_namespace = "validation"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/view/context"
4
+
5
+ module <%= classified_app_name %>
6
+ module View
7
+ class Context < Hanami::View::Context
8
+ def initialize(**args)
9
+ defaults = {content: {}}
10
+
11
+ super(**defaults.merge(args))
12
+ end
13
+ end
14
+ end
15
+ 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,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,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,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,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