hanami-cli 2.1.1 → 2.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +18 -13
  3. data/CHANGELOG.md +15 -0
  4. data/Gemfile +4 -2
  5. data/README.md +11 -7
  6. data/docker-compose.yml +9 -0
  7. data/hanami-cli.gemspec +2 -2
  8. data/lib/hanami/cli/command.rb +1 -1
  9. data/lib/hanami/cli/commands/app/command.rb +2 -16
  10. data/lib/hanami/cli/commands/app/db/command.rb +116 -0
  11. data/lib/hanami/cli/commands/app/db/create.rb +19 -11
  12. data/lib/hanami/cli/commands/app/db/drop.rb +19 -10
  13. data/lib/hanami/cli/commands/app/db/migrate.rb +19 -13
  14. data/lib/hanami/cli/commands/app/db/prepare.rb +42 -0
  15. data/lib/hanami/cli/commands/app/db/seed.rb +11 -22
  16. data/lib/hanami/cli/commands/app/db/structure/dump.rb +30 -7
  17. data/lib/hanami/cli/commands/app/db/structure/load.rb +52 -0
  18. data/lib/hanami/cli/commands/app/db/utils/database.rb +68 -73
  19. data/lib/hanami/cli/commands/app/db/utils/mysql.rb +2 -2
  20. data/lib/hanami/cli/commands/app/db/utils/postgres.rb +38 -19
  21. data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +58 -10
  22. data/lib/hanami/cli/commands/app/db/version.rb +12 -9
  23. data/lib/hanami/cli/commands/app/generate/action.rb +4 -3
  24. data/lib/hanami/cli/commands/app/generate/command.rb +49 -0
  25. data/lib/hanami/cli/commands/app/generate/component.rb +49 -0
  26. data/lib/hanami/cli/commands/app/generate/migration.rb +27 -0
  27. data/lib/hanami/cli/commands/app/generate/operation.rb +26 -0
  28. data/lib/hanami/cli/commands/app/generate/part.rb +1 -1
  29. data/lib/hanami/cli/commands/app/generate/relation.rb +35 -0
  30. data/lib/hanami/cli/commands/app/generate/repo.rb +46 -0
  31. data/lib/hanami/cli/commands/app/generate/slice.rb +20 -3
  32. data/lib/hanami/cli/commands/app/generate/struct.rb +27 -0
  33. data/lib/hanami/cli/commands/app/install.rb +1 -1
  34. data/lib/hanami/cli/commands/app/middleware.rb +1 -1
  35. data/lib/hanami/cli/commands/app/server.rb +2 -2
  36. data/lib/hanami/cli/commands/app.rb +21 -2
  37. data/lib/hanami/cli/commands/gem/new.rb +78 -14
  38. data/lib/hanami/cli/errors.rb +28 -0
  39. data/lib/hanami/cli/generators/app/action_context.rb +5 -13
  40. data/lib/hanami/cli/generators/app/component/component.erb +8 -0
  41. data/lib/hanami/cli/generators/app/component/slice_component.erb +8 -0
  42. data/lib/hanami/cli/generators/app/component.rb +61 -0
  43. data/lib/hanami/cli/generators/app/component_context.rb +82 -0
  44. data/lib/hanami/cli/generators/app/migration.rb +69 -0
  45. data/lib/hanami/cli/generators/app/operation.rb +48 -0
  46. data/lib/hanami/cli/generators/app/part_context.rb +5 -21
  47. data/lib/hanami/cli/generators/app/relation.rb +44 -0
  48. data/lib/hanami/cli/generators/app/repo.rb +40 -0
  49. data/lib/hanami/cli/generators/app/ruby_file_writer.rb +149 -0
  50. data/lib/hanami/cli/generators/app/slice/operation.erb +7 -0
  51. data/lib/hanami/cli/generators/app/slice/relation.erb +8 -0
  52. data/lib/hanami/cli/generators/app/slice/{slice.erb → repo.erb} +3 -1
  53. data/lib/hanami/cli/generators/app/slice/struct.erb +8 -0
  54. data/lib/hanami/cli/generators/app/slice.rb +14 -6
  55. data/lib/hanami/cli/generators/app/slice_context.rb +9 -2
  56. data/lib/hanami/cli/generators/app/struct.rb +39 -0
  57. data/lib/hanami/cli/generators/app/view_context.rb +4 -16
  58. data/lib/hanami/cli/generators/constants.rb +39 -0
  59. data/lib/hanami/cli/generators/context.rb +48 -0
  60. data/lib/hanami/cli/generators/gem/app/action.erb +3 -0
  61. data/lib/hanami/cli/generators/gem/app/env.erb +4 -0
  62. data/lib/hanami/cli/generators/gem/app/gemfile.erb +11 -0
  63. data/lib/hanami/cli/generators/gem/app/gitignore.erb +1 -1
  64. data/lib/hanami/cli/generators/gem/app/operation.erb +13 -0
  65. data/lib/hanami/cli/generators/gem/app/relation.erb +10 -0
  66. data/lib/hanami/cli/generators/gem/app/repo.erb +10 -0
  67. data/lib/hanami/cli/generators/gem/app/struct.erb +10 -0
  68. data/lib/hanami/cli/generators/gem/app.rb +19 -0
  69. data/lib/hanami/cli/ruby_file_generator.rb +123 -0
  70. data/lib/hanami/cli/version.rb +1 -1
  71. metadata +39 -16
  72. data/lib/hanami/cli/commands/app/db/create_migration.rb +0 -32
  73. data/lib/hanami/cli/commands/app/db/reset.rb +0 -28
  74. data/lib/hanami/cli/commands/app/db/rollback.rb +0 -81
  75. data/lib/hanami/cli/commands/app/db/sample_data.rb +0 -42
  76. data/lib/hanami/cli/commands/app/db/setup.rb +0 -26
  77. data/lib/hanami/cli/commands/app/db/utils/database_config.rb +0 -60
  78. data/lib/hanami/cli/generators/app/slice/repository.erb +0 -10
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../constants"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ module App
9
+ # @since 2.2.0
10
+ # @api private
11
+ class Operation
12
+ # @since 2.2.0
13
+ # @api private
14
+ def initialize(fs:, inflector:, out: $stdout)
15
+ @fs = fs
16
+ @inflector = inflector
17
+ @out = out
18
+ end
19
+
20
+ # @since 2.2.0
21
+ # @api private
22
+ def call(app_namespace, key, slice)
23
+ RubyFileWriter.new(
24
+ fs: fs,
25
+ inflector: inflector,
26
+ app_namespace: app_namespace,
27
+ key: key,
28
+ slice: slice,
29
+ relative_parent_class: "Operation",
30
+ body: ["def call", "end"],
31
+ ).call
32
+
33
+ unless key.match?(KEY_SEPARATOR)
34
+ out.puts(
35
+ " Note: We generated a top-level operation. " \
36
+ "To generate into a directory, add a namespace: `my_namespace.add_book`"
37
+ )
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :fs, :inflector, :out
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "slice_context"
4
3
  require "dry/files/path"
