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,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/configuration"
|
|
4
|
+
require "hanami/configuration/views"
|
|
5
|
+
require "saharspec/matchers/dont"
|
|
6
|
+
|
|
7
|
+
RSpec.describe Hanami::Configuration, "#views" do
|
|
8
|
+
let(:configuration) { described_class.new(app_name: app_name, env: :development) }
|
|
9
|
+
let(:app_name) { "MyApp::app" }
|
|
10
|
+
|
|
11
|
+
subject(:views) { configuration.views }
|
|
12
|
+
|
|
13
|
+
context "Hanami::View available" do
|
|
14
|
+
it "exposes Hanami::Views's app configuration" do
|
|
15
|
+
is_expected.to be_an_instance_of(Hanami::Configuration::Views)
|
|
16
|
+
|
|
17
|
+
is_expected.to respond_to(:finalize!)
|
|
18
|
+
is_expected.to respond_to(:layouts_dir)
|
|
19
|
+
is_expected.to respond_to(:layouts_dir=)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "includes base view configuration" do
|
|
23
|
+
expect(views).to respond_to(:paths)
|
|
24
|
+
expect(views).to respond_to(:paths=)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "does not include the inflector setting" do
|
|
28
|
+
expect(views).not_to respond_to(:inflector)
|
|
29
|
+
expect(views).not_to respond_to(:inflector=)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "#settings" do
|
|
33
|
+
it "includes locally defined settings" do
|
|
34
|
+
expect(views.settings).to include :parts_path
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "includes all view settings apart from inflector" do
|
|
38
|
+
expect(views.settings).to include (Hanami::View.settings - [:inflector])
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "preserves default values from the base view configuration" do
|
|
43
|
+
expect(views.layouts_dir).to eq Hanami::View.config.layouts_dir
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "allows settings to be configured independently of the base view configuration" do
|
|
47
|
+
expect { views.layouts_dir = "custom_layouts" }
|
|
48
|
+
.to change { views.layouts_dir }.to("custom_layouts")
|
|
49
|
+
.and(dont.change { Hanami::View.config.layouts_dir })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "specialised default values" do
|
|
53
|
+
describe "paths" do
|
|
54
|
+
it 'is ["templates"]' do
|
|
55
|
+
expect(views.paths).to match [
|
|
56
|
+
an_object_satisfying { |path| path.dir.to_s == "templates" }
|
|
57
|
+
]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "template_inference_base" do
|
|
62
|
+
it 'is "views"' do
|
|
63
|
+
expect(views.template_inference_base).to eq "views"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "layout" do
|
|
68
|
+
it 'is "app"' do
|
|
69
|
+
expect(views.layout).to eq "app"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "finalized configuration" do
|
|
75
|
+
before do
|
|
76
|
+
views.finalize!
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "is frozen" do
|
|
80
|
+
expect(views).to be_frozen
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "does not allow changes to locally defined settings" do
|
|
84
|
+
expect { views.parts_path = "parts" }.to raise_error(Dry::Configurable::FrozenConfig)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "does not allow changes to base view settings" do
|
|
88
|
+
expect { views.paths = [] }.to raise_error(Dry::Configurable::FrozenConfig)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
context "Hanami::View not available" do
|
|
94
|
+
before do
|
|
95
|
+
load_error = LoadError.new.tap do |error|
|
|
96
|
+
error.instance_variable_set :@path, "hanami/view"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
allow_any_instance_of(described_class)
|
|
100
|
+
.to receive(:require)
|
|
101
|
+
.with(anything)
|
|
102
|
+
.and_call_original
|
|
103
|
+
|
|
104
|
+
allow_any_instance_of(described_class)
|
|
105
|
+
.to receive(:require)
|
|
106
|
+
.with("hanami/view")
|
|
107
|
+
.and_raise load_error
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "does not expose any settings" do
|
|
111
|
+
is_expected.not_to be_an_instance_of(Hanami::Configuration::Views)
|
|
112
|
+
is_expected.not_to respond_to(:layouts_dir)
|
|
113
|
+
is_expected.not_to respond_to(:layouts_dir=)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "can be finalized" do
|
|
117
|
+
is_expected.to respond_to(:finalize!)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "hanami/configuration"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Hanami::Configuration do
|
|
4
|
+
let(:config) { described_class.new(app_name: app_name, env: env) }
|
|
5
|
+
let(:app_name) { "MyApp::app" }
|
|
6
|
+
let(:env) { :development }
|
|
7
|
+
|
|
8
|
+
describe "environment-specific configuration" do
|
|
9
|
+
before do
|
|
10
|
+
config.logger.level = :debug__set_without_env
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
config.environment :production do |c|
|
|
15
|
+
c.logger.level = :info__set_for_production_env
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "settings configured for current env" do
|
|
20
|
+
let(:env) { :production }
|
|
21
|
+
|
|
22
|
+
it "applies the settings" do
|
|
23
|
+
expect(config.logger.level).to eq :info__set_for_production_env
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "leaves the settings in place when finalizing" do
|
|
27
|
+
expect { config.finalize! }.not_to(change { config.logger.level })
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "settings configured for a different env" do
|
|
32
|
+
let(:env) { :development }
|
|
33
|
+
|
|
34
|
+
it "does not apply the settings" do
|
|
35
|
+
expect(config.logger.level).to eq :debug__set_without_env
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "does not apply the settings when finalizing" do
|
|
39
|
+
expect { config.finalize! }.not_to(change { config.logger.level })
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Hanami, ".env" do
|
|
4
|
+
subject(:env) { described_class.env }
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
@orig_env = ENV.to_h
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after do
|
|
11
|
+
ENV.replace(@orig_env)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "HANAMI_ENV in ENV" do
|
|
15
|
+
before do
|
|
16
|
+
ENV["HANAMI_ENV"] = "test"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "is the value of HANAMI_ENV" do
|
|
20
|
+
is_expected.to eq :test
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context "RACK_ENV in ENV" do
|
|
25
|
+
before do
|
|
26
|
+
ENV["RACK_ENV"] = "test"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "is the value of RACK_ENV" do
|
|
30
|
+
is_expected.to eq :test
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "both HANAMI_ENV and RACK_ENV in ENV" do
|
|
35
|
+
before do
|
|
36
|
+
ENV["HANAMI_ENV"] = "test"
|
|
37
|
+
ENV["RACK_ENV"] = "production"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "is the value of HANAMI_ENV" do
|
|
41
|
+
is_expected.to eq :test
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "no ENV vars set" do
|
|
46
|
+
before do
|
|
47
|
+
ENV.delete("HANAMI_ENV")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "defaults to \"development\"" do
|
|
51
|
+
is_expected.to eq :development
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/routes"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Hanami::Routes do
|
|
6
|
+
describe ".define" do
|
|
7
|
+
it "sets routes block" do
|
|
8
|
+
routes_class = Class.new(described_class)
|
|
9
|
+
|
|
10
|
+
routes_class.define { "Dummy routes" }
|
|
11
|
+
|
|
12
|
+
expect(routes_class.routes.call).to eq("Dummy routes")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe ".routes" do
|
|
17
|
+
context "when called before the routes have been defined" do
|
|
18
|
+
it "raises an error" do
|
|
19
|
+
routes_class = Class.new(described_class)
|
|
20
|
+
|
|
21
|
+
expect { routes_class.routes }.to raise_error(RuntimeError)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/settings/dotenv_store"
|
|
4
|
+
require "dotenv"
|
|
5
|
+
|
|
6
|
+
RSpec.describe Hanami::Settings::DotenvStore do
|
|
7
|
+
def mock_dotenv(store)
|
|
8
|
+
dotenv = spy(:dotenv)
|
|
9
|
+
allow(store).to receive(:require).and_call_original
|
|
10
|
+
stub_const "Dotenv", dotenv
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "#with_dotenv_loaded" do
|
|
14
|
+
context "dotenv available and environment other than test" do
|
|
15
|
+
it "requires and loads a range of dotenv files, specific to the current environment" do
|
|
16
|
+
store = described_class.new(store: {}, hanami_env: :development)
|
|
17
|
+
dotenv = mock_dotenv(store)
|
|
18
|
+
|
|
19
|
+
store.with_dotenv_loaded
|
|
20
|
+
|
|
21
|
+
expect(store).to have_received(:require).with("dotenv").ordered
|
|
22
|
+
expect(dotenv).to have_received(:load).ordered.with(
|
|
23
|
+
".env.development.local",
|
|
24
|
+
".env.local",
|
|
25
|
+
".env.development",
|
|
26
|
+
".env"
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "returns self" do
|
|
31
|
+
store = described_class.new(store: {}, hanami_env: :development)
|
|
32
|
+
|
|
33
|
+
expect(store.with_dotenv_loaded).to be(store)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "dotenv available and test environment" do
|
|
38
|
+
it "does not load .env.local (which is intended for non-test settings only)" do
|
|
39
|
+
store = described_class.new(store: {}, hanami_env: :test)
|
|
40
|
+
dotenv = mock_dotenv(store)
|
|
41
|
+
|
|
42
|
+
store.with_dotenv_loaded
|
|
43
|
+
|
|
44
|
+
expect(store).to have_received(:require).with("dotenv").ordered
|
|
45
|
+
expect(dotenv).to have_received(:load).ordered.with(
|
|
46
|
+
".env.test.local",
|
|
47
|
+
".env.test",
|
|
48
|
+
".env"
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "returns self" do
|
|
53
|
+
store = described_class.new(store: {}, hanami_env: :test)
|
|
54
|
+
|
|
55
|
+
expect(store.with_dotenv_loaded).to be(store)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "dotenv unavailable" do
|
|
60
|
+
let(:store) { described_class.new(store: {}) }
|
|
61
|
+
|
|
62
|
+
before do
|
|
63
|
+
allow(store).to receive(:require).with("dotenv").and_raise LoadError
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "attempts to require dotenv" do
|
|
67
|
+
store.with_dotenv_loaded
|
|
68
|
+
|
|
69
|
+
expect(store).to have_received(:require).with("dotenv")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "gracefully ignores load errors" do
|
|
73
|
+
expect { store.with_dotenv_loaded }.not_to raise_error
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "returns self" do
|
|
77
|
+
expect(store.with_dotenv_loaded).to be(store)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "#fetch" do
|
|
83
|
+
it "fetches from ENV" do
|
|
84
|
+
store = described_class.new(store: { "FOO" => "bar" })
|
|
85
|
+
|
|
86
|
+
expect(store.fetch("FOO")).to eq("bar")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "capitalizes name" do
|
|
90
|
+
store = described_class.new(store: { "FOO" => "bar" })
|
|
91
|
+
|
|
92
|
+
expect(store.fetch("foo")).to eq("bar")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "coerces name to string" do
|
|
96
|
+
store = described_class.new(store: { "FOO" => "bar" })
|
|
97
|
+
|
|
98
|
+
expect(store.fetch(:foo)).to eq("bar")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "returns default when value is not found" do
|
|
102
|
+
store = described_class.new(store: { "FOO" => "bar" })
|
|
103
|
+
|
|
104
|
+
expect(store.fetch("BAZ", "qux")).to eq("qux")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "returns the block execution when value is not found" do
|
|
108
|
+
store = described_class.new(store: { "FOO" => "bar" })
|
|
109
|
+
|
|
110
|
+
expect(store.fetch("BAZ") { "qux" }).to eq("qux")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "raises KeyError when value is not found and no default is given" do
|
|
114
|
+
store = described_class.new(store: { "FOO" => "bar" })
|
|
115
|
+
|
|
116
|
+
expect{ store.fetch("BAZ") }.to raise_error(KeyError)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/settings"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Hanami::Settings do
|
|
6
|
+
describe "#initialize" do
|
|
7
|
+
it "uses values from the store when present" do
|
|
8
|
+
config_class = Class.new(described_class) do
|
|
9
|
+
setting :database_url, default: "postgres://localhost/test_app_development"
|
|
10
|
+
end
|
|
11
|
+
store = { database_url: "mysql://localhost/test_app_development" }.freeze
|
|
12
|
+
|
|
13
|
+
instance = config_class.new(store)
|
|
14
|
+
|
|
15
|
+
expect(instance.config.database_url).to eq("mysql://localhost/test_app_development")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "uses defaults when values are not present in the store" do
|
|
19
|
+
config_class = Class.new(described_class) do
|
|
20
|
+
setting :database_url, default: "postgres://localhost/test_app_development"
|
|
21
|
+
end
|
|
22
|
+
store = {}.freeze
|
|
23
|
+
|
|
24
|
+
instance = config_class.new(store)
|
|
25
|
+
|
|
26
|
+
expect(instance.config.database_url).to eq("postgres://localhost/test_app_development")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "collects error for all failed settings" do
|
|
30
|
+
config_class = Class.new(described_class) do
|
|
31
|
+
setting :database_url, constructor: ->(_v) { raise "nope to database" }
|
|
32
|
+
setting :redis_url, constructor: ->(_v) { raise "nope to redis" }
|
|
33
|
+
end
|
|
34
|
+
store = {
|
|
35
|
+
database_url: "postgres://localhost/test_app_development",
|
|
36
|
+
redis_url: "redis://localhost:6379"
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
39
|
+
expect { config_class.new(store) }.to raise_error(
|
|
40
|
+
described_class::InvalidSettingsError,
|
|
41
|
+
/(database_url: nope to database).*(redis_url: nope to redis)/m,
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "delegates unknown methods to config" do
|
|
47
|
+
config_class = Class.new(described_class) do
|
|
48
|
+
setting :foo, default: "bar"
|
|
49
|
+
end
|
|
50
|
+
store = {}.freeze
|
|
51
|
+
|
|
52
|
+
instance = config_class.new(store)
|
|
53
|
+
|
|
54
|
+
expect(instance.foo).to eq("bar")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/app"
|
|
4
|
+
require "hanami/slice_configurable"
|
|
5
|
+
|
|
6
|
+
RSpec.describe Hanami::SliceConfigurable, :app_integration do
|
|
7
|
+
before do
|
|
8
|
+
module TestApp
|
|
9
|
+
class App < Hanami::App
|
|
10
|
+
register_slice :main
|
|
11
|
+
register_slice :admin
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class BaseClass
|
|
15
|
+
extend Hanami::SliceConfigurable
|
|
16
|
+
|
|
17
|
+
def self.configure_for_slice(slice)
|
|
18
|
+
traces << slice
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.traces
|
|
22
|
+
@traces ||= []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.inherited(subclass)
|
|
26
|
+
subclass.instance_variable_set(:@traces, traces.dup)
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Hanami.app.prepare
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "subclass inside slice namespace" do
|
|
36
|
+
before do
|
|
37
|
+
module Main
|
|
38
|
+
class MySubclass < TestApp::BaseClass; end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subject(:subclass) { Main::MySubclass }
|
|
43
|
+
|
|
44
|
+
it "calls `configure_for_slice` with the slice" do
|
|
45
|
+
expect(subclass.traces).to eq [Main::Slice]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "further subclass, within same slice" do
|
|
49
|
+
before do
|
|
50
|
+
module Main
|
|
51
|
+
class MySubSubclass < Main::MySubclass; end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
subject(:subclass) { Main::MySubSubclass }
|
|
56
|
+
|
|
57
|
+
it "calls `configure_for_slice` again for the same slice" do
|
|
58
|
+
expect(subclass.traces).to eq [Main::Slice, Main::Slice]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "further subclass, within another slice namespace" do
|
|
63
|
+
before do
|
|
64
|
+
module Admin
|
|
65
|
+
class MySubSubclass < Main::MySubclass; end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
subject(:subclass) { Admin::MySubSubclass }
|
|
70
|
+
|
|
71
|
+
it "calls `configure_for_slice` with the other slice" do
|
|
72
|
+
expect(subclass.traces).to eq [Main::Slice, Admin::Slice]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context "class inside app" do
|
|
78
|
+
before do
|
|
79
|
+
module TestApp
|
|
80
|
+
class MySubclass < TestApp::BaseClass; end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
subject(:subclass) { TestApp::MySubclass }
|
|
85
|
+
|
|
86
|
+
it "calls `configure_for_slice` with the app instance" do
|
|
87
|
+
expect(subclass.traces).to eq [TestApp::App]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
context "further subclass, within another slice namespace" do
|
|
91
|
+
before do
|
|
92
|
+
module Main
|
|
93
|
+
class MySubSubclass < TestApp::MySubclass; end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
subject(:subclass) { Main::MySubSubclass }
|
|
98
|
+
|
|
99
|
+
it "calls `configure_for_slice` with the other slice" do
|
|
100
|
+
expect(subclass.traces).to eq [TestApp::App, Main::Slice]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/slice_name"
|
|
4
|
+
|
|
5
|
+
require "dry/inflector"
|
|
6
|
+
|
|
7
|
+
RSpec.describe Hanami::SliceName do
|
|
8
|
+
subject(:slice_name) { described_class.new(slice, inflector: -> { inflector }) }
|
|
9
|
+
let(:slice) { double(name: "Main::Slice") }
|
|
10
|
+
let(:inflector) { Dry::Inflector.new }
|
|
11
|
+
|
|
12
|
+
let(:slice_module) { Module.new }
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
stub_const "Main", slice_module
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#name" do
|
|
19
|
+
it "returns the downcased, underscored string name of the module containing the slice" do
|
|
20
|
+
expect(slice_name.name).to eq "main"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "#to_s" do
|
|
25
|
+
it "returns the downcased, underscored string name of the module containing the slice" do
|
|
26
|
+
expect(slice_name.to_s).to eq "main"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#to_sym" do
|
|
31
|
+
it "returns the downcased, underscored, symbolized name of the module containing the slice" do
|
|
32
|
+
expect(slice_name.to_sym).to eq :main
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "#namespace_name" do
|
|
37
|
+
it "returns the string name of the module containing the slice" do
|
|
38
|
+
expect(slice_name.namespace_name).to eq "Main"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "#namespace_const" do
|
|
43
|
+
it "returns the module containing the slice" do
|
|
44
|
+
expect(slice_name.namespace).to be slice_module
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "hanami/slice"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Hanami::Slice, :app_integration do
|
|
4
|
+
before do
|
|
5
|
+
module TestApp
|
|
6
|
+
class App < Hanami::App
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe ".prepare" do
|
|
12
|
+
it "raises an error if the slice class is anonymous" do
|
|
13
|
+
expect { Class.new(described_class).prepare }
|
|
14
|
+
.to raise_error Hanami::SliceLoadError, /Slice must have a class name/
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/web/rack_logger"
|
|
4
|
+
require "hanami/logger"
|
|
5
|
+
require "stringio"
|
|
6
|
+
require "rack/mock"
|
|
7
|
+
|
|
8
|
+
RSpec.describe Hanami::Web::RackLogger do
|
|
9
|
+
subject { described_class.new(logger) }
|
|
10
|
+
let(:logger) { Hanami::Logger.new(app_name, stream: stream, level: Hanami::Logger::DEBUG, filter: filters) }
|
|
11
|
+
let(:stream) { StringIO.new }
|
|
12
|
+
let(:filters) { ["user.password"] }
|
|
13
|
+
let(:app_name) { "my_app" }
|
|
14
|
+
|
|
15
|
+
describe "#initialize" do
|
|
16
|
+
it "returns an instance of #{described_class}" do
|
|
17
|
+
expect(subject).to be_kind_of(described_class)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "#log_request" do
|
|
22
|
+
it "logs current request" do
|
|
23
|
+
time = Time.parse("2022-02-04 11:38:25.218816 +0100")
|
|
24
|
+
expect(Time).to receive(:now).at_least(:once).and_return(time)
|
|
25
|
+
|
|
26
|
+
path = "/users"
|
|
27
|
+
ip = "127.0.0.1"
|
|
28
|
+
status = 200
|
|
29
|
+
elapsed = 0.0001
|
|
30
|
+
content_length = 23
|
|
31
|
+
verb = "POST"
|
|
32
|
+
|
|
33
|
+
env = Rack::MockRequest.env_for(path, method: verb)
|
|
34
|
+
env["Content-Length"] = content_length
|
|
35
|
+
env["REMOTE_ADDR"] = ip
|
|
36
|
+
|
|
37
|
+
params = {"user" => {"password" => "secret"}}
|
|
38
|
+
env["router.params"] = params
|
|
39
|
+
|
|
40
|
+
subject.log_request(env, status, elapsed)
|
|
41
|
+
|
|
42
|
+
stream.rewind
|
|
43
|
+
actual = stream.read
|
|
44
|
+
|
|
45
|
+
expect(actual).to include(%([#{app_name}] [INFO] [#{time}] #{verb} #{status} #{elapsed}ms #{ip} #{path} #{content_length} {"user"=>{"password"=>"[FILTERED]"}}))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "ip" do
|
|
49
|
+
it "takes into account HTTP proxy forwarding" do
|
|
50
|
+
env = Rack::MockRequest.env_for("/")
|
|
51
|
+
env["REMOTE_ADDR"] = remote = "127.0.0.1"
|
|
52
|
+
env["HTTP_X_FORWARDED_FOR"] = forwarded = "::1"
|
|
53
|
+
|
|
54
|
+
subject.log_request(env, 200, 0.1)
|
|
55
|
+
|
|
56
|
+
stream.rewind
|
|
57
|
+
actual = stream.read
|
|
58
|
+
|
|
59
|
+
expect(actual).to include(forwarded)
|
|
60
|
+
expect(actual).to_not include(remote)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context "path prefix" do
|
|
65
|
+
it "logs full referenced relative path" do
|
|
66
|
+
env = Rack::MockRequest.env_for(path = "/users")
|
|
67
|
+
env["SCRIPT_NAME"] = script_name = "/v1"
|
|
68
|
+
|
|
69
|
+
subject.log_request(env, 200, 0.1)
|
|
70
|
+
|
|
71
|
+
stream.rewind
|
|
72
|
+
actual = stream.read
|
|
73
|
+
|
|
74
|
+
expect(actual).to include(script_name + path)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|