hanami-cli 2.0.0.beta1 → 2.0.0.beta3

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 (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