4
+ require_relative "slice_context"
5
+ require_relative "../constants"
5
6
 
6
7
  module Hanami
7
8
  module CLI
@@ -12,23 +13,6 @@ module Hanami
12
13
  # @since 2.1.0
13
14
  # @api private
14
15
  class PartContext < SliceContext
15
- # TODO: move these constants somewhere that will let us reuse them
16
-
17
- # @since 2.1.0
18
- # @api private
19
- KEY_SEPARATOR = "."
20
- private_constant :KEY_SEPARATOR
21
-
22
- # @since 2.1.0
23
- # @api private
24
- INDENTATION = " "
25
- private_constant :INDENTATION
26
-
27
- # @since 2.1.0
28
- # @api private
29
- OFFSET = INDENTATION * 2
30
- private_constant :OFFSET
31
-
32
16
  # @since 2.1.0
33
17
  # @api private
34
18
  attr_reader :key
@@ -74,7 +58,7 @@ module Hanami
74
58
  # @api private
75
59
  def module_namespace_declaration
76
60
  namespaces.each_with_index.map { |token, i|
77
- "#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
61
+ "#{NESTED_OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
78
62
  }.join($/)
79
63
  end
80
64
 
@@ -82,14 +66,14 @@ module Hanami
82
66
  # @api private
83
67
  def module_namespace_end
84
68
  namespaces.each_with_index.map { |_, i|
85
- "#{OFFSET}#{INDENTATION * i}end"
69
+ "#{NESTED_OFFSET}#{INDENTATION * i}end"
86
70
  }.reverse.join($/)
