hanami-cli 2.0.0.beta1 → 2.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -1
  3. data/CHANGELOG.md +34 -2
  4. data/hanami-cli.gemspec +1 -1
  5. data/lib/hanami/cli/commands/app/command.rb +4 -3
  6. data/lib/hanami/cli/commands/app/console.rb +1 -0
  7. data/lib/hanami/cli/commands/app/db/create.rb +1 -0
  8. data/lib/hanami/cli/commands/app/generate/action.rb +9 -12
  9. data/lib/hanami/cli/commands/app/middlewares.rb +47 -0
  10. data/lib/hanami/cli/commands/app/routes.rb +1 -1
  11. data/lib/hanami/cli/commands/app.rb +9 -11
  12. data/lib/hanami/cli/commands/db/utils/database.rb +8 -1
  13. data/lib/hanami/cli/commands/db/utils/postgres.rb +5 -0
  14. data/lib/hanami/cli/commands.rb +9 -1
  15. data/lib/hanami/cli/generators/app/action/action.erb +5 -5
  16. data/lib/hanami/cli/generators/app/action/slice_action.erb +13 -0
  17. data/lib/hanami/cli/generators/app/action/template.html.erb +1 -1
  18. data/lib/hanami/cli/generators/app/action/view.erb +2 -2
  19. data/lib/hanami/cli/generators/app/action.rb +56 -33
  20. data/lib/hanami/cli/generators/app/action_context.rb +5 -5
  21. data/lib/hanami/cli/generators/app/slice/action.erb +2 -4
  22. data/lib/hanami/cli/generators/app/slice/entities.erb +1 -1
  23. data/lib/hanami/cli/generators/app/slice/repository.erb +2 -2
  24. data/lib/hanami/cli/generators/app/slice/routes.erb +3 -2
  25. data/lib/hanami/cli/generators/app/slice/slice.erb +6 -0
  26. data/lib/hanami/cli/generators/app/slice/view.erb +2 -2
  27. data/lib/hanami/cli/generators/app/slice.rb +13 -10
  28. data/lib/hanami/cli/generators/app/slice_context.rb +2 -2
  29. data/lib/hanami/cli/generators/context.rb +2 -2
  30. data/lib/hanami/cli/generators/gem/app/action.erb +1 -1
  31. data/lib/hanami/cli/generators/gem/app/app.erb +1 -1
  32. data/lib/hanami/cli/generators/gem/app/gemfile.erb +12 -3
  33. data/lib/hanami/cli/generators/gem/app/puma.erb +15 -0
  34. data/lib/hanami/cli/generators/gem/app/readme.erb +1 -1
  35. data/lib/hanami/cli/generators/gem/app/routes.erb +2 -6
  36. data/lib/hanami/cli/generators/gem/app/settings.erb +4 -2
  37. data/lib/hanami/cli/generators/gem/app/types.erb +4 -3
  38. data/lib/hanami/cli/generators/gem/app/validator.erb +1 -1
  39. data/lib/hanami/cli/generators/gem/app.rb +1 -0
  40. data/lib/hanami/cli/middleware_stack_inspector.rb +41 -0
  41. data/lib/hanami/cli/version.rb +1 -1
  42. data/lib/hanami/console/context.rb +3 -2
  43. metadata +11 -13
  44. data/lib/hanami/cli/generators/gem/app/operation.erb +0 -9
  45. data/lib/hanami/cli/generators/gem/app/relation.erb +0 -7
  46. data/lib/hanami/cli/generators/gem/app/repository.erb +0 -9
  47. data/lib/hanami/cli/generators/gem/app/transformations.erb +0 -13
  48. data/lib/hanami/cli/generators/gem/app/view.erb +0 -9
  49. data/lib/hanami/cli/generators/gem/app/view_context.erb +0 -10
  50. data/lib/hanami/cli/generators/gem/app/view_part.erb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d2d0f04510c5ac3d7c729c2830997ec692f9d9be7161b199cf87fcfd1284cf3
4
- data.tar.gz: 970de26f0dfdaba799141c24b72ea84ab94721d378f62c3bbbfe494e9a63c3bd
3
+ metadata.gz: 2652fcd9f660db53b10864142c4a96a0acf3991330b5a5d1b3f0270b5dcbc8ff
4
+ data.tar.gz: 983957c43ad1d96d7e77db6924346a6021928229eca699d0aafc28c7fcc41cd8
5
5
  SHA512:
6
- metadata.gz: 5dab4fa87022146327320394b66cdd8efb18ca9c2f98eb743bc65f38017a5904a0f06db3d45aca5990e51fac27868f0e9c026feab80639bf97791353670a219b
7
- data.tar.gz: 594c4c1f6fa03910543700dbcfa0ff16f5be462cc4f39b19c9918d66de3b071beeb9e8b187ae39d3f55285296126d1378a188cd87b08d575d00d9ef1a0b21ffd
6
+ metadata.gz: 9ad2064d1634dfae55848d8e318a7e6b4a0022c56e50b7ed83634e0bc29b5bcd18ba8a8adad14d764125e219827955cbbeef2dc8cc7b788da5543e7f263f5c56
7
+ data.tar.gz: e911ef639b84a76e24a7b30ed7d778a0f4deb43fee8bd406cc52d44188f1ddc0de16622db87f406f42f6551c8fccc67e8fbe9ae4c6b7b8947610df06dfd21d32
data/.rubocop.yml CHANGED
@@ -7,9 +7,10 @@ inherit_from:
7
7
  Layout/LineLength:
