hanami 2.0.0.beta1.1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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