hanami-cli 2.3.0.beta1 → 2.3.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -3
  3. data/.rubocop.yml +2 -0
  4. data/.rubocop_todo.yml +150 -0
  5. data/CHANGELOG.md +44 -0
  6. data/README.md +1 -4
  7. data/hanami-cli.gemspec +3 -3
  8. data/lib/hanami/cli/commands/app/command.rb +5 -1
  9. data/lib/hanami/cli/commands/app/console.rb +10 -1
  10. data/lib/hanami/cli/commands/app/db/migrate.rb +5 -2
  11. data/lib/hanami/cli/commands/app/db/rollback.rb +26 -14
  12. data/lib/hanami/cli/commands/app/db/structure/dump.rb +7 -3
  13. data/lib/hanami/cli/commands/app/db/utils/database.rb +22 -0
  14. data/lib/hanami/cli/commands/app/db/utils/mysql.rb +3 -1
  15. data/lib/hanami/cli/commands/app/db/utils/postgres.rb +16 -3
  16. data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +1 -1
  17. data/lib/hanami/cli/commands/app/db/version.rb +2 -0
  18. data/lib/hanami/cli/commands/app/generate/action.rb +9 -12
  19. data/lib/hanami/cli/commands/app/generate/slice.rb +8 -8
  20. data/lib/hanami/cli/commands/app/run.rb +108 -0
  21. data/lib/hanami/cli/commands/app.rb +1 -0
  22. data/lib/hanami/cli/commands/gem/new.rb +33 -11
  23. data/lib/hanami/cli/errors.rb +2 -0
  24. data/lib/hanami/cli/generators/app/action.rb +11 -5
  25. data/lib/hanami/cli/generators/app/component.rb +1 -1
  26. data/lib/hanami/cli/generators/app/relation.rb +1 -1
  27. data/lib/hanami/cli/generators/app/ruby_file.rb +7 -12
  28. data/lib/hanami/cli/generators/app/slice.rb +19 -8
  29. data/lib/hanami/cli/generators/app/view.rb +1 -1
  30. data/lib/hanami/cli/generators/constants.rb +1 -1
  31. data/lib/hanami/cli/generators/context.rb +12 -1
  32. data/lib/hanami/cli/generators/gem/app/context.erb +10 -0
  33. data/lib/hanami/cli/generators/gem/app/gemfile.erb +3 -2
  34. data/lib/hanami/cli/generators/gem/app/readme.erb +6 -6
  35. data/lib/hanami/cli/generators/gem/app/setup.erb +27 -0
  36. data/lib/hanami/cli/generators/gem/app/types.erb +1 -1
  37. data/lib/hanami/cli/generators/gem/app.rb +4 -0
  38. data/lib/hanami/cli/generators/version.rb +5 -7
  39. data/lib/hanami/cli/interactive_system_call.rb +1 -2
  40. data/lib/hanami/cli/system_call.rb +1 -1
  41. data/lib/hanami/cli/version.rb +1 -1
  42. data/lib/hanami/console/context.rb +3 -0
  43. data/lib/hanami/console/plugins/unbooted_slice_warnings.rb +40 -0
  44. data/script/ci +31 -0
  45. metadata +10 -4
@@ -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,
@@ -60,7 +59,6 @@ module Hanami
60
59
 
61
60
  # option :format, required: false, type: :string, default: DEFAULT_FORMAT, desc: "Template format"
62
61
 
