hanami 2.0.0.alpha8 → 2.0.0.beta1

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 (224) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +442 -241
  3. data/FEATURES.md +30 -9
  4. data/README.md +1 -3
  5. data/hanami.gemspec +21 -11
  6. data/lib/hanami/app.rb +141 -0
  7. data/lib/hanami/assets/application_configuration.rb +10 -4
  8. data/lib/hanami/configuration/actions/content_security_policy.rb +5 -5
  9. data/lib/hanami/configuration/actions/cookies.rb +2 -2
  10. data/lib/hanami/configuration/actions.rb +10 -4
  11. data/lib/hanami/configuration/logger.rb +4 -4
  12. data/lib/hanami/configuration/router.rb +2 -6
  13. data/lib/hanami/configuration/sessions.rb +1 -1
  14. data/lib/hanami/configuration/views.rb +9 -4
  15. data/lib/hanami/configuration.rb +118 -46
  16. data/lib/hanami/constants.rb +24 -2
  17. data/lib/hanami/errors.rb +1 -1
  18. data/lib/hanami/{application → extensions}/action/slice_configured_action.rb +9 -9
  19. data/lib/hanami/extensions/action.rb +79 -0
  20. data/lib/hanami/extensions/view/context.rb +106 -0
  21. data/lib/hanami/{application → extensions}/view/slice_configured_context.rb +10 -10
  22. data/lib/hanami/{application → extensions}/view/slice_configured_view.rb +12 -6
  23. data/lib/hanami/extensions/view.rb +33 -0
  24. data/lib/hanami/extensions.rb +10 -0
  25. data/lib/hanami/providers/inflector.rb +13 -0
  26. data/lib/hanami/providers/logger.rb +13 -0
  27. data/lib/hanami/providers/rack.rb +27 -0
  28. data/lib/hanami/providers/routes.rb +33 -0
  29. data/lib/hanami/providers/settings.rb +23 -0
  30. data/lib/hanami/rake_tasks.rb +61 -0
  31. data/lib/hanami/routes.rb +51 -0
  32. data/lib/hanami/server.rb +1 -1
  33. data/lib/hanami/settings/dotenv_store.rb +58 -0
  34. data/lib/hanami/settings.rb +90 -0
  35. data/lib/hanami/setup.rb +4 -2
  36. data/lib/hanami/{application → slice}/router.rb +18 -13
  37. data/lib/hanami/slice/routes_helper.rb +37 -0
  38. data/lib/hanami/{application → slice}/routing/middleware/stack.rb +43 -5
  39. data/lib/hanami/slice/routing/resolver.rb +97 -0
  40. data/lib/hanami/{application → slice}/view_name_inferrer.rb +3 -3
  41. data/lib/hanami/slice.rb +246 -73
  42. data/lib/hanami/slice_configurable.rb +4 -17
  43. data/lib/hanami/slice_name.rb +6 -6
  44. data/lib/hanami/slice_registrar.rb +119 -0
  45. data/lib/hanami/version.rb +1 -1
  46. data/lib/hanami/web/rack_logger.rb +1 -1
  47. data/lib/hanami.rb +34 -26
  48. data/spec/integration/application_middleware_stack_spec.rb +84 -0
  49. data/spec/integration/assets/cdn_spec.rb +48 -0
  50. data/spec/integration/assets/fingerprint_spec.rb +42 -0
  51. data/spec/integration/assets/helpers_spec.rb +50 -0
  52. data/spec/integration/assets/serve_spec.rb +70 -0
  53. data/spec/integration/assets/subresource_integrity_spec.rb +54 -0
  54. data/spec/integration/body_parsers_spec.rb +50 -0
  55. data/spec/integration/cli/assets/precompile_spec.rb +147 -0
  56. data/spec/integration/cli/assets_spec.rb +14 -0
  57. data/spec/integration/cli/console_spec.rb +105 -0
  58. data/spec/integration/cli/db/apply_spec.rb +74 -0
  59. data/spec/integration/cli/db/console_spec.rb +40 -0
  60. data/spec/integration/cli/db/create_spec.rb +50 -0
  61. data/spec/integration/cli/db/drop_spec.rb +54 -0
  62. data/spec/integration/cli/db/migrate_spec.rb +108 -0
  63. data/spec/integration/cli/db/prepare_spec.rb +36 -0
  64. data/spec/integration/cli/db/rollback_spec.rb +96 -0
  65. data/spec/integration/cli/db/version_spec.rb +38 -0
  66. data/spec/integration/cli/db_spec.rb +21 -0
  67. data/spec/integration/cli/destroy/action_spec.rb +143 -0
  68. data/spec/integration/cli/destroy/app_spec.rb +118 -0
  69. data/spec/integration/cli/destroy/mailer_spec.rb +74 -0
  70. data/spec/integration/cli/destroy/migration_spec.rb +70 -0
  71. data/spec/integration/cli/destroy/model_spec.rb +113 -0
  72. data/spec/integration/cli/destroy_spec.rb +18 -0
  73. data/spec/integration/cli/generate/action_spec.rb +469 -0
  74. data/spec/integration/cli/generate/app_spec.rb +215 -0
  75. data/spec/integration/cli/generate/mailer_spec.rb +189 -0
  76. data/spec/integration/cli/generate/migration_spec.rb +72 -0
  77. data/spec/integration/cli/generate/model_spec.rb +290 -0
  78. data/spec/integration/cli/generate/secret_spec.rb +56 -0
  79. data/spec/integration/cli/generate_spec.rb +19 -0
  80. data/spec/integration/cli/new/database_spec.rb +235 -0
  81. data/spec/integration/cli/new/hanami_head_spec.rb +27 -0
  82. data/spec/integration/cli/new/template_spec.rb +118 -0
  83. data/spec/integration/cli/new/test_spec.rb +274 -0
  84. data/spec/integration/cli/new_spec.rb +970 -0
  85. data/spec/integration/cli/plugins_spec.rb +39 -0
  86. data/spec/integration/cli/routes_spec.rb +49 -0
  87. data/spec/integration/cli/server_spec.rb +626 -0
  88. data/spec/integration/cli/version_spec.rb +85 -0
  89. data/spec/integration/early_hints_spec.rb +35 -0
  90. data/spec/integration/handle_exceptions_spec.rb +244 -0
  91. data/spec/integration/head_spec.rb +89 -0
  92. data/spec/integration/http_headers_spec.rb +29 -0
  93. data/spec/integration/mailer_spec.rb +32 -0
  94. data/spec/integration/middleware_spec.rb +81 -0
  95. data/spec/integration/mount_applications_spec.rb +88 -0
  96. data/spec/integration/project_initializers_spec.rb +40 -0
  97. data/spec/integration/rackup_spec.rb +35 -0
  98. data/spec/integration/rake/with_minitest_spec.rb +67 -0
  99. data/spec/integration/rake/with_rspec_spec.rb +69 -0
  100. data/spec/integration/routing_helpers_spec.rb +61 -0
  101. data/spec/integration/security/content_security_policy_spec.rb +46 -0
  102. data/spec/integration/security/csrf_protection_spec.rb +42 -0
  103. data/spec/integration/security/force_ssl_spec.rb +29 -0
  104. data/spec/integration/security/x_content_type_options_spec.rb +46 -0
  105. data/spec/integration/security/x_frame_options_spec.rb +46 -0
  106. data/spec/integration/security/x_xss_protection_spec.rb +46 -0
  107. data/spec/integration/send_file_spec.rb +51 -0
  108. data/spec/integration/sessions_spec.rb +247 -0
  109. data/spec/integration/static_middleware_spec.rb +21 -0
  110. data/spec/integration/streaming_spec.rb +41 -0
  111. data/spec/integration/unsafe_send_file_spec.rb +52 -0
  112. data/spec/isolation/hanami/application/already_configured_spec.rb +19 -0
  113. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +10 -0
  114. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +14 -0
  115. data/spec/isolation/hanami/application/not_configured_spec.rb +9 -0
  116. data/spec/isolation/hanami/application/routes/configured_spec.rb +44 -0
  117. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +16 -0
  118. data/spec/isolation/hanami/boot/success_spec.rb +50 -0
  119. data/spec/new_integration/action/configuration_spec.rb +26 -0
  120. data/spec/new_integration/action/cookies_spec.rb +58 -0
  121. data/spec/new_integration/action/csrf_protection_spec.rb +54 -0
  122. data/spec/new_integration/action/routes_spec.rb +73 -0
  123. data/spec/new_integration/action/sessions_spec.rb +50 -0
  124. data/spec/new_integration/action/view_integration_spec.rb +165 -0
  125. data/spec/new_integration/action/view_rendering/automatic_rendering_spec.rb +247 -0
  126. data/spec/new_integration/action/view_rendering/paired_view_inference_spec.rb +115 -0
  127. data/spec/new_integration/action/view_rendering_spec.rb +107 -0
  128. data/spec/new_integration/code_loading/loading_from_app_spec.rb +152 -0
  129. data/spec/new_integration/code_loading/loading_from_slice_spec.rb +165 -0
  130. data/spec/new_integration/container/application_routes_helper_spec.rb +48 -0
  131. data/spec/new_integration/container/auto_injection_spec.rb +53 -0
  132. data/spec/new_integration/container/auto_registration_spec.rb +86 -0
  133. data/spec/new_integration/container/autoloader_spec.rb +80 -0
  134. data/spec/new_integration/container/imports_spec.rb +253 -0
  135. data/spec/new_integration/container/prepare_container_spec.rb +123 -0
  136. data/spec/new_integration/container/shutdown_spec.rb +91 -0
  137. data/spec/new_integration/container/standard_bootable_components_spec.rb +124 -0
  138. data/spec/new_integration/rack_app/middleware_spec.rb +215 -0
  139. data/spec/new_integration/rack_app/non_booted_rack_app_spec.rb +105 -0
  140. data/spec/new_integration/rack_app/rack_app_spec.rb +524 -0
  141. data/spec/new_integration/settings_spec.rb +115 -0
  142. data/spec/new_integration/slices/external_slice_spec.rb +92 -0
  143. data/spec/new_integration/slices/slice_configuration_spec.rb +40 -0
  144. data/spec/new_integration/slices/slice_routing_spec.rb +226 -0
  145. data/spec/new_integration/slices/slice_settings_spec.rb +141 -0
  146. data/spec/new_integration/slices_spec.rb +101 -0
  147. data/spec/new_integration/view/configuration_spec.rb +49 -0
  148. data/spec/new_integration/view/context/assets_spec.rb +67 -0
  149. data/spec/new_integration/view/context/inflector_spec.rb +48 -0
  150. data/spec/new_integration/view/context/request_spec.rb +61 -0
  151. data/spec/new_integration/view/context/routes_spec.rb +86 -0
  152. data/spec/new_integration/view/context/settings_spec.rb +50 -0
  153. data/spec/new_integration/view/inflector_spec.rb +57 -0
  154. data/spec/new_integration/view/part_namespace_spec.rb +96 -0
  155. data/spec/new_integration/view/path_spec.rb +56 -0
  156. data/spec/new_integration/view/template_spec.rb +68 -0
  157. data/spec/new_integration/view/views_spec.rb +103 -0
  158. data/spec/spec_helper.rb +16 -0
  159. data/spec/support/app_integration.rb +91 -0
  160. data/spec/support/coverage.rb +1 -0
  161. data/spec/support/fixtures/hanami-plugin/Gemfile +8 -0
  162. data/spec/support/fixtures/hanami-plugin/README.md +35 -0
  163. data/spec/support/fixtures/hanami-plugin/Rakefile +4 -0
  164. data/spec/support/fixtures/hanami-plugin/bin/console +15 -0
  165. data/spec/support/fixtures/hanami-plugin/bin/setup +8 -0
  166. data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +28 -0
  167. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +19 -0
  168. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +7 -0
  169. data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +8 -0
  170. data/spec/support/rspec.rb +27 -0
  171. data/spec/support/shared_examples/cli/generate/app.rb +494 -0
  172. data/spec/support/shared_examples/cli/generate/migration.rb +32 -0
  173. data/spec/support/shared_examples/cli/generate/model.rb +81 -0
  174. data/spec/support/shared_examples/cli/new.rb +97 -0
  175. data/spec/unit/hanami/configuration/actions/content_security_policy_spec.rb +102 -0
  176. data/spec/unit/hanami/configuration/actions/cookies_spec.rb +46 -0
  177. data/spec/unit/hanami/configuration/actions/csrf_protection_spec.rb +57 -0
  178. data/spec/unit/hanami/configuration/actions/default_values_spec.rb +52 -0
  179. data/spec/unit/hanami/configuration/actions/sessions_spec.rb +50 -0
  180. data/spec/unit/hanami/configuration/actions_spec.rb +78 -0
  181. data/spec/unit/hanami/configuration/base_url_spec.rb +25 -0
  182. data/spec/unit/hanami/configuration/inflector_spec.rb +35 -0
  183. data/spec/unit/hanami/configuration/logger_spec.rb +203 -0
  184. data/spec/unit/hanami/configuration/views_spec.rb +120 -0
  185. data/spec/unit/hanami/configuration_spec.rb +43 -0
  186. data/spec/unit/hanami/env_spec.rb +54 -0
  187. data/spec/unit/hanami/routes_spec.rb +25 -0
  188. data/spec/unit/hanami/settings/dotenv_store_spec.rb +119 -0
  189. data/spec/unit/hanami/settings_spec.rb +56 -0
  190. data/spec/unit/hanami/slice_configurable_spec.rb +104 -0
  191. data/spec/unit/hanami/slice_name_spec.rb +47 -0
  192. data/spec/unit/hanami/slice_spec.rb +17 -0
  193. data/spec/unit/hanami/version_spec.rb +7 -0
  194. data/spec/unit/hanami/web/rack_logger_spec.rb +78 -0
  195. metadata +353 -57
  196. data/lib/hanami/application/action.rb +0 -72
  197. data/lib/hanami/application/container/providers/inflector.rb +0 -7
  198. data/lib/hanami/application/container/providers/logger.rb +0 -7
  199. data/lib/hanami/application/container/providers/rack_logger.rb +0 -15
  200. data/lib/hanami/application/container/providers/rack_monitor.rb +0 -12
  201. data/lib/hanami/application/container/providers/routes_helper.rb +0 -9
  202. data/lib/hanami/application/container/providers/settings.rb +0 -7
  203. data/lib/hanami/application/routes.rb +0 -55
  204. data/lib/hanami/application/routes_helper.rb +0 -34
  205. data/lib/hanami/application/routing/resolver/node.rb +0 -50
  206. data/lib/hanami/application/routing/resolver/trie.rb +0 -59
  207. data/lib/hanami/application/routing/resolver.rb +0 -87
  208. data/lib/hanami/application/routing/router.rb +0 -36
  209. data/lib/hanami/application/settings/dotenv_store.rb +0 -60
  210. data/lib/hanami/application/settings.rb +0 -93
  211. data/lib/hanami/application/slice_registrar.rb +0 -106
  212. data/lib/hanami/application/view/context.rb +0 -95
  213. data/lib/hanami/application/view.rb +0 -24
  214. data/lib/hanami/application.rb +0 -273
  215. data/lib/hanami/cli/application/cli.rb +0 -40
  216. data/lib/hanami/cli/application/command.rb +0 -47
  217. data/lib/hanami/cli/application/commands/console.rb +0 -81
  218. data/lib/hanami/cli/application/commands.rb +0 -16
  219. data/lib/hanami/cli/base_command.rb +0 -48
  220. data/lib/hanami/cli/commands/command.rb +0 -171
  221. data/lib/hanami/cli/commands/server.rb +0 -88
  222. data/lib/hanami/cli/commands.rb +0 -65
  223. data/lib/hanami/configuration/middleware.rb +0 -20
  224. data/lib/hanami/configuration/source_dirs.rb +0 -42
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ RSpec.describe "CLI plugins", type: :integration do
6
+ xit "includes its commands in CLI output" do
7
+ with_project do
8
+ bundle_exec "hanami"
9
+ expect(out).to include("hanami plugin [SUBCOMMAND]")
10
+ end
11
+ end
12
+
13
+ xit "executes command from plugin" do
14
+ with_project do
15
+ bundle_exec "hanami plugin version"
16
+ expect(out).to include("v0.1.0")
17
+ end
18
+ end
19
+
20
+ # See https://github.com/hanami/hanami/issues/838
21
+ it "guarantees 'hanami new' to generate a project" do
22
+ project = "bookshelf_without_gemfile"
23
+
24
+ with_system_tmp_directory do
25
+ run_cmd_with_clean_env "hanami new #{project}"
26
+ destination = Pathname.new(Dir.pwd).join(project)
27
+
28
+ expect(destination).to exist
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def with_project
35
+ super("bookshelf", gems: { "hanami-plugin" => { groups: [:plugins], path: Pathname.new(__dir__).join("..", "..", "..", "spec", "support", "fixtures", "hanami-plugin").realpath.to_s } }) do
36
+ yield
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "hanami routes", type: :integration do
4
+ it "prints app routes" do
5
+ with_project do
6
+ generate "app admin"
7
+
8
+ write "lib/ping.rb", <<~EOF
9
+ class Ping
10
+ def call(env)
11
+ [200, {}, ["PONG"]]
12
+ end
13
+ end
14
+ EOF
15
+
16
+ unshift "config/environment.rb", "require_relative '../lib/ping'"
17
+ replace "config/environment.rb", "Hanami.configure do", "Hanami.configure do\nmount Ping, at: '/ping'"
18
+
19
+ generate "action web home#index --url=/"
20
+ generate "action web books#create --url=/books --method=POST"
21
+
22
+ generate "action admin home#index --url=/"
23
+
24
+ hanami "routes"
25
+
26
+ expect(out).to eq "Name Method Path Action \n\n /ping Ping \n\n Name Method Path Action \n\n GET, HEAD /admin Admin::Controllers::Home::Index\n\n Name Method Path Action \n\n GET, HEAD / Web::Controllers::Home::Index \n POST /books Web::Controllers::Books::Create"
27
+ end
28
+ end
29
+
30
+ it "prints help message" do
31
+ with_project do
32
+ output = <<~OUT
33
+ Command:
34
+ hanami routes
35
+
36
+ Usage:
37
+ hanami routes
38
+
39
+ Description:
40
+ Prints routes
41
+
42
+ Options:
43
+ --help, -h # Print this help
44
+ OUT
45
+
46
+ run_cmd 'hanami routes --help', output
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,626 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ RSpec.describe "hanami server", type: :integration do
6
+ context "without routes" do
7
+ it "shows welcome page" do
8
+ with_project do
9
+ server do
10
+ visit "/"
11
+
12
+ expect(page).to have_title("Hanami | The web, with simplicity")
13
+
14
+ expect(page).to have_content("The web, with simplicity.")
15
+ expect(page).to have_content("Hanami is Open Source Software for MVC web development with Ruby.")
16
+ expect(page).to have_content("bundle exec hanami generate action web 'home#index' --url=/")
17
+ end
18
+ end
19
+ end
20
+
21
+ it "shows welcome page for generated app" do
22
+ with_project do
23
+ generate "app admin"
24
+
25
+ server do
26
+ visit "/admin"
27
+
28
+ expect(page).to have_content("bundle exec hanami generate action admin 'home#index' --url=/")
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ context "with routes" do
35
+ it "serves action" do
36
+ with_project do
37
+ server do
38
+ generate "action web home#index --url=/"
39
+
40
+ visit "/"
41
+ expect(page).to have_title("Web")
42
+ end
43
+ end
44
+ end
45
+
46
+ it "serves static asset" do
47
+ with_project do
48
+ server do
49
+ write "apps/web/assets/javascripts/app.js", <<~EOF
50
+ console.log('test');
51
+ EOF
52
+ visit "/assets/app.js"
53
+ expect(page).to have_content("console.log('test');")
54
+ end
55
+ end
56
+ end
57
+
58
+ it "serves contents from database" do
59
+ with_project do
60
+ setup_model
61
+ console do |input, _, _|
62
+ input.puts("BookRepository.new.create(title: 'Learn Hanami')")
63
+ input.puts("exit")
64
+ end
65
+
66
+ generate "action web books#show --url=/books/:id"
67
+ rewrite "apps/web/controllers/books/show.rb", <<~EOF
68
+ module Web::Controllers::Books
69
+ class Show
70
+ include Web::Action
71
+ expose :book
72
+
73
+ def call(params)
74
+ @book = BookRepository.new.find(params[:id]) or halt(404)
75
+ end
76
+ end
77
+ end
78
+ EOF
79
+ rewrite "apps/web/templates/books/show.html.erb", <<~EOF
80
+ <h1><%= book.title %></h1>
81
+ EOF
82
+
83
+ server do
84
+ visit "/books/1"
85
+ expect(page).to have_content("Learn Hanami")
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ context "logging" do
92
+ let(:log) { "log/development.log" }
93
+ let(:project) { "bookshelf" }
94
+
95
+ context "when enabled" do
96
+ it "logs GET requests" do
97
+ with_project(project) do
98
+ touch log
99
+ replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
100
+
101
+ server do
102
+ visit "/"
103
+ expect(page).to have_title("Hanami | The web, with simplicity")
104
+ end
105
+
106
+ content = contents(log)
107
+ expect(content).to include("[#{project}] [INFO]")
108
+ expect(content).to match(%r{HTTP/1.1 GET 200 (.*) /})
109
+ end
110
+ end
111
+
112
+ it "logs GET requests with query string" do
113
+ with_project(project) do
114
+ touch log
115
+ replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
116
+
117
+ run_cmd "hanami generate action web home#index --url=/", []
118
+
119
+ server do
120
+ visit "/?ping=pong"
121
+ expect(page).to have_title("Web")
122
+ end
123
+
124
+ content = contents(log)
125
+ expect(content).to include("[#{project}] [INFO]")
126
+ expect(content).to match(%({"ping"=>"pong"}))
127
+ end
128
+ end
129
+
130
+ it "logs non-GET requests with payload" do
131
+ with_project(project) do
132
+ touch log
133
+ replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
134
+
135
+ run_cmd "hanami generate action web books#create --method=POST", []
136
+
137
+ server do
138
+ post "/books", book: { title: "Functions" }
139
+ end
140
+
141
+ content = contents(log)
142
+ expect(content).to include("[#{project}] [INFO]")
143
+ expect(content).to match(%({"book"=>{"title"=>"Functions"}}))
144
+ end
145
+ end
146
+
147
+ it "logs non-GET requests from body parsers" do
148
+ with_project(project) do
149
+ touch log
150
+ replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
151
+ inject_line_after "config/environment.rb", "Hanami.configure", "require 'hanami/middleware/body_parser'\nmiddleware.use Hanami::Middleware::BodyParser, :json"
152
+
153
+ run_cmd "hanami generate action web books#create --method=POST", []
154
+ inject_line_after "apps/web/controllers/books/create.rb", "call", 'Hanami.logger.debug(request.env["CONTENT_TYPE"]);self.body = %({"status":"OK"})'
155
+
156
+ server do
157
+ post "/books", book: { title: "Why we sleep" }
158
+ post "/books", JSON.generate(book: { title: "Parsers" }), "CONTENT_TYPE" => "app/json", "HTTP_ACCEPT" => "app/json"
159
+ post "/books", JSON.generate(%w[this is cool]), "CONTENT_TYPE" => "app/json", "HTTP_ACCEPT" => "app/json"
160
+ end
161
+
162
+ content = contents(log)
163
+ expect(content).to include("[#{project}] [INFO]")
164
+ expect(content).to include("POST 200")
165
+
166
+ expect(content).to include("app/x-www-form-urlencoded")
167
+ expect(content).to include(%({"book"=>{"title"=>"Why we sleep"}}))
168
+
169
+ expect(content).to include("app/json")
170
+ expect(content).to include(%({"book"=>{"title"=>"Parsers"}}))
171
+ expect(content).to include(%({"_"=>["this", "is", "cool"]}))
172
+ end
173
+ end
174
+ end
175
+
176
+ context "when not enabled" do
177
+ it "does not log request" do
178
+ with_project(project) do
179
+ replace "config/environment.rb", "logger level: :debug", ""
180
+
181
+ server do
182
+ visit "/"
183
+ end
184
+
185
+ expect(log).to_not be_an_existing_file
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ context "--host" do
192
+ it "starts on given host" do
193
+ with_project do
194
+ server(host: "127.0.0.1") do
195
+ visit "/"
196
+
197
+ expect(page).to have_title("Hanami | The web, with simplicity")
198
+ end
199
+ end
200
+ end
201
+
202
+ xit "fails when missing" do
203
+ with_project do
204
+ server(host: nil)
205
+
206
+ expect(exitstatus).to eq(1)
207
+ end
208
+ end
209
+ end
210
+
211
+ context "--port" do
212
+ it "starts on given port" do
213
+ with_project do
214
+ server(port: 1982) do
215
+ visit "/"
216
+
217
+ expect(page).to have_title("Hanami | The web, with simplicity")
218
+ end
219
+ end
220
+ end
221
+
222
+ xit "fails when missing" do
223
+ with_project do
224
+ server(port: nil)
225
+
226
+ expect(exitstatus).to eq(1)
227
+ end
228
+ end
229
+ end
230
+
231
+ context "environment" do
232
+ it "starts with given environment" do
233
+ with_project do
234
+ generate "action web home#index --url=/"
235
+
236
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
237
+ module Web::Controllers::Home
238
+ class Index
239
+ include Web::Action
240
+
241
+ def call(params)
242
+ self.body = Hanami.env
243
+ end
244
+ end
245
+ end
246
+ EOF
247
+
248
+ RSpec::Support::Env["HANAMI_ENV"] = env = "production"
249
+ RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
250
+ RSpec::Support::Env["SMTP_HOST"] = "localhost"
251
+ RSpec::Support::Env["SMTP_PORT"] = "25"
252
+
253
+ server do
254
+ visit "/"
255
+
256
+ expect(page).to have_content(env)
257
+ end
258
+ end
259
+ end
260
+
261
+ xit "fails when missing" do
262
+ with_project do
263
+ server(environment: nil)
264
+
265
+ expect(exitstatus).to eq(1)
266
+ end
267
+ end
268
+ end
269
+
270
+ context "puma" do
271
+ it "starts" do
272
+ with_project("bookshelf_server_puma", server: :puma) do
273
+ server do
274
+ visit "/"
275
+
276
+ expect(page).to have_title("Hanami | The web, with simplicity")
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ context "unicorn" do
283
+ it "starts" do
284
+ with_project("bookshelf_server_unicorn", server: :unicorn) do
285
+ server do
286
+ visit "/"
287
+
288
+ expect(page).to have_title("Hanami | The web, with simplicity")
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ context "code reloading" do
295
+ it "reloads templates code" do
296
+ with_project do
297
+ server do
298
+ visit "/"
299
+
300
+ expect(page).to have_title("Hanami | The web, with simplicity")
301
+ generate "action web home#index --url=/"
302
+
303
+ rewrite "apps/web/templates/home/index.html.erb", <<~EOF
304
+ <h1>Hello, World!</h1>
305
+ EOF
306
+
307
+ visit "/"
308
+ expect(page).to have_title("Web")
309
+ expect(page).to have_content("Hello, World!")
310
+ end
311
+ end
312
+ end
313
+
314
+ it "reloads view" do
315
+ with_project do
316
+ server do
317
+ visit "/"
318
+
319
+ expect(page).to have_title("Hanami | The web, with simplicity")
320
+ generate "action web home#index --url=/"
321
+
322
+ rewrite "apps/web/views/home/index.rb", <<~EOF
323
+ module Web::Views::Home
324
+ class Index
325
+ include Web::View
326
+
327
+ def greeting
328
+ "Ciao!"
329
+ end
330
+ end
331
+ end
332
+ EOF
333
+
334
+ rewrite "apps/web/templates/home/index.html.erb", <<~EOF
335
+ <%= greeting %>
336
+ EOF
337
+
338
+ visit "/"
339
+ expect(page).to have_title("Web")
340
+ expect(page).to have_content("Ciao!")
341
+ end
342
+ end
343
+ end
344
+
345
+ it "reloads action" do
346
+ with_project do
347
+ server do
348
+ visit "/"
349
+
350
+ expect(page).to have_title("Hanami | The web, with simplicity")
351
+ generate "action web home#index --url=/"
352
+
353
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
354
+ module Web::Controllers::Home
355
+ class Index
356
+ include Web::Action
357
+
358
+ def call(params)
359
+ self.body = "Hi!"
360
+ end
361
+ end
362
+ end
363
+ EOF
364
+
365
+ visit "/"
366
+ expect(page).to have_content("Hi!")
367
+ end
368
+ end
369
+ end
370
+
371
+ it "reloads model" do
372
+ project_name = "bookshelf"
373
+
374
+ with_project(project_name) do
375
+ # STEP 1: prepare the database and the repository
376
+ generate_model "user"
377
+ generate_migration "create_users", <<~EOF
378
+ Hanami::Model.migration do
379
+ change do
380
+ create_table :users do
381
+ primary_key :id
382
+ column :name, String
383
+ end
384
+
385
+ execute "INSERT INTO users (name) VALUES('L')"
386
+ execute "INSERT INTO users (name) VALUES('MG')"
387
+ end
388
+ end
389
+ EOF
390
+
391
+ rewrite "lib/#{project_name}/repositories/user_repository.rb", <<~EOF
392
+ class UserRepository < Hanami::Repository
393
+ def listing
394
+ all
395
+ end
396
+ end
397
+ EOF
398
+
399
+ hanami "db prepare"
400
+
401
+ # STEP 2: generate the action
402
+ generate "action web users#index --url=/users"
403
+
404
+ rewrite "apps/web/controllers/users/index.rb", <<~EOF
405
+ module Web::Controllers::Users
406
+ class Index
407
+ include Web::Action
408
+
409
+ def call(params)
410
+ self.body = UserRepository.new.listing.map(&:name).join(", ")
411
+ end
412
+ end
413
+ end
414
+ EOF
415
+
416
+ server do
417
+ # STEP 3: visit the page
418
+ visit "/users"
419
+
420
+ expect(page).to have_content("L, MG")
421
+
422
+ # STEP 4: change the repository, then visit the page again
423
+ rewrite "lib/#{project_name}/repositories/user_repository.rb", <<~EOF
424
+ class UserRepository < Hanami::Repository
425
+ def listing
426
+ all.reverse
427
+ end
428
+ end
429
+ EOF
430
+ visit "/users"
431
+
432
+ expect(page).to have_content("MG, L")
433
+ end
434
+ end
435
+ end
436
+
437
+ xit "reloads asset" do
438
+ with_project do
439
+ server do
440
+ write "apps/web/assets/stylesheets/style.css", <<~EOF
441
+ body { background-color: #fff; }
442
+ EOF
443
+
444
+ visit "/assets/style.css"
445
+ expect(page).to have_content("#fff")
446
+
447
+ rewrite "apps/web/assets/stylesheets/style.css", <<~EOF
448
+ body { background-color: #333; }
449
+ EOF
450
+
451
+ visit "/assets/style.css"
452
+ expect(page).to have_content("#333")
453
+ end
454
+ end
455
+ end
456
+ end
457
+
458
+ context "without code reloading" do
459
+ it "doesn't reload code" do
460
+ with_project do
461
+ server("no-code-reloading" => nil) do
462
+ visit "/"
463
+
464
+ expect(page).to have_title("Hanami | The web, with simplicity")
465
+ generate "action web home#index --url=/"
466
+
467
+ visit "/"
468
+ expect(page).to have_title("Hanami | The web, with simplicity")
469
+ end
470
+ end
471
+ end
472
+ end
473
+
474
+ context "without hanami model" do
475
+ it "uses custom model domain" do
476
+ project_without_hanami_model("bookshelf", gems: ["dry-struct"]) do
477
+ write "lib/entities/access_token.rb", <<~EOF
478
+ require 'dry-struct'
479
+ require 'securerandom'
480
+
481
+ module Types
482
+ include Dry::Types.module
483
+ end
484
+
485
+ class AccessToken < Dry::Struct
486
+ attribute :id, Types::String.default { SecureRandom.uuid }
487
+ attribute :secret, Types::String
488
+ attribute :digest, Types::String
489
+ end
490
+ EOF
491
+ generate "action web access_tokens#show --url=/access_tokens/:id"
492
+ rewrite "apps/web/controllers/access_tokens/show.rb", <<~EOF
493
+ module Web::Controllers::AccessTokens
494
+ class Show
495
+ include Web::Action
496
+ expose :access_token
497
+
498
+ def call(params)
499
+ @access_token = AccessToken.new(id: '1', secret: 'shh', digest: 'abc')
500
+ end
501
+ end
502
+ end
503
+ EOF
504
+ rewrite "apps/web/templates/access_tokens/show.html.erb", <<~EOF
505
+ <h1><%= access_token.secret %></h1>
506
+ EOF
507
+
508
+ server do
509
+ visit "/access_tokens/1"
510
+ visit "/access_tokens/1" # forces code reloading
511
+ expect(page).to have_content("shh")
512
+ end
513
+ end
514
+ end
515
+ end
516
+
517
+ context "without mailer" do
518
+ it "returns page" do
519
+ with_project do
520
+ remove_block "config/environment.rb", "mailer do"
521
+
522
+ server do
523
+ visit "/"
524
+ expect(page).to have_title("Hanami | The web, with simplicity")
525
+ end
526
+ end
527
+ end
528
+ end
529
+
530
+ it "prints help message" do
531
+ with_project do
532
+ output = <<-OUT
533
+ Command:
534
+ hanami server
535
+
536
+ Usage:
537
+ hanami server
538
+
539
+ Description:
540
+ Start Hanami server (only for development)
541
+
542
+ Options:
543
+ --server=VALUE # Force a server engine (eg, webrick, puma, thin, etc..)
544
+ --host=VALUE # The host address to bind to
545
+ --port=VALUE, -p VALUE # The port to run the server on
546
+ --debug=VALUE # Turn on debug output
547
+ --warn=VALUE # Turn on warnings
548
+ --daemonize=VALUE # Daemonize the server
549
+ --pid=VALUE # Path to write a pid file after daemonize
550
+ --[no-]code-reloading # Code reloading, default: true
551
+ --help, -h # Print this help
552
+
553
+ Examples:
554
+ hanami server # Basic usage (it uses the bundled server engine)
555
+ hanami server --server=webrick # Force `webrick` server engine
556
+ hanami server --host=0.0.0.0 # Bind to a host
557
+ hanami server --port=2306 # Bind to a port
558
+ hanami server --no-code-reloading # Disable code reloading
559
+ OUT
560
+
561
+ run_cmd 'hanami server --help', output
562
+ end
563
+ end
564
+
565
+ context "with HANAMI_APPS ENV variable" do
566
+ it "loads only specific app" do
567
+ with_project do
568
+ generate "app admin"
569
+
570
+ remove_line "config/environment.rb", "require_relative '../apps/admin/app'"
571
+ remove_line "config/environment.rb", "mount Admin::App"
572
+
573
+ remove_line "config/environment.rb", "require_relative '../apps/web/app'"
574
+ remove_line "config/environment.rb", "mount Web::App"
575
+
576
+ inject_line_after "config/environment.rb", "Hanami.configure", <<-EOL
577
+ if Hanami.app?(:admin)
578
+ require_relative '../apps/admin/app'
579
+ mount Admin::App, at: '/admin'
580
+ end
581
+
582
+ if Hanami.app?(:web)
583
+ require_relative '../apps/web/app'
584
+ mount Web::App, at: '/'
585
+ end
586
+ EOL
587
+ generate "action web home#index --url=/"
588
+
589
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
590
+ module Web::Controllers::Home
591
+ class Index
592
+ include Web::Action
593
+
594
+ def call(params)
595
+ self.body = "app: web"
596
+ end
597
+ end
598
+ end
599
+ EOF
600
+ generate "action admin home#index --url=/"
601
+
602
+ rewrite "apps/admin/controllers/home/index.rb", <<~EOF
603
+ module Web::Controllers::Home
604
+ class Index
605
+ include Admin::Action
606
+
607
+ def call(params)
608
+ self.body = "app: admin"
609
+ end
610
+ end
611
+ end
612
+ EOF
613
+
614
+ RSpec::Support::Env["HANAMI_APPS"] = "web"
615
+
616
+ server do
617
+ visit "/"
618
+ expect(page).to have_content("app: web")
619
+
620
+ visit "/admin"
621
+ expect(page).to have_content("Not Found")
622
+ end
623
+ end
624
+ end
625
+ end
626
+ end