87
71
  end
88
72
 
89
73
  # @since 2.1.0
90
74
  # @api private
91
75
  def module_namespace_offset
92
- "#{OFFSET}#{INDENTATION * namespaces.count}"
76
+ "#{NESTED_OFFSET}#{INDENTATION * namespaces.count}"
93
77
  end
94
78
  end
95
79
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../constants"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ module App
9
+ # @since 2.2.0
10
+ # @api private
11
+ class Relation
12
+ # @since 2.2.0
13
+ # @api private
14
+ def initialize(fs:, inflector:, out: $stdout)
15
+ @fs = fs
16
+ @inflector = inflector
17
+ @out = out
18
+ end
19
+
20
+ # @since 2.2.0
21
+ # @api private
22
+ def call(app_namespace, key, slice)
23
+ schema_name = key.split(KEY_SEPARATOR).last
24
+
25
+ RubyFileWriter.new(
26
+ fs: fs,
27
+ inflector: inflector,
28
+ app_namespace: app_namespace,
29
+ key: key,
30
+ slice: slice,
31
+ extra_namespace: "Relations",
32
+ relative_parent_class: "DB::Relation",
33
+ body: ["schema :#{schema_name}, infer: true"],
34
+ ).call
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :fs, :inflector, :out
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Generators
6
+ module App
7
+ # @since 2.2.0
8
+ # @api private
9
+ class Repo
10
+ # @since 2.2.0
11
+ # @api private
12
+ def initialize(fs:, inflector:, out: $stdout)
13
+ @fs = fs
14
+ @inflector = inflector
15
+ @out = out
16
+ end
17
+
18
+ # @since 2.2.0
19
+ # @api private
20
+ def call(app_namespace, key, slice)
21
+ RubyFileWriter.new(
22
+ fs: fs,
23
+ inflector: inflector,
24
+ app_namespace: app_namespace,
25
+ key: key,
26
+ slice: slice,
27
+ extra_namespace: "Repos",
28
+ relative_parent_class: "DB::Repo",
29
+ body: [],
30
+ ).call
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :fs, :inflector, :out
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "dry/files"
5
+ require_relative "../constants"
6
+ require_relative "../../errors"
7
+
8
+ module Hanami
9
+ module CLI
10
+ module Generators
11
+ module App
12
+ # @since 2.2.0
13
+ # @api private
14
+ class RubyFileWriter
15
+ # @since 2.2.0
16
+ # @api private
17
+ def initialize(
18
+ fs:,
19
+ inflector:,
20
+ app_namespace:,
21
+ key:,
22
+ slice:,
23
+ relative_parent_class:,
24
+ extra_namespace: nil,
25
+ body: []
26
+ )
27
+ @fs = fs
28
+ @inflector = inflector
29
+ @app_namespace = app_namespace
30
+ @key = key
31
+ @slice = slice
32
+ @extra_namespace = extra_namespace&.downcase
33
+ @relative_parent_class = relative_parent_class
34
+ @body = body
35
+ raise_missing_slice_error_if_missing(slice) if slice
36
+ end
37
+
38
+ # @since 2.2.0
39
+ # @api private
40
+ def call
41
+ fs.mkdir(directory)
42
+ fs.write(path, file_contents)
43
+ end
44
+
45
+ private
46
+
47
+ # @since 2.2.0
48
+ # @api private
49
+ attr_reader(
50
+ :fs,
51
+ :inflector,
52
+ :app_namespace,
53
+ :key,
54
+ :slice,
55
+ :extra_namespace,
56
+ :relative_parent_class,
57
+ :body,
58
+ )
59
+
60
+ # @since 2.2.0
61
+ # @api private
62
+ def file_contents
63
+ class_definition(
64
+ class_name: class_name,
65
+ container_namespace: container_namespace,
66
+ local_namespaces: local_namespaces,
67
+ )
68
+ end
69
+
70
+ # @since 2.2.0
71
+ # @api private
72
+ def class_name
73
+ key.split(KEY_SEPARATOR)[-1]
74
+ end
75
+
76
+ # @since 2.2.0
77
+ # @api private
78
+ def container_namespace
79
+ slice || app_namespace
80
+ end
81
+
82
+ # @since 2.2.0
83
+ # @api private
84
+ def local_namespaces
85
+ Array(extra_namespace) + key.split(KEY_SEPARATOR)[..-2]
86
+ end
87
+
88
+ # @since 2.2.0
89
+ # @api private
90
+ def directory
91
+ base = if slice
92
+ fs.join("slices", slice)
93
+ else
94
+ fs.join("app")
95
+ end
96
+
97
+ @directory ||= if local_namespaces.any?
98
+ fs.join(base, local_namespaces)
99
+ else
100
+ fs.join(base)
101
+ end
102
+ end
103
+
104
+ # @since 2.2.0
105
+ # @api private
106
+ def path
107
+ fs.join(directory, "#{class_name}.rb")
108
+ end
109
+
110
+ # @since 2.2.0
111
+ # @api private
112
+ def class_definition(class_name:, container_namespace:, local_namespaces:)
113
+ container_module = normalize(container_namespace)
114
+
115
+ modules = local_namespaces
116
+ .map { normalize(_1) }
117
+ .compact
118
+ .prepend(container_module)
119
+
120
+ parent_class = [container_module, relative_parent_class].join("::")
121
+
122
+ RubyFileGenerator.class(
123
+ normalize(class_name),
124
+ parent_class: parent_class,
125
+ modules: modules,
126
+ header: ["# frozen_string_literal: true"],
127
+ body: body
128
+ )
129
+ end
130
+
131
+ # @since 2.2.0
132
+ # @api private
133
+ def normalize(name)
134
+ inflector.camelize(name).gsub(/[^\p{Alnum}]/, "")
135
+ end
136
+
137
+ # @since 2.2.0
138
+ # @api private
139
+ def raise_missing_slice_error_if_missing(slice)
140
+ if slice
141
+ slice_directory = fs.join("slices", slice)
142
+ raise MissingSliceError.new(slice) unless fs.directory?(slice_directory)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,7 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ module <%= camelized_slice_name %>
5
+ class Operation < <%= camelized_app_name %>::Operation
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_slice_name %>
4
+ module DB
5
+ class Relation < <%= camelized_app_name %>::DB::Relation
6
+ end
7
+ end
8
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module <%= camelized_slice_name %>
4
- class Slice < Hanami::Slice
4
+ module DB
5
+ class Repo < <%= camelized_app_name %>::DB::Repo
6
+ end
5
7
  end
