hanami-cli 0.2.0 → 2.0.0.alpha2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +42 -0
- data/.gitignore +4 -2
- data/.rspec +1 -0
- data/.rubocop.yml +25 -1
- data/CHANGELOG.md +39 -1
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +13 -6
- data/LICENSE.txt +21 -0
- data/README.md +13 -488
- data/Rakefile +8 -15
- data/bin/console +1 -0
- data/exe/hanami +10 -0
- data/hanami-cli.gemspec +24 -18
- data/lib/hanami/cli.rb +10 -121
- data/lib/hanami/cli/bundler.rb +73 -0
- data/lib/hanami/cli/command.rb +16 -355
- data/lib/hanami/cli/command_line.rb +17 -0
- data/lib/hanami/cli/commands.rb +26 -0
- data/lib/hanami/cli/commands/application.rb +63 -0
- data/lib/hanami/cli/commands/db/utils/database.rb +122 -0
- data/lib/hanami/cli/commands/db/utils/database_config.rb +48 -0
- data/lib/hanami/cli/commands/db/utils/mysql.rb +27 -0
- data/lib/hanami/cli/commands/db/utils/postgres.rb +49 -0
- data/lib/hanami/cli/commands/db/utils/sqlite.rb +37 -0
- data/lib/hanami/cli/commands/gem.rb +21 -0
- data/lib/hanami/cli/commands/gem/new.rb +77 -0
- data/lib/hanami/cli/commands/gem/version.rb +18 -0
- data/lib/hanami/cli/commands/monolith.rb +55 -0
- data/lib/hanami/cli/commands/monolith/console.rb +50 -0
- data/lib/hanami/cli/commands/monolith/db/create.rb +25 -0
- data/lib/hanami/cli/commands/monolith/db/create_migration.rb +29 -0
- data/lib/hanami/cli/commands/monolith/db/drop.rb +25 -0
- data/lib/hanami/cli/commands/monolith/db/migrate.rb +40 -0
- data/lib/hanami/cli/commands/monolith/db/reset.rb +26 -0
- data/lib/hanami/cli/commands/monolith/db/rollback.rb +55 -0
- data/lib/hanami/cli/commands/monolith/db/sample_data.rb +40 -0
- data/lib/hanami/cli/commands/monolith/db/seed.rb +40 -0
- data/lib/hanami/cli/commands/monolith/db/setup.rb +24 -0
- data/lib/hanami/cli/commands/monolith/db/structure/dump.rb +25 -0
- data/lib/hanami/cli/commands/monolith/db/version.rb +26 -0
- data/lib/hanami/cli/commands/monolith/generate.rb +14 -0
- data/lib/hanami/cli/commands/monolith/generate/action.rb +62 -0
- data/lib/hanami/cli/commands/monolith/generate/slice.rb +62 -0
- data/lib/hanami/cli/commands/monolith/install.rb +16 -0
- data/lib/hanami/cli/commands/monolith/version.rb +18 -0
- data/lib/hanami/cli/error.rb +8 -0
- data/lib/hanami/cli/generators/context.rb +38 -0
- data/lib/hanami/cli/generators/gem/application.rb +21 -0
- data/lib/hanami/cli/generators/gem/application/monolith.rb +83 -0
- data/lib/hanami/cli/generators/gem/application/monolith/action.erb +21 -0
- data/lib/hanami/cli/generators/gem/application/monolith/application.erb +8 -0
- data/lib/hanami/cli/generators/gem/application/monolith/config_ru.erb +5 -0
- data/lib/hanami/cli/generators/gem/application/monolith/entities.erb +9 -0
- data/lib/hanami/cli/generators/gem/application/monolith/env.erb +0 -0
- data/lib/hanami/cli/generators/gem/application/monolith/functions.erb +13 -0
- data/lib/hanami/cli/generators/gem/application/monolith/gemfile.erb +19 -0
- data/lib/hanami/cli/generators/gem/application/monolith/keep.erb +0 -0
- data/lib/hanami/cli/generators/gem/application/monolith/operation.erb +18 -0
- data/lib/hanami/cli/generators/gem/application/monolith/rakefile.erb +3 -0
- data/lib/hanami/cli/generators/gem/application/monolith/readme.erb +1 -0
- data/lib/hanami/cli/generators/gem/application/monolith/repository.erb +13 -0
- data/lib/hanami/cli/generators/gem/application/monolith/routes.erb +4 -0
- data/lib/hanami/cli/generators/gem/application/monolith/settings.erb +6 -0
- data/lib/hanami/cli/generators/gem/application/monolith/types.erb +10 -0
- data/lib/hanami/cli/generators/gem/application/monolith/validation_contract.erb +14 -0
- data/lib/hanami/cli/generators/gem/application/monolith/view_context.erb +15 -0
- data/lib/hanami/cli/generators/monolith/action.rb +123 -0
- data/lib/hanami/cli/generators/monolith/action/action.erb +13 -0
- data/lib/hanami/cli/generators/monolith/action/template.erb +0 -0
- data/lib/hanami/cli/generators/monolith/action/template.html.erb +2 -0
- data/lib/hanami/cli/generators/monolith/action/view.erb +13 -0
- data/lib/hanami/cli/generators/monolith/action_context.rb +76 -0
- data/lib/hanami/cli/generators/monolith/slice.rb +56 -0
- data/lib/hanami/cli/generators/monolith/slice/action.erb +9 -0
- data/lib/hanami/cli/generators/monolith/slice/entities.erb +9 -0
- data/lib/hanami/cli/generators/monolith/slice/keep.erb +0 -0
- data/lib/hanami/cli/generators/monolith/slice/repository.erb +10 -0
- data/lib/hanami/cli/generators/monolith/slice/routes.erb +2 -0
- data/lib/hanami/cli/generators/monolith/slice/view.erb +9 -0
- data/lib/hanami/cli/generators/monolith/slice_context.rb +33 -0
- data/lib/hanami/cli/repl/core.rb +55 -0
- data/lib/hanami/cli/repl/irb.rb +41 -0
- data/lib/hanami/cli/repl/pry.rb +29 -0
- data/lib/hanami/cli/system_call.rb +51 -0
- data/lib/hanami/cli/url.rb +34 -0
- data/lib/hanami/cli/version.rb +4 -3
- data/lib/hanami/console/context.rb +39 -0
- data/lib/hanami/console/plugins/slice_readers.rb +42 -0
- data/lib/hanami/rake_tasks.rb +52 -0
- metadata +138 -41
- data/.travis.yml +0 -16
- data/lib/hanami/cli/banner.rb +0 -126
- data/lib/hanami/cli/command_registry.rb +0 -213
- data/lib/hanami/cli/errors.rb +0 -32
- data/lib/hanami/cli/option.rb +0 -125
- data/lib/hanami/cli/parser.rb +0 -122
- data/lib/hanami/cli/program_name.rb +0 -19
- data/lib/hanami/cli/registry.rb +0 -328
- data/lib/hanami/cli/usage.rb +0 -88
- 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
|
File without changes
|
@@ -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 @@
|
|
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,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
|
File without changes
|
@@ -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
|