hanami-cli 2.3.0.beta1 → 2.3.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 +0 -1
- data/CHANGELOG.md +17 -0
- data/hanami-cli.gemspec +1 -1
- data/lib/hanami/cli/commands/app/generate/action.rb +2 -2
- data/lib/hanami/cli/commands/app/run.rb +108 -0
- data/lib/hanami/cli/commands/app.rb +1 -0
- data/lib/hanami/cli/errors.rb +1 -1
- data/lib/hanami/cli/generators/app/action.rb +10 -4
- data/lib/hanami/cli/generators/app/component.rb +1 -1
- data/lib/hanami/cli/generators/app/relation.rb +1 -1
- data/lib/hanami/cli/generators/app/ruby_file.rb +7 -12
- data/lib/hanami/cli/generators/app/view.rb +1 -1
- data/lib/hanami/cli/generators/constants.rb +1 -1
- data/lib/hanami/cli/generators/gem/app/types.erb +1 -1
- data/lib/hanami/cli/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a260c1daad682ef7898890f677c8768f548512fdb591b2af60414ecaccc38fb
|
4
|
+
data.tar.gz: 58e9910a76e011243775ca794fc8dcd1769a1e31224136c5c8636a13a4e62f9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f871a3391a7c985202dd517dc4775dfef0d4c7aa9c498e11d60ac235c12833b3de59ee0a5356ab48069753e95f166f3d41d1a3c3acb3b5b9acc5ec9c8d68376
|
7
|
+
data.tar.gz: 4649a2c721659508d58b5a90c1ec7d955d321d50277f254a2db187222539c96086bd5bdce91ab55f0a1ba5b5244e08a76ddd767be6aec2f3c223b532928bf225
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,23 @@ Hanami Command Line Interface
|
|
4
4
|
|
5
5
|
## Unreleased
|
6
6
|
|
7
|
+
## v2.3.0.beta2 - 2025-10-17
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Add `hanami run` command, to run your own code. For example, `hanami run path/to/script.rb` or `hanami run 'puts Hanami.app["repos.user_repo"].all.count'`. (@afomera in #338)
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
- Add `--skip-tests` flag to `generate action` command. (@kyleplump in #335)
|
16
|
+
- In new apps, updated generated types module to `Dry.Types(default: :strict)`. (@minaslater in #323)
|
17
|
+
- When generators add routes, add those routes to per-slice routes files (`config/routes.rb` within slice directories) if they exist. (@stephannv in #342)
|
18
|
+
- Drop support for Ruby 3.1
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
|
22
|
+
- Handle mixed case names given to `generate` subcommands. (@cllns in #327)
|
23
|
+
|
7
24
|
## v2.3.0.beta1 - 2025-10-03
|
8
25
|
|
9
26
|
### Added
|
data/hanami-cli.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
29
|
spec.require_paths = ["lib"]
|
30
30
|
spec.metadata["rubygems_mfa_required"] = "true"
|
31
|
-
spec.required_ruby_version = ">= 3.
|
31
|
+
spec.required_ruby_version = ">= 3.2"
|
32
32
|
|
33
33
|
spec.add_dependency "bundler", "~> 2.1"
|
34
34
|
spec.add_dependency "dry-cli", "~> 1.0", ">= 1.1.0"
|
@@ -41,7 +41,6 @@ module Hanami
|
|
41
41
|
default: DEFAULT_SKIP_VIEW,
|
42
42
|
desc: "Skip view and template generation"
|
43
43
|
|
44
|
-
# TODO: Implement this
|
45
44
|
option \
|
46
45
|
:skip_tests,
|
47
46
|
required: false,
|
@@ -90,7 +89,7 @@ module Hanami
|
|
90
89
|
http_method: nil,
|
91
90
|
skip_view: DEFAULT_SKIP_VIEW,
|
92
91
|
skip_route: DEFAULT_SKIP_ROUTE,
|
93
|
-
skip_tests: DEFAULT_SKIP_TESTS
|
92
|
+
skip_tests: DEFAULT_SKIP_TESTS
|
94
93
|
)
|
95
94
|
name = Naming.new(inflector:).action_name(name)
|
96
95
|
|
@@ -103,6 +102,7 @@ module Hanami
|
|
103
102
|
skip_route:,
|
104
103
|
http_method:,
|
105
104
|
skip_view: skip_view || !Hanami.bundled?("hanami-view"),
|
105
|
+
skip_tests:
|
106
106
|
)
|
107
107
|
end
|
108
108
|
# rubocop:enable Lint/ParameterLists
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami"
|
4
|
+
require_relative "../../errors"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
module CLI
|
8
|
+
module Commands
|
9
|
+
module App
|
10
|
+
# Run a given code or file in the context of the application
|
11
|
+
#
|
12
|
+
# This command is useful for running scripts that need to load the application environment.
|
13
|
+
# You can pass a Ruby file to be executed, or you can run an interactive Ruby shell (IRB)
|
14
|
+
# with the application environment loaded.
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
#
|
18
|
+
# $ bundle exec hanami run path/to/script.rb
|
19
|
+
# $ bundle exec hanami run 'puts Hanami.app["repos.user_repo"].all.count'
|
20
|
+
#
|
21
|
+
# @since 2.0.0
|
22
|
+
# @api private
|
23
|
+
class Run < Hanami::CLI::Command
|
24
|
+
RunError = Class.new(StandardError)
|
25
|
+
|
26
|
+
desc "Run code in the context of the application"
|
27
|
+
|
28
|
+
example [
|
29
|
+
"path/to/script.rb # Run a Ruby script in the context of the application",
|
30
|
+
"'puts Hanami.app[\"repos.user_repo\"].all.count' # Run inline Ruby code in the context of the application",
|
31
|
+
]
|
32
|
+
|
33
|
+
argument :code_or_path, required: true, desc: "Path to a Ruby file or inline Ruby code to be executed"
|
34
|
+
|
35
|
+
def initialize(command_exit: method(:exit), **opts)
|
36
|
+
super(**opts)
|
37
|
+
@command_exit = command_exit
|
38
|
+
end
|
39
|
+
|
40
|
+
# rubocop:disable Metrics/AbcSize
|
41
|
+
def call(code_or_path:, **)
|
42
|
+
require "hanami/prepare"
|
43
|
+
|
44
|
+
if File.exist?(code_or_path)
|
45
|
+
validate_file_path!(code_or_path)
|
46
|
+
Kernel.load code_or_path
|
47
|
+
else
|
48
|
+
validate_inline_code!(code_or_path)
|
49
|
+
begin
|
50
|
+
eval(code_or_path, binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
|
51
|
+
rescue SyntaxError => e
|
52
|
+
err.puts "Syntax error in code: #{e.message}"
|
53
|
+
raise RunError, "Syntax error in code: #{e.message}"
|
54
|
+
rescue NameError => e
|
55
|
+
err.puts "Name error in code: #{e.message}"
|
56
|
+
raise RunError, "Name error in code: #{e.message}"
|
57
|
+
rescue StandardError => e
|
58
|
+
err.puts "Error executing code: #{e.class}: #{e.message}"
|
59
|
+
raise RunError, "Error executing code: #{e.class}: #{e.message}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
rescue RunError
|
63
|
+
@command_exit.call(1)
|
64
|
+
end
|
65
|
+
# rubocop:enable Metrics/AbcSize
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def validate_file_path!(file_path)
|
70
|
+
errors = []
|
71
|
+
|
72
|
+
# Ensure the file is a Ruby file
|
73
|
+
unless file_path.end_with?(".rb")
|
74
|
+
errors << "Error: Only Ruby files (.rb) are allowed"
|
75
|
+
end
|
76
|
+
|
77
|
+
# Resolve the absolute path and ensure it's within the app directory
|
78
|
+
resolved_path = File.expand_path(file_path)
|
79
|
+
app_root = File.expand_path(Dir.pwd)
|
80
|
+
|
81
|
+
unless resolved_path.start_with?(app_root)
|
82
|
+
errors << "Error: File must be within the application directory"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Check file size (prevent loading extremely large files)
|
86
|
+
file_size = File.size(file_path)
|
87
|
+
if file_size > 10 * 1024 * 1024 # 10MB limit
|
88
|
+
errors << "Error: File too large (maximum 10MB allowed)"
|
89
|
+
end
|
90
|
+
|
91
|
+
unless errors.empty?
|
92
|
+
errors.each { |error| err.puts error }
|
93
|
+
raise RunError, errors.join("\n")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def validate_inline_code!(code)
|
98
|
+
# Basic validation for inline code
|
99
|
+
if code.length > 10_000 # 10KB limit for inline code
|
100
|
+
err.puts "Error: Inline code too long (maximum 10,000 characters allowed)"
|
101
|
+
raise RunError, "Error: Inline code too long (maximum 10,000 characters allowed)"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -18,6 +18,7 @@ module Hanami
|
|
18
18
|
register "console", Commands::App::Console, aliases: ["c"]
|
19
19
|
register "server", Commands::App::Server, aliases: ["s"]
|
20
20
|
register "routes", Commands::App::Routes
|
21
|
+
register "run", Commands::App::Run
|
21
22
|
register "middleware", Commands::App::Middleware
|
22
23
|
|
23
24
|
if Hanami.bundled?("hanami-assets")
|
data/lib/hanami/cli/errors.rb
CHANGED
@@ -47,7 +47,7 @@ module Hanami
|
|
47
47
|
# @api public
|
48
48
|
class FileAlreadyExistsError < Error
|
49
49
|
ERROR_MESSAGE = <<~ERROR.chomp
|
50
|
-
The file
|
50
|
+
The file `%<file_path>s` could not be generated because it already exists.
|
51
51
|
ERROR
|
52
52
|
|
53
53
|
def initialize(file_path)
|
@@ -27,7 +27,7 @@ module Hanami
|
|
27
27
|
|
28
28
|
# @since 2.0.0
|
29
29
|
# @api private
|
30
|
-
def call(key:, namespace:, base_path:, url_path:, http_method:, skip_view:, skip_route:)
|
30
|
+
def call(key:, namespace:, base_path:, url_path:, http_method:, skip_view:, skip_route:, skip_tests:)
|
31
31
|
insert_route(key:, namespace:, url_path:, http_method:) unless skip_route
|
32
32
|
|
33
33
|
generate_action(key:, namespace:, base_path:, include_placeholder_body: skip_view)
|
@@ -83,8 +83,14 @@ module Hanami
|
|
83
83
|
if namespace == Hanami.app.namespace
|
84
84
|
fs.inject_line_at_class_bottom(routes_location, "class Routes", route)
|
85
85
|
else
|
86
|
-
|
87
|
-
|
86
|
+
slice_routes = fs.join("slices", namespace, "config", "routes.rb")
|
87
|
+
|
88
|
+
if fs.exist?(slice_routes)
|
89
|
+
fs.inject_line_at_class_bottom(slice_routes, "class Routes", route)
|
90
|
+
else
|
91
|
+
slice_matcher = /slice[[:space:]]*:#{namespace}/
|
92
|
+
fs.inject_line_at_block_bottom(routes_location, slice_matcher, route)
|
93
|
+
end
|
88
94
|
end
|
89
95
|
end
|
90
96
|
|
@@ -95,7 +101,7 @@ module Hanami
|
|
95
101
|
fs: fs,
|
96
102
|
inflector: inflector,
|
97
103
|
namespace: namespace,
|
98
|
-
key:
|
104
|
+
key: key,
|
99
105
|
base_path: base_path,
|
100
106
|
parent_class_name: "#{inflector.camelize(namespace)}::Action",
|
101
107
|
extra_namespace: "Actions",
|
@@ -20,7 +20,7 @@ module Hanami
|
|
20
20
|
# @since 2.2.0
|
21
21
|
# @api private
|
22
22
|
def call(key:, namespace:, base_path:, gateway:)
|
23
|
-
schema_name = key.split(KEY_SEPARATOR).last
|
23
|
+
schema_name = inflector.underscore(key.split(KEY_SEPARATOR).last)
|
24
24
|
body_content = ["schema :#{schema_name}, infer: true"]
|
25
25
|
|
26
26
|
body_content.prepend("gateway :#{gateway}") if gateway
|
@@ -17,11 +17,11 @@ module Hanami
|
|
17
17
|
extra_namespace: nil,
|
18
18
|
auto_register: nil,
|
19
19
|
body: [],
|
20
|
-
**
|
20
|
+
**_opts
|
21
21
|
)
|
22
22
|
@fs = fs
|
23
23
|
@inflector = inflector
|
24
|
-
@
|
24
|
+
@key_segments = key.split(KEY_SEPARATOR).map { |segment| inflector.underscore(segment) }
|
25
25
|
@namespace = namespace
|
26
26
|
@base_path = base_path
|
27
27
|
@extra_namespace = extra_namespace&.downcase
|
@@ -42,13 +42,13 @@ module Hanami
|
|
42
42
|
# @api private
|
43
43
|
def fully_qualified_name
|
44
44
|
inflector.camelize(
|
45
|
-
[namespace, extra_namespace, *
|
45
|
+
[namespace, extra_namespace, *key_segments].join("/"),
|
46
46
|
)
|
47
47
|
end
|
48
48
|
|
49
49
|
# @api private
|
50
50
|
def path
|
51
|
-
fs.join(directory, "#{
|
51
|
+
fs.join(directory, "#{key_segments.last}.rb")
|
52
52
|
end
|
53
53
|
|
54
54
|
private
|
@@ -57,7 +57,7 @@ module Hanami
|
|
57
57
|
attr_reader(
|
58
58
|
:fs,
|
59
59
|
:inflector,
|
60
|
-
:
|
60
|
+
:key_segments,
|
61
61
|
:base_path,
|
62
62
|
:namespace,
|
63
63
|
:extra_namespace,
|
@@ -79,7 +79,7 @@ module Hanami
|
|
79
79
|
|
80
80
|
# @api private
|
81
81
|
def local_namespaces
|
82
|
-
Array(extra_namespace) +
|
82
|
+
Array(extra_namespace) + key_segments[..-2]
|
83
83
|
end
|
84
84
|
|
85
85
|
# @api private
|
@@ -100,7 +100,7 @@ module Hanami
|
|
100
100
|
|
101
101
|
# @api private
|
102
102
|
def constant_name
|
103
|
-
normalize(
|
103
|
+
normalize(key_segments.last)
|
104
104
|
end
|
105
105
|
|
106
106
|
# @api private
|
@@ -116,11 +116,6 @@ module Hanami
|
|
116
116
|
def normalize(name)
|
117
117
|
inflector.camelize(name).gsub(/[^\p{Alnum}]/, "")
|
118
118
|
end
|
119
|
-
|
120
|
-
# @api private
|
121
|
-
def key_parts
|
122
|
-
key.split(KEY_SEPARATOR)
|
123
|
-
end
|
124
119
|
end
|
125
120
|
end
|
126
121
|
end
|
data/lib/hanami/cli/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.0.
|
4
|
+
version: 2.3.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -255,6 +255,7 @@ files:
|
|
255
255
|
- lib/hanami/cli/commands/app/install.rb
|
256
256
|
- lib/hanami/cli/commands/app/middleware.rb
|
257
257
|
- lib/hanami/cli/commands/app/routes.rb
|
258
|
+
- lib/hanami/cli/commands/app/run.rb
|
258
259
|
- lib/hanami/cli/commands/app/server.rb
|
259
260
|
- lib/hanami/cli/commands/app/version.rb
|
260
261
|
- lib/hanami/cli/commands/gem.rb
|
@@ -342,7 +343,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
342
343
|
requirements:
|
343
344
|
- - ">="
|
344
345
|
- !ruby/object:Gem::Version
|
345
|
-
version: '3.
|
346
|
+
version: '3.2'
|
346
347
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
347
348
|
requirements:
|
348
349
|
- - ">="
|