hanami 1.3.1 → 2.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
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