hanami-cli 2.1.1 → 2.2.0.beta2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +29 -13
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +6 -1
- data/README.md +13 -7
- data/docker-compose.yml +14 -0
- data/hanami-cli.gemspec +2 -2
- data/lib/hanami/cli/command.rb +2 -2
- data/lib/hanami/cli/commands/app/command.rb +2 -16
- data/lib/hanami/cli/commands/app/db/command.rb +148 -0
- data/lib/hanami/cli/commands/app/db/create.rb +21 -11
- data/lib/hanami/cli/commands/app/db/drop.rb +21 -10
- data/lib/hanami/cli/commands/app/db/migrate.rb +50 -12
- data/lib/hanami/cli/commands/app/db/prepare.rb +68 -0
- data/lib/hanami/cli/commands/app/db/seed.rb +22 -21
- data/lib/hanami/cli/commands/app/db/structure/dump.rb +32 -7
- data/lib/hanami/cli/commands/app/db/structure/load.rb +54 -0
- data/lib/hanami/cli/commands/app/db/utils/database.rb +100 -75
- data/lib/hanami/cli/commands/app/db/utils/mysql.rb +59 -6
- data/lib/hanami/cli/commands/app/db/utils/postgres.rb +42 -21
- data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +58 -10
- data/lib/hanami/cli/commands/app/db/version.rb +14 -8
- data/lib/hanami/cli/commands/app/generate/action.rb +4 -3
- data/lib/hanami/cli/commands/app/generate/command.rb +60 -0
- data/lib/hanami/cli/commands/app/generate/component.rb +49 -0
- data/lib/hanami/cli/commands/app/generate/migration.rb +47 -0
- data/lib/hanami/cli/commands/app/generate/operation.rb +26 -0
- data/lib/hanami/cli/commands/app/generate/part.rb +1 -1
- data/lib/hanami/cli/commands/app/generate/relation.rb +29 -0
- data/lib/hanami/cli/commands/app/generate/repo.rb +42 -0
- data/lib/hanami/cli/commands/app/generate/slice.rb +20 -3
- data/lib/hanami/cli/commands/app/generate/struct.rb +27 -0
- data/lib/hanami/cli/commands/app/install.rb +1 -1
- data/lib/hanami/cli/commands/app/middleware.rb +1 -1
- data/lib/hanami/cli/commands/app/server.rb +2 -2
- data/lib/hanami/cli/commands/app.rb +21 -2
- data/lib/hanami/cli/commands/gem/new.rb +78 -14
- data/lib/hanami/cli/errors.rb +28 -0
- data/lib/hanami/cli/files.rb +22 -0
- data/lib/hanami/cli/generators/app/action_context.rb +5 -13
- data/lib/hanami/cli/generators/app/component/component.erb +8 -0
- data/lib/hanami/cli/generators/app/component/slice_component.erb +8 -0
- data/lib/hanami/cli/generators/app/component.rb +61 -0
- data/lib/hanami/cli/generators/app/component_context.rb +82 -0
- data/lib/hanami/cli/generators/app/migration.rb +66 -0
- data/lib/hanami/cli/generators/app/operation.rb +49 -0
- data/lib/hanami/cli/generators/app/part_context.rb +5 -21
- data/lib/hanami/cli/generators/app/relation.rb +45 -0
- data/lib/hanami/cli/generators/app/repo.rb +41 -0
- data/lib/hanami/cli/generators/app/ruby_file_writer.rb +151 -0
- data/lib/hanami/cli/generators/app/slice/{entities.erb → operation.erb} +1 -3
- data/lib/hanami/cli/generators/app/slice/relation.erb +8 -0
- data/lib/hanami/cli/generators/app/slice/{slice.erb → repo.erb} +3 -1
- data/lib/hanami/cli/generators/app/slice/struct.erb +8 -0
- data/lib/hanami/cli/generators/app/slice.rb +14 -6
- data/lib/hanami/cli/generators/app/slice_context.rb +9 -2
- data/lib/hanami/cli/generators/app/struct.rb +40 -0
- data/lib/hanami/cli/generators/app/view_context.rb +4 -16
- data/lib/hanami/cli/generators/constants.rb +39 -0
- data/lib/hanami/cli/generators/context.rb +48 -0
- data/lib/hanami/cli/generators/gem/app/action.erb +3 -0
- data/lib/hanami/cli/generators/gem/app/env.erb +4 -0
- data/lib/hanami/cli/generators/gem/app/gemfile.erb +11 -0
- data/lib/hanami/cli/generators/gem/app/gitignore.erb +4 -1
- data/lib/hanami/cli/generators/gem/app/operation.erb +13 -0
- data/lib/hanami/cli/generators/gem/app/relation.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/repo.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/struct.erb +10 -0
- data/lib/hanami/cli/generators/gem/app.rb +19 -0
- data/lib/hanami/cli/ruby_file_generator.rb +123 -0
- data/lib/hanami/cli/version.rb +1 -1
- metadata +39 -17
- data/lib/hanami/cli/commands/app/db/create_migration.rb +0 -32
- data/lib/hanami/cli/commands/app/db/reset.rb +0 -28
- data/lib/hanami/cli/commands/app/db/rollback.rb +0 -81
- data/lib/hanami/cli/commands/app/db/sample_data.rb +0 -42
- data/lib/hanami/cli/commands/app/db/setup.rb +0 -26
- data/lib/hanami/cli/commands/app/db/utils/database_config.rb +0 -60
- data/lib/hanami/cli/generators/app/slice/repository.erb +0 -10
data/lib/hanami/cli/files.rb
CHANGED
@@ -18,7 +18,11 @@ module Hanami
|
|
18
18
|
# @api private
|
19
19
|
def write(path, *content)
|
20
20
|
already_exists = exist?(path)
|
21
|
+
|
21
22
|
super
|
23
|
+
|
24
|
+
delete_keepfiles(path) unless already_exists
|
25
|
+
|
22
26
|
if already_exists
|
23
27
|
updated(path)
|
24
28
|
else
|
@@ -46,6 +50,24 @@ module Hanami
|
|
46
50
|
|
47
51
|
attr_reader :out
|
48
52
|
|
53
|
+
# Removes .keep files in any directories leading up to the given path.
|
54
|
+
#
|
55
|
+
# Does not attempt to remove `.keep` files in the following scenarios:
|
56
|
+
# - When the given path is a `.keep` file itself.
|
57
|
+
# - When the given path is absolute, since ascending up this path may lead to removal of
|
58
|
+
# files outside the Hanami project directory.
|
59
|
+
def delete_keepfiles(path)
|
60
|
+
path = Pathname(path)
|
61
|
+
|
62
|
+
return if path.absolute?
|
63
|
+
return if path.relative_path_from(path.dirname).to_s == ".keep"
|
64
|
+
|
65
|
+
path.dirname.ascend do |part|
|
66
|
+
keepfile = (part + ".keep").to_path
|
67
|
+
delete(keepfile) if exist?(keepfile)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
49
71
|
def updated(path)
|
50
72
|
out.puts "Updated #{path}"
|
51
73
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "slice_context"
|
4
3
|
require "dry/files/path"
|
4
|
+
require_relative "slice_context"
|
5
|
+
require_relative "../constants"
|
5
6
|
|
6
7
|
module Hanami
|
7
8
|
module CLI
|
@@ -52,7 +53,7 @@ module Hanami
|
|
52
53
|
# @api private
|
53
54
|
def module_controller_declaration
|
54
55
|
controller.each_with_index.map do |token, i|
|
55
|
-
"#{
|
56
|
+
"#{NESTED_OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
|
56
57
|
end.join($/)
|
57
58
|
end
|
58
59
|
|
@@ -60,14 +61,14 @@ module Hanami
|
|
60
61
|
# @api private
|
61
62
|
def module_controller_end
|
62
63
|
controller.each_with_index.map do |_, i|
|
63
|
-
"#{
|
64
|
+
"#{NESTED_OFFSET}#{INDENTATION * i}end"
|
64
65
|
end.reverse.join($/)
|
65
66
|
end
|
66
67
|
|
67
68
|
# @since 2.0.0
|
68
69
|
# @api private
|
69
70
|
def module_controller_offset
|
70
|
-
"#{
|
71
|
+
"#{NESTED_OFFSET}#{INDENTATION * controller.count}"
|
71
72
|
end
|
72
73
|
|
73
74
|
# @since 2.0.0
|
@@ -79,15 +80,6 @@ module Hanami
|
|
79
80
|
|
80
81
|
private
|
81
82
|
|
82
|
-
NAMESPACE_SEPARATOR = "::"
|
83
|
-
private_constant :NAMESPACE_SEPARATOR
|
84
|
-
|
85
|
-
INDENTATION = " "
|
86
|
-
private_constant :INDENTATION
|
87
|
-
|
88
|
-
OFFSET = INDENTATION * 2
|
89
|
-
private_constant :OFFSET
|
90
|
-
|
91
83
|
attr_reader :controller
|
92
84
|
|
93
85
|
attr_reader :action
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require "dry/files"
|
5
|
+
module Hanami
|
6
|
+
module CLI
|
7
|
+
module Generators
|
8
|
+
module App
|
9
|
+
# @api private
|
10
|
+
# @since 2.2.0
|
11
|
+
class Component
|
12
|
+
# @api private
|
13
|
+
# @since 2.2.0
|
14
|
+
def initialize(fs:, inflector:)
|
15
|
+
@fs = fs
|
16
|
+
@inflector = inflector
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
# @since 2.2.0
|
21
|
+
def call(app, key, slice)
|
22
|
+
context = ComponentContext.new(inflector, app, slice, key)
|
23
|
+
|
24
|
+
if slice
|
25
|
+
generate_for_slice(context, slice)
|
26
|
+
else
|
27
|
+
generate_for_app(context)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :fs
|
34
|
+
|
35
|
+
attr_reader :inflector
|
36
|
+
|
37
|
+
def generate_for_slice(context, slice)
|
38
|
+
slice_directory = fs.join("slices", slice)
|
39
|
+
raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
|
40
|
+
|
41
|
+
fs.mkdir(directory = fs.join(slice_directory, context.namespaces))
|
42
|
+
fs.write(fs.join(directory, "#{context.underscored_name}.rb"), t("slice_component.erb", context))
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_for_app(context)
|
46
|
+
fs.mkdir(directory = fs.join("app", context.namespaces))
|
47
|
+
fs.write(fs.join(directory, "#{context.underscored_name}.rb"), t("component.erb", context))
|
48
|
+
end
|
49
|
+
|
50
|
+
def template(path, context)
|
51
|
+
ERB.new(
|
52
|
+
File.read(__dir__ + "/component/#{path}")
|
53
|
+
).result(context.ctx)
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method :t, :template
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../constants"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module CLI
|
7
|
+
module Generators
|
8
|
+
module App
|
9
|
+
class ComponentContext < SliceContext
|
10
|
+
# @api private
|
11
|
+
# @since 2.2.0
|
12
|
+
attr_reader :key
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
# @since 2.2.0
|
16
|
+
def initialize(inflector, app, slice, key)
|
17
|
+
@key = key
|
18
|
+
super(inflector, app, slice, nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
# @since 2.2.0
|
23
|
+
def namespaces
|
24
|
+
@namespaces ||= key.split(MATCHER_PATTERN)[..-2].map { inflector.underscore(_1) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
# @since 2.2.0
|
29
|
+
def name
|
30
|
+
@name ||= key.split(MATCHER_PATTERN)[-1]
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
# @since 2.2.0
|
35
|
+
def camelized_namespace
|
36
|
+
namespaces.map { inflector.camelize(_1) }.join(NAMESPACE_SEPARATOR)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
# @since 2.2.0
|
41
|
+
def camelized_name
|
42
|
+
inflector.camelize(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
# @since 2.2.0
|
47
|
+
def underscored_namespace
|
48
|
+
namespaces.map { inflector.underscore(_1) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
# @since 2.2.0
|
53
|
+
def underscored_name
|
54
|
+
inflector.underscore(name)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @api private
|
58
|
+
# @since 2.2.0
|
59
|
+
def module_namespace_declaration
|
60
|
+
namespaces.each_with_index.map { |token, i|
|
61
|
+
"#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
|
62
|
+
}.join($/)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api private
|
66
|
+
# @since 2.2.0
|
67
|
+
def module_namespace_end
|
68
|
+
namespaces.each_with_index.map { |_, i|
|
69
|
+
"#{OFFSET}#{INDENTATION * i}end"
|
70
|
+
}.reverse.join($/)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @api private
|
74
|
+
# @since 2.2.0
|
75
|
+
def module_namespace_offset
|
76
|
+
"#{OFFSET}#{INDENTATION * namespaces.count}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
module Generators
|
6
|
+
module App
|
7
|
+
# @since 2.2.0
|
8
|
+
# @api private
|
9
|
+
class Migration
|
10
|
+
# @since 2.2.0
|
11
|
+
# @api private
|
12
|
+
def initialize(fs:, inflector:, out: $stdout)
|
13
|
+
@fs = fs
|
14
|
+
@inflector = inflector
|
15
|
+
@out = out
|
16
|
+
end
|
17
|
+
|
18
|
+
# @since 2.2.0
|
19
|
+
# @api private
|
20
|
+
def call(key:, base_path:, gateway: nil, **_opts)
|
21
|
+
name = inflector.underscore(key)
|
22
|
+
ensure_valid_name(name)
|
23
|
+
|
24
|
+
base_path = nil if base_path == "app" # Migrations are in the root dir, not app/
|
25
|
+
migrate_dir = gateway ? "#{gateway}_migrate" : "migrate"
|
26
|
+
|
27
|
+
path = fs.join(*[base_path, "config", "db", migrate_dir, file_name(name)].compact)
|
28
|
+
|
29
|
+
fs.write(path, FILE_CONTENTS)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :fs, :inflector, :out
|
35
|
+
|
36
|
+
VALID_NAME_REGEX = /^[_a-z0-9]+$/
|
37
|
+
private_constant :VALID_NAME_REGEX
|
38
|
+
|
39
|
+
def ensure_valid_name(name)
|
40
|
+
unless VALID_NAME_REGEX.match?(name.downcase)
|
41
|
+
raise InvalidMigrationNameError.new(name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def file_name(name)
|
46
|
+
"#{Time.now.strftime(VERSION_FORMAT)}_#{name}.rb"
|
47
|
+
end
|
48
|
+
|
49
|
+
VERSION_FORMAT = "%Y%m%d%H%M%S"
|
50
|
+
private_constant :VERSION_FORMAT
|
51
|
+
|
52
|
+
FILE_CONTENTS = <<~RUBY
|
53
|
+
# frozen_string_literal: true
|
54
|
+
|
55
|
+
ROM::SQL.migration do
|
56
|
+
# Add your migration here.
|
57
|
+
#
|
58
|
+
# See https://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html for details.
|
59
|
+
end
|
60
|
+
RUBY
|
61
|
+
private_constant :FILE_CONTENTS
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../constants"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module CLI
|
7
|
+
module Generators
|
8
|
+
module App
|
9
|
+
# @since 2.2.0
|
10
|
+
# @api private
|
11
|
+
class Operation
|
12
|
+
# @since 2.2.0
|
13
|
+
# @api private
|
14
|
+
def initialize(fs:, inflector:, out: $stdout)
|
15
|
+
@fs = fs
|
16
|
+
@inflector = inflector
|
17
|
+
@out = out
|
18
|
+
end
|
19
|
+
|
20
|
+
# @since 2.2.0
|
21
|
+
# @api private
|
22
|
+
def call(key:, namespace:, base_path:)
|
23
|
+
RubyFileWriter.new(
|
24
|
+
fs: fs,
|
25
|
+
inflector: inflector,
|
26
|
+
).call(
|
27
|
+
namespace: namespace,
|
28
|
+
base_path: base_path,
|
29
|
+
key: key,
|
30
|
+
relative_parent_class: "Operation",
|
31
|
+
body: ["def call", "end"],
|
32
|
+
)
|
33
|
+
|
34
|
+
unless key.match?(KEY_SEPARATOR)
|
35
|
+
out.puts(
|
36
|
+
" Note: We generated a top-level operation. " \
|
37
|
+
"To generate into a directory, add a namespace: `my_namespace.add_book`"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :fs, :inflector, :out
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "slice_context"
|
4
3
|
require "dry/files/path"
|
4
|
+
require_relative "slice_context"
|
5
|
+
require_relative "../constants"
|
5
6
|
|
6
7
|
module Hanami
|
7
8
|
module CLI
|
@@ -12,23 +13,6 @@ module Hanami
|
|
12
13
|
# @since 2.1.0
|
13
14
|
# @api private
|
14
15
|
class PartContext < SliceContext
|
15
|
-
# TODO: move these constants somewhere that will let us reuse them
|
16
|
-
|
17
|
-
# @since 2.1.0
|
18
|
-
# @api private
|
19
|
-
KEY_SEPARATOR = "."
|
20
|
-
private_constant :KEY_SEPARATOR
|
21
|
-
|
22
|
-
# @since 2.1.0
|
23
|
-
# @api private
|
24
|
-
INDENTATION = " "
|
25
|
-
private_constant :INDENTATION
|
26
|
-
|
27
|
-
# @since 2.1.0
|
28
|
-
# @api private
|
29
|
-
OFFSET = INDENTATION * 2
|
30
|
-
private_constant :OFFSET
|
31
|
-
|
32
16
|
# @since 2.1.0
|
33
17
|
# @api private
|
34
18
|
attr_reader :key
|
@@ -74,7 +58,7 @@ module Hanami
|
|
74
58
|
# @api private
|
75
59
|
def module_namespace_declaration
|
76
60
|
namespaces.each_with_index.map { |token, i|
|
77
|
-
"#{
|
61
|
+
"#{NESTED_OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
|
78
62
|
}.join($/)
|
79
63
|
end
|
80
64
|
|
@@ -82,14 +66,14 @@ module Hanami
|
|
82
66
|
# @api private
|
83
67
|
def module_namespace_end
|
84
68
|
namespaces.each_with_index.map { |_, i|
|
85
|
-
"#{
|
69
|
+
"#{NESTED_OFFSET}#{INDENTATION * i}end"
|
86
70
|
}.reverse.join($/)
|
87
71
|
end
|
88
72
|
|
89
73
|
# @since 2.1.0
|
90
74
|
# @api private
|
91
75
|
def module_namespace_offset
|
92
|
-
"#{
|
76
|
+
"#{NESTED_OFFSET}#{INDENTATION * namespaces.count}"
|
93
77
|
end
|
94
78
|
end
|
95
79
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../constants"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module CLI
|
7
|
+
module Generators
|
8
|
+
module App
|
9
|
+
# @since 2.2.0
|
10
|
+
# @api private
|
11
|
+
class Relation
|
12
|
+
# @since 2.2.0
|
13
|
+
# @api private
|
14
|
+
def initialize(fs:, inflector:, out: $stdout)
|
15
|
+
@fs = fs
|
16
|
+
@inflector = inflector
|
17
|
+
@out = out
|
18
|
+
end
|
19
|
+
|
20
|
+
# @since 2.2.0
|
21
|
+
# @api private
|
22
|
+
def call(key:, namespace:, base_path:)
|
23
|
+
schema_name = key.split(KEY_SEPARATOR).last
|
24
|
+
|
25
|
+
RubyFileWriter.new(
|
26
|
+
fs: fs,
|
27
|
+
inflector: inflector,
|
28
|
+
).call(
|
29
|
+
namespace: namespace,
|
30
|
+
key: key,
|
31
|
+
base_path: base_path,
|
32
|
+
extra_namespace: "Relations",
|
33
|
+
relative_parent_class: "DB::Relation",
|
34
|
+
body: ["schema :#{schema_name}, infer: true"],
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :fs, :inflector, :out
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
module Generators
|
6
|
+
module App
|
7
|
+
# @since 2.2.0
|
8
|
+
# @api private
|
9
|
+
class Repo
|
10
|
+
# @since 2.2.0
|
11
|
+
# @api private
|
12
|
+
def initialize(fs:, inflector:, out: $stdout)
|
13
|
+
@fs = fs
|
14
|
+
@inflector = inflector
|
15
|
+
@out = out
|
16
|
+
end
|
17
|
+
|
18
|
+
# @since 2.2.0
|
19
|
+
# @api private
|
20
|
+
def call(key:, namespace:, base_path:)
|
21
|
+
RubyFileWriter.new(
|
22
|
+
fs: fs,
|
23
|
+
inflector: inflector,
|
24
|
+
).call(
|
25
|
+
key: key,
|
26
|
+
namespace: namespace,
|
27
|
+
base_path: base_path,
|
28
|
+
extra_namespace: "Repos",
|
29
|
+
relative_parent_class: "DB::Repo",
|
30
|
+
body: [],
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :fs, :inflector, :out
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require "dry/files"
|
5
|
+
require_relative "../constants"
|
6
|
+
require_relative "../../errors"
|
7
|
+
|
8
|
+
module Hanami
|
9
|
+
module CLI
|
10
|
+
module Generators
|
11
|
+
module App
|
12
|
+
# @since 2.2.0
|
13
|
+
# @api private
|
14
|
+
class RubyFileWriter
|
15
|
+
# @since 2.2.0
|
16
|
+
# @api private
|
17
|
+
def initialize(fs:, inflector:)
|
18
|
+
@fs = fs
|
19
|
+
@inflector = inflector
|
20
|
+
end
|
21
|
+
|
22
|
+
# @since 2.2.0
|
23
|
+
# @api private
|
24
|
+
def call(key:, namespace:, base_path:, relative_parent_class:, extra_namespace: nil, body: [])
|
25
|
+
ClassFile.new(
|
26
|
+
fs: fs,
|
27
|
+
inflector: inflector,
|
28
|
+
key: key,
|
29
|
+
namespace: namespace,
|
30
|
+
base_path: base_path,
|
31
|
+
relative_parent_class: relative_parent_class,
|
32
|
+
extra_namespace: extra_namespace,
|
33
|
+
body: body,
|
34
|
+
).write
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @since 2.2.0
|
40
|
+
# @api private
|
41
|
+
attr_reader :fs, :inflector
|
42
|
+
end
|
43
|
+
|
44
|
+
class ClassFile
|
45
|
+
def initialize(
|
46
|
+
fs:,
|
47
|
+
inflector:,
|
48
|
+
key:,
|
49
|
+
namespace:,
|
50
|
+
base_path:,
|
51
|
+
relative_parent_class:,
|
52
|
+
extra_namespace: nil,
|
53
|
+
body: []
|
54
|
+
)
|
55
|
+
@fs = fs
|
56
|
+
@inflector = inflector
|
57
|
+
@key = key
|
58
|
+
@namespace = namespace
|
59
|
+
@base_path = base_path
|
60
|
+
@extra_namespace = extra_namespace&.downcase
|
61
|
+
@relative_parent_class = relative_parent_class
|
62
|
+
@body = body
|
63
|
+
end
|
64
|
+
|
65
|
+
def write
|
66
|
+
fs.write(path, file_contents)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# @since 2.2.0
|
72
|
+
# @api private
|
73
|
+
attr_reader(
|
74
|
+
:fs,
|
75
|
+
:inflector,
|
76
|
+
:key,
|
77
|
+
:namespace,
|
78
|
+
:base_path,
|
79
|
+
:extra_namespace,
|
80
|
+
:relative_parent_class,
|
81
|
+
:body,
|
82
|
+
)
|
83
|
+
|
84
|
+
# @since 2.2.0
|
85
|
+
# @api private
|
86
|
+
def file_contents
|
87
|
+
class_definition(
|
88
|
+
class_name: class_name,
|
89
|
+
local_namespaces: local_namespaces,
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @since 2.2.0
|
94
|
+
# @api private
|
95
|
+
def class_name
|
96
|
+
key.split(KEY_SEPARATOR)[-1]
|
97
|
+
end
|
98
|
+
|
99
|
+
# @since 2.2.0
|
100
|
+
# @api private
|
101
|
+
def local_namespaces
|
102
|
+
Array(extra_namespace) + key.split(KEY_SEPARATOR)[..-2]
|
103
|
+
end
|
104
|
+
|
105
|
+
# @since 2.2.0
|
106
|
+
# @api private
|
107
|
+
def directory
|
108
|
+
@directory ||= if local_namespaces.any?
|
109
|
+
fs.join(base_path, local_namespaces)
|
110
|
+
else
|
111
|
+
base_path
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @since 2.2.0
|
116
|
+
# @api private
|
117
|
+
def path
|
118
|
+
fs.join(directory, "#{class_name}.rb")
|
119
|
+
end
|
120
|
+
|
121
|
+
# @since 2.2.0
|
122
|
+
# @api private
|
123
|
+
def class_definition(class_name:, local_namespaces:)
|
124
|
+
container_module = normalize(namespace)
|
125
|
+
|
126
|
+
modules = local_namespaces
|
127
|
+
.map { normalize(_1) }
|
128
|
+
.compact
|
129
|
+
.prepend(container_module)
|
130
|
+
|
131
|
+
parent_class = [container_module, relative_parent_class].join("::")
|
132
|
+
|
133
|
+
RubyFileGenerator.class(
|
134
|
+
normalize(class_name),
|
135
|
+
parent_class: parent_class,
|
136
|
+
modules: modules,
|
137
|
+
header: ["# frozen_string_literal: true"],
|
138
|
+
body: body
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
# @since 2.2.0
|
143
|
+
# @api private
|
144
|
+
def normalize(name)
|
145
|
+
inflector.camelize(name).gsub(/[^\p{Alnum}]/, "")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|