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,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Sessions", type: :integration do
4
+ it "shows welcome page" do
5
+ with_project do
6
+ server do
7
+ visit "/"
8
+
9
+ expect(page).to have_title("Hanami | The web, with simplicity")
10
+
11
+ expect(page).to have_content("The web, with simplicity.")
12
+ expect(page).to have_content("Hanami is Open Source Software for MVC web development with Ruby.")
13
+ expect(page).to have_content("bundle exec hanami generate action web 'home#index' --url=/")
14
+ end
15
+ end
16
+ end
17
+
18
+ it "is empty by default" do
19
+ with_project do
20
+ prepare
21
+
22
+ server do
23
+ visit "/"
24
+
25
+ expect(current_path).to eq("/")
26
+ expect(page).to have_content("Sign in")
27
+ end
28
+ end
29
+ end
30
+
31
+ it "preserves data across requests" do
32
+ with_project do
33
+ prepare
34
+
35
+ server do
36
+ visit "/"
37
+ expect(current_path).to eq("/")
38
+
39
+ click_link("Sign in")
40
+ expect(current_path).to eq("/signin")
41
+
42
+ within "form#signin-form" do
43
+ click_button "Sign in"
44
+ end
45
+
46
+ visit "/" # Without this, Capybara losts the session.
47
+
48
+ expect(current_path).to eq("/")
49
+ expect(page).to have_content("Welcome, Luca")
50
+ end
51
+ end
52
+ end
53
+
54
+ it "clears the session" do
55
+ with_project do
56
+ prepare
57
+
58
+ server do
59
+ given_signedin_user
60
+
61
+ click_link "Sign out"
62
+
63
+ expect(current_path).to eq("/")
64
+ expect(page).to_not have_content("Welcome, Luca")
65
+ end
66
+ end
67
+ end
68
+
69
+ context "when sessions aren't enabled" do
70
+ it "raises error when trying to use `session'" do
71
+ with_project do
72
+ generate "action web home#index --url=/"
73
+
74
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
75
+ module Web::Controllers::Home
76
+ class Index
77
+ include Web::Action
78
+
79
+ def call(params)
80
+ self.body = session[:foo]
81
+ end
82
+ end
83
+ end
84
+ EOF
85
+ server do
86
+ visit "/"
87
+ expect(page).to have_content("To use `session', please enable sessions for the current app.")
88
+ end
89
+ end
90
+ end
91
+
92
+ it "raises error when trying to use `flash'" do
93
+ with_project do
94
+ generate "action web home#index --url=/"
95
+
96
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
97
+ module Web::Controllers::Home
98
+ class Index
99
+ include Web::Action
100
+
101
+ def call(params)
102
+ self.body = flash[:notice]
103
+ end
104
+ end
105
+ end
106
+ EOF
107
+ server do
108
+ visit "/"
109
+ expect(page).to have_content("To use `flash', please enable sessions for the current app.")
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def prepare
118
+ # Enable sessions
119
+ replace "apps/web/app.rb", "# sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']", "sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']"
120
+
121
+ generate_user
122
+ generate_actions
123
+ end
124
+
125
+ def generate_user # rubocop:disable Metrics/MethodLength
126
+ generate_model "user"
127
+ generate_migration "create_users", <<~EOF
128
+ Hanami::Model.migration do
129
+ change do
130
+ create_table :users do
131
+ primary_key :id
132
+ column :name, String
133
+ end
134
+
135
+ execute "INSERT INTO users (name) VALUES('Luca')"
136
+ end
137
+ end
138
+ EOF
139
+
140
+ hanami "db prepare"
141
+ end
142
+
143
+ def generate_actions
144
+ generate_home
145
+ generate_signin
146
+ generate_signout
147
+ end
148
+
149
+ def generate_home # rubocop:disable Metrics/MethodLength
150
+ generate "action web home#index --url=/"
151
+ replace "apps/web/config/routes.rb", "home#index", 'root to: "home#index"'
152
+
153
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
154
+ module Web::Controllers::Home
155
+ class Index
156
+ include Web::Action
157
+ expose :current_user
158
+
159
+ def call(params)
160
+ @current_user = UserRepository.new.find(session[:user_id])
161
+ end
162
+ end
163
+ end
164
+ EOF
165
+
166
+ rewrite "apps/web/templates/home/index.html.erb", <<~EOF
167
+ <h1>Bookshelf</h1>
168
+
169
+ <% if current_user.nil? %>
170
+ <%= link_to "Sign in", "/signin" %>
171
+ <% else %>
172
+ Welcome, <%= current_user.name %>
173
+ <%= link_to "Sign out", "/signout" %>
174
+ <% end %>
175
+ EOF
176
+ end
177
+
178
+ def generate_signin
179
+ generate_signin_new_action
180
+ generate_signin_create_action
181
+ end
182
+
183
+ def generate_signin_new_action
184
+ generate "action web sessions#new --url=/signin --method=GET"
185
+ rewrite "apps/web/templates/sessions/new.html.erb", <<~EOF
186
+ <h1>Sign in</h1>
187
+
188
+ <%=
189
+ form_for :signin, "/signin", id: "signin-form" do
190
+ div do
191
+ button "Sign in"
192
+ end
193
+ end
194
+ %>
195
+ EOF
196
+ end
197
+
198
+ def generate_signin_create_action
199
+ generate "action web sessions#create --url=/signin --method=POST"
200
+ rewrite "apps/web/controllers/sessions/create.rb", <<~EOF
201
+ module Web::Controllers::Sessions
202
+ class Create
203
+ include Web::Action
204
+
205
+ def call(params)
206
+ session[:user_id] = UserRepository.new.first.id
207
+ redirect_to routes.root_url
208
+ end
209
+ end
210
+ end
211
+ EOF
212
+ end
213
+
214
+ def generate_signout
215
+ generate "action web sessions#destroy --url=/signout --method=GET"
216
+
217
+ rewrite "apps/web/controllers/sessions/destroy.rb", <<~EOF
218
+ module Web::Controllers::Sessions
219
+ class Destroy
220
+ include Web::Action
221
+
222
+ def call(params)
223
+ session[:user_id] = nil
224
+ redirect_to routes.root_url
225
+ end
226
+ end
227
+ end
228
+ EOF
229
+ end
230
+
231
+ def given_signedin_user # rubocop:disable Metrics/AbcSize
232
+ visit "/"
233
+ expect(current_path).to eq("/")
234
+
235
+ click_link("Sign in")
236
+ expect(current_path).to eq("/signin")
237
+
238
+ within "form#signin-form" do
239
+ click_button "Sign in"
240
+ end
241
+
242
+ visit "/" # Without this, Capybara losts the session.
243
+
244
+ expect(current_path).to eq("/")
245
+ expect(page).to have_content("Welcome, Luca")
246
+ end
247
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Static middleware", type: :integration do
4
+ it "serves a public file" do
5
+ with_project do
6
+ write "public/static.txt", "Static file"
7
+
8
+ RSpec::Support::Env["HANAMI_ENV"] = "production"
9
+ RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
10
+ RSpec::Support::Env["SERVE_STATIC_ASSETS"] = "true"
11
+ RSpec::Support::Env["SMTP_HOST"] = "localhost"
12
+ RSpec::Support::Env["SMTP_PORT"] = "25"
13
+
14
+ server do
15
+ visit "/static.txt"
16
+
17
+ expect(page.body).to include("Static file")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Streaming", type: :integration do
4
+ xit "streams the body" do
5
+ with_project do
6
+ generate "action web home#index --url=/"
7
+
8
+ # Require Rack::Chunked
9
+ unshift "apps/web/app.rb", "require 'rack/chunked'"
10
+
11
+ # Mount middleware
12
+ replace "apps/web/app.rb", "# middleware.use", "middleware.use ::Rack::Chunked"
13
+ replace "apps/web/app.rb", "controller.prepare do", "controller.format text: 'text/plain'\ncontroller.prepare do"
14
+
15
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
16
+ module Web::Controllers::Home
17
+ class Index
18
+ include Web::Action
19
+
20
+ def call(params)
21
+ self.format = :text
22
+ self.body = Enumerator.new do |y|
23
+ %w(one two three).each { |s| y << s }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ EOF
29
+
30
+ server do
31
+ get "/", {}, "HTTP_VERSION" => "HTTP/1.1"
32
+
33
+ expect(last_response.headers).to_not have_key("Content-Length")
34
+ expect(last_response.headers["Transfer-Encoding"]).to eq("chunked")
35
+
36
+ expect(last_response.status).to eq(200)
37
+ expect(last_response.body).to eq("3\r\none\r\n3\r\ntwo\r\n5\r\nthree\r\n0\r\n\r\n")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Unsafe send file", type: :integration do
4
+ it "sends file from the public directory" do
5
+ with_project do
6
+ write "public/static.txt", "Static file"
7
+ generate "action web home#index --url=/"
8
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
9
+ module Web::Controllers::Home
10
+ class Index
11
+ include Web::Action
12
+
13
+ def call(params)
14
+ unsafe_send_file "public/static.txt"
15
+ end
16
+ end
17
+ end
18
+ EOF
19
+
20
+ server do
21
+ get "/"
22
+
23
+ expect(last_response.status).to eq(200)
24
+ expect(last_response.body).to include("Static file")
25
+ end
26
+ end
27
+ end
28
+
29
+ it "sends file outside of the public directory" do
30
+ with_project do
31
+ generate "action web home#index --url=/"
32
+ rewrite "apps/web/controllers/home/index.rb", <<~EOF
33
+ module Web::Controllers::Home
34
+ class Index
35
+ include Web::Action
36
+
37
+ def call(params)
38
+ unsafe_send_file __FILE__
39
+ end
40
+ end
41
+ end
42
+ EOF
43
+
44
+ server do
45
+ get "/"
46
+
47
+ expect(last_response.status).to eq(200)
48
+ expect(last_response.body).to include("Web::Controllers::Home")
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bookshelf
4
+ class App < Hanami::App
5
+ end
6
+ end
7
+
8
+ RSpec.describe Hanami do
9
+ describe ".application" do
10
+ it "it raises error when already assigned" do
11
+ expect do
12
+ module Soundcard
13
+ class App < Hanami::App
14
+ end
15
+ end
16
+ end.to raise_error("Hanami.app already configured")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Hanami do
4
+ describe ".application" do
5
+ it "it doesn't assign when anonymous class inherits Hanami::Application" do
6
+ Class.new(Hanami::Application)
7
+ expect { Hanami.app }.to raise_error("Hanami.app not configured")
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bookshelf
4
+ class App < Hanami::App
5
+ end
6
+ end
7
+
8
+ RSpec.describe Hanami do
9
+ describe ".application" do
10
+ it "it assign when concrete class inherits Hanami::Application" do
11
+ expect(Hanami.app).to eq(Bookshelf::Application)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Hanami do
4
+ describe ".application" do
5
+ it "it raises error when not configured" do
6
+ expect { Hanami.app }.to raise_error("Hanami.app not configured")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action"
4
+
5
+ module Bookshelf
6
+ class App < Hanami::App
7
+ end
8
+ end
9
+
10
+ module Web
11
+ end
12
+ Hanami.app.register_slice :web, namespace: Web
13
+
14
+ Hanami.prepare
15
+
16
+ module Web
17
+ class Action < Hanami::Action
18
+ end
19
+
20
+ module Actions
21
+ module Home
22
+ class Index < Web::Action
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ Hanami.app.routes do
29
+ mount :web, at: "/" do
30
+ root to: "home#index"
31
+ end
32
+ end
33
+
34
+ RSpec.describe Hanami::Application do
35
+ describe ".routes" do
36
+ subject { Hanami.app.routes }
37
+
38
+ it "returns configured routes" do
39
+ expect(subject).to be_kind_of(Proc)
40
+ # FIXME: make this expectation to pass
41
+ # expect(subject.for(:web).url(:root)).to eq("/")
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bookshelf
4
+ class App < Hanami::App
5
+ end
6
+ end
7
+
8
+ RSpec.describe Hanami::Application do
9
+ describe ".routes" do
10
+ subject { Hanami.app.routes }
11
+
12
+ it "raises error when not configured" do
13
+ expect { subject }.to raise_error("Hanami.app.routes not configured")
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action"
4
+ require "hanami/logger"
5
+
6
+ module Bookshelf
7
+ class App < Hanami::App
8
+ end
9
+ end
10
+
11
+ module Web
12
+ end
13
+ slice = Hanami.app.register_slice :web, namespace: Web
14
+
15
+ Hanami.prepare
16
+
17
+ Hanami.app.routes do
18
+ mount :web, at: "/" do
19
+ root to: "home#index"
20
+ end
21
+ end
22
+
23
+ module Web
24
+ class Action < Hanami::Action
25
+ end
26
+
27
+ module Actions
28
+ module Home
29
+ class Index < Web::Action
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ slice.register "actions.home.index" do
36
+ Web::Actions::Home::Index.new
37
+ end
38
+
39
+ RSpec.describe Hanami do
40
+ describe ".boot" do
41
+ it "assigns Hanami.app, .root, and .logger" do
42
+ pending "Failing due to dry-system changes"
43
+
44
+ Hanami.boot
45
+ expect(Hanami.app.ancestors).to include(Hanami::Application)
46
+ expect(Hanami.app.root).to eq(Dir.pwd)
47
+ expect(Hanami.logger).to be_kind_of(Hanami::Logger)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "App action / Configuration", :app_integration do
4
+ before do
5
+ module TestApp
6
+ class App < Hanami::App
7
+ config.actions.default_response_format = :json
8
+ register_slice :main
9
+ end
10
+ end
11
+
12
+ Hanami.app.prepare
13
+
14
+ module TestApp
15
+ class Action < Hanami::Action
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:action_class) { TestApp::Action }
21
+ subject(:configuration) { action_class.config }
22
+
23
+ it "applies 'config.actions' configuration from the app" do
24
+ expect(configuration.default_response_format).to eq :json
25
+ end
26
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "App action / Cookies", :app_integration do
4
+ before do
5
+ module TestApp
6
+ class App < Hanami::App
7
+ end
8
+ end
9
+
10
+ Hanami.app.instance_eval(&app_hook) if respond_to?(:app_hook)
11
+ Hanami.app.prepare
12
+
13
+ module TestApp
14
+ class Action < Hanami::Action
15
+ end
16
+ end
17
+ end
18
+
19
+ subject(:action_class) { TestApp::Action }
20
+
21
+ context "default configuration" do
22
+ it "has cookie support enabled" do
23
+ expect(action_class.ancestors).to include Hanami::Action::Cookies
24
+ end
25
+ end
26
+
27
+ context "custom cookie options given in app-level config" do
28
+ subject(:app_hook) {
29
+ proc do
30
+ config.actions.cookies = {max_age: 300}
31
+ end
32
+ }
33
+
34
+ it "has cookie support enabled" do
35
+ expect(action_class.ancestors).to include Hanami::Action::Cookies
36
+ end
37
+
38
+ it "has the cookie options configured" do
39
+ expect(action_class.config.cookies).to eq(max_age: 300)
40
+ end
41
+ end
42
+
43
+ context "cookies disabled in app-level config" do
44
+ subject(:app_hook) {
45
+ proc do
46
+ config.actions.cookies = nil
47
+ end
48
+ }
49
+
50
+ it "does not have cookie support enabled" do
51
+ expect(action_class.ancestors.map(&:to_s)).not_to include "Hanami::Action::Cookies"
52
+ end
53
+
54
+ it "has no cookie options configured" do
55
+ expect(action_class.config.cookies).to eq({})
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "App action / CSRF protection", :app_integration do
4
+ before do
5
+ module TestApp
6
+ class App < Hanami::App
7
+ end
8
+ end
9
+
10
+ Hanami.app.instance_eval(&app_hook) if respond_to?(:app_hook)
11
+ Hanami.app.register_slice :main
12
+ Hanami.app.prepare
13
+
14
+ module TestApp
15
+ class Action < Hanami::Action
16
+ end
17
+ end
18
+ end
19
+
20
+ subject(:action_class) { TestApp::Action }
21
+
22
+ context "app sessions enabled" do
23
+ context "CSRF protection not explicitly configured" do
24
+ let(:app_hook) {
25
+ proc do
26
+ config.actions.sessions = :cookie, {secret: "abc123"}
27
+ end
28
+ }
29
+
30
+ it "has CSRF protection enabled" do
31
+ expect(action_class.ancestors).to include Hanami::Action::CSRFProtection
32
+ end
33
+ end
34
+
35
+ context "CSRF protection explicitly disabled" do
36
+ let(:app_hook) {
37
+ proc do
38
+ config.sessions = :cookie, {secret: "abc123"}
39
+ config.actions.csrf_protection = false
40
+ end
41
+ }
42
+
43
+ it "does not have CSRF protection enabled" do
44
+ expect(action_class.ancestors.map(&:to_s)).not_to include "Hanami::Action::CSRFProtection"
45
+ end
46
+ end
47
+ end
48
+
49
+ context "app sessions not enabled" do
50
+ it "does not have CSRF protection enabled" do
51
+ expect(action_class.ancestors.map(&:to_s)).not_to include "Hanami::Action::CSRFProtection"
52
+ end
53
+ end
54
+ end