8
8
  Exclude:
9
9
  - Gemfile
10
+ - 'spec/**/*.rb'
10
11
  Lint/EmptyFile:
11
12
  Exclude:
12
- - "spec/fixtures/**/*.rb"
13
+ - 'spec/fixtures/**/*.rb'
13
14
  Naming/HeredocDelimiterNaming:
14
15
  Enabled: false
15
16
  Naming/MethodParameterName:
@@ -21,6 +22,8 @@ Style/BlockDelimiters:
21
22
  Enabled: false
22
23
  Style/CommentedKeyword:
23
24
  Enabled: false
25
+ Style/EmptyHeredoc:
26
+ Enabled: false
24
27
  Style/LambdaCall:
25
28
  Enabled: false
26
29
  Style/TrailingCommaInArguments:
@@ -29,3 +32,5 @@ Style/TrailingCommaInArrayLiteral:
29
32
  Enabled: false
30
33
  Style/TrailingCommaInHashLiteral:
31
34
  Enabled: false
35
+ Style/StringConcatenation:
36
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -2,14 +2,46 @@
2
2
 
3
3
  Hanami Command Line Interface
4
4
 
5
+ ## v2.0.0.beta3 - 2022-09-21
6
+
7
+ ### Added
8
+
9
+ - [Luca Guidi] New applications to support Puma server out of the box. Add the `puma` gem to `Gemfile` and generate `config/puma.rb`.
10
+ - [Luca Guidi] New applications to support code reloading for `hanami server` via `hanami-reloader`. This gem is added to app's `Gemfile`.
11
+ - [Marc Busqué] Introduce code reloading for `hanami console` via `#reload` method to be invoked within the console context.
12
+
13
+ ### Fixed
14
+
15
+ - [Luca Guidi] Respect plural when generating code via `hanami new` and `hanami generate`. Example: `hanami new code_insights` => `CodeInsights` instead of `CodeInsight`
16
+ - [Luca Guidi] Ensure `hanami generate action` to not crash when invoked with non-RESTful action names. Example: `hanami generate action talent.apply`
17
+
18
+ ### Changed
19
+
20
+ - [Piotr Solnica] `hanami generate action` to add routes to `config/routes.rb` without the `define` block context. See https://github.com/hanami/hanami/pull/1208
21
+
22
+ ## v2.0.0.beta2 - 2022-08-16
23
+
24
+ ### Added
25
+
26
+ - [Luca Guidi] Added `hanami generate slice` command, for generating a new slice [#32]
27
+ - [Luca Guidi] Added `hanami generate action` command, for generating a new action in the app or a slice [#33]
28
+ - [Marc Busqué] Added `hanami middlewares` command, for listing middleware configed in app and/or in `config/routes.rb` [#30]
29
+
30
+ ### Changed
31
+
32
+ - [Marc Busqué, Tim Riley] `hanami` command will detect the app even when called inside nested subdirectories [#34]
33
+ - [Seb Wilgosz] Include hanami-validations in generated app’s `Gemfile`, allowing action classes to specify `.params` [#31]
34
+ - [Tim Riley] Include dry-types in generated app’s `Gemfile`, which is used by the types module defined in `lib/[app_name]/types.rb` (dry-types is no longer a hard dependency of the hanami gem as of 2.0.0.beta2) [#29]
35
+ - [Tim Riley] Include dotenv in generated app’s `Gemfile`, allowing `ENV` values to be loaded from `.env*` files [#29]
36
+
5
37
  ## v2.0.0.beta1 - 2022-07-20
6
38
 
7
39
  ### Added
8
40
 
9
41
  - [Luca Guidi] Implemented `hanami new` to generate a new Hanami app
10
42
  - [Piotr Solnica] Implemented `hanami console` to start an interactive console (REPL)
11
- - [Marc Busque] Implemented `hanami server` to start a HTTP server based on Rack
12
- - [Marc Busque] Implemented `hanami routes` to print app routes
43
+ - [Marc Busqué] Implemented `hanami server` to start a HTTP server based on Rack
44
+ - [Marc Busqué] Implemented `hanami routes` to print app routes
13
45
  - [Luca Guidi] Implemented `hanami version` to print app Hanami version
14
46
 
15
47
  ## v2.0.0.alpha8.1 - 2022-05-23
data/hanami-cli.gemspec CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_dependency "bundler", "~> 2.1"
34
34
  spec.add_dependency "rake", "~> 13.0"
35
35
  spec.add_dependency "dry-cli", "~> 0.7"
36
- spec.add_dependency "dry-files", "~> 0.2", ">= 0.2.0"
36
+ spec.add_dependency "dry-files", "~> 0.3", ">= 0.3.0"
37
37
  spec.add_dependency "dry-inflector", "~> 0.2"
38
38
 
39
39
  spec.add_development_dependency "rspec", "~> 3.9"
@@ -8,21 +8,22 @@ module Hanami
8
8
  module Commands
9
9
  module App
10
10
  class Command < Hanami::CLI::Command
11
+ ACTION_SEPARATOR = "."
12
+
11
13
  module Environment
12
- def call(**opts)
14
+ def call(*args, **opts)
13
15
  env = opts[:env]
14
16
 
15
17
  hanami_env = env ? env.to_s : ENV.fetch("HANAMI_ENV", "development")
16
18
 
17
19
  ENV["HANAMI_ENV"] = hanami_env
18
20
 
19
- super(**opts)
21
+ super(*args, **opts)
20
22
  end
21
23
  end
22
24
 
23
25
  def self.inherited(klass)
24
26
  super
25
- klass.option(:env, required: false, desc: "App's environment")
26
27
  klass.prepend(Environment)
27
28
  end
28
29
 
@@ -25,6 +25,7 @@ module Hanami
25
25
 
26
26
  desc "App REPL"
27
27
 
28
+ option :env, required: false, desc: "Application environment"
28
29
  option :repl, required: false, desc: "REPL gem that should be used ('pry' or 'irb')"
29
30
 
30
31
  # @api private
@@ -15,6 +15,7 @@ module Hanami
15
15
  out.puts "=> database #{database.name} created"
16
16
  else
17
17
  out.puts "=> failed to create database #{database.name}"
18
+ exit $?.exitstatus
18
19
  end
19
20
  end
20
21
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/cli/command"
3
+ require "hanami/cli/commands/app/command"
4
4
  require "hanami/cli/generators/app/action"
5
5
  require "dry/inflector"
6
6
  require "dry/files"
@@ -11,7 +11,7 @@ module Hanami
11
11
  module Commands
12
12
  module App
13
13
  module Generate
14
- class Action < Command
14
+ class Action < App::Command
15
15
  # TODO: ideally the default format should lookup
16
16
  # slice configuration (Action's `default_response_format`)
17
17
  DEFAULT_FORMAT = "html"
@@ -20,13 +20,13 @@ module Hanami
20
20
  DEFAULT_SKIP_VIEW = false
21
21
  private_constant :DEFAULT_SKIP_VIEW
22
22
 
23
- argument :slice, required: true, desc: "Slice name"
24
23
  argument :name, required: true, desc: "Action name"
25
24
  option :url, required: false, type: :string, desc: "Action URL"
26
25
  option :http, required: false, type: :string, desc: "Action HTTP method"
27
- option :format, required: false, type: :string, default: DEFAULT_FORMAT, desc: "Template format"
28
- option :skip_view, required: false, type: :boolean, default: DEFAULT_SKIP_VIEW,
29
- desc: "Skip view and template generation"
26
+ # option :format, required: false, type: :string, default: DEFAULT_FORMAT, desc: "Template format"
27
+ # option :skip_view, required: false, type: :boolean, default: DEFAULT_SKIP_VIEW,
28
+ # desc: "Skip view and template generation"
29
+ option :slice, required: false, desc: "Slice name"
30
30
 
31
31
  def initialize(fs: Dry::Files.new, inflector: Dry::Inflector.new,
32
32
  generator: Generators::App::Action.new(fs: fs, inflector: inflector), **)
@@ -35,8 +35,8 @@ module Hanami
35
35
  end
36
36
 
37
37
  # rubocop:disable Metrics/ParameterLists
38
- def call(slice:, name:, url: nil, http: nil, format: DEFAULT_FORMAT, skip_view: DEFAULT_SKIP_VIEW, **)
39
- slice = inflector.underscore(Shellwords.shellescape(slice))
38
+ def call(name:, url: nil, http: nil, format: DEFAULT_FORMAT, skip_view: DEFAULT_SKIP_VIEW, slice: nil, **)
39
+ slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
40
40
  name = inflector.underscore(Shellwords.shellescape(name))
41
41
  *controller, action = name.split(ACTION_SEPARATOR)
42
42
 
@@ -44,15 +44,12 @@ module Hanami
44
44
  raise ArgumentError.new("cannot parse controller and action name: `#{name}'\n\texample: users.show")
45
45
  end
46
46
 
47
- generator.call(slice, controller, action, url, http, format, skip_view)
47
+ generator.call(app.namespace, controller, action, url, http, format, skip_view, slice)
48
48
  end
49
49
  # rubocop:enable Metrics/ParameterLists
50
50
 
51
51
  private
52
52
 
53
- ACTION_SEPARATOR = "."
54
- private_constant :ACTION_SEPARATOR
55
-
56
53
  attr_reader :generator
57
54
  end
58
55
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami"
4
+ require "hanami/cli/middleware_stack_inspector"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Commands
9
+ module App
10
+ # List registered middlewares in the app router
11
+ #
12
+ # It outputs middlewares registered along with the paths where they
13
+ # apply:
14
+ #
15
+ # ```
16
+ # $ hanami middlewares
17
+ # / Rack::Session::Cookie
18
+ # ```
19
+ #
20
+ # Given arguments can be inspected:
21
+ #
22
+ # ```
23
+ # $ hanami middlewares --with-arguments
24
+ # / Rack::Session::Cookie args: [{:secret=>"foo"}]
25
+ # ```
26
+ class Middlewares < Hanami::CLI::Command
27
+ desc "List all the registered middlewares"
28
+
29
+ DEFAULT_WITH_ARGUMENTS = false
30
+
31
+ option :with_arguments, default: DEFAULT_WITH_ARGUMENTS, required: false,
32
+ desc: "Include inspected arguments", type: :boolean
33
+
34
+ # @api private
35
+ def call(with_arguments: DEFAULT_WITH_ARGUMENTS)
36
+ require "hanami/prepare"
37
+
38
+ if Hanami.app.router
39
+ inspector = MiddlewareStackInspector.new(stack: Hanami.app.router.middleware_stack)
40
+ out.puts inspector.inspect(include_arguments: with_arguments)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "hanami"
4
- require "hanami/router/inspector"
5
4
 
6
5
  module Hanami
7
6
  module CLI
@@ -40,6 +39,7 @@ module Hanami
40
39
 
41
40
  # @api private
42
41
  def call(format: DEFAULT_FORMAT, **)
42
+ require "hanami/router/inspector"
43
43
  require "hanami/prepare"
44
44
  inspector = Hanami::Router::Inspector.new(formatter: resolve_formatter(format))
45
45
  app.router(inspector: inspector)
@@ -9,7 +9,8 @@ module Hanami
9
9
  require_relative "app/console"
10
10
  require_relative "app/server"
11
11
  require_relative "app/routes"
12
- # require_relative "app/generate"
12
+ require_relative "app/generate"
13
+ require_relative "app/middlewares"
13
14
  # require_relative "app/db/create"
14
15
  # require_relative "app/db/create_migration"
15
16
  # require_relative "app/db/drop"
@@ -27,17 +28,14 @@ module Hanami
27
28
  register "version", Commands::App::Version, aliases: ["v", "-v", "--version"]
28
29
  register "install", Commands::App::Install
29
30
  register "console", Commands::App::Console, aliases: ["c"]
30
- register "server", Commands::App::Server, aliases: ["s"]
31
- register "routes", Commands::App::Routes
31
+ register "server", Commands::App::Server, aliases: ["s"]
32
+ register "routes", Commands::App::Routes
33
+ register "middlewares", Commands::App::Middlewares
32
34
 
33
- # FIXME: temporary disabled for Hanami v2.0.0.alpha2
34
- # register "install", Install
35
-
36
- # FIXME: temporary disabled for Hanami v2.0.0.alpha2
37
- # register "generate", aliases: ["g"] do |prefix|
38
- # prefix.register "slice", Generate::Slice
39
- # prefix.register "action", Generate::Action
40
- # end
35
+ register "generate", aliases: ["g"] do |prefix|
36
+ prefix.register "slice", Generate::Slice
37
+ prefix.register "action", Generate::Action
38
+ end
41
39
  end
42
40
  end
43
41
  end
@@ -30,7 +30,14 @@ module Hanami
30
30
  }.freeze
31
31
 
32
32
  def self.[](app)
33
- config = DatabaseConfig.new(app.settings.database_url)
33
+ database_url =
34
+ if app.key?(:settings) && app[:settings].respond_to?(:database_url)
35
+ app[:settings].database_url
36
+ else
37
+ ENV.fetch("DATABASE_URL")
38
+ end
39
+
40
+ config = DatabaseConfig.new(database_url)
34
41
 
35
42
  resolver = SCHEME_MAP.fetch(config.db_type) do
36
43
  raise "#{config.db_type} is not a supported db scheme"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
+ require "open3"
4
5
  require_relative "database"
5
6
 
6
7
  module Hanami
@@ -10,6 +11,10 @@ module Hanami
10
11
  module Utils
11
12
  class Postgres < Database
12
13
  def create_command
14
+ existing_stdout, status = Open3.capture2(cli_env_vars, "psql -t -c '\\l #{escaped_name}'")
15
+
16
+ return true if status.success? && existing_stdout.include?(escaped_name)
17
+
13
18
  system(cli_env_vars, "createdb #{escaped_name}")
14
19
  end
15
20
 
@@ -2,6 +2,13 @@
2
2
 
3
3
  module Hanami
4
4
  module CLI
5
+ # Returns true if the CLI is being called from inside an Hanami app.
6
+ #
7
+ # This is typically used to determine whether to register commands that are applicable either
8
+ # inside or outside an app.
9
+ #
10
+ # @api public
11
+ # @since 2.0.0
5
12
  def self.within_hanami_app?
6
13
  File.exist?("config/app.rb") ||
7
14
  File.exist?("app.rb")
@@ -10,7 +17,8 @@ module Hanami
10
17
  module Commands
11
18
  end
12
19
 
13
- def self.register_commands!(within_hanami_app = Hanami::CLI.within_hanami_app?)
20
+ # @api private
21
+ def self.register_commands!(within_hanami_app = within_hanami_app?)
14
22
  commands = if within_hanami_app
15
23
  require_relative "commands/app"
16
24
  Commands::App
@@ -1,12 +1,12 @@
1
- # auto_register: false
2
1
  # frozen_string_literal: true
3
2
 
4
- require "<%= underscored_slice_name %>/action"
5
-
6
- module <%= classified_slice_name %>
3
+ module <%= camelized_app_name %>
7
4
  module Actions
8
5
  <%= module_controller_declaration %>
9
- <%= module_controller_offset %>class <%= classified_action_name %> < <%= classified_slice_name %>::Action
6
+ <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_app_name %>::Action
7
+ <%= module_controller_offset %> def handle(*, response)
8
+ <%= module_controller_offset %> response.body = self.class.name
9
+ <%= module_controller_offset %> end
10
10
  <%= module_controller_offset %>end
11
11
  <%= module_controller_end %>
12
12
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_slice_name %>
4
+ module Actions
5
+ <%= module_controller_declaration %>
6
+ <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_slice_name %>::Action
7
+ <%= module_controller_offset %> def handle(*, response)
8
+ <%= module_controller_offset %> response.body = self.class.name
9
+ <%= module_controller_offset %> end
10
+ <%= module_controller_offset %>end
11
+ <%= module_controller_end %>
12
+ end
13
+ end
@@ -1,2 +1,2 @@
1
- <h1><%= classified_slice_name %>::Views::<%= classified_controller_name %>::<%= classified_action_name %></h1>
1
+ <h1><%= camelized_slice_name %>::Views::<%= camelized_controller_name %>::<%= camelized_action_name %></h1>
2
2
  <h2><%= template_path %></h2>
@@ -3,10 +3,10 @@
3
3
 
4
4
  require "<%= underscored_slice_name %>/view"
5
5
 
6
- module <%= classified_slice_name %>
6
+ module <%= camelized_slice_name %>
7
7
  module Views
8
8
  <%= module_controller_declaration %>
9
- <%= module_controller_offset %>class <%= classified_action_name %> < <%= classified_slice_name %>::View
9
+ <%= module_controller_offset %>class <%= camelized_action_name %> < <%= camelized_slice_name %>::View
10
10
  <%= module_controller_offset %>end
11
11
  <%= module_controller_end %>
12
12
  end
@@ -5,6 +5,7 @@ require "dry/files"
5
5
  require "hanami/cli/generators/app/action_context"
6
6
  require "hanami/cli/url"
7
7
 
8
+ # rubocop:disable Metrics/ParameterLists
8
9
  module Hanami
9
10
  module CLI
10
11
  module Generators
@@ -15,35 +16,15 @@ module Hanami
15
16
  @inflector = inflector
16
17
  end
17
18
 
18
- # rubocop:disable Metrics/AbcSize
19
- # rubocop:disable Metrics/ParameterLists
20
19
  # 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
20
+ def call(app, controller, action, url, http, format, skip_view, slice, context: ActionContext.new(inflector, app, slice, controller, action))
21
+ if slice
22
+ generate_for_slice(controller, action, url, http, format, skip_view, slice, context)
23
+ else
24
+ generate_for_app(controller, action, url, http, format, skip_view, context)
42
25
  end
43
26
  end
44
27
  # rubocop:enable Layout/LineLength
45
- # rubocop:enable Metrics/ParameterLists
46
- # rubocop:enable Metrics/AbcSize
47
28
 
48
29
  private
49
30
 
@@ -61,20 +42,58 @@ module Hanami
61
42
  private_constant :ROUTE_RESTFUL_HTTP_METHODS
62
43
 
63
44
  ROUTE_RESTFUL_URL_SUFFIXES = {
64
- "index" => "",
65
- "new" => "/new",
66
- "create" => "",
67
- "edit" => "/:id/edit",
68
- "update" => "/:id",
69
- "show" => "/:id",
70
- "destroy" => "/:id"
45
+ "index" => [],
46
+ "new" => ["new"],
47
+ "create" => [],
48
+ "edit" => [":id", "edit"],
49
+ "update" => [":id"],
50
+ "show" => [":id"],
51
+ "destroy" => [":id"]
71
52
  }.freeze
72
53
  private_constant :ROUTE_RESTFUL_URL_SUFFIXES
73
54
 
55
+ PATH_SEPARATOR = "/"
56
+ private_constant :PATH_SEPARATOR
57
+
74
58
  attr_reader :fs
75
59
 
76
60
  attr_reader :inflector
77
61
 
62
+ def generate_for_slice(controller, action, url, http, _format, _skip_view, slice, context)
63
+ slice_directory = fs.join("slices", slice)
64
+ raise ArgumentError.new("slice not found `#{slice}'") unless fs.directory?(slice_directory)
65
+
66
+ fs.inject_line_at_block_bottom(
67
+ fs.join("config", "routes.rb"),
68
+ slice_matcher(slice),
69
+ route(controller, action, url, http)
70
+ )
71
+
72
+ fs.chdir(slice_directory) do
73
+ fs.mkdir(directory = fs.join("actions", controller))
74
+ fs.write(fs.join(directory, "#{action}.rb"), t("slice_action.erb", context))
75
+
76
+ # unless skip_view
77
+ # fs.mkdir(directory = fs.join("views", controller))
78
+ # fs.write(fs.join(directory, "#{action}.rb"), t("view.erb", context))
79
+ #
80
+ # fs.mkdir(directory = fs.join("templates", controller))
81
+ # fs.write(fs.join(directory, "#{action}.#{format}.erb"), t(template_format(format), context))
82
+ # end
83
+ end
84
+ end
85
+
86
+ def generate_for_app(controller, action, url, http, _format, _skip_view, context)
87
+ fs.inject_line_at_class_bottom(
88
+ fs.join("config", "routes.rb"),
89
+ "class Routes",
90
+ route(controller, action, url, http)
91
+ )
92
+
93
+ fs.mkdir(directory = fs.join("app", "actions", controller))
94
+ fs.write(fs.join(directory, "#{action}.rb"), t("action.erb", context))
95
+ end
96
+
78
97
  def slice_matcher(slice)
79
98
  /slice[[:space:]]*:#{slice}/
80
99
  end
@@ -104,7 +123,10 @@ module Hanami
104
123
  alias_method :t, :template
105
124
 
106
125
  def route_url(controller, action, url)
107
- CLI::URL.call(url || ("/#{controller.join('/')}" + ROUTE_RESTFUL_URL_SUFFIXES.fetch(action)))
126
+ action = ROUTE_RESTFUL_URL_SUFFIXES.fetch(action) { [action] }
127
+ url ||= "#{PATH_SEPARATOR}#{(controller + action).join(PATH_SEPARATOR)}"
128
+
129
+ CLI::URL.call(url)
108
130
  end
109
131
 
110
132
  def route_http(action, http)
@@ -121,3 +143,4 @@ module Hanami
121
143
  end
122
144
  end
123
145
  end
146
+ # rubocop:enable Metrics/ParameterLists
@@ -8,20 +8,20 @@ module Hanami
8
8
  module Generators
9
9
  module App
10
10
  class ActionContext < SliceContext
11
- def initialize(inflector, slice, controller, action)
11
+ def initialize(inflector, app, slice, controller, action)
12
12
  @controller = controller
13
13
  @action = action
14
- super(inflector, nil, slice, nil)
14
+ super(inflector, app, slice, nil)
15
15
  end
16
16
 
17
- def classified_controller_name
17
+ def camelized_controller_name
18
18
  controller.map do |token|
19
19
  inflector.camelize(token)
20
20
  end.join(NAMESPACE_SEPARATOR)
21
21
  end
22
22
 
23
- def classified_action_name
24
- inflector.classify(action)
23
+ def camelized_action_name
24
+ inflector.camelize(action)
25
25
  end
26
26
 
27
27
  def underscored_controller_name
@@ -1,9 +1,7 @@
1
1
  # auto_register: false
2
2
  # frozen_string_literal: true
3
3
 
4
- require "<%= underscored_app_name %>/action"
5
-
6
- module <%= classified_slice_name %>
7
- class Action < <%= classified_app_name %>::Action
4
+ module <%= camelized_slice_name %>
5
+ class Action < <%= camelized_app_name %>::Action
8
6
  end
9
7
  end
@@ -1,7 +1,7 @@
1
1
  # auto_register: false
2
2
  # frozen_string_literal: true
3
3
 
4
- module <%= classified_slice_name %>
4
+ module <%= camelized_slice_name %>
5
5
  module Entities
6
6
  end
7
7
  end
@@ -3,8 +3,8 @@
3
3
  require "<%= underscored_app_name %>/repository"
4
4
  require_relative "entities"
5
5
 
6
- module <%= classified_slice_name %>
7
- class Repository < <%= classified_app_name %>::Repository
6
+ module <%= camelized_slice_name %>
7
+ class Repository < <%= camelized_app_name %>::Repository
8
8
  struct_namespace Entities
9
9
  end
10
10
  end
@@ -1,2 +1,3 @@
1
- slice :<%= underscored_slice_name %>, at: "<%= slice_url_prefix %>" do
2
- end
1
+
2
+ slice :<%= underscored_slice_name %>, at: "<%= slice_url_prefix %>" do
3
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_slice_name %>
4
+ class Slice < Hanami::Slice
5
+ end
6
+ end
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "<%= underscored_app_name %>/view"
5
5
 
6
- module <%= classified_slice_name %>
7
- class View < <%= classified_app_name %>::View
6
+ module <%= camelized_slice_name %>
7
+ class View < <%= camelized_app_name %>::View
8
8
  end
9
9
  end
@@ -14,23 +14,26 @@ module Hanami
14
14
  @inflector = inflector
15
15
  end
16
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)
17
+ def call(app, slice, slice_url_prefix, context: SliceContext.new(inflector, app, slice, slice_url_prefix))
18
+ fs.inject_line_at_class_bottom(
19
+ fs.join("config", "routes.rb"), "class Routes", t("routes.erb", context).chomp
20
+ )
19
21
 
20
22
  fs.mkdir(directory = "slices/#{slice}")
21
23
 
22
24
  fs.chdir(directory) do
25
+ # fs.write("config/slice.rb", t("slice.erb", context))
23
26
  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
+ # fs.write("view.rb", t("view.erb", context))
28
+ # fs.write("entities.rb", t("entities.erb", context))
29
+ # fs.write("repository.rb", t("repository.erb", context))
27
30
 
28
31
  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))
32
+ # fs.write("views/.keep", t("keep.erb", context))
33
+ # fs.write("templates/.keep", t("keep.erb", context))
34
+ # fs.write("templates/layouts/.keep", t("keep.erb", context))
35
+ # fs.write("entities/.keep", t("keep.erb", context))
36
+ # fs.write("repositories/.keep", t("keep.erb", context))
34
37
  end
35
38
  end
36
39
 
@@ -13,8 +13,8 @@ module Hanami
13
13
  super(inflector, app)
14
14
  end
15
15
 
16
- def classified_slice_name
17
- inflector.classify(slice)
16
+ def camelized_slice_name
17
+ inflector.camelize(slice)
18
18
  end
19
19
 
20
20
  def underscored_slice_name
@@ -19,8 +19,8 @@ module Hanami
19
19
  Version.gem_requirement
20
20
  end
21
21
 
22
- def classified_app_name
23
- inflector.classify(app)
22
+ def camelized_app_name
23
+ inflector.camelize(app)
24
24
  end
25
25
 
26
26
  def underscored_app_name
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "hanami/action"
5
5
 
6
- module <%= classified_app_name %>
6
+ module <%= camelized_app_name %>
7
7
  class Action < Hanami::Action
8
8
  end
9
9
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "hanami"
4
4
 
5
- module <%= classified_app_name %>
5
+ module <%= camelized_app_name %>
6
6
  class App < Hanami::App
7
7
  end
8
8
  end
@@ -2,13 +2,22 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rake"
6
-
5
+ gem "hanami", "<%= hanami_version %>"
7
6
  gem "hanami-router", "<%= hanami_version %>"
8
7
  gem "hanami-controller", "<%= hanami_version %>"
9
- gem "hanami", "<%= hanami_version %>"
8
+ gem "hanami-validations", "<%= hanami_version %>"
10
9
 
10
+ gem "dry-types"
11
11
  gem "puma"
12
+ gem "rake"
13
+
14
+ group :development, :test do
15
+ gem "dotenv"
16
+ end
17
+
18
+ group :cli, :development do
19
+ gem "hanami-reloader"
20
+ end
12
21
 
13
22
  group :cli, :development, :test do
14
23
  gem "hanami-rspec"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ max_threads_count = ENV.fetch("HANAMI_MAX_THREADS", 5)
4
+ min_threads_count = ENV.fetch("HANAMI_MIN_THREADS") { max_threads_count }
5
+ threads min_threads_count, max_threads_count
6
+
7
+ port ENV.fetch("HANAMI_PORT", 2300)
8
+ environment ENV.fetch("HANAMI_ENV", "development")
9
+ workers ENV.fetch("HANAMI_WEB_CONCURRENCY", 2)
10
+
11
+ on_worker_boot do
12
+ Hanami.shutdown
13
+ end
14
+
15
+ preload_app!
@@ -1 +1 @@
1
- # <%= classified_app_name %>
1
+ # <%= camelized_app_name %>
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/routes"
4
-
5
- module <%= classified_app_name %>
3
+ module <%= camelized_app_name %>
6
4
  class Routes < Hanami::Routes
7
- define do
8
- root { "Hello from Hanami" }
9
- end
5
+ root { "Hello from Hanami" }
10
6
  end
11
7
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "<%= underscored_app_name %>/types"
4
- require "hanami/settings"
5
4
 
6
- module <%= classified_app_name %>
5
+ module <%= camelized_app_name %>
7
6
  class Settings < Hanami::Settings
7
+ # Define your app settings here, for example:
8
+ #
9
+ # setting :my_flag, default: false, constructor: Types::Params::Bool
8
10
  end
9
11
  end
@@ -1,10 +1,11 @@
1
- # auto_register: false
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require "dry/types"
5
4
 
6
- module <%= classified_app_name %>
5
+ module <%= camelized_app_name %>
6
+ Types = Dry.Types
7
+
7
8
  module Types
8
- include Dry.Types
9
+ # Define your custom types here
9
10
  end
10
11
  end
@@ -4,7 +4,7 @@
4
4
  require "dry/validation"
5
5
  require "dry/schema/messages/i18n"
6
6
 
7
- module <%= classified_app_name %>
7
+ module <%= camelized_app_name %>
8
8
  module Validator < Dry::Validation::Contract
9
9
  config.messages.backend = :i18n
10
10
  config.messages.top_namespace = "validation"
@@ -40,6 +40,7 @@ module Hanami
40
40
  fs.write("config/app.rb", t("app.erb", context))
41
41
  fs.write("config/settings.rb", t("settings.erb", context))
42
42
  fs.write("config/routes.rb", t("routes.erb", context))
43
+ fs.write("config/puma.rb", t("puma.erb", context))
43
44
 
44
45
  fs.write("lib/tasks/.keep", t("keep.erb", context))
45
46
  fs.write("lib/#{app}/types.rb", t("types.erb", context))
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ # @api private
6
+ class MiddlewareStackInspector
7
+ def initialize(stack:)
8
+ @stack = stack
9
+ end
10
+
11
+ def inspect(include_arguments: false)
12
+ max_path_length = @stack.map { |(path)| path.length }.max
13
+
14
+ @stack.map { |path, middlewares|
15
+ middlewares.map { |(middleware, arguments)|
16
+ "#{path.ljust(max_path_length + 3)} #{format_middleware(middleware)}".tap { |line|
17
+ line << " #{format_arguments(arguments)}" if include_arguments
18
+ }
19
+ }
20
+ }.join("\n") + "\n"
21
+ end
22
+
23
+ private
24
+
25
+ def format_middleware(middleware)
26
+ case middleware
27
+ when Class
28
+ middleware.name || "(class)"
29
+ when Module
30
+ middleware.name || "(module)"
31
+ else
32
+ "#{middleware.class.name} (instance)"
33
+ end
34
+ end
35
+
36
+ def format_arguments(arguments)
37
+ "args: #{arguments.inspect}"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Hanami
4
4
  module CLI
5
- VERSION = "2.0.0.beta1"
5
+ VERSION = "2.0.0.beta3"
6
6
  end
7
7
  end
@@ -34,8 +34,9 @@ module Hanami
34
34
  hanami_app
35
35
  end
36
36
 
37
- define_method(:app) do
38
- hanami_app
37
+ define_method(:reload) do
38
+ puts "Reloading..."
39
+ Kernel.exec("#{$PROGRAM_NAME} console")
39
40
  end
40
41
 
41
42
  define_method(:method_missing) do |name, *args, &block|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta1
4
+ version: 2.0.0.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-20 00:00:00.000000000 Z
11
+ date: 2022-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -58,20 +58,20 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.2'
61
+ version: '0.3'
62
62
  - - ">="
63
63
  - !ruby/object:Gem::Version
64
- version: 0.2.0
64
+ version: 0.3.0
65
65
  type: :runtime
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
68
68
  requirements:
69
69
  - - "~>"
70
70
  - !ruby/object:Gem::Version
71
- version: '0.2'
71
+ version: '0.3'
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
- version: 0.2.0
74
+ version: 0.3.0
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: dry-inflector
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -175,6 +175,7 @@ files:
175
175
  - lib/hanami/cli/commands/app/generate/action.rb
176
176
  - lib/hanami/cli/commands/app/generate/slice.rb
177
177
  - lib/hanami/cli/commands/app/install.rb
178
+ - lib/hanami/cli/commands/app/middlewares.rb
178
179
  - lib/hanami/cli/commands/app/routes.rb
179
180
  - lib/hanami/cli/commands/app/server.rb
180
181
  - lib/hanami/cli/commands/app/version.rb
@@ -189,6 +190,7 @@ files:
189
190
  - lib/hanami/cli/error.rb
190
191
  - lib/hanami/cli/generators/app/action.rb
191
192
  - lib/hanami/cli/generators/app/action/action.erb
193
+ - lib/hanami/cli/generators/app/action/slice_action.erb
192
194
  - lib/hanami/cli/generators/app/action/template.erb
193
195
  - lib/hanami/cli/generators/app/action/template.html.erb
194
196
  - lib/hanami/cli/generators/app/action/view.erb
@@ -199,6 +201,7 @@ files:
199
201
  - lib/hanami/cli/generators/app/slice/keep.erb
200
202
  - lib/hanami/cli/generators/app/slice/repository.erb
201
203
  - lib/hanami/cli/generators/app/slice/routes.erb
204
+ - lib/hanami/cli/generators/app/slice/slice.erb
202
205
  - lib/hanami/cli/generators/app/slice/view.erb
203
206
  - lib/hanami/cli/generators/app/slice_context.rb
204
207
  - lib/hanami/cli/generators/context.rb
@@ -209,20 +212,15 @@ files:
209
212
  - lib/hanami/cli/generators/gem/app/env.erb
210
213
  - lib/hanami/cli/generators/gem/app/gemfile.erb
211
214
  - lib/hanami/cli/generators/gem/app/keep.erb
212
- - lib/hanami/cli/generators/gem/app/operation.erb
215
+ - lib/hanami/cli/generators/gem/app/puma.erb
213
216
  - lib/hanami/cli/generators/gem/app/rakefile.erb
214
217
  - lib/hanami/cli/generators/gem/app/readme.erb
215
- - lib/hanami/cli/generators/gem/app/relation.erb
216
- - lib/hanami/cli/generators/gem/app/repository.erb
217
218
  - lib/hanami/cli/generators/gem/app/routes.erb
218
219
  - lib/hanami/cli/generators/gem/app/settings.erb
219
- - lib/hanami/cli/generators/gem/app/transformations.erb
220
220
  - lib/hanami/cli/generators/gem/app/types.erb
221
221
  - lib/hanami/cli/generators/gem/app/validator.erb
222
- - lib/hanami/cli/generators/gem/app/view.erb
223
- - lib/hanami/cli/generators/gem/app/view_context.erb
224
- - lib/hanami/cli/generators/gem/app/view_part.erb
225
222
  - lib/hanami/cli/generators/version.rb
223
+ - lib/hanami/cli/middleware_stack_inspector.rb
226
224
  - lib/hanami/cli/rake_tasks.rb
227
225
  - lib/hanami/cli/repl/core.rb
228
226
  - lib/hanami/cli/repl/irb.rb
@@ -1,9 +0,0 @@
1
- # auto_register: false
2
- # frozen_string_literal: true
3
-
4
- require "hanami/operation"
5
-
6
- module <%= classified_app_name %>
7
- class Operation < Hanami::Operation
8
- end
9
- end
@@ -1,7 +0,0 @@
1
- # auto_register: false
2
- # frozen_string_literal: true
3
-
4
- module <%= classified_app_name %>
5
- class Relation < Hanami::Relation
6
- end
7
- end
@@ -1,9 +0,0 @@
1
- # auto_register: false
2
- # frozen_string_literal: true
3
-
4
- require "hanami/repository"
5
-
6
- module <%= classified_app_name %>
7
- class Repository < Hanami::Repository
8
- end
9
- end
@@ -1,13 +0,0 @@
1
- # auto_register: false
2
- # frozen_string_literal: true
3
-
4
- require "dry/transformer"
5
-
6
- module <%= classified_app_name %>
7
- module Transformations
8
- extend Dry::Transformer::Registry
9
-
10
- import Dry::Transformer::ArrayTransformations
11
- import Dry::Transformer::HashTransformations
12
- end
13
- end
@@ -1,9 +0,0 @@
1
- # auto_register: false
2
- # frozen_string_literal: true
3
-
4
- require "hanami/view"
5
-
6
- module <%= classified_app_name %>
7
- class View < Hanami::View
8
- end
9
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "hanami/view/context"
4
-
5
- module <%= classified_app_name %>
6
- module Views
7
- class Context < Hanami::View::Context
8
- end
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "hanami/view/part"
4
-
5
- module <%= classified_app_name %>
6
- module Views
7
- class Part < Hanami::View::Part
8
- end
9
- end
10
- end