6
8
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= camelized_slice_name %>
4
+ module DB
5
+ class Struct < <%= camelized_app_name %>::DB::Struct
6
+ end
7
+ end
8
+ end
@@ -19,18 +19,20 @@ module Hanami
19
19
 
20
20
  # @since 2.0.0
21
21
  # @api private
22
- def call(app, slice, url, context: SliceContext.new(inflector, app, slice, url)) # rubocop:disable Metrics/AbcSize
22
+ def call(app, slice, url, context: nil, **opts)
23
+ context ||= SliceContext.new(inflector, app, slice, url, **opts)
24
+
23
25
  fs.inject_line_at_class_bottom(
24
26
  fs.join("config", "routes.rb"), "class Routes", t("routes.erb", context).chomp
25
27
  )
26
28
 
27
29
  fs.mkdir(directory = "slices/#{slice}")
28
30
 
29
- # fs.write("#{directory}/config/slice.rb", t("slice.erb", context))
30
31
  fs.write(fs.join(directory, "action.rb"), t("action.erb", context))
31
32
  fs.write(fs.join(directory, "view.rb"), t("view.erb", context))
32
33
  fs.write(fs.join(directory, "views", "helpers.rb"), t("helpers.erb", context))
33
34
  fs.write(fs.join(directory, "templates", "layouts", "app.html.erb"), t("app_layout.erb", context))
35
+ fs.write(fs.join(directory, "operation.rb"), t("operation.erb", context))
34
36
 
35
37
  if context.bundled_assets?
36
38
  fs.write(fs.join(directory, "assets", "js", "app.js"), t("app_js.erb", context))
