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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +442 -241
- data/FEATURES.md +30 -9
- data/README.md +1 -3
- data/hanami.gemspec +21 -11
- data/lib/hanami/app.rb +141 -0
- data/lib/hanami/assets/application_configuration.rb +10 -4
- data/lib/hanami/configuration/actions/content_security_policy.rb +5 -5
- data/lib/hanami/configuration/actions/cookies.rb +2 -2
- data/lib/hanami/configuration/actions.rb +10 -4
- data/lib/hanami/configuration/logger.rb +4 -4
- data/lib/hanami/configuration/router.rb +2 -6
- data/lib/hanami/configuration/sessions.rb +1 -1
- data/lib/hanami/configuration/views.rb +9 -4
- data/lib/hanami/configuration.rb +118 -46
- data/lib/hanami/constants.rb +24 -2
- data/lib/hanami/errors.rb +1 -1
- data/lib/hanami/{application → extensions}/action/slice_configured_action.rb +9 -9
- data/lib/hanami/extensions/action.rb +79 -0
- data/lib/hanami/extensions/view/context.rb +106 -0
- data/lib/hanami/{application → extensions}/view/slice_configured_context.rb +10 -10
- data/lib/hanami/{application → extensions}/view/slice_configured_view.rb +12 -6
- data/lib/hanami/extensions/view.rb +33 -0
- data/lib/hanami/extensions.rb +10 -0
- data/lib/hanami/providers/inflector.rb +13 -0
- data/lib/hanami/providers/logger.rb +13 -0
- data/lib/hanami/providers/rack.rb +27 -0
- data/lib/hanami/providers/routes.rb +33 -0
- data/lib/hanami/providers/settings.rb +23 -0
- data/lib/hanami/rake_tasks.rb +61 -0
- data/lib/hanami/routes.rb +51 -0
- data/lib/hanami/server.rb +1 -1
- data/lib/hanami/settings/dotenv_store.rb +58 -0
- data/lib/hanami/settings.rb +90 -0
- data/lib/hanami/setup.rb +4 -2
- data/lib/hanami/{application → slice}/router.rb +18 -13
- data/lib/hanami/slice/routes_helper.rb +37 -0
- data/lib/hanami/{application → slice}/routing/middleware/stack.rb +43 -5
- data/lib/hanami/slice/routing/resolver.rb +97 -0
- data/lib/hanami/{application → slice}/view_name_inferrer.rb +3 -3
- data/lib/hanami/slice.rb +246 -73
- data/lib/hanami/slice_configurable.rb +4 -17
- data/lib/hanami/slice_name.rb +6 -6
- data/lib/hanami/slice_registrar.rb +119 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +1 -1
- data/lib/hanami.rb +34 -26
- data/spec/integration/application_middleware_stack_spec.rb +84 -0
- data/spec/integration/assets/cdn_spec.rb +48 -0
- data/spec/integration/assets/fingerprint_spec.rb +42 -0
- data/spec/integration/assets/helpers_spec.rb +50 -0
- data/spec/integration/assets/serve_spec.rb +70 -0
- data/spec/integration/assets/subresource_integrity_spec.rb +54 -0
- data/spec/integration/body_parsers_spec.rb +50 -0
- data/spec/integration/cli/assets/precompile_spec.rb +147 -0
- data/spec/integration/cli/assets_spec.rb +14 -0
- data/spec/integration/cli/console_spec.rb +105 -0
- data/spec/integration/cli/db/apply_spec.rb +74 -0
- data/spec/integration/cli/db/console_spec.rb +40 -0
- data/spec/integration/cli/db/create_spec.rb +50 -0
- data/spec/integration/cli/db/drop_spec.rb +54 -0
- data/spec/integration/cli/db/migrate_spec.rb +108 -0
- data/spec/integration/cli/db/prepare_spec.rb +36 -0
- data/spec/integration/cli/db/rollback_spec.rb +96 -0
- data/spec/integration/cli/db/version_spec.rb +38 -0
- data/spec/integration/cli/db_spec.rb +21 -0
- data/spec/integration/cli/destroy/action_spec.rb +143 -0
- data/spec/integration/cli/destroy/app_spec.rb +118 -0
- data/spec/integration/cli/destroy/mailer_spec.rb +74 -0
- data/spec/integration/cli/destroy/migration_spec.rb +70 -0
- data/spec/integration/cli/destroy/model_spec.rb +113 -0
- data/spec/integration/cli/destroy_spec.rb +18 -0
- data/spec/integration/cli/generate/action_spec.rb +469 -0
- data/spec/integration/cli/generate/app_spec.rb +215 -0
- data/spec/integration/cli/generate/mailer_spec.rb +189 -0
- data/spec/integration/cli/generate/migration_spec.rb +72 -0
- data/spec/integration/cli/generate/model_spec.rb +290 -0
- data/spec/integration/cli/generate/secret_spec.rb +56 -0
- data/spec/integration/cli/generate_spec.rb +19 -0
- data/spec/integration/cli/new/database_spec.rb +235 -0
- data/spec/integration/cli/new/hanami_head_spec.rb +27 -0
- data/spec/integration/cli/new/template_spec.rb +118 -0
- data/spec/integration/cli/new/test_spec.rb +274 -0
- data/spec/integration/cli/new_spec.rb +970 -0
- data/spec/integration/cli/plugins_spec.rb +39 -0
- data/spec/integration/cli/routes_spec.rb +49 -0
- data/spec/integration/cli/server_spec.rb +626 -0
- data/spec/integration/cli/version_spec.rb +85 -0
- data/spec/integration/early_hints_spec.rb +35 -0
- data/spec/integration/handle_exceptions_spec.rb +244 -0
- data/spec/integration/head_spec.rb +89 -0
- data/spec/integration/http_headers_spec.rb +29 -0
- data/spec/integration/mailer_spec.rb +32 -0
- data/spec/integration/middleware_spec.rb +81 -0
- data/spec/integration/mount_applications_spec.rb +88 -0
- data/spec/integration/project_initializers_spec.rb +40 -0
- data/spec/integration/rackup_spec.rb +35 -0
- data/spec/integration/rake/with_minitest_spec.rb +67 -0
- data/spec/integration/rake/with_rspec_spec.rb +69 -0
- data/spec/integration/routing_helpers_spec.rb +61 -0
- data/spec/integration/security/content_security_policy_spec.rb +46 -0
- data/spec/integration/security/csrf_protection_spec.rb +42 -0
- data/spec/integration/security/force_ssl_spec.rb +29 -0
- data/spec/integration/security/x_content_type_options_spec.rb +46 -0
- data/spec/integration/security/x_frame_options_spec.rb +46 -0
- data/spec/integration/security/x_xss_protection_spec.rb +46 -0
- data/spec/integration/send_file_spec.rb +51 -0
- data/spec/integration/sessions_spec.rb +247 -0
- data/spec/integration/static_middleware_spec.rb +21 -0
- data/spec/integration/streaming_spec.rb +41 -0
- data/spec/integration/unsafe_send_file_spec.rb +52 -0
- data/spec/isolation/hanami/application/already_configured_spec.rb +19 -0
- data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +10 -0
- data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +14 -0
- data/spec/isolation/hanami/application/not_configured_spec.rb +9 -0
- data/spec/isolation/hanami/application/routes/configured_spec.rb +44 -0
- data/spec/isolation/hanami/application/routes/not_configured_spec.rb +16 -0
- data/spec/isolation/hanami/boot/success_spec.rb +50 -0
- data/spec/new_integration/action/configuration_spec.rb +26 -0
- data/spec/new_integration/action/cookies_spec.rb +58 -0
- data/spec/new_integration/action/csrf_protection_spec.rb +54 -0
- data/spec/new_integration/action/routes_spec.rb +73 -0
- data/spec/new_integration/action/sessions_spec.rb +50 -0
- data/spec/new_integration/action/view_integration_spec.rb +165 -0
- data/spec/new_integration/action/view_rendering/automatic_rendering_spec.rb +247 -0
- data/spec/new_integration/action/view_rendering/paired_view_inference_spec.rb +115 -0
- data/spec/new_integration/action/view_rendering_spec.rb +107 -0
- data/spec/new_integration/code_loading/loading_from_app_spec.rb +152 -0
- data/spec/new_integration/code_loading/loading_from_slice_spec.rb +165 -0
- data/spec/new_integration/container/application_routes_helper_spec.rb +48 -0
- data/spec/new_integration/container/auto_injection_spec.rb +53 -0
- data/spec/new_integration/container/auto_registration_spec.rb +86 -0
- data/spec/new_integration/container/autoloader_spec.rb +80 -0
- data/spec/new_integration/container/imports_spec.rb +253 -0
- data/spec/new_integration/container/prepare_container_spec.rb +123 -0
- data/spec/new_integration/container/shutdown_spec.rb +91 -0
- data/spec/new_integration/container/standard_bootable_components_spec.rb +124 -0
- data/spec/new_integration/rack_app/middleware_spec.rb +215 -0
- data/spec/new_integration/rack_app/non_booted_rack_app_spec.rb +105 -0
- data/spec/new_integration/rack_app/rack_app_spec.rb +524 -0
- data/spec/new_integration/settings_spec.rb +115 -0
- data/spec/new_integration/slices/external_slice_spec.rb +92 -0
- data/spec/new_integration/slices/slice_configuration_spec.rb +40 -0
- data/spec/new_integration/slices/slice_routing_spec.rb +226 -0
- data/spec/new_integration/slices/slice_settings_spec.rb +141 -0
- data/spec/new_integration/slices_spec.rb +101 -0
- data/spec/new_integration/view/configuration_spec.rb +49 -0
- data/spec/new_integration/view/context/assets_spec.rb +67 -0
- data/spec/new_integration/view/context/inflector_spec.rb +48 -0
- data/spec/new_integration/view/context/request_spec.rb +61 -0
- data/spec/new_integration/view/context/routes_spec.rb +86 -0
- data/spec/new_integration/view/context/settings_spec.rb +50 -0
- data/spec/new_integration/view/inflector_spec.rb +57 -0
- data/spec/new_integration/view/part_namespace_spec.rb +96 -0
- data/spec/new_integration/view/path_spec.rb +56 -0
- data/spec/new_integration/view/template_spec.rb +68 -0
- data/spec/new_integration/view/views_spec.rb +103 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/app_integration.rb +91 -0
- data/spec/support/coverage.rb +1 -0
- data/spec/support/fixtures/hanami-plugin/Gemfile +8 -0
- data/spec/support/fixtures/hanami-plugin/README.md +35 -0
- data/spec/support/fixtures/hanami-plugin/Rakefile +4 -0
- data/spec/support/fixtures/hanami-plugin/bin/console +15 -0
- data/spec/support/fixtures/hanami-plugin/bin/setup +8 -0
- data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +28 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +19 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +7 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +8 -0
- data/spec/support/rspec.rb +27 -0
- data/spec/support/shared_examples/cli/generate/app.rb +494 -0
- data/spec/support/shared_examples/cli/generate/migration.rb +32 -0
- data/spec/support/shared_examples/cli/generate/model.rb +81 -0
- data/spec/support/shared_examples/cli/new.rb +97 -0
- data/spec/unit/hanami/configuration/actions/content_security_policy_spec.rb +102 -0
- data/spec/unit/hanami/configuration/actions/cookies_spec.rb +46 -0
- data/spec/unit/hanami/configuration/actions/csrf_protection_spec.rb +57 -0
- data/spec/unit/hanami/configuration/actions/default_values_spec.rb +52 -0
- data/spec/unit/hanami/configuration/actions/sessions_spec.rb +50 -0
- data/spec/unit/hanami/configuration/actions_spec.rb +78 -0
- data/spec/unit/hanami/configuration/base_url_spec.rb +25 -0
- data/spec/unit/hanami/configuration/inflector_spec.rb +35 -0
- data/spec/unit/hanami/configuration/logger_spec.rb +203 -0
- data/spec/unit/hanami/configuration/views_spec.rb +120 -0
- data/spec/unit/hanami/configuration_spec.rb +43 -0
- data/spec/unit/hanami/env_spec.rb +54 -0
- data/spec/unit/hanami/routes_spec.rb +25 -0
- data/spec/unit/hanami/settings/dotenv_store_spec.rb +119 -0
- data/spec/unit/hanami/settings_spec.rb +56 -0
- data/spec/unit/hanami/slice_configurable_spec.rb +104 -0
- data/spec/unit/hanami/slice_name_spec.rb +47 -0
- data/spec/unit/hanami/slice_spec.rb +17 -0
- data/spec/unit/hanami/version_spec.rb +7 -0
- data/spec/unit/hanami/web/rack_logger_spec.rb +78 -0
- metadata +353 -57
- data/lib/hanami/application/action.rb +0 -72
- data/lib/hanami/application/container/providers/inflector.rb +0 -7
- data/lib/hanami/application/container/providers/logger.rb +0 -7
- data/lib/hanami/application/container/providers/rack_logger.rb +0 -15
- data/lib/hanami/application/container/providers/rack_monitor.rb +0 -12
- data/lib/hanami/application/container/providers/routes_helper.rb +0 -9
- data/lib/hanami/application/container/providers/settings.rb +0 -7
- data/lib/hanami/application/routes.rb +0 -55
- data/lib/hanami/application/routes_helper.rb +0 -34
- data/lib/hanami/application/routing/resolver/node.rb +0 -50
- data/lib/hanami/application/routing/resolver/trie.rb +0 -59
- data/lib/hanami/application/routing/resolver.rb +0 -87
- data/lib/hanami/application/routing/router.rb +0 -36
- data/lib/hanami/application/settings/dotenv_store.rb +0 -60
- data/lib/hanami/application/settings.rb +0 -93
- data/lib/hanami/application/slice_registrar.rb +0 -106
- data/lib/hanami/application/view/context.rb +0 -95
- data/lib/hanami/application/view.rb +0 -24
- data/lib/hanami/application.rb +0 -273
- data/lib/hanami/cli/application/cli.rb +0 -40
- data/lib/hanami/cli/application/command.rb +0 -47
- data/lib/hanami/cli/application/commands/console.rb +0 -81
- data/lib/hanami/cli/application/commands.rb +0 -16
- data/lib/hanami/cli/base_command.rb +0 -48
- data/lib/hanami/cli/commands/command.rb +0 -171
- data/lib/hanami/cli/commands/server.rb +0 -88
- data/lib/hanami/cli/commands.rb +0 -65
- data/lib/hanami/configuration/middleware.rb +0 -20
- data/lib/hanami/configuration/source_dirs.rb +0 -42
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "App action / Routes", :app_integration do
|
|
4
|
+
specify "Access app routes from an action" do
|
|
5
|
+
with_tmp_directory(Dir.mktmpdir) do
|
|
6
|
+
write "config/app.rb", <<~RUBY
|
|
7
|
+
require "hanami"
|
|
8
|
+
|
|
9
|
+
module TestApp
|
|
10
|
+
class App < Hanami::App; end
|
|
11
|
+
end
|
|
12
|
+
RUBY
|
|
13
|
+
|
|
14
|
+
write "config/routes.rb", <<~RUBY
|
|
15
|
+
module TestApp
|
|
16
|
+
class Routes < Hanami::Routes
|
|
17
|
+
define do
|
|
18
|
+
root to: "home.index"
|
|
19
|
+
|
|
20
|
+
slice :admin, at: "/admin" do
|
|
21
|
+
root to: "dashboard.index"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
RUBY
|
|
27
|
+
|
|
28
|
+
write "app/action.rb", <<~RUBY
|
|
29
|
+
# auto_register: false
|
|
30
|
+
|
|
31
|
+
module TestApp
|
|
32
|
+
class Action < Hanami::Action; end
|
|
33
|
+
end
|
|
34
|
+
RUBY
|
|
35
|
+
|
|
36
|
+
write "app/actions/home/index.rb", <<~RUBY
|
|
37
|
+
module TestApp
|
|
38
|
+
module Actions
|
|
39
|
+
module Home
|
|
40
|
+
class Index < TestApp::Action
|
|
41
|
+
def handle(req, res)
|
|
42
|
+
res.body = routes.path(:root)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
RUBY
|
|
49
|
+
|
|
50
|
+
write "slices/admin/actions/dashboard/index.rb", <<~RUBY
|
|
51
|
+
module Admin
|
|
52
|
+
module Actions
|
|
53
|
+
module Dashboard
|
|
54
|
+
class Index < TestApp::Action
|
|
55
|
+
def handle(req, res)
|
|
56
|
+
res.body = routes.path(:admin_root)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
RUBY
|
|
63
|
+
|
|
64
|
+
require "hanami/prepare"
|
|
65
|
+
|
|
66
|
+
response = TestApp::App["actions.home.index"].call({})
|
|
67
|
+
expect(response.body).to eq ["/"]
|
|
68
|
+
|
|
69
|
+
response = Admin::Slice["actions.dashboard.index"].call({})
|
|
70
|
+
expect(response.body).to eq ["/admin"]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "App action / Sessions", :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 "HTTP sessions enabled" do
|
|
22
|
+
let(:app_hook) {
|
|
23
|
+
proc do
|
|
24
|
+
config.actions.sessions = :cookie, {secret: "abc123"}
|
|
25
|
+
end
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
it "has HTTP sessions enabled" do
|
|
29
|
+
expect(action_class.ancestors).to include(Hanami::Action::Session)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "HTTP sessions explicitly disabled" do
|
|
34
|
+
let(:app_hook) {
|
|
35
|
+
proc do
|
|
36
|
+
config.actions.sessions = nil
|
|
37
|
+
end
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
it "does not have HTTP sessions enabled" do
|
|
41
|
+
expect(action_class.ancestors.map(&:to_s)).not_to include("Hanami::Action::Session")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "HTTP sessions not enabled" do
|
|
46
|
+
it "does not have HTTP session enabled" do
|
|
47
|
+
expect(action_class.ancestors.map(&:to_s)).not_to include("Hanami::Action::Session")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "App action / View integration", :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; end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module TestApp
|
|
18
|
+
module Actions
|
|
19
|
+
module Articles
|
|
20
|
+
class Index < TestApp::Action; end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
let(:action_class) { TestApp::Actions::Articles::Index }
|
|
27
|
+
subject(:action) { action_class.new(**action_args) }
|
|
28
|
+
let(:action_args) { {} }
|
|
29
|
+
|
|
30
|
+
describe "#view_context" do
|
|
31
|
+
subject(:view_context) { action.view_context }
|
|
32
|
+
|
|
33
|
+
context "Explicitly injected view context" do
|
|
34
|
+
let(:view_context) { double(:view_context) }
|
|
35
|
+
let(:action_args) { {view_context: view_context} }
|
|
36
|
+
|
|
37
|
+
it "returns the injected object" do
|
|
38
|
+
is_expected.to eql view_context
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context "No view context registered" do
|
|
43
|
+
it "is nil" do
|
|
44
|
+
is_expected.to be_nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "Default view context identifier" do
|
|
49
|
+
context "View context registered in slice" do
|
|
50
|
+
before do
|
|
51
|
+
TestApp::App.register "views.context", slice_view_context
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
let(:slice_view_context) { double(:slice_view_context) }
|
|
55
|
+
|
|
56
|
+
it "is the slice's view context" do
|
|
57
|
+
is_expected.to eql slice_view_context
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context "View context registered in app" do
|
|
62
|
+
before do
|
|
63
|
+
Hanami.app.register "views.context", app_view_context
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
let(:app_view_context) { double(:app_view_context) }
|
|
67
|
+
|
|
68
|
+
it "is the apps's view context" do
|
|
69
|
+
is_expected.to eql app_view_context
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context "custom view context identifier" do
|
|
75
|
+
let(:app_hook) {
|
|
76
|
+
proc do |app|
|
|
77
|
+
app.config.actions.view_context_identifier = "view.custom_context"
|
|
78
|
+
end
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
before do
|
|
82
|
+
TestApp::App.register "view.custom_context", custom_view_context
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
let(:custom_view_context) { double(:custom_view_context) }
|
|
86
|
+
|
|
87
|
+
it "is the context registered with the custom identifier" do
|
|
88
|
+
is_expected.to eql custom_view_context
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "#view_options" do
|
|
94
|
+
subject(:view_options) { action.send(:view_options, req, res) }
|
|
95
|
+
let(:req) { double(:req) }
|
|
96
|
+
let(:res) { double(:res) }
|
|
97
|
+
|
|
98
|
+
context "without view context" do
|
|
99
|
+
it "is an empty hash" do
|
|
100
|
+
is_expected.to eq({})
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "with view context" do
|
|
105
|
+
let(:initial_view_context) { double(:initial_view_context) }
|
|
106
|
+
let(:action_args) { {view_context: initial_view_context} }
|
|
107
|
+
|
|
108
|
+
context "default #view_context_options" do
|
|
109
|
+
let(:request_view_context) { double(:request_view_context) }
|
|
110
|
+
|
|
111
|
+
before do
|
|
112
|
+
allow(initial_view_context).to receive(:with).with(
|
|
113
|
+
request: req,
|
|
114
|
+
response: res,
|
|
115
|
+
) { request_view_context }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "is the view context with the request and response provided" do
|
|
119
|
+
is_expected.to eq(context: request_view_context)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context "custom #view_context_options" do
|
|
124
|
+
let(:custom_view_context) { double(:custom_view_context)}
|
|
125
|
+
|
|
126
|
+
before do
|
|
127
|
+
action_class.class_eval do
|
|
128
|
+
def view_context_options(req, res)
|
|
129
|
+
{custom_option: "custom option"}
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
allow(initial_view_context).to receive(:with).with(
|
|
134
|
+
custom_option: "custom option"
|
|
135
|
+
) { custom_view_context }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "is the view context with the custom options provided" do
|
|
139
|
+
is_expected.to eq(context: custom_view_context)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context "specialized method (calling super) defined in action class" do
|
|
144
|
+
let(:request_view_context) { double(:request_view_context) }
|
|
145
|
+
|
|
146
|
+
before do
|
|
147
|
+
allow(initial_view_context).to receive(:with).with(
|
|
148
|
+
request: req,
|
|
149
|
+
response: res,
|
|
150
|
+
) { request_view_context }
|
|
151
|
+
|
|
152
|
+
action_class.class_eval do
|
|
153
|
+
def view_options(req, res)
|
|
154
|
+
super.merge(extra_option: "extra option")
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "includes the options provided by the specialized method" do
|
|
160
|
+
is_expected.to eq(context: request_view_context, extra_option: "extra option")
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe "App action / View rendering / Automatic rendering", :app_integration do
|
|
4
|
+
it "Renders a view automatically, passing all params and exposures" do
|
|
5
|
+
within_app do
|
|
6
|
+
write "app/actions/profile/show.rb", <<~RUBY
|
|
7
|
+
module TestApp
|
|
8
|
+
module Actions
|
|
9
|
+
module Profile
|
|
10
|
+
class Show < TestApp::Action
|
|
11
|
+
def handle(req, res)
|
|
12
|
+
res[:favorite_number] = 123
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
RUBY
|
|
19
|
+
|
|
20
|
+
write "app/views/profile/show.rb", <<~RUBY
|
|
21
|
+
module TestApp
|
|
22
|
+
module Views
|
|
23
|
+
module Profile
|
|
24
|
+
class Show < TestApp::View
|
|
25
|
+
expose :name, :favorite_number
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
RUBY
|
|
31
|
+
|
|
32
|
+
write "app/templates/profile/show.html.slim", <<~'SLIM'
|
|
33
|
+
h1 Hello, #{name}. Your favorite number is #{favorite_number}, right?
|
|
34
|
+
SLIM
|
|
35
|
+
|
|
36
|
+
require "hanami/prepare"
|
|
37
|
+
|
|
38
|
+
action = TestApp::App["actions.profile.show"]
|
|
39
|
+
response = action.(name: "Jennifer")
|
|
40
|
+
rendered = response.body[0]
|
|
41
|
+
|
|
42
|
+
expect(rendered).to eq "<html><body><h1>Hello, Jennifer. Your favorite number is 123, right?</h1></body></html>"
|
|
43
|
+
expect(response.status).to eq 200
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "Does not render a view automatically when #render? returns false " do
|
|
48
|
+
within_app do
|
|
49
|
+
write "app/actions/profile/show.rb", <<~RUBY
|
|
50
|
+
module TestApp
|
|
51
|
+
module Actions
|
|
52
|
+
module Profile
|
|
53
|
+
class Show < TestApp::Action
|
|
54
|
+
def handle(req, res)
|
|
55
|
+
res[:favorite_number] = 123
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def auto_render?(_res)
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
66
|
+
|
|
67
|
+
write "app/views/profile/show.rb", <<~RUBY
|
|
68
|
+
module TestApp
|
|
69
|
+
module Views
|
|
70
|
+
module Profile
|
|
71
|
+
class Show < TestApp::View
|
|
72
|
+
expose :name, :favorite_number
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
RUBY
|
|
78
|
+
|
|
79
|
+
write "app/templates/profile/show.html.slim", <<~'SLIM'
|
|
80
|
+
h1 Hello, #{name}. Your favorite number is #{favorite_number}, right?
|
|
81
|
+
SLIM
|
|
82
|
+
|
|
83
|
+
require "hanami/prepare"
|
|
84
|
+
|
|
85
|
+
action = TestApp::App["actions.profile.show"]
|
|
86
|
+
response = action.(name: "Jennifer")
|
|
87
|
+
|
|
88
|
+
expect(response.body).to eq []
|
|
89
|
+
expect(response.status).to eq 200
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "Doesn't render view automatically when body is already assigned" do
|
|
94
|
+
within_app do
|
|
95
|
+
write "app/actions/profile/show.rb", <<~RUBY
|
|
96
|
+
module TestApp
|
|
97
|
+
module Actions
|
|
98
|
+
module Profile
|
|
99
|
+
class Show < TestApp::Action
|
|
100
|
+
def handle(req, res)
|
|
101
|
+
res.body = "200: Okay okay okay"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
RUBY
|
|
108
|
+
|
|
109
|
+
write "app/views/profile/show.rb", <<~RUBY
|
|
110
|
+
module TestApp
|
|
111
|
+
module Views
|
|
112
|
+
module Profile
|
|
113
|
+
class Show < TestApp::View
|
|
114
|
+
expose :name, :favorite_number
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
RUBY
|
|
120
|
+
|
|
121
|
+
write "app/templates/profile/show.html.slim", <<~'SLIM'
|
|
122
|
+
h1 Hello, #{name}. Your favorite number is #{favorite_number}, right?
|
|
123
|
+
SLIM
|
|
124
|
+
|
|
125
|
+
require "hanami/prepare"
|
|
126
|
+
|
|
127
|
+
action = TestApp::App["actions.profile.show"]
|
|
128
|
+
response = action.(name: "Jennifer")
|
|
129
|
+
rendered = response.body[0]
|
|
130
|
+
|
|
131
|
+
expect(rendered).to eq "200: Okay okay okay"
|
|
132
|
+
expect(response.status).to eq 200
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "Doesn't render view automatically when halt is called" do
|
|
137
|
+
within_app do
|
|
138
|
+
write "app/actions/profile/show.rb", <<~RUBY
|
|
139
|
+
module TestApp
|
|
140
|
+
module Actions
|
|
141
|
+
module Profile
|
|
142
|
+
class Show < TestApp::Action
|
|
143
|
+
def handle(req, res)
|
|
144
|
+
halt 404
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
RUBY
|
|
151
|
+
|
|
152
|
+
write "app/views/profile/show.rb", <<~RUBY
|
|
153
|
+
module TestApp
|
|
154
|
+
module Views
|
|
155
|
+
module Profile
|
|
156
|
+
class Show < TestApp::View
|
|
157
|
+
expose :name
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
RUBY
|
|
163
|
+
|
|
164
|
+
# This template will crash if not rendered with a valid `name` string. The absence
|
|
165
|
+
# of a crash here tells us that the view was never rendered.
|
|
166
|
+
write "app/templates/profile/show.html.slim", <<~'SLIM'
|
|
167
|
+
h1 Hello, #{name.to_str}!
|
|
168
|
+
SLIM
|
|
169
|
+
|
|
170
|
+
require "hanami/prepare"
|
|
171
|
+
|
|
172
|
+
# Call the action without a `name` param, thereby ensuring the view will raise an
|
|
173
|
+
# error if rendered
|
|
174
|
+
action = TestApp::App["actions.profile.show"]
|
|
175
|
+
response = action.({})
|
|
176
|
+
rendered = response.body[0]
|
|
177
|
+
|
|
178
|
+
aggregate_failures do
|
|
179
|
+
expect(rendered).to eq "Not Found"
|
|
180
|
+
expect(response.status).to eq 404
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "Does not render if no view is available" do
|
|
186
|
+
within_app do
|
|
187
|
+
write "app/actions/profile/show.rb", <<~RUBY
|
|
188
|
+
module TestApp
|
|
189
|
+
module Actions
|
|
190
|
+
module Profile
|
|
191
|
+
class Show < TestApp::Action
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
RUBY
|
|
197
|
+
|
|
198
|
+
require "hanami/prepare"
|
|
199
|
+
|
|
200
|
+
action = TestApp::App["actions.profile.show"]
|
|
201
|
+
response = action.({})
|
|
202
|
+
expect(response.body).to eq []
|
|
203
|
+
expect(response.status).to eq 200
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def within_app
|
|
208
|
+
with_tmp_directory(Dir.mktmpdir) do
|
|
209
|
+
write "config/app.rb", <<~RUBY
|
|
210
|
+
require "hanami"
|
|
211
|
+
|
|
212
|
+
module TestApp
|
|
213
|
+
class App < Hanami::App
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
RUBY
|
|
217
|
+
|
|
218
|
+
write "app/action.rb", <<~RUBY
|
|
219
|
+
# auto_register: false
|
|
220
|
+
|
|
221
|
+
module TestApp
|
|
222
|
+
class Action < Hanami::Action
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
RUBY
|
|
226
|
+
|
|
227
|
+
write "app/view.rb", <<~RUBY
|
|
228
|
+
# auto_register: false
|
|
229
|
+
|
|
230
|
+
require "hanami/view"
|
|
231
|
+
|
|
232
|
+
module TestApp
|
|
233
|
+
class View < Hanami::View
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
RUBY
|
|
237
|
+
|
|
238
|
+
write "app/templates/layouts/app.html.slim", <<~SLIM
|
|
239
|
+
html
|
|
240
|
+
body
|
|
241
|
+
== yield
|
|
242
|
+
SLIM
|
|
243
|
+
|
|
244
|
+
yield
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "App action / View rendering / Paired view inference", :app_integration do
|
|
6
|
+
before do
|
|
7
|
+
module TestApp
|
|
8
|
+
class App < Hanami::App
|
|
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) { action_class.new }
|
|
21
|
+
|
|
22
|
+
describe "Ordinary action" do
|
|
23
|
+
before do
|
|
24
|
+
module TestApp
|
|
25
|
+
module Actions
|
|
26
|
+
module Articles
|
|
27
|
+
class Index < TestApp::Action
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:action_class) { TestApp::Actions::Articles::Index }
|
|
35
|
+
|
|
36
|
+
context "Paired view exists" do
|
|
37
|
+
before do
|
|
38
|
+
TestApp::App.register "views.articles.index", view
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
let(:view) { double(:view) }
|
|
42
|
+
|
|
43
|
+
it "auto-injects a paired view from a matching container identifier" do
|
|
44
|
+
expect(action.view).to be view
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context "Another view explicitly auto-injected" do
|
|
48
|
+
before do
|
|
49
|
+
module TestApp
|
|
50
|
+
module Actions
|
|
51
|
+
module Articles
|
|
52
|
+
class Index < TestApp::Action
|
|
53
|
+
include Deps[view: "views.articles.custom"]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
TestApp::App.register "views.articles.custom", explicit_view
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
let(:action_class) { TestApp::Actions::Articles::Index }
|
|
63
|
+
let(:explicit_view) { double(:explicit_view) }
|
|
64
|
+
|
|
65
|
+
it "respects the explicitly auto-injected view" do
|
|
66
|
+
expect(action.view).to be explicit_view
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context "No paired view exists" do
|
|
72
|
+
it "does not auto-inject any view" do
|
|
73
|
+
expect(action.view).to be_nil
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe "RESTful action" do
|
|
79
|
+
before do
|
|
80
|
+
module TestApp
|
|
81
|
+
module Actions
|
|
82
|
+
module Articles
|
|
83
|
+
class Create < TestApp::Action
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
let(:action_class) { TestApp::Actions::Articles::Create }
|
|
91
|
+
let(:direct_paired_view) { double(:direct_paired_view) }
|
|
92
|
+
let(:alternative_paired_view) { double(:alternative_paired_view) }
|
|
93
|
+
|
|
94
|
+
context "Direct paired view exists" do
|
|
95
|
+
before do
|
|
96
|
+
TestApp::App.register "views.articles.create", direct_paired_view
|
|
97
|
+
TestApp::App.register "views.articles.new", alternative_paired_view
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "auto-injects the directly paired view" do
|
|
101
|
+
expect(action.view).to be direct_paired_view
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
context "Alternative paired view exists" do
|
|
106
|
+
before do
|
|
107
|
+
TestApp::App.register "views.articles.new", alternative_paired_view
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "auto-injects the alternative paired view" do
|
|
111
|
+
expect(action.view).to be alternative_paired_view
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|