63
- # rubocop:disable Layout/LineLength
64
62
  example [
65
63
  %(books.index # GET /books to: "books.index" (MyApp::Actions::Books::Index)),
66
64
  %(books.new # GET /books/new to: "books.new" (MyApp::Actions::Books::New)),
@@ -74,15 +72,13 @@ module Hanami
74
72
  %(authors.update --http=put # PUT /authors/:id to: "authors.update" (MyApp::Actions::Authors::Update)),
75
73
  %(users.index --slice=admin # GET /admin/users to: "users.index" (Admin::Actions::Users::Update))
76
74
  ]
77
- # rubocop:enable Layout/LineLength
78
-
79
75
  def generator_class
80
76
  Generators::App::Action
81
77
  end
82
78
 
83
79
  # @since 2.0.0
84
80
  # @api private
85
- # rubocop:disable Lint/ParameterLists
81
+ # rubocop:disable Metrics/ParameterLists
86
82
  def call(
87
83
  name:,
88
84
  slice: nil,
@@ -90,22 +86,23 @@ module Hanami
90
86
  http_method: nil,
91
87
  skip_view: DEFAULT_SKIP_VIEW,
92
88
  skip_route: DEFAULT_SKIP_ROUTE,
93
- skip_tests: DEFAULT_SKIP_TESTS # rubocop:disable Lint/UnusedMethodArgument
89
+ skip_tests: DEFAULT_SKIP_TESTS
94
90
  )
95
91
  name = Naming.new(inflector:).action_name(name)
96
92
 
97
93
  raise InvalidActionNameError.new(name) unless name.include?(".")
98
94
 
99
95
  super(
100
- name:,
101
- slice:,
102
- url_path:,
103
- skip_route:,
104
- http_method:,
96
+ name: name,
97
+ slice: slice,
98
+ url_path: url_path,
99
+ skip_route: skip_route,
100
+ http_method: http_method,
105
101
  skip_view: skip_view || !Hanami.bundled?("hanami-view"),
102
+ skip_tests: skip_tests
106
103
  )
107
104
  end
108
- # rubocop:enable Lint/ParameterLists
105
+ # rubocop:enable Metrics/ParameterLists
109
106
  end
110
107
  end
111
108
  end
@@ -29,17 +29,17 @@ module Hanami
29
29
  # @since 2.2.0
30
30
  # @api private
31
31
  option :skip_db,
32
- type: :flag,
33
- required: false,
34
- default: SKIP_DB_DEFAULT,
35
- desc: "Skip database"
32
+ type: :flag,
33
+ required: false,
34
+ default: SKIP_DB_DEFAULT,
35
+ desc: "Skip database"
36
36
  # @since 2.2.0
37
37
  # @api private
38
38
  option :skip_route,
39
- type: :flag,
40
- required: false,
41
- default: DEFAULT_SKIP_ROUTE,
42
- desc: "Skip route generation"
39
+ type: :flag,
40
+ required: false,
41
+ default: DEFAULT_SKIP_ROUTE,
42
+ desc: "Skip route generation"
43
43
 
44
44
  example [
45
45
  "admin # Admin slice (/admin URL prefix)",
@@ -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")
@@ -10,16 +10,21 @@ module Hanami
10
10
  # @since 2.0.0
11
11
  # @api private
12
12
  class New < Command
13
- # @since 2.0.0
14
- # @api private
15
- SKIP_INSTALL_DEFAULT = false
16
- private_constant :SKIP_INSTALL_DEFAULT
17
-
18
13
  # @since 2.1.0
19
14
  # @api private
20
15
  HEAD_DEFAULT = false
21
16
  private_constant :HEAD_DEFAULT
22
17
 
18
+ # @since 2.3.0
19
+ # @api private
20
+ GEM_SOURCE_DEFAULT = "rubygems.org"
21
+ private_constant :GEM_SOURCE_DEFAULT
22
+
23
+ # @since 2.0.0
24
+ # @api private
25
+ SKIP_INSTALL_DEFAULT = false
26
+ private_constant :SKIP_INSTALL_DEFAULT
27
+
23
28
  # @since 2.1.0
24
29
  # @api private
25
30
  SKIP_ASSETS_DEFAULT = false
@@ -60,18 +65,24 @@ module Hanami
60
65
  # @api private
61
66
  argument :app, required: true, desc: "App name"
62
67
 
63
- # @since 2.0.0
64
- # @api private
65
- option :skip_install, type: :flag, required: false,
66
- default: SKIP_INSTALL_DEFAULT,
67
- desc: "Skip app installation (Bundler, third-party Hanami plugins)"
68
-
69
68
  # @since 2.1.0
70
69
  # @api private
71
70
  option :head, type: :flag, required: false,
72
71
  default: HEAD_DEFAULT,
73
72
  desc: "Use Hanami HEAD version (from GitHub `main` branches)"
74
73
 
74
+ # @since 2.3.0
75
+ # @api private
76
+ option :gem_source, required: true,
77
+ default: GEM_SOURCE_DEFAULT,
78
+ desc: "Where to source Ruby gems from"
79
+
80
+ # @since 2.0.0
81
+ # @api private
82
+ option :skip_install, type: :flag, required: false,
83
+ default: SKIP_INSTALL_DEFAULT,
84
+ desc: "Skip app installation (Bundler, third-party Hanami plugins)"
85
+
75
86
  # @since 2.1.0
76
87
  # @api private
77
88
  option :skip_assets, type: :flag, required: false,
@@ -100,6 +111,7 @@ module Hanami
100
111
  example [
101
112
  "bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
102
113
  "bookshelf --head # Generate a new Hanami app, using Hanami HEAD version from GitHub `main' branches",
114
+ "bookshelf --gem-source=gem.coop # Generate a new Hanami app, using https://gem.coop as Ruby gem source",
103
115
  "bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation",
104
116
  "bookshelf --skip-assets # Generate a new Hanami app without hanami-assets",
105
117
  "bookshelf --skip-db # Generate a new Hanami app without hanami-db",
@@ -132,6 +144,7 @@ module Hanami
132
144
  def call(
133
145
  app:,
134
146
  head: HEAD_DEFAULT,
147
+ gem_source: GEM_SOURCE_DEFAULT,
135
148
  skip_install: SKIP_INSTALL_DEFAULT,
136
149
  skip_assets: SKIP_ASSETS_DEFAULT,
137
150
  skip_db: SKIP_DB_DEFAULT,
@@ -152,6 +165,7 @@ module Hanami
152
165
  inflector,
153
166
  app,
154
167
  head: head,
168
+ gem_source: gem_source,
155
169
  skip_assets: skip_assets,
156
170
  skip_db: skip_db,
157
171
  skip_view: skip_view,
@@ -177,6 +191,9 @@ module Hanami
177
191
  out.puts "Running hanami install..."
178
192
  run_install_command!(head: head)
179
193
 
194
+ out.puts "Running bundle binstubs hanami-cli rake..."
195
+ install_binstubs!
196
+
180
197
  out.puts "Initializing git repository..."
181
198
  init_git_repository
182
199
  end
@@ -215,6 +232,11 @@ module Hanami
215
232
  end
216
233
  end
217
234
 
235
+ # @api private
236
+ def install_binstubs!
237
+ bundler.bundle("binstubs hanami-cli rake")
238
+ end
239
+
218
240
  # @api private
219
241
  def init_git_repository
220
242
  system_call.call("git", ["init"]).tap do |result|
@@ -110,6 +110,7 @@ module Hanami
110
110
  end
111
111
  end
112
112
 
113
+ # rubocop:disable Layout/LineLength
113
114
  # @since 2.2.0
114
115
  # @api public
115
116
  class DatabaseNotSupportedError < Error
@@ -117,6 +118,7 @@ module Hanami
117
118
  super("`#{invalid_database}' is not a supported database. Supported databases are: #{supported_databases.join(', ')}")
118
119
  end
119
120
  end
121
+ # rubocop:enable Layout/LineLength
120
122
 
121
123
  # @since 2.2.0
122
124
  # @api public
@@ -27,10 +27,10 @@ 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
- generate_action(key:, namespace:, base_path:, include_placeholder_body: skip_view)
33
+ generate_action(key: key, namespace: namespace, base_path: base_path, include_placeholder_body: skip_view)
34
34
 
35
35
  generate_view(key:, namespace:, base_path:) unless skip_view
36
36
  end
@@ -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
- slice_matcher = /slice[[:space:]]*:#{namespace}/
87
- fs.inject_line_at_block_bottom(routes_location, slice_matcher, route)
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: inflector.underscore(key),
104
+ key: key,
99
105
  base_path: base_path,
100
106
  parent_class_name: "#{inflector.camelize(namespace)}::Action",
101
107
  extra_namespace: "Actions",
@@ -24,7 +24,7 @@ module Hanami
24
24
  RubyClassFile.new(
25
25
  fs: fs,
26
26
  inflector: inflector,
27
- key: inflector.underscore(key),
27
+ key: key,
28
28
  namespace: namespace,
29
29
  base_path: base_path,
30
30
  ).create
@@ -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
- **opts
20
+ **_opts
21
21
  )
22
22
  @fs = fs
23
23
  @inflector = inflector
24
- @key = key
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, *key_parts].join("/"),
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, "#{key_parts.last}.rb")
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
- :key,
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) + key_parts[..-2]
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(key_parts.last)
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
@@ -66,6 +66,17 @@ module Hanami
66
66
  body: ["# Add your view helpers here"]
67
67
  ).create
68
68
 
69
+ RubyClassFile.new(
70
+ fs: fs,
71
+ inflector: inflector,
72
+ namespace: slice,
73
+ key: "views.context",
74
+ base_path: directory,
75
+ parent_class_name: "#{Hanami.app.namespace}::View::Context",
76
+ auto_register: false,
77
+ body: ["# Define your view context here. See https://guides.hanamirb.org/views/context/ for details."]
78
+ ).create
79
+
69
80
  fs.create(
70
81
  fs.join(directory, "templates", "layouts", "app.html.erb"),
71
82
  app_layout_template(
@@ -93,11 +104,11 @@ module Hanami
93
104
  fs.create(
94
105
  fs.join(directory, "assets", "css", "app.css"),
95
106
  <<~CSS
96
- body {
97
- background-color: #fff;
98
- color: #000;
99
- font-family: sans-serif;
100
- }
107
+ body {
108
+ background-color: #fff;
109
+ color: #000;
110
+ font-family: sans-serif;
111
+ }
101
112
  CSS
102
113
  )
103
114
  fs.create(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
@@ -161,9 +172,9 @@ module Hanami
161
172
  <head>
162
173
  <meta charset="UTF-8">
163
174
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
164
- <title>#{ page_title }</title>
165
- #{'<%= favicon_tag %>' if bundled_assets }
166
- #{'<%= stylesheet_tag "app" %>' if bundled_assets }
175
+ <title>#{page_title}</title>
176
+ #{'<%= favicon_tag %>' if bundled_assets}
177
+ #{'<%= stylesheet_tag "app" %>' if bundled_assets}
167
178
  </head>
168
179
  <body>
169
180
  <%= yield %>
@@ -44,7 +44,7 @@ module Hanami
44
44
  fs: fs,
45
45
  inflector: inflector,
46
46
  namespace: namespace,
47
- key: inflector.underscore(key),
47
+ key: key,
48
48
  base_path: base_path,
49
49
  parent_class_name: "#{inflector.camelize(namespace)}::View",
50
50
  extra_namespace: "Views",
@@ -22,7 +22,7 @@ module Hanami
22
22
 
23
23
  # @since 2.2.0
24
24
  # @api private
25
- KEY_SEPARATOR = %r{[.\/]}
25
+ KEY_SEPARATOR = %r{::|[.\/]}
26
26
  private_constant :KEY_SEPARATOR
27
27
 
28
28
  # @since 2.2.0
@@ -32,7 +32,9 @@ module Hanami
32
32
 
33
33
  # @since 2.0.0
34
34
  # @api private
35
- def hanami_gem_version(gem_name)
35
+ def hanami_gem_version(name)
36
+ gem_name = name == "hanami" ? "hanami" : "hanami-#{name}"
37
+
36
38
  if hanami_head?
37
39
  %(github: "hanami/#{gem_name}", branch: "main")
38
40
  else
@@ -74,6 +76,15 @@ module Hanami
74
76
  options.fetch(:head)
75
77
  end
76
78
 
79
+ # @since 2.3.0
80
+ # @api private
81
+ def gem_source
82
+ value = options.fetch(:gem_source)
83
+ return value if value.match? %r{\A\w+://}
84
+
85
+ "https://#{value}"
86
+ end
87
+
77
88
  # @since 2.1.0
78
89
  # @api private
79
90
  def generate_assets?
@@ -0,0 +1,10 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ module <%= camelized_app_name %>
5
+ module Views
6
+ class Context < Hanami::View::Context
7
+ # Define your view context here. See https://guides.hanamirb.org/views/context/ for details.
8
+ end
9
+ end
10
+ end
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source "<%= gem_source %>"
4
4
 
5
5
  <%= hanami_gem("hanami") %>
6
6
  <%- if hanami_head? -%>
7
7
  <%= hanami_gem("cli") %>
8
+ <%= hanami_gem("utils") %>
8
9
  <%- end -%>
9
10
  <%- if generate_assets? -%>
10
11
  <%= hanami_gem("assets") %>
@@ -20,7 +21,7 @@ source "https://rubygems.org"
20
21
  <%- end -%>
21
22
 
22
23
  gem "dry-types", "~> 1.7"
23
- gem "dry-operation"
24
+ gem "dry-operation", ">= 1.0.1"
24
25
  gem "puma"
25
26
  gem "rake"
26
27
  <%- if generate_sqlite? -%>
@@ -1,15 +1,15 @@
1
1
  # <%= camelized_app_name %>
2
2
 
3
- Welcome to your Hanami app!
3
+ 🌸 Welcome to your Hanami app!
4
4
 
5
- ## Getting Started
5
+ ## Getting started
6
6
 
7
+ - Set up the project up with `bin/setup`
7
8
  - Run the server with `bin/dev`
8
9
  - View the app at [http://localhost:2300](http://localhost:2300)
9
10
  - Run the tests with `bundle exec rake`
10
11
 
11
- ## Useful Links
12
+ ## Useful links
12
13
 
13
- - [Hanami Home](http://hanamirb.org)
14
- - [Hanami Guides](https://guides.hanamirb.org/)
15
- - [Hanami API Doc](https://gemdocs.org/gems/hanami/latest)
14
+ - [Hanami](http://hanamirb.org)
15
+ - [Hanami guides](https://guides.hanamirb.org/)
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ # This script is a way to set up and keep your development environment updated
6
+ # automatically. It is meant to be idempotent so that you can run it at any
7
+ # time to get the same result. Add any new necessary setup steps to this file
8
+ # as your application evolves.
9
+
10
+ announce() {
11
+ local bold='\033[1m'
12
+ local reset='\033[0m'
13
+ printf "${bold}${1}${reset}\n"
14
+ }
15
+
16
+ announce "Running bundle install..."
17
+ bundle check || bundle install
18
+
19
+ announce "\nRunning npm install..."
20
+ npm install
21
+
22
+ <%- if generate_db? -%>
23
+ announce "\nPreparing the database..."
24
+ hanami db prepare
25
+
26
+ <%- end -%>
27
+ announce "\n🌸 Setup complete!"
@@ -3,7 +3,7 @@
3
3
  require "dry/types"
4
4
 
5
5
  module <%= camelized_app_name %>
6
- Types = Dry.Types
6
+ Types = Dry.Types(default: :strict)
7
7
 
8
8
  module Types
9
9
  # Define your custom types here
@@ -46,6 +46,9 @@ module Hanami
46
46
  fs.create("bin/dev", file("dev"))
47
47
  fs.chmod("bin/dev", 0o755)
48
48
 
49
+ fs.create("bin/setup", t("setup.erb", context))
50
+ fs.chmod("bin/setup", 0o755)
51
+
49
52
  fs.create("config/app.rb", t("app.erb", context))
50
53
  fs.create("config/settings.rb", t("settings.erb", context))
51
54
  fs.create("config/routes.rb", t("routes.erb", context))
@@ -60,6 +63,7 @@ module Hanami
60
63
  if context.generate_view?
61
64
  fs.create("app/view.rb", t("view.erb", context))
62
65
  fs.create("app/views/helpers.rb", t("helpers.erb", context))
66
+ fs.create("app/views/context.rb", t("context.erb", context))
63
67
  fs.create("app/templates/layouts/app.html.erb", t("app_layout.erb", context))
64
68
 
65
69
  fs.create("public/404.html", file("404.html"))