hanami 2.0.0.beta1.1 → 2.0.0.beta2

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/hanami.gemspec +1 -2
  4. data/lib/hanami/app.rb +76 -16
  5. data/lib/hanami/assets/{application_configuration.rb → app_configuration.rb} +1 -1
  6. data/lib/hanami/configuration.rb +20 -20
  7. data/lib/hanami/extensions/action/slice_configured_action.rb +44 -1
  8. data/lib/hanami/extensions/view/slice_configured_view.rb +47 -7
  9. data/lib/hanami/providers/rack.rb +2 -0
  10. data/lib/hanami/providers/settings.rb +81 -6
  11. data/lib/hanami/settings/env_store.rb +32 -0
  12. data/lib/hanami/settings.rb +8 -12
  13. data/lib/hanami/setup.rb +1 -6
  14. data/lib/hanami/slice/routing/middleware/stack.rb +26 -5
  15. data/lib/hanami/slice.rb +38 -45
  16. data/lib/hanami/slice_configurable.rb +14 -1
  17. data/lib/hanami/slice_registrar.rb +65 -5
  18. data/lib/hanami/version.rb +1 -1
  19. data/lib/hanami.rb +53 -2
  20. data/spec/new_integration/action/slice_configuration_spec.rb +287 -0
  21. data/spec/new_integration/code_loading/loading_from_lib_spec.rb +208 -0
  22. data/spec/new_integration/dotenv_loading_spec.rb +137 -0
  23. data/spec/new_integration/settings/access_to_constants_spec.rb +169 -0
  24. data/spec/new_integration/settings/loading_from_env_spec.rb +187 -0
  25. data/spec/new_integration/settings/settings_component_loading_spec.rb +113 -0
  26. data/spec/new_integration/settings/using_types_spec.rb +87 -0
  27. data/spec/new_integration/setup_spec.rb +145 -0
  28. data/spec/new_integration/slices/slice_loading_spec.rb +171 -0
  29. data/spec/new_integration/view/context/settings_spec.rb +5 -1
  30. data/spec/new_integration/view/slice_configuration_spec.rb +289 -0
  31. data/spec/support/app_integration.rb +4 -5
  32. data/spec/unit/hanami/configuration/slices_spec.rb +34 -0
  33. data/spec/unit/hanami/settings/env_store_spec.rb +52 -0
  34. data/spec/unit/hanami/slice_configurable_spec.rb +2 -2
  35. data/spec/unit/hanami/version_spec.rb +1 -1
  36. metadata +30 -28
  37. data/lib/hanami/settings/dotenv_store.rb +0 -58
  38. data/spec/new_integration/action/configuration_spec.rb +0 -26
  39. data/spec/new_integration/settings_spec.rb +0 -115
  40. data/spec/new_integration/view/configuration_spec.rb +0 -49
  41. data/spec/unit/hanami/settings/dotenv_store_spec.rb +0 -119
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Settings / Access to constants", :app_integration do
4
+ before do
5
+ @env = ENV.to_h
6
+ end
7
+
8
+ after do
9
+ ENV.replace(@env)
10
+ end
11
+
12
+ describe "Settings can access autoloadable constants" do
13
+ describe "settings for app" do
14
+ specify "constant defined in app directory" do
15
+ with_directory(make_tmp_directory) do
16
+ write "config/app.rb", <<~'RUBY'
17
+ require "hanami"
18
+
19
+ module TestApp
20
+ class App < Hanami::App
21
+ end
22
+ end
23
+ RUBY
24
+
25
+ write ".env", <<~'TEXT'
26
+ SOME_FLAG=true
27
+ TEXT
28
+
29
+ write "config/settings.rb", <<~'RUBY'
30
+ module TestApp
31
+ class Settings < Hanami::Settings
32
+ setting :some_flag, constructor: Types::Params::Bool
33
+ end
34
+ end
35
+ RUBY
36
+
37
+ write "app/types.rb", <<~'RUBY'
38
+ # auto_register: false
39
+
40
+ require "dry/types"
41
+
42
+ module TestApp
43
+ Types = Dry.Types()
44
+ end
45
+ RUBY
46
+
47
+ require "hanami/prepare"
48
+
49
+ expect(Hanami.app[:settings].some_flag).to be true
50
+ end
51
+ end
52
+
53
+ specify "constant defined in root lib directory" do
54
+ with_directory(make_tmp_directory) do
55
+ write "config/app.rb", <<~'RUBY'
56
+ require "hanami"
57
+
58
+ module TestApp
59
+ class App < Hanami::App
60
+ end
61
+ end
62
+ RUBY
63
+
64
+ write ".env", <<~'TEXT'
65
+ SOME_FLAG=true
66
+ TEXT
67
+
68
+ write "config/settings.rb", <<~'RUBY'
69
+ module TestApp
70
+ class Settings < Hanami::Settings
71
+ setting :some_flag, constructor: Types::Params::Bool
72
+ end
73
+ end
74
+ RUBY
75
+
76
+ write "lib/test_app/types.rb", <<~'RUBY'
77
+ require "dry/types"
78
+
79
+ module TestApp
80
+ Types = Dry.Types()
81
+ end
82
+ RUBY
83
+
84
+ require "hanami/prepare"
85
+
86
+ expect(Hanami.app[:settings].some_flag).to be true
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "settings for slice" do
92
+ specify "constant defined in slice directory" do
93
+ with_directory(make_tmp_directory) do
94
+ write "config/app.rb", <<~'RUBY'
95
+ require "hanami"
96
+
97
+ module TestApp
98
+ class App < Hanami::App
99
+ end
100
+ end
101
+ RUBY
102
+
103
+ write ".env", <<~'TEXT'
104
+ SOME_FLAG=true
105
+ TEXT
106
+
107
+ write "slices/main/config/settings.rb", <<~'RUBY'
108
+ module Main
109
+ class Settings < Hanami::Settings
110
+ setting :some_flag, constructor: Types::Params::Bool
111
+ end
112
+ end
113
+ RUBY
114
+
115
+ write "slices/main/types.rb", <<~'RUBY'
116
+ # auto_register: false
117
+
118
+ require "dry/types"
119
+
120
+ module Main
121
+ Types = Dry.Types()
122
+ end
123
+ RUBY
124
+
125
+ require "hanami/prepare"
126
+
127
+ expect(Main::Slice[:settings].some_flag).to be true
128
+ end
129
+ end
130
+
131
+ specify "constant defined in root lib directory" do
132
+ with_directory(make_tmp_directory) do
133
+ write "config/app.rb", <<~'RUBY'
134
+ require "hanami"
135
+
136
+ module TestApp
137
+ class App < Hanami::App
138
+ end
139
+ end
140
+ RUBY
141
+
142
+ write ".env", <<~'TEXT'
143
+ SOME_FLAG=true
144
+ TEXT
145
+
146
+ write "slices/main/config/settings.rb", <<~'RUBY'
147
+ module Main
148
+ class Settings < Hanami::Settings
149
+ setting :some_flag, constructor: TestApp::Types::Params::Bool
150
+ end
151
+ end
152
+ RUBY
153
+
154
+ write "lib/test_app/types.rb", <<~'RUBY'
155
+ require "dry/types"
156
+
157
+ module TestApp
158
+ Types = Dry.Types()
159
+ end
160
+ RUBY
161
+
162
+ require "hanami/prepare"
163
+
164
+ expect(Main::Slice[:settings].some_flag).to be true
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Settings / Access to constants", :app_integration do
4
+ before do
5
+ @env = ENV.to_h
6
+ end
7
+
8
+ after do
9
+ ENV.replace(@env)
10
+ end
11
+
12
+ specify "settings are loaded from ENV" do
13
+ with_tmp_directory(Dir.mktmpdir) do
14
+ write "config/app.rb", <<~'RUBY'
15
+ require "hanami"
16
+
17
+ module TestApp
18
+ class App < Hanami::App
19
+ end
20
+ end
21
+ RUBY
22
+
23
+ write "config/settings.rb", <<~'RUBY'
24
+ module TestApp
25
+ class Settings < Hanami::Settings
26
+ setting :database_url
27
+ end
28
+ end
29
+ RUBY
30
+
31
+ ENV["DATABASE_URL"] = "postgres://localhost/database"
32
+
33
+ require "hanami/prepare"
34
+
35
+ expect(Hanami.app["settings"].database_url).to eq "postgres://localhost/database"
36
+ end
37
+ end
38
+
39
+ describe "settings are loaded from .env files" do
40
+ context "hanami env is development" do
41
+ it "loads settings from .env.development.local, .env.local, .env.development and .env (in this order)" do
42
+ with_tmp_directory(Dir.mktmpdir) do
43
+ write "config/app.rb", <<~'RUBY'
44
+ require "hanami"
45
+
46
+ module TestApp
47
+ class App < Hanami::App
48
+ end
49
+ end
50
+ RUBY
51
+
52
+ write "config/settings.rb", <<~'RUBY'
53
+ module TestApp
54
+ class Settings < Hanami::Settings
55
+ setting :from_specific_env_local
56
+ setting :from_base_local
57
+ setting :from_specific_env
58
+ setting :from_base
59
+ end
60
+ end
61
+ RUBY
62
+
63
+ write ".env.development.local", <<~'TEXT'
64
+ FROM_SPECIFIC_ENV_LOCAL="from .env.development.local"
65
+ TEXT
66
+
67
+ write ".env.local", <<~'TEXT'
68
+ FROM_BASE_LOCAL="from .env.local"
69
+
70
+ FROM_SPECIFIC_ENV_LOCAL=nope
71
+ TEXT
72
+
73
+ write ".env.development", <<~'TEXT'
74
+ FROM_SPECIFIC_ENV="from .env.development"
75
+
76
+ FROM_SPECIFIC_ENV_LOCAL=nope
77
+ FROM_BASE_LOCAL=nope
78
+ TEXT
79
+
80
+ write ".env", <<~'TEXT'
81
+ FROM_BASE="from .env"
82
+
83
+ FROM_SPECIFIC_ENV_LOCAL=nope
84
+ FROM_BASE_LOCAL=nope
85
+ FROM_SPECIFIC_ENV=nope
86
+ TEXT
87
+
88
+ ENV["HANAMI_ENV"] = "development"
89
+
90
+ require "hanami/prepare"
91
+
92
+ expect(Hanami.app["settings"].from_specific_env_local).to eq "from .env.development.local"
93
+ expect(Hanami.app["settings"].from_base_local).to eq "from .env.local"
94
+ expect(Hanami.app["settings"].from_specific_env).to eq "from .env.development"
95
+ expect(Hanami.app["settings"].from_base).to eq "from .env"
96
+ end
97
+ end
98
+
99
+ context "hanami env is test" do
100
+ it "loads settings from .env.development.local, .env.development and .env (in this order)" do
101
+ with_tmp_directory(Dir.mktmpdir) do
102
+ write "config/app.rb", <<~'RUBY'
103
+ require "hanami"
104
+
105
+ module TestApp
106
+ class App < Hanami::App
107
+ end
108
+ end
109
+ RUBY
110
+
111
+ write "config/settings.rb", <<~'RUBY'
112
+ module TestApp
113
+ class Settings < Hanami::Settings
114
+ setting :from_specific_env_local
115
+ setting :from_base_local
116
+ setting :from_specific_env
117
+ setting :from_base
118
+ end
119
+ end
120
+ RUBY
121
+
122
+ write ".env.test.local", <<~'TEXT'
123
+ FROM_SPECIFIC_ENV_LOCAL="from .env.test.local"
124
+ TEXT
125
+
126
+ write ".env.local", <<~'TEXT'
127
+ FROM_BASE_LOCAL="from .env.local"
128
+ TEXT
129
+
130
+ write ".env.test", <<~'TEXT'
131
+ FROM_SPECIFIC_ENV="from .env.test"
132
+
133
+ FROM_SPECIFIC_ENV_LOCAL=nope
134
+ TEXT
135
+
136
+ write ".env", <<~'TEXT'
137
+ FROM_BASE="from .env"
138
+
139
+ FROM_SPECIFIC_ENV_LOCAL=nope
140
+ FROM_SPECIFIC_ENV=nope
141
+ TEXT
142
+
143
+ ENV["HANAMI_ENV"] = "test"
144
+
145
+ require "hanami/prepare"
146
+
147
+ expect(Hanami.app["settings"].from_specific_env_local).to eq "from .env.test.local"
148
+ expect(Hanami.app["settings"].from_base_local).to be nil
149
+ expect(Hanami.app["settings"].from_specific_env).to eq "from .env.test"
150
+ expect(Hanami.app["settings"].from_base).to eq "from .env"
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ it "prefers ENV values are preferred over .env files" do
158
+ with_tmp_directory(Dir.mktmpdir) do
159
+ write "config/app.rb", <<~'RUBY'
160
+ require "hanami"
161
+
162
+ module TestApp
163
+ class App < Hanami::App
164
+ end
165
+ end
166
+ RUBY
167
+
168
+ write "config/settings.rb", <<~'RUBY'
169
+ module TestApp
170
+ class Settings < Hanami::Settings
171
+ setting :database_url
172
+ end
173
+ end
174
+ RUBY
175
+
176
+ write ".env", <<~'TEXT'
177
+ DATABASE_URL=nope
178
+ TEXT
179
+
180
+ ENV["DATABASE_URL"] = "postgres://localhost/database"
181
+
182
+ require "hanami/prepare"
183
+
184
+ expect(Hanami.app["settings"].database_url).to eq "postgres://localhost/database"
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Settings / Component loading", :app_integration do
4
+ describe "Settings are loaded from a class defined in config/settings.rb" do
5
+ specify "in app" do
6
+ with_directory(make_tmp_directory) do
7
+ write "config/app.rb", <<~'RUBY'
8
+ require "hanami"
9
+
10
+ module TestApp
11
+ class App < Hanami::App
12
+ end
13
+ end
14
+ RUBY
15
+
16
+ write "config/settings.rb", <<~'RUBY'
17
+ module TestApp
18
+ class Settings < Hanami::Settings
19
+ setting :foo
20
+ end
21
+ end
22
+ RUBY
23
+
24
+ require "hanami/prepare"
25
+
26
+ expect(Hanami.app["settings"]).to be_an_instance_of TestApp::Settings
27
+ expect(Hanami.app["settings"]).to respond_to :foo
28
+ end
29
+ end
30
+
31
+ specify "in slice" do
32
+ with_directory(make_tmp_directory) do
33
+ write "config/app.rb", <<~'RUBY'
34
+ require "hanami"
35
+
36
+ module TestApp
37
+ class App < Hanami::App
38
+ end
39
+ end
40
+ RUBY
41
+
42
+ write "slices/main/config/settings.rb", <<~'RUBY'
43
+ module Main
44
+ class Settings < Hanami::Settings
45
+ setting :foo
46
+ end
47
+ end
48
+ RUBY
49
+
50
+ require "hanami/prepare"
51
+
52
+ expect(Main::Slice["settings"]).to be_an_instance_of Main::Settings
53
+ expect(Main::Slice["settings"]).to respond_to :foo
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "Settings are loaded from a `Settings` class if already defined" do
59
+ specify "in app" do
60
+ with_directory(make_tmp_directory) do
61
+ write "config/app.rb", <<~'RUBY'
62
+ require "hanami"
63
+ require "hanami/settings"
64
+
65
+ module TestApp
66
+ class App < Hanami::App
67
+ end
68
+
69
+ class Settings < Hanami::Settings
70
+ setting :foo
71
+ end
72
+ end
73
+ RUBY
74
+
75
+ require "hanami/prepare"
76
+
77
+ expect(Hanami.app["settings"]).to be_an_instance_of TestApp::Settings
78
+ expect(Hanami.app["settings"]).to respond_to :foo
79
+ end
80
+ end
81
+
82
+ specify "in slice" do
83
+ with_directory(make_tmp_directory) do
84
+ write "config/app.rb", <<~'RUBY'
85
+ require "hanami"
86
+
87
+ module TestApp
88
+ class App < Hanami::App
89
+ end
90
+ end
91
+ RUBY
92
+
93
+ write "config/slices/main.rb", <<~'RUBY'
94
+ require "hanami/settings"
95
+
96
+ module Main
97
+ class Slice < Hanami::Slice
98
+ end
99
+
100
+ class Settings < Hanami::Settings
101
+ setting :foo
102
+ end
103
+ end
104
+ RUBY
105
+
106
+ require "hanami/prepare"
107
+
108
+ expect(Main::Slice["settings"]).to be_an_instance_of Main::Settings
109
+ expect(Main::Slice["settings"]).to respond_to :foo
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/settings"
4
+
5
+ RSpec.describe "Settings / Using types", :app_integration do
6
+ before do
7
+ @env = ENV.to_h
8
+ end
9
+
10
+ after do
11
+ ENV.replace(@env)
12
+ end
13
+
14
+ specify "dry-types can be used as setting constructors to coerce values" do
15
+ with_tmp_directory(Dir.mktmpdir) do
16
+ write "config/app.rb", <<~RUBY
17
+ require "hanami"
18
+
19
+ module TestApp
20
+ class App < Hanami::App
21
+ end
22
+ end
23
+ RUBY
24
+
25
+ write "config/settings.rb", <<~RUBY
26
+ require "dry/types"
27
+
28
+ module TestApp
29
+ class Settings < Hanami::Settings
30
+ Types = Dry.Types()
31
+
32
+ setting :numeric, constructor: Types::Params::Integer
33
+ setting :flag, constructor: Types::Params::Bool
34
+ end
35
+ end
36
+ RUBY
37
+
38
+ ENV["NUMERIC"] = "42"
39
+ ENV["FLAG"] = "true"
40
+
41
+ require "hanami/prepare"
42
+
43
+ expect(Hanami.app["settings"].numeric).to eq 42
44
+ expect(Hanami.app["settings"].flag).to be true
45
+ end
46
+ end
47
+
48
+ specify "errors raised from setting constructors are collected and re-raised in aggregate" do
49
+ with_tmp_directory(Dir.mktmpdir) do
50
+ write "config/app.rb", <<~RUBY
51
+ require "hanami"
52
+
53
+ module TestApp
54
+ class App < Hanami::App
55
+ end
56
+ end
57
+ RUBY
58
+
59
+ write "config/settings.rb", <<~RUBY
60
+ require "dry/types"
61
+
62
+ module TestApp
63
+ class Settings < Hanami::Settings
64
+ Types = Dry.Types()
65
+
66
+ setting :numeric, constructor: Types::Params::Integer
67
+ setting :flag, constructor: Types::Params::Bool
68
+ end
69
+ end
70
+ RUBY
71
+
72
+ ENV["NUMERIC"] = "never gonna"
73
+ ENV["FLAG"] = "give you up"
74
+
75
+ numeric_error = "numeric: invalid value for Integer"
76
+ flag_error = "flag: give you up cannot be coerced"
77
+
78
+ expect {
79
+ require "hanami/prepare"
80
+ }.to raise_error(
81
+ Hanami::Settings::InvalidSettingsError,
82
+ /#{numeric_error}.+#{flag_error}|#{flag_error}.+#{numeric_error}/m
83
+ )
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "Hanami setup", :app_integration do
4
+ describe "Hanami.setup" do
5
+ shared_examples "hanami setup" do
6
+ it "requires the app file when found" do
7
+ with_tmp_directory(Dir.mktmpdir) do
8
+ write "config/app.rb", <<~RUBY
9
+ require "hanami"
10
+
11
+ module TestApp
12
+ class App < Hanami::App
13
+ end
14
+ end
15
+ RUBY
16
+
17
+ expect { setup }.to change { Hanami.app? }.to true
18
+ expect(Hanami.app).to be TestApp::App
19
+ end
20
+ end
21
+
22
+ it "requires the app file when found in a parent directory" do
23
+ with_tmp_directory(Dir.mktmpdir) do
24
+ write "config/app.rb", <<~RUBY
25
+ require "hanami"
26
+
27
+ module TestApp
28
+ class App < Hanami::App
29
+ end
30
+ end
31
+ RUBY
32
+
33
+ write "lib/foo/bar/.keep"
34
+
35
+ Dir.chdir("lib/foo/bar") do
36
+ expect { setup }.to change { Hanami.app? }.to true
37
+ expect(Hanami.app).to be TestApp::App
38
+ end
39
+ end
40
+ end
41
+
42
+ it "raises when the app file is not found" do
43
+ with_tmp_directory(Dir.mktmpdir) do
44
+ expect { setup }.to raise_error Hanami::AppLoadError, /Could not locate your Hanami app file/
45
+ end
46
+ end
47
+
48
+ it "doesn't raise when the app file is not found but the app is already set" do
49
+ require "hanami"
50
+
51
+ module TestApp
52
+ class App < Hanami::App
53
+ end
54
+ end
55
+
56
+ expect { setup }.not_to raise_error
57
+ end
58
+ end
59
+
60
+ describe "using hanami/setup require" do
61
+ def setup
62
+ require "hanami/setup"
63
+ end
64
+
65
+ it_behaves_like "hanami setup"
66
+ end
67
+
68
+ describe "using Hanami.setup method" do
69
+ def setup(...)
70
+ require "hanami"
71
+ Hanami.setup(...)
72
+ end
73
+
74
+ it_behaves_like "hanami setup"
75
+
76
+ it "returns the loaded app when the app file is found" do
77
+ with_tmp_directory(Dir.mktmpdir) do
78
+ write "config/app.rb", <<~RUBY
79
+ require "hanami"
80
+
81
+ module TestApp
82
+ class App < Hanami::App
83
+ end
84
+ end
85
+ RUBY
86
+
87
+ # Multiple calls return the same app
88
+ expect(setup).to be(Hanami.app)
89
+ expect(setup).to be(Hanami.app)
90
+ end
91
+ end
92
+
93
+ it "returns nil when given `raise_exception: false` and the app file is not found" do
94
+ with_tmp_directory(Dir.mktmpdir) do
95
+ expect(setup(raise_exception: false)).to be nil
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "Hanami.app_path" do
102
+ subject(:app_path) { Hanami.app_path }
103
+
104
+ context "config/app.rb exists in current directory" do
105
+ it "returns its absolute path" do
106
+ with_tmp_directory(Dir.mktmpdir) do
107
+ write "config/app.rb"
108
+
109
+ expect(app_path).to match(%r{^/.*/config/app.rb$})
110
+ end
111
+ end
112
+ end
113
+
114
+ context "config/app.rb exists in a parent directory" do
115
+ it "returns its absolute path" do
116
+ with_tmp_directory(Dir.mktmpdir) do
117
+ write "config/app.rb"
118
+ write "lib/foo/bar/.keep"
119
+
120
+ Dir.chdir("lib/foo/bar") do
121
+ expect(app_path).to match(%r{^/.*/config/app.rb$})
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ context "no app file in any directory" do
128
+ it "returns nil" do
129
+ with_tmp_directory(Dir.mktmpdir) do
130
+ expect(app_path).to be(nil)
131
+ end
132
+ end
133
+ end
134
+
135
+ context "directory exists with same name as the app file" do
136
+ it "returns nil" do
137
+ with_tmp_directory(Dir.mktmpdir) do
138
+ write "config/app.rb/.keep"
139
+
140
+ expect(app_path).to be(nil)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end