hanami 1.3.1 → 2.0.0.alpha2

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 (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -6
  3. data/FEATURES.md +9 -1
  4. data/LICENSE.md +1 -1
  5. data/README.md +4 -5
  6. data/hanami.gemspec +26 -30
  7. data/lib/hanami.rb +40 -243
  8. data/lib/hanami/application.rb +338 -174
  9. data/lib/hanami/application/autoloader/inflector_adapter.rb +22 -0
  10. data/lib/hanami/application/container/boot/inflector.rb +7 -0
  11. data/lib/hanami/application/container/boot/logger.rb +8 -0
  12. data/lib/hanami/application/container/boot/rack_logger.rb +19 -0
  13. data/lib/hanami/application/container/boot/rack_monitor.rb +12 -0
  14. data/lib/hanami/application/container/boot/settings.rb +7 -0
  15. data/lib/hanami/application/router.rb +59 -0
  16. data/lib/hanami/application/routing/middleware/stack.rb +89 -0
  17. data/lib/hanami/application/routing/resolver.rb +82 -0
  18. data/lib/hanami/application/routing/resolver/node.rb +50 -0
  19. data/lib/hanami/application/routing/resolver/trie.rb +59 -0
  20. data/lib/hanami/application/settings.rb +23 -0
  21. data/lib/hanami/application/settings/definition.rb +26 -0
  22. data/lib/hanami/application/settings/loader.rb +97 -0
  23. data/lib/hanami/application/settings/struct.rb +65 -0
  24. data/lib/hanami/boot.rb +5 -0
  25. data/lib/hanami/cli/application/cli.rb +40 -0
  26. data/lib/hanami/cli/application/command.rb +47 -0
  27. data/lib/hanami/cli/application/commands.rb +16 -0
  28. data/lib/hanami/cli/application/commands/console.rb +81 -0
  29. data/lib/hanami/cli/base_command.rb +48 -0
  30. data/lib/hanami/cli/commands.rb +6 -42
  31. data/lib/hanami/cli/commands/command.rb +20 -30
  32. data/lib/hanami/cli/commands/server.rb +63 -14
  33. data/lib/hanami/configuration.rb +248 -190
  34. data/lib/hanami/configuration/middleware.rb +8 -28
  35. data/lib/hanami/configuration/router.rb +50 -0
  36. data/lib/hanami/configuration/sessions.rb +50 -0
  37. data/lib/hanami/init.rb +5 -0
  38. data/lib/hanami/server.rb +5 -69
  39. data/lib/hanami/setup.rb +8 -2
  40. data/lib/hanami/slice.rb +138 -0
  41. data/lib/hanami/version.rb +3 -1
  42. data/lib/hanami/web/rack_logger.rb +96 -0
  43. metadata +84 -228
  44. data/bin/hanami +0 -6
  45. data/lib/hanami/action/csrf_protection.rb +0 -211
  46. data/lib/hanami/action/routing_helpers.rb +0 -40
  47. data/lib/hanami/app.rb +0 -96
  48. data/lib/hanami/application_configuration.rb +0 -1495
  49. data/lib/hanami/application_name.rb +0 -108
  50. data/lib/hanami/application_namespace.rb +0 -14
  51. data/lib/hanami/assets/asset.rb +0 -72
  52. data/lib/hanami/assets/static.rb +0 -102
  53. data/lib/hanami/cli/commands/assets.rb +0 -16
  54. data/lib/hanami/cli/commands/assets/precompile.rb +0 -42
  55. data/lib/hanami/cli/commands/console.rb +0 -95
  56. data/lib/hanami/cli/commands/db.rb +0 -32
  57. data/lib/hanami/cli/commands/db/apply.rb +0 -32
  58. data/lib/hanami/cli/commands/db/console.rb +0 -44
  59. data/lib/hanami/cli/commands/db/create.rb +0 -32
  60. data/lib/hanami/cli/commands/db/drop.rb +0 -32
  61. data/lib/hanami/cli/commands/db/migrate.rb +0 -39
  62. data/lib/hanami/cli/commands/db/prepare.rb +0 -32
  63. data/lib/hanami/cli/commands/db/rollback.rb +0 -59
  64. data/lib/hanami/cli/commands/db/version.rb +0 -32
  65. data/lib/hanami/cli/commands/destroy.rb +0 -26
  66. data/lib/hanami/cli/commands/destroy/action.rb +0 -137
  67. data/lib/hanami/cli/commands/destroy/app.rb +0 -159
  68. data/lib/hanami/cli/commands/destroy/mailer.rb +0 -74
  69. data/lib/hanami/cli/commands/destroy/migration.rb +0 -51
  70. data/lib/hanami/cli/commands/destroy/model.rb +0 -84
  71. data/lib/hanami/cli/commands/generate.rb +0 -28
  72. data/lib/hanami/cli/commands/generate/action.rb +0 -321
  73. data/lib/hanami/cli/commands/generate/action/action.erb +0 -7
  74. data/lib/hanami/cli/commands/generate/action/action_spec.minitest.erb +0 -11
  75. data/lib/hanami/cli/commands/generate/action/action_spec.rspec.erb +0 -9
  76. data/lib/hanami/cli/commands/generate/action/action_without_view.erb +0 -7
  77. data/lib/hanami/cli/commands/generate/action/view.erb +0 -7
  78. data/lib/hanami/cli/commands/generate/action/view_spec.minitest.erb +0 -12
  79. data/lib/hanami/cli/commands/generate/action/view_spec.rspec.erb +0 -10
  80. data/lib/hanami/cli/commands/generate/app.rb +0 -243
  81. data/lib/hanami/cli/commands/generate/app/application.erb +0 -313
  82. data/lib/hanami/cli/commands/generate/app/favicon.ico +0 -0
  83. data/lib/hanami/cli/commands/generate/app/gitkeep.erb +0 -0
  84. data/lib/hanami/cli/commands/generate/app/layout.erb +0 -7
  85. data/lib/hanami/cli/commands/generate/app/layout_spec.minitest.erb +0 -10
  86. data/lib/hanami/cli/commands/generate/app/layout_spec.rspec.erb +0 -10
  87. data/lib/hanami/cli/commands/generate/app/routes.erb +0 -5
  88. data/lib/hanami/cli/commands/generate/app/template.erb.erb +0 -10
  89. data/lib/hanami/cli/commands/generate/app/template.haml.erb +0 -7
  90. data/lib/hanami/cli/commands/generate/app/template.slim.erb +0 -8
  91. data/lib/hanami/cli/commands/generate/mailer.rb +0 -104
  92. data/lib/hanami/cli/commands/generate/mailer/mailer.erb +0 -9
  93. data/lib/hanami/cli/commands/generate/mailer/mailer_spec.minitest.erb +0 -7
  94. data/lib/hanami/cli/commands/generate/mailer/mailer_spec.rspec.erb +0 -5
  95. data/lib/hanami/cli/commands/generate/migration.rb +0 -41
  96. data/lib/hanami/cli/commands/generate/migration/migration.erb +0 -4
  97. data/lib/hanami/cli/commands/generate/model.rb +0 -125
  98. data/lib/hanami/cli/commands/generate/model/entity.erb +0 -2
  99. data/lib/hanami/cli/commands/generate/model/entity_spec.minitest.erb +0 -5
  100. data/lib/hanami/cli/commands/generate/model/entity_spec.rspec.erb +0 -3
  101. data/lib/hanami/cli/commands/generate/model/migration.erb +0 -10
  102. data/lib/hanami/cli/commands/generate/model/repository.erb +0 -5
  103. data/lib/hanami/cli/commands/generate/model/repository_spec.minitest.erb +0 -5
  104. data/lib/hanami/cli/commands/generate/model/repository_spec.rspec.erb +0 -3
  105. data/lib/hanami/cli/commands/generate/secret.rb +0 -48
  106. data/lib/hanami/cli/commands/new.rb +0 -578
  107. data/lib/hanami/cli/commands/new/.env.development.erb +0 -3
  108. data/lib/hanami/cli/commands/new/.env.test.erb +0 -3
  109. data/lib/hanami/cli/commands/new/.gitkeep.erb +0 -0
  110. data/lib/hanami/cli/commands/new/Gemfile.erb +0 -58
  111. data/lib/hanami/cli/commands/new/README.md.erb +0 -33
  112. data/lib/hanami/cli/commands/new/config.ru.erb +0 -3
  113. data/lib/hanami/cli/commands/new/config/boot.erb +0 -2
  114. data/lib/hanami/cli/commands/new/config/environment.erb +0 -49
  115. data/lib/hanami/cli/commands/new/gitignore.erb +0 -2
  116. data/lib/hanami/cli/commands/new/gitignore_with_sqlite.erb +0 -3
  117. data/lib/hanami/cli/commands/new/hanamirc.erb +0 -3
  118. data/lib/hanami/cli/commands/new/lib/project.erb +0 -2
  119. data/lib/hanami/cli/commands/new/minitest/Rakefile.erb +0 -12
  120. data/lib/hanami/cli/commands/new/minitest/features_helper.erb +0 -11
  121. data/lib/hanami/cli/commands/new/minitest/spec_helper.erb +0 -7
  122. data/lib/hanami/cli/commands/new/rspec/Rakefile.erb +0 -9
  123. data/lib/hanami/cli/commands/new/rspec/capybara.erb +0 -8
  124. data/lib/hanami/cli/commands/new/rspec/features_helper.erb +0 -12
  125. data/lib/hanami/cli/commands/new/rspec/rspec.erb +0 -2
  126. data/lib/hanami/cli/commands/new/rspec/spec_helper.erb +0 -103
  127. data/lib/hanami/cli/commands/new/schema.sql.erb +0 -0
  128. data/lib/hanami/cli/commands/project.rb +0 -421
  129. data/lib/hanami/cli/commands/routes.rb +0 -21
  130. data/lib/hanami/cli/commands/templates.rb +0 -31
  131. data/lib/hanami/cli/commands/version.rb +0 -19
  132. data/lib/hanami/common_logger.rb +0 -93
  133. data/lib/hanami/components.rb +0 -156
  134. data/lib/hanami/components/app/assets.rb +0 -59
  135. data/lib/hanami/components/app/controller.rb +0 -74
  136. data/lib/hanami/components/app/routes.rb +0 -59
  137. data/lib/hanami/components/app/view.rb +0 -44
  138. data/lib/hanami/components/component.rb +0 -182
  139. data/lib/hanami/components/components.rb +0 -479
  140. data/lib/hanami/components/routes_inspector.rb +0 -72
  141. data/lib/hanami/config/cookies.rb +0 -69
  142. data/lib/hanami/config/framework_configuration.rb +0 -43
  143. data/lib/hanami/config/load_paths.rb +0 -46
  144. data/lib/hanami/config/mapper.rb +0 -47
  145. data/lib/hanami/config/routes.rb +0 -20
  146. data/lib/hanami/config/security.rb +0 -110
  147. data/lib/hanami/config/sessions.rb +0 -103
  148. data/lib/hanami/configuration/app.rb +0 -18
  149. data/lib/hanami/early_hints.rb +0 -129
  150. data/lib/hanami/env.rb +0 -69
  151. data/lib/hanami/environment.rb +0 -513
  152. data/lib/hanami/environment_application_configurations.rb +0 -37
  153. data/lib/hanami/frameworks.rb +0 -22
  154. data/lib/hanami/hanamirc.rb +0 -169
  155. data/lib/hanami/mailer/glue.rb +0 -27
  156. data/lib/hanami/middleware_stack.rb +0 -172
  157. data/lib/hanami/rake_helper.rb +0 -78
  158. data/lib/hanami/rake_tasks.rb +0 -2
  159. data/lib/hanami/rendering_policy.rb +0 -95
  160. data/lib/hanami/routes.rb +0 -219
  161. data/lib/hanami/routing/default.rb +0 -32
  162. data/lib/hanami/static.rb +0 -63
  163. data/lib/hanami/templates/default.html.erb +0 -30
  164. data/lib/hanami/templates/welcome.html.erb +0 -52
  165. data/lib/hanami/views/default.rb +0 -37
  166. data/lib/hanami/views/default_template_finder.rb +0 -22
  167. data/lib/hanami/views/null_view.rb +0 -15
  168. data/lib/hanami/welcome.rb +0 -36
@@ -1,159 +0,0 @@
1
- module Hanami
2
- class CLI
3
- module Commands
4
- module Destroy
5
- # @since 1.1.0
6
- # @api private
7
- class App < Command
8
- desc "Destroy an app"
9
-
10
- argument :app, required: true, desc: "The application name (eg. `web`)"
11
-
12
- example [
13
- "admin # Destroy `admin` app"
14
- ]
15
-
16
- # @since 1.1.0
17
- # @api private
18
- def call(app:, **options) # rubocop:disable Metrics/MethodLength
19
- app = Utils::String.underscore(app)
20
- context = Context.new(app: app, options: options)
21
-
22
- assert_valid_app!(context)
23
- context = context.with(base_url: base_url(context))
24
-
25
- remove_test_http_session_secret(context)
26
- remove_development_http_session_secret(context)
27
-
28
- remove_mount_app(context)
29
- remove_require_app(context)
30
-
31
- recursively_destroy_precompiled_assets(context)
32
- destroy_assets_manifest(context)
33
-
34
- recursively_destroy_specs(context)
35
- recursively_destroy_app(context)
36
- end
37
-
38
- private
39
-
40
- # @since 1.1.0
41
- # @api private
42
- def assert_valid_app!(context)
43
- return if project.app?(context)
44
-
45
- existing_apps = project.apps.map { |name| "`#{name}'" }.join(' ')
46
- warn "`#{context.app}' is not a valid APP. Please specify one of: #{existing_apps}"
47
- exit(1)
48
- end
49
-
50
- # @since 1.1.0
51
- # @api private
52
- def remove_test_http_session_secret(context)
53
- content = "#{context.app.upcase}_SESSIONS_SECRET"
54
- destination = project.env(context, "test")
55
-
56
- files.remove_line(destination, content)
57
- say(:subtract, destination)
58
- end
59
-
60
- # @since 1.1.0
61
- # @api private
62
- def remove_development_http_session_secret(context)
63
- content = "#{context.app.upcase}_SESSIONS_SECRET"
64
- destination = project.env(context, "development")
65
-
66
- files.remove_line(destination, content)
67
- say(:subtract, destination)
68
- end
69
-
70
- # @since 1.1.0
71
- # @api private
72
- def remove_mount_app(context)
73
- content = "mount #{context.app.classify}::Application"
74
- destination = project.environment(context)
75
-
76
- files.remove_line(destination, content)
77
- say(:subtract, destination)
78
- end
79
-
80
- # @since 1.1.0
81
- # @api private
82
- def remove_require_app(context)
83
- content = "require_relative '../apps/#{context.app}/application'"
84
- destination = project.environment(context)
85
-
86
- files.remove_line(destination, content)
87
- say(:subtract, destination)
88
- end
89
-
90
- # @since 1.1.0
91
- # @api private
92
- def recursively_destroy_precompiled_assets(context)
93
- destination = project.public_app_assets(context)
94
- return unless files.directory?(destination)
95
-
96
- files.delete_directory(destination)
97
- say(:remove, destination)
98
- end
99
-
100
- # @since 1.1.0
101
- # @api private
102
- def destroy_assets_manifest(context)
103
- destination = project.assets_manifest(context)
104
- return unless files.exist?(destination)
105
-
106
- files.delete(destination)
107
- say(:remove, destination)
108
- end
109
-
110
- # @since 1.1.0
111
- # @api private
112
- def recursively_destroy_specs(context)
113
- destination = project.app_spec(context)
114
-
115
- files.delete_directory(destination)
116
- say(:remove, destination)
117
- end
118
-
119
- # @since 1.1.0
120
- # @api private
121
- def recursively_destroy_app(context)
122
- destination = project.app(context)
123
-
124
- files.delete_directory(destination)
125
- say(:remove, destination)
126
- end
127
-
128
- # @since 1.1.0
129
- # @api private
130
- def base_url(context)
131
- content = "mount #{context.app.classify}::Application"
132
- destination = project.environment(context)
133
-
134
- line = read_matching_line(destination, content)
135
- *, at = line.split(/at\:[[:space:]]*/)
136
-
137
- at.strip.gsub(/["']*/, "")
138
- end
139
-
140
- # @since 1.1.0
141
- # @api private
142
- def read_matching_line(path, target)
143
- content = ::File.readlines(path)
144
- line = content.find do |l|
145
- case target
146
- when String
147
- l.include?(target)
148
- when Regexp
149
- l =~ target
150
- end
151
- end
152
-
153
- line or raise ArgumentError.new("Cannot find `#{target}' inside `#{path}'.")
154
- end
155
- end
156
- end
157
- end
158
- end
159
- end
@@ -1,74 +0,0 @@
1
- module Hanami
2
- class CLI
3
- module Commands
4
- module Destroy
5
- # @since 1.1.0
6
- # @api private
7
- class Mailer < Command
8
- requires "environment"
9
- desc "Destroy a mailer"
10
-
11
- argument :mailer, required: true, desc: "The mailer name (eg. `welcome`)"
12
-
13
- example [
14
- "welcome # Destroy `WelcomeMailer` mailer"
15
- ]
16
-
17
- # @since 1.1.0
18
- # @api private
19
- def call(mailer:, **options)
20
- mailer = Utils::String.transform(mailer, :underscore, :singularize)
21
- context = Context.new(mailer: mailer, options: options)
22
-
23
- assert_valid_mailer!(context)
24
-
25
- destroy_mailer_spec(context)
26
- destroy_templates(context)
27
- destroy_mailer(context)
28
- end
29
-
30
- private
31
-
32
- # @since 1.1.0
33
- # @api private
34
- def assert_valid_mailer!(context)
35
- destination = project.mailer(context)
36
- return if files.exist?(destination)
37
-
38
- destination = project.mailers(context)
39
- warn "cannot find `#{context.mailer}' mailer. Please have a look at `#{destination}' directory to find an existing mailer."
40
- exit(1)
41
- end
42
-
43
- # @since 1.1.0
44
- # @api private
45
- def destroy_mailer_spec(context)
46
- destination = project.mailer_spec(context)
47
-
48
- files.delete(destination)
49
- say(:remove, destination)
50
- end
51
-
52
- # @since 1.1.0
53
- # @api private
54
- def destroy_templates(context)
55
- destinations = project.mailer_templates(context)
56
- destinations.each do |destination|
57
- files.delete(destination)
58
- say(:remove, destination)
59
- end
60
- end
61
-
62
- # @since 1.1.0
63
- # @api private
64
- def destroy_mailer(context)
65
- destination = project.mailer(context)
66
-
67
- files.delete(destination)
68
- say(:remove, destination)
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
@@ -1,51 +0,0 @@
1
- module Hanami
2
- class CLI
3
- module Commands
4
- module Destroy
5
- # @since 1.1.0
6
- # @api private
7
- class Migration < Command
8
- desc "Destroy a migration"
9
-
10
- argument :migration, required: true, desc: "The migration name (eg. `create_users`)"
11
-
12
- example [
13
- "create_users # Destroy `db/migrations/#{Project.migration_timestamp}_create_users.rb`"
14
- ]
15
-
16
- # @since 1.1.0
17
- # @api private
18
- def call(migration:, **options)
19
- migration = Utils::String.underscore(migration)
20
- context = Context.new(migration: migration, options: options)
21
- context = context.with(destination: project.find_migration(context))
22
-
23
- assert_valid_migration!(context)
24
-
25
- destroy_migration(context)
26
- true
27
- end
28
-
29
- private
30
-
31
- # @since 1.1.0
32
- # @api private
33
- def assert_valid_migration!(context)
34
- return if !context.destination.nil? && files.exist?(context.destination)
35
-
36
- destination = project.migrations(context)
37
- warn "cannot find `#{context.migration}'. Please have a look at `#{destination}' directory to find an existing migration"
38
- exit(1)
39
- end
40
-
41
- # @since 1.1.0
42
- # @api private
43
- def destroy_migration(context)
44
- files.delete(context.destination)
45
- say(:remove, context.destination)
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,84 +0,0 @@
1
- module Hanami
2
- class CLI
3
- module Commands
4
- module Destroy
5
- # @since 1.1.0
6
- # @api private
7
- class Model < Command
8
- requires "environment"
9
-
10
- desc "Destroy a model"
11
-
12
- argument :model, required: true, desc: "The model name (eg. `user`)"
13
-
14
- example [
15
- "user # Destroy `User` entity and `UserRepository` repository"
16
- ]
17
-
18
- # @since 1.1.0
19
- # @api private
20
- def call(model:, **options)
21
- model = Utils::String.transform(model, :underscore, :singularize)
22
- context = Context.new(model: model, options: options)
23
-
24
- assert_valid_model!(context)
25
-
26
- destroy_repository_spec(context)
27
- destroy_entity_spec(context)
28
- destroy_repository(context)
29
- destroy_entity(context)
30
- end
31
-
32
- private
33
-
34
- # @since 1.1.0
35
- # @api private
36
- def assert_valid_model!(context)
37
- destination = project.entity(context)
38
- return if files.exist?(destination)
39
-
40
- destination = project.entities(context)
41
- warn "cannot find `#{context.model}' model. Please have a look at `#{destination}' directory to find an existing model."
42
- exit(1)
43
- end
44
-
45
- # @since 1.1.0
46
- # @api private
47
- def destroy_repository_spec(context)
48
- destination = project.repository_spec(context)
49
-
50
- files.delete(destination)
51
- say(:remove, destination)
52
- end
53
-
54
- # @since 1.1.0
55
- # @api private
56
- def destroy_entity_spec(context)
57
- destination = project.entity_spec(context)
58
-
59
- files.delete(destination)
60
- say(:remove, destination)
61
- end
62
-
63
- # @since 1.1.0
64
- # @api private
65
- def destroy_repository(context)
66
- destination = project.repository(context)
67
-
68
- files.delete(destination)
69
- say(:remove, destination)
70
- end
71
-
72
- # @since 1.1.0
73
- # @api private
74
- def destroy_entity(context)
75
- destination = project.entity(context)
76
-
77
- files.delete(destination)
78
- say(:remove, destination)
79
- end
80
- end
81
- end
82
- end
83
- end
84
- end
@@ -1,28 +0,0 @@
1
- module Hanami
2
- # Hanami CLI
3
- #
4
- # @since 1.1.0
5
- class CLI
6
- module Commands
7
- # @since 1.1.0
8
- # @api private
9
- module Generate
10
- require "hanami/cli/commands/generate/app"
11
- require "hanami/cli/commands/generate/action"
12
- require "hanami/cli/commands/generate/mailer"
13
- require "hanami/cli/commands/generate/migration"
14
- require "hanami/cli/commands/generate/model"
15
- require "hanami/cli/commands/generate/secret"
16
- end
17
- end
18
-
19
- register "generate", aliases: ["g"] do |prefix|
20
- prefix.register "app", Commands::Generate::App
21
- prefix.register "action", Commands::Generate::Action
22
- prefix.register "mailer", Commands::Generate::Mailer
23
- prefix.register "migration", Commands::Generate::Migration
24
- prefix.register "model", Commands::Generate::Model
25
- prefix.register "secret", Commands::Generate::Secret
26
- end
27
- end
28
- end
@@ -1,321 +0,0 @@
1
- module Hanami
2
- class CLI
3
- module Commands
4
- module Generate
5
- # @since 1.1.0
6
- # @api private
7
- class Action < Command
8
- requires "environment"
9
-
10
- desc "Generate an action for app"
11
-
12
- example [
13
- "web home#index # Basic usage",
14
- "admin home#index # Generate for `admin` app",
15
- "web home#index --url=/ # Specify URL",
16
- "web sessions#destroy --method=GET # Specify HTTP method",
17
- "web books#create --skip-view # Skip view and template"
18
- ]
19
-
20
- argument :app, required: true, desc: "The application name (eg. `web`)"
21
- argument :action, required: true, desc: "The action name (eg. `home#index`)"
22
-
23
- option :url, desc: "The action URL"
24
- option :method, desc: "The action HTTP method"
25
- option :skip_view, type: :boolean, default: false, desc: "Skip view and template"
26
-
27
- # @since 1.1.0
28
- # @api private
29
- #
30
- # rubocop:disable Metrics/AbcSize
31
- # rubocop:disable Metrics/MethodLength
32
- def call(app:, action:, **options)
33
- *controller, action = controller_and_action_name(action)
34
- classified_controller_name = classified_controller(controller)
35
- http_method = route_http_method(action, options)
36
- context = Context.new(app: app, controller: controller, classified_controller_name: classified_controller_name, action: action, test: options.fetch(:test), http_method: http_method, options: options)
37
- context = context.with(template: project.template(context),
38
- action_class_definition: action_class_definition(context),
39
- view_class_definition: view_class_definition(context),
40
- beginning_module_definition: beginning_module_definition(context),
41
- ending_module_definition: ending_module_definition(context))
42
-
43
- assert_valid_app!(context)
44
- assert_valid_route_url!(context)
45
- assert_valid_route_http_method!(context)
46
-
47
- generate_action(context)
48
- generate_view(context)
49
- generate_template(context)
50
- generate_action_spec(context)
51
- generate_view_spec(context)
52
- insert_route(context)
53
- end
54
- # rubocop:enable Metrics/MethodLength
55
- # rubocop:enable Metrics/AbcSize
56
-
57
- private
58
-
59
- # @since 1.3.0
60
- # @api private
61
- def beginning_module_definition(context)
62
- length = context.classified_controller_name.split("::").size
63
- first_indentation = " " * 4
64
- indentation = first_indentation
65
- beginning_module_definition = ""
66
-
67
- context.classified_controller_name.split("::").each_with_index do |module_name, index|
68
- beginning_module_definition += "module #{module_name}".prepend(indentation)
69
- beginning_module_definition += "\n" unless length == index + 1
70
- indentation += " "
71
- end
72
- beginning_module_definition
73
- end
74
-
75
- # @since 1.3.0
76
- # @api private
77
- def ending_module_definition(context)
78
- length = context.classified_controller_name.split("::").size
79
- first_indentation = " " * 4
80
- indentation = first_indentation + " " * (length - 1)
81
- ending_module_definition = ""
82
-
83
- context.classified_controller_name.split("::").each_with_index do |_, index|
84
- ending_module_definition += "end".prepend(indentation)
85
- ending_module_definition += "\n" unless length == index + 1
86
- indentation = " " * (indentation.size - 2)
87
- end
88
- ending_module_definition
89
- end
90
-
91
- # @since 1.3.0
92
- # @api private
93
- def action_class_definition(context)
94
- body_line_position = 4
95
- view_class = [
96
- "class #{context.action.classify}",
97
- " include #{context.app.classify}::Action",
98
- "",
99
- " def call(params)",
100
- " end",
101
- "end"
102
- ]
103
- view_class.insert(body_line_position, " self.body = 'OK'") if skip_view?(context)
104
-
105
- add_indentation_to_class_definition(context, view_class)
106
- end
107
-
108
- # @since 1.3.0
109
- # @api private
110
- def view_class_definition(context)
111
- view_class = [
112
- "class #{context.action.classify}",
113
- " include #{context.app.classify}::View",
114
- "end"
115
- ]
116
-
117
- add_indentation_to_class_definition(context, view_class)
118
- end
119
-
120
- # @since 1.3.0
121
- # @api private
122
- def add_indentation_to_class_definition(context, view_content)
123
- length = context.classified_controller_name.split("::").size
124
- first_indentation = " " * 4
125
- indentation = first_indentation + " " * length * 2
126
-
127
- content = ""
128
- view_content.each do |line|
129
- next content += "\n" if line.empty?
130
- content += "#{line}\n".prepend(indentation)
131
- end
132
- content.chomp
133
- end
134
-
135
- # @since 1.1.0
136
- # @api private
137
- def controller_and_action_name(name)
138
- # FIXME: extract this regexp
139
- name.split(/#|\//)
140
- end
141
-
142
- # @since 1.1.0
143
- # @api private
144
- def assert_valid_app!(context)
145
- return if project.app?(context)
146
-
147
- existing_apps = project.apps.map { |name| "`#{name}'" }.join(' ')
148
- warn "`#{context.app}' is not a valid APP. Please specify one of: #{existing_apps}"
149
- exit(1)
150
- end
151
-
152
- # @since 1.1.0
153
- # @api private
154
- def assert_valid_route_url!(context)
155
- if context.options.key?(:url) && Utils::Blank.blank?(context.options[:url]) # rubocop:disable Style/GuardClause
156
- warn "`#{context.options[:url]}' is not a valid URL"
157
- exit(1)
158
- end
159
- end
160
-
161
- # @since 1.1.0
162
- # @api private
163
- def assert_valid_route_http_method!(context)
164
- unless Hanami::Routing::Route::VALID_HTTP_VERBS.include?(context.http_method.upcase) # rubocop:disable Style/GuardClause
165
- warn "`#{context.http_method.upcase}' is not a valid HTTP method. Please use one of: #{Hanami::Routing::Route::VALID_HTTP_VERBS.map { |verb| "`#{verb}'" }.join(' ')}"
166
- exit(1)
167
- end
168
- end
169
-
170
- # @since 1.1.0
171
- # @api private
172
- def generate_action(context)
173
- source = if skip_view?(context)
174
- templates.find("action_without_view.erb")
175
- else
176
- templates.find("action.erb")
177
- end
178
- destination = project.action(context)
179
-
180
- generate_file(source, destination, context)
181
- say(:create, destination)
182
- end
183
-
184
- # @since 1.1.0
185
- # @api private
186
- def generate_view(context)
187
- return if skip_view?(context)
188
-
189
- source = templates.find("view.erb")
190
- destination = project.view(context)
191
-
192
- generate_file(source, destination, context)
193
- say(:create, destination)
194
- end
195
-
196
- # @since 1.1.0
197
- # @api private
198
- def generate_template(context)
199
- return if skip_view?(context)
200
- destination = project.template(context)
201
-
202
- files.touch(destination)
203
- say(:create, destination)
204
- end
205
-
206
- # @since 1.1.0
207
- # @api private
208
- def generate_action_spec(context)
209
- source = templates.find("action_spec.#{context.test}.erb")
210
- destination = project.action_spec(context)
211
-
212
- generate_file(source, destination, context)
213
- say(:create, destination)
214
- end
215
-
216
- # @since 1.1.0
217
- # @api private
218
- def generate_view_spec(context)
219
- return if skip_view?(context)
220
-
221
- source = templates.find("view_spec.#{context.test}.erb")
222
- destination = project.view_spec(context)
223
-
224
- generate_file(source, destination, context)
225
- say(:create, destination)
226
- end
227
-
228
- # @since 1.1.0
229
- # @api private
230
- def insert_route(context)
231
- content = "#{context.http_method} '#{route_url(context)}', to: '#{route_endpoint(context)}'".downcase
232
- destination = project.app_routes(context)
233
-
234
- files.append(destination, content)
235
- say(:insert, destination)
236
- end
237
-
238
- # @since 1.1.0
239
- # @api private
240
- def route_http_method(action, options)
241
- options.fetch(:method) { route_resourceful_http_method(action) }
242
- end
243
-
244
- # @since 1.1.0
245
- # @api private
246
- def skip_view?(context)
247
- context.options.fetch(:skip_view, false)
248
- end
249
-
250
- # @since 1.1.0
251
- # @api private
252
- DEFAULT_HTTP_METHOD = 'GET'.freeze
253
-
254
- # @since 1.1.0
255
- # @api private
256
- RESOURCEFUL_HTTP_METHODS = {
257
- 'create' => 'POST',
258
- 'update' => 'PATCH',
259
- 'destroy' => 'DELETE'
260
- }.freeze
261
-
262
- # @since 1.1.0
263
- # @api private
264
- def route_resourceful_http_method(action)
265
- RESOURCEFUL_HTTP_METHODS.fetch(action, DEFAULT_HTTP_METHOD)
266
- end
267
-
268
- # @since 1.1.0
269
- # @api private
270
- def route_url(context)
271
- context.options.fetch(:url) { route_resourceful_url(context) }
272
- end
273
-
274
- # @since 1.1.0
275
- # @api private
276
- def route_resourceful_url(context)
277
- "/#{namespaced_controller(context)}#{route_resourceful_url_suffix(context)}"
278
- end
279
-
280
- # @since 1.1.0
281
- # @api private
282
- RESOURCEFUL_ROUTE_URL_SUFFIXES = {
283
- 'index' => '',
284
- 'new' => '/new',
285
- 'create' => '',
286
- 'edit' => '/:id/edit',
287
- 'update' => '/:id',
288
- 'show' => '/:id',
289
- 'destroy' => '/:id'
290
- }.freeze
291
-
292
- # @since 1.1.0
293
- # @api private
294
- def route_resourceful_url_suffix(context)
295
- RESOURCEFUL_ROUTE_URL_SUFFIXES.fetch(context.action) { "/#{context.action}" }
296
- end
297
-
298
- # @since 1.1.0
299
- # @api private
300
- def route_endpoint(context)
301
- "#{namespaced_controller(context)}##{context.action}"
302
- end
303
-
304
- # @since 1.1.0
305
- # @api private
306
- def classified_controller(controller)
307
- controller.
308
- map { |controller_name| Utils::String.new(controller_name).classify }.
309
- join("::")
310
- end
311
-
312
- # @since 1.1.0
313
- # @api private
314
- def namespaced_controller(context)
315
- context.controller.join("/")
316
- end
317
- end
318
- end
319
- end
320
- end
321
- end