@@ -38,15 +40,21 @@ module Hanami
38
40
  fs.write(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
39
41
  end
40
42
 
41
- # fs.write(fs.join(directory, "/entities.rb"), t("entities.erb", context))
42
- # fs.write(fs.join(directory, "/repository.rb"), t("repository.erb", context))
43
+ if context.generate_db?
44
+ fs.write(fs.join(directory, "db", "relation.rb"), t("relation.erb", context))
45
+ fs.write(fs.join(directory, "relations", ".keep"), t("keep.erb", context))
46
+
47
+ fs.write(fs.join(directory, "db", "repo.rb"), t("repo.erb", context))
48
+ fs.write(fs.join(directory, "repos", ".keep"), t("keep.erb", context))
49
+
50
+ fs.write(fs.join(directory, "db", "struct.rb"), t("struct.erb", context))
51
+ fs.write(fs.join(directory, "structs", ".keep"), t("keep.erb", context))
52
+ end
43
53
 
44
54
  fs.write(fs.join(directory, "actions/.keep"), t("keep.erb", context))
45
55
  fs.write(fs.join(directory, "views/.keep"), t("keep.erb", context))
46
56
  fs.write(fs.join(directory, "templates/.keep"), t("keep.erb", context))
47
57
  fs.write(fs.join(directory, "templates/layouts/.keep"), t("keep.erb", context))
48
- # fs.write(fs.join(directory, entities/.keep"), t("keep.erb", context))
49
- # fs.write(fs.join(directory, repositories/.keep"), t("keep.erb", context))
50
58
  end
51
59
 
52
60
  private
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../context"
4
+ require_relative "../constants"
4
5
 
5
6
  module Hanami
6
7
  module CLI
@@ -11,10 +12,10 @@ module Hanami
11
12
  class SliceContext < Generators::Context
12
13
  # @since 2.0.0
13
14
  # @api private
14
- def initialize(inflector, app, slice, url)
15
+ def initialize(inflector, app, slice, url, **options)
15
16
  @slice = slice
16
17
  @url = url
17
- super(inflector, app)
18
+ super(inflector, app, **options)
18
19
  end
19
20
 
20
21
  # @since 2.0.0
@@ -47,6 +48,12 @@ module Hanami
47
48
  %(<%= javascript_tag "app" %>)
48
49
  end
49
50
 
51
+ # @since 2.2.0
52
+ # @api private
53
+ def generate_db?
54
+ !options.fetch(:skip_db, false)
55
+ end
56
+
50
57
  private
51
58
 
52
59
  attr_reader :slice
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Generators
6
+ module App
7
+ # @since 2.2.0
8
+ # @api private
9
+ class Struct
10
+ # @since 2.2.0
11
+ # @api private
12
+ def initialize(fs:, inflector:, out: $stdout)
13
+ @fs = fs
14
+ @inflector = inflector
15
+ @out = out
16
+ end
17
+
18
+ # @since 2.2.0
19
+ # @api private
20
+ def call(app_namespace, key, slice)
21
+ RubyFileWriter.new(
22
+ fs: fs,
23
+ inflector: inflector,
24
+ app_namespace: app_namespace,
25
+ key: key,
26
+ slice: slice,
27
+ extra_namespace: "Structs",
28
+ relative_parent_class: "DB::Struct",
29
+ ).call
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :fs, :inflector, :out
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "slice_context"
4
4
  require "dry/files/path"
5
+ require_relative "../constants"
5
6
 
6
7
  module Hanami
7
8
  module CLI
@@ -12,19 +13,6 @@ module Hanami
12
13
  # @since 2.1.0
13
14
  # @api private
14
15
  class ViewContext < SliceContext
15
- # TODO: move these constants somewhere that will let us reuse them
16
- KEY_SEPARATOR = "."
17
- private_constant :KEY_SEPARATOR
18
-
19
- NAMESPACE_SEPARATOR = "::"
20
- private_constant :NAMESPACE_SEPARATOR
21
-
22
- INDENTATION = " "
23
- private_constant :INDENTATION
24
-
25
- OFFSET = INDENTATION * 2
26
- private_constant :OFFSET
27
-
28
16
  # @since 2.1.0
29
17
  # @api private
30
18
  attr_reader :key
@@ -76,7 +64,7 @@ module Hanami
76
64
  # @api private
77
65
  def module_namespace_declaration
78
66
  namespaces.each_with_index.map { |token, i|
79
- "#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
67
+ "#{NESTED_OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
80
68
  }.join($/)
81
69
  end
82
70
 
@@ -84,14 +72,14 @@ module Hanami
84
72
  # @api private
85
73
  def module_namespace_end
86
74
  namespaces.each_with_index.map { |_, i|
87
- "#{OFFSET}#{INDENTATION * i}end"
75
+ "#{NESTED_OFFSET}#{INDENTATION * i}end"
88
76
  }.reverse.join($/)
89
77
  end
90
78
 
91
79
  # @since 2.1.0
92
80
  # @api private
93
81
  def module_namespace_offset
94
- "#{OFFSET}#{INDENTATION * namespaces.count}"
82
+ "#{NESTED_OFFSET}#{INDENTATION * namespaces.count}"
95
83
  end
96
84
  end
97
85
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ # @since 2.2.0
6
+ # @api private
7
+ module Generators
8
+ # @since 2.2.0
9
+ # @api private
10
+ INDENTATION = " "
11
+ private_constant :INDENTATION
12
+
13
+ # @since 2.2.0
14
+ # @api private
15
+ OFFSET = INDENTATION
16
+ private_constant :OFFSET
17
+
18
+ # @since 2.2.0
19
+ # @api private
20
+ NESTED_OFFSET = INDENTATION * 2
21
+ private_constant :OFFSET
22
+
23
+ # @since 2.2.0
24
+ # @api private
25
+ KEY_SEPARATOR = %r{[.\/]}
26
+ private_constant :KEY_SEPARATOR
27
+
28
+ # @since 2.2.0
29
+ # @api private
30
+ MATCHER_PATTERN = /::|\./
31
+ private_constant :MATCHER_PATTERN
32
+
33
+ # @since 2.2.0
34
+ # @api private
35
+ NAMESPACE_SEPARATOR = "::"
36
+ private_constant :NAMESPACE_SEPARATOR
37
+ end
38
+ end
39
+ end
@@ -80,6 +80,44 @@ module Hanami
80
80
  !options.fetch(:skip_assets, false)
81
81
  end
82
82
 
83
+ # @since 2.2.0
84
+ # @api private
85
+ def generate_db?
86
+ !options.fetch(:skip_db, false)
87
+ end
88
+
89
+ # @since 2.2.0
90
+ # @api private
91
+ def generate_sqlite?
92
+ database_option == Commands::Gem::New::DATABASE_SQLITE
93
+ end
94
+
95
+ # @since 2.2.0
96
+ # @api private
97
+ def generate_postgres?
98
+ database_option == Commands::Gem::New::DATABASE_POSTGRES
99
+ end
100
+
101
+ # @since 2.2.0
102
+ # @api private
103
+ def generate_mysql?
104
+ database_option == Commands::Gem::New::DATABASE_MYSQL
105
+ end
106
+
107
+ # @since 2.2.0
108
+ # @api private
109
+ def database_url
110
+ if generate_sqlite?
111
+ "sqlite://db/#{app}.sqlite"
112
+ elsif generate_postgres?
113
+ "postgres://localhost/#{app}"
114
+ elsif generate_mysql?
115
+ "mysql://localhost/#{app}"
116
+ else
117
+ raise "Unknown database option: #{database_option}"
118
+ end
119
+ end
120
+
83
121
  # @since 2.1.0
84
122
  # @api private
85
123
  def bundled_views?
@@ -92,6 +130,12 @@ module Hanami
92
130
  Hanami.bundled?("hanami-assets")
93
131
  end
94
132
 
133
+ # @since 2.2.0
134
+ # @api private
135
+ def bundled_dry_monads?
136
+ Hanami.bundled?("dry-monads")
137
+ end
138
+
95
139
  # @since 2.1.0
96
140
  # @api private
97
141
  #
@@ -102,6 +146,10 @@ module Hanami
102
146
 
103
147
  private
104
148
 
149
+ def database_option
150
+ options.fetch(:database, Commands::Gem::New::DATABASE_SQLITE)
151
+ end
152
+
105
153
  # @since 2.0.0
106
154
  # @api private
107
155
  attr_reader :inflector
@@ -2,8 +2,11 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "hanami/action"
5
+ require "dry/monads"
5
6
 
6
7
  module <%= camelized_app_name %>
7
8
  class Action < Hanami::Action
9
+ # Provide `Success` and `Failure` for pattern matching on operation results
10
+ include Dry::Monads[:result]
8
11
  end
9
12
  end
@@ -0,0 +1,4 @@
1
+ # This is checked into source control, so put sensitive values into `.env.local`
2
+ <%- if generate_db? -%>
3
+ DATABASE_URL=<%= database_url %>
4
+ <%- end -%>