hanami 2.0.3 → 2.1.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -2
  3. data/LICENSE.md +1 -1
  4. data/README.md +26 -10
  5. data/hanami.gemspec +2 -2
  6. data/lib/hanami/app.rb +5 -0
  7. data/lib/hanami/config/actions.rb +4 -11
  8. data/lib/hanami/config/assets.rb +84 -0
  9. data/lib/hanami/config/null_config.rb +3 -0
  10. data/lib/hanami/config/views.rb +0 -4
  11. data/lib/hanami/config.rb +71 -5
  12. data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
  13. data/lib/hanami/extensions/action.rb +8 -6
  14. data/lib/hanami/extensions/router/errors.rb +58 -0
  15. data/lib/hanami/extensions/view/context.rb +129 -60
  16. data/lib/hanami/extensions/view/part.rb +26 -0
  17. data/lib/hanami/extensions/view/scope.rb +26 -0
  18. data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
  19. data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
  20. data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
  21. data/lib/hanami/extensions/view/standard_helpers.rb +18 -0
  22. data/lib/hanami/extensions.rb +10 -3
  23. data/lib/hanami/helpers/assets_helper.rb +752 -0
  24. data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
  25. data/lib/hanami/helpers/form_helper/values.rb +75 -0
  26. data/lib/hanami/helpers/form_helper.rb +213 -0
  27. data/lib/hanami/middleware/assets.rb +21 -0
  28. data/lib/hanami/middleware/public_errors_app.rb +75 -0
  29. data/lib/hanami/middleware/render_errors.rb +90 -0
  30. data/lib/hanami/providers/assets.rb +44 -0
  31. data/lib/hanami/rake_tasks.rb +19 -18
  32. data/lib/hanami/settings.rb +1 -1
  33. data/lib/hanami/slice.rb +48 -2
  34. data/lib/hanami/slice_configurable.rb +3 -2
  35. data/lib/hanami/version.rb +1 -1
  36. data/lib/hanami/web/rack_logger.rb +1 -1
  37. data/lib/hanami.rb +3 -3
  38. data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
  39. data/spec/integration/action/view_rendering_spec.rb +0 -18
  40. data/spec/integration/assets/assets_spec.rb +101 -0
  41. data/spec/integration/assets/serve_static_assets_spec.rb +152 -0
  42. data/spec/integration/logging/exception_logging_spec.rb +115 -0
  43. data/spec/integration/logging/notifications_spec.rb +68 -0
  44. data/spec/integration/logging/request_logging_spec.rb +128 -0
  45. data/spec/integration/rack_app/middleware_spec.rb +22 -22
  46. data/spec/integration/rack_app/rack_app_spec.rb +3 -220
  47. data/spec/integration/rake_tasks_spec.rb +107 -0
  48. data/spec/integration/view/config/default_context_spec.rb +149 -0
  49. data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
  50. data/spec/integration/view/config/part_class_spec.rb +147 -0
  51. data/spec/integration/view/config/part_namespace_spec.rb +103 -0
  52. data/spec/integration/view/config/paths_spec.rb +119 -0
  53. data/spec/integration/view/config/scope_class_spec.rb +147 -0
  54. data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
  55. data/spec/integration/view/config/template_spec.rb +38 -0
  56. data/spec/integration/view/context/assets_spec.rb +3 -9
  57. data/spec/integration/view/context/request_spec.rb +3 -7
  58. data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
  59. data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
  60. data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
  61. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
  62. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
  63. data/spec/integration/view/slice_configuration_spec.rb +9 -9
  64. data/spec/integration/web/render_detailed_errors_spec.rb +107 -0
  65. data/spec/integration/web/render_errors_spec.rb +242 -0
  66. data/spec/spec_helper.rb +1 -1
  67. data/spec/support/app_integration.rb +46 -2
  68. data/spec/support/matchers.rb +32 -0
  69. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +24 -36
  70. data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +4 -3
  71. data/spec/unit/hanami/config/actions/default_values_spec.rb +3 -6
  72. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
  73. data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
  74. data/spec/unit/hanami/config/views_spec.rb +0 -18
  75. data/spec/unit/hanami/env_spec.rb +11 -25
  76. data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
  77. data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +109 -0
  78. data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +132 -0
  79. data/spec/unit/hanami/helpers/assets_helper/favicon_link_tag_spec.rb +91 -0
  80. data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +92 -0
  81. data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +143 -0
  82. data/spec/unit/hanami/helpers/assets_helper/stylesheet_link_tag_spec.rb +126 -0
  83. data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +132 -0
  84. data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
  85. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
  86. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
  87. data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
  88. data/spec/unit/hanami/version_spec.rb +1 -1
  89. data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
  90. metadata +95 -35
  91. data/lib/hanami/assets/app_config.rb +0 -61
  92. data/lib/hanami/assets/config.rb +0 -53
  93. data/spec/integration/action/view_integration_spec.rb +0 -165
  94. data/spec/integration/view/part_namespace_spec.rb +0 -96
  95. data/spec/integration/view/path_spec.rb +0 -56
  96. data/spec/integration/view/template_spec.rb +0 -68
  97. data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
  98. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
  99. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
  100. data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
  101. data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
  102. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
  103. data/spec/isolation/hanami/boot/success_spec.rb +0 -50
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+
5
+ # rubocop:disable Style/OpenStructUse
6
+
7
+ RSpec.describe "App view / Helpers / User-defined helpers / Scope helpers", :app_integration do
8
+ before do
9
+ with_directory(make_tmp_directory) do
10
+ write "config/app.rb", <<~RUBY
11
+ module TestApp
12
+ class App < Hanami::App
13
+ end
14
+ end
15
+ RUBY
16
+
17
+ write "app/view.rb", <<~RUBY
18
+ # auto_register: false
19
+
20
+ require "hanami/view"
21
+
22
+ module TestApp
23
+ class View < Hanami::View
24
+ config.layout = nil
25
+ end
26
+ end
27
+ RUBY
28
+
29
+ write "app/views/helpers.rb", <<~'RUBY'
30
+ # auto_register: false
31
+
32
+ module TestApp
33
+ module Views
34
+ module Helpers
35
+ def exclaim_from_app(str)
36
+ tag.h1("#{str}! (app helper)")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ RUBY
42
+
43
+ before_prepare if respond_to?(:before_prepare)
44
+ require "hanami/prepare"
45
+ end
46
+ end
47
+
48
+ describe "app view and parts" do
49
+ def before_prepare
50
+ write "app/views/posts/show.rb", <<~RUBY
51
+ module TestApp
52
+ module Views
53
+ module Posts
54
+ class Show < TestApp::View
55
+ expose :post
56
+ end
57
+ end
58
+ end
59
+ end
60
+ RUBY
61
+
62
+ write "app/views/parts/post.rb", <<~RUBY
63
+ module TestApp
64
+ module Views
65
+ module Parts
66
+ class Post < TestApp::Views::Part
67
+ def title
68
+ exclaim_from_app(value.title)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ RUBY
75
+
76
+ write "app/templates/posts/show.html.erb", <<~ERB
77
+ <%= post.title %>
78
+ ERB
79
+ end
80
+
81
+ it "makes user-defined helpers available in parts" do
82
+ post = OpenStruct.new(title: "Hello world")
83
+ output = TestApp::App["views.posts.show"].call(post: post).to_s.strip
84
+
85
+ expect(output).to eq "<h1>Hello world! (app helper)</h1>"
86
+ end
87
+ end
88
+
89
+ describe "slice view and parts" do
90
+ def before_prepare
91
+ write "slices/main/view.rb", <<~RUBY
92
+ module Main
93
+ class View < TestApp::View
94
+ # FIXME: base slice views should override paths from the base app view
95
+ config.paths = [File.join(File.expand_path(__dir__), "templates")]
96
+ end
97
+ end
98
+ RUBY
99
+
100
+ write "slices/main/views/helpers.rb", <<~'RUBY'
101
+ # auto_register: false
102
+
103
+ module Main
104
+ module Views
105
+ module Helpers
106
+ def exclaim_from_slice(str)
107
+ tag.h1("#{str}! (slice helper)")
108
+ end
109
+ end
110
+ end
111
+ end
112
+ RUBY
113
+
114
+ write "slices/main/views/posts/show.rb", <<~RUBY
115
+ module Main
116
+ module Views
117
+ module Posts
118
+ class Show < Main::View
119
+ expose :post
120
+ end
121
+ end
122
+ end
123
+ end
124
+ RUBY
125
+
126
+ write "slices/main/views/parts/post.rb", <<~RUBY
127
+ module Main
128
+ module Views
129
+ module Parts
130
+ class Post < Main::Views::Part
131
+ def title
132
+ exclaim_from_slice(value.title)
133
+ end
134
+
135
+ def title_from_app
136
+ exclaim_from_app(value.title)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ RUBY
143
+
144
+ write "slices/main/templates/posts/show.html.erb", <<~ERB
145
+ <%= post.title %>
146
+ <%= post.title_from_app %>
147
+ ERB
148
+ end
149
+
150
+ it "makes user-defined helpers (from app as well as slice) available in parts" do
151
+ post = OpenStruct.new(title: "Hello world")
152
+ output = Main::Slice["views.posts.show"].call(post: post).to_s
153
+
154
+ expect(output).to eq <<~HTML
155
+ <h1>Hello world! (slice helper)</h1>
156
+ <h1>Hello world! (app helper)</h1>
157
+ HTML
158
+ end
159
+ end
160
+ end
161
+
162
+ # rubocop:enable Style/OpenStructUse
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "App view / Helpers / User-defined helpers / Scope helpers", :app_integration do
4
+ before do
5
+ with_directory(make_tmp_directory) do
6
+ write "config/app.rb", <<~RUBY
7
+ module TestApp
8
+ class App < Hanami::App
9
+ end
10
+ end
11
+ RUBY
12
+
13
+ write "app/view.rb", <<~RUBY
14
+ # auto_register: false
15
+
16
+ require "hanami/view"
17
+
18
+ module TestApp
19
+ class View < Hanami::View
20
+ config.layout = nil
21
+ end
22
+ end
23
+ RUBY
24
+
25
+ write "app/views/helpers.rb", <<~'RUBY'
26
+ # auto_register: false
27
+
28
+ module TestApp
29
+ module Views
30
+ module Helpers
31
+ def exclaim_from_app(str)
32
+ tag.h1("#{str}! (app helper)")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ RUBY
38
+
39
+ before_prepare if respond_to?(:before_prepare)
40
+ require "hanami/prepare"
41
+ end
42
+ end
43
+
44
+ describe "app view" do
45
+ def before_prepare
46
+ write "app/views/posts/show.rb", <<~RUBY
47
+ module TestApp
48
+ module Views
49
+ module Posts
50
+ class Show < TestApp::View
51
+ end
52
+ end
53
+ end
54
+ end
55
+ RUBY
56
+
57
+ write "app/templates/posts/show.html.erb", <<~ERB
58
+ <%= exclaim_from_app("Hello world") %>
59
+ ERB
60
+ end
61
+
62
+ it "makes user-defined helpers available in templates" do
63
+ output = TestApp::App["views.posts.show"].call.to_s.strip
64
+ expect(output).to eq "<h1>Hello world! (app helper)</h1>"
65
+ end
66
+ end
67
+
68
+ describe "slice view" do
69
+ def before_prepare
70
+ write "slices/main/view.rb", <<~RUBY
71
+ module Main
72
+ class View < TestApp::View
73
+ # FIXME: base slice views should override paths from the base app view
74
+ config.paths = [File.join(File.expand_path(__dir__), "templates")]
75
+ end
76
+ end
77
+ RUBY
78
+
79
+ write "slices/main/views/helpers.rb", <<~'RUBY'
80
+ # auto_register: false
81
+
82
+ module Main
83
+ module Views
84
+ module Helpers
85
+ def exclaim_from_slice(str)
86
+ tag.h1("#{str}! (slice helper)")
87
+ end
88
+ end
89
+ end
90
+ end
91
+ RUBY
92
+
93
+ write "slices/main/views/posts/show.rb", <<~RUBY
94
+ module Main
95
+ module Views
96
+ module Posts
97
+ class Show < Main::View
98
+ end
99
+ end
100
+ end
101
+ end
102
+ RUBY
103
+
104
+ write "slices/main/templates/posts/show.html.erb", <<~ERB
105
+ <%= exclaim_from_slice("Hello world") %>
106
+ <%= exclaim_from_app("Hello world") %>
107
+ ERB
108
+ end
109
+
110
+ it "makes user-defined helpers (from app as well as slice) available in templates" do
111
+ output = Main::Slice["views.posts.show"].call.to_s
112
+
113
+ expect(output).to eq <<~HTML
114
+ <h1>Hello world! (slice helper)</h1>
115
+ <h1>Hello world! (app helper)</h1>
116
+ HTML
117
+ end
118
+ end
119
+ end
@@ -3,7 +3,7 @@
3
3
  RSpec.describe "App view / Slice configuration", :app_integration do
4
4
  before do
5
5
  with_directory(@dir = make_tmp_directory) do
6
- write "config/app.rb", <<~'RUBY'
6
+ write "config/app.rb", <<~RUBY
7
7
  require "hanami"
8
8
 
9
9
  module TestApp
@@ -12,7 +12,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
12
12
  end
13
13
  RUBY
14
14
 
15
- write "app/view.rb", <<~'RUBY'
15
+ write "app/view.rb", <<~RUBY
16
16
  require "hanami/view"
17
17
 
18
18
  module TestApp
@@ -60,7 +60,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
60
60
  describe "subclass in app" do
61
61
  before do
62
62
  with_directory(@dir) do
63
- write "app/views/articles/index.rb", <<~'RUBY'
63
+ write "app/views/articles/index.rb", <<~RUBY
64
64
  module TestApp
65
65
  module Views
66
66
  module Articles
@@ -100,7 +100,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
100
100
  describe "subclass in slice" do
101
101
  before do
102
102
  with_directory(@dir) do
103
- write "slices/admin/views/articles/index.rb", <<~'RUBY'
103
+ write "slices/admin/views/articles/index.rb", <<~RUBY
104
104
  module Admin
105
105
  module Views
106
106
  module Articles
@@ -141,7 +141,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
141
141
  describe "inheriting from a slice-level base class, in turn inheriting from an app-level base class" do
142
142
  before do
143
143
  with_directory(@dir) do
144
- write "slices/admin/view.rb", <<~'RUBY'
144
+ write "slices/admin/view.rb", <<~RUBY
145
145
  module Admin
146
146
  class View < TestApp::View
147
147
  end
@@ -177,7 +177,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
177
177
  context "slice views config present" do
178
178
  before do
179
179
  with_directory(@dir) do
180
- write "config/slices/admin.rb", <<~'RUBY'
180
+ write "config/slices/admin.rb", <<~RUBY
181
181
  module Admin
182
182
  class Slice < Hanami::Slice
183
183
  config.views.layout = "slice_layout"
@@ -215,7 +215,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
215
215
  describe "subclass in slice" do
216
216
  before do
217
217
  with_directory(@dir) do
218
- write "slices/admin/views/articles/index.rb", <<~'RUBY'
218
+ write "slices/admin/views/articles/index.rb", <<~RUBY
219
219
  module Admin
220
220
  module Views
221
221
  module Articles
@@ -245,7 +245,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
245
245
 
246
246
  it "applies views config from the slice" do
247
247
  with_directory(@dir) do
248
- write "config/slices/admin.rb", <<~'RUBY'
248
+ write "config/slices/admin.rb", <<~RUBY
249
249
  module Admin
250
250
  class Slice < Hanami::Slice
251
251
  config.views.layout = "slice_layout"
@@ -269,7 +269,7 @@ RSpec.describe "App view / Slice configuration", :app_integration do
269
269
 
270
270
  it "prefers config from the slice base class over views config from the slice" do
271
271
  with_directory(@dir) do
272
- write "config/slices/admin.rb", <<~'RUBY'
272
+ write "config/slices/admin.rb", <<~RUBY
273
273
  module Admin
274
274
  class Slice < Hanami::Slice
275
275
  config.views.layout = "slice_layout"
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "rack/test"
5
+
6
+ RSpec.describe "Web / Rendering detailed errors", :app_integration do
7
+ include Rack::Test::Methods
8
+
9
+ let(:app) { Hanami.app }
10
+
11
+ before do
12
+ with_directory(@dir = make_tmp_directory) do
13
+ write "config/app.rb", <<~RUBY
14
+ require "hanami"
15
+
16
+ module TestApp
17
+ class App < Hanami::App
18
+ config.logger.stream = File.new("/dev/null", "w")
19
+ config.render_detailed_errors = true
20
+ end
21
+ end
22
+ RUBY
23
+
24
+ write "config/routes.rb", <<~RUBY
25
+ module TestApp
26
+ class Routes < Hanami::Routes
27
+ get "error", to: "error"
28
+ end
29
+ end
30
+ RUBY
31
+
32
+ write "app/actions/error.rb", <<~RUBY
33
+ module TestApp
34
+ module Actions
35
+ class Error < Hanami::Action
36
+ def handle(*)
37
+ raise "oops"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ RUBY
43
+
44
+ before_prepare if respond_to?(:before_prepare)
45
+ require "hanami/prepare"
46
+ end
47
+ end
48
+
49
+ describe "HTML request" do
50
+ it "renders a detailed HTML error page" do
51
+ get "/error", {}, "HTTP_ACCEPT" => "text/html"
52
+
53
+ expect(last_response.status).to eq 500
54
+
55
+ html = Capybara.string(last_response.body)
56
+ expect(html).to have_selector("header", text: "RuntimeError at /error")
57
+ expect(html).to have_selector("ul.frames li.application", text: "app/actions/error.rb")
58
+ end
59
+
60
+ it "renders a detailed HTML error page and returns a 404 status for a not found error" do
61
+ get "/__not_found__", {}, "HTTP_ACCEPT" => "text/html"
62
+
63
+ expect(last_response.status).to eq 404
64
+
65
+ html = Capybara.string(last_response.body)
66
+ expect(html).to have_selector("header", text: "Hanami::Router::NotFoundError at /__not_found__")
67
+ end
68
+ end
69
+
70
+ describe "Other request types" do
71
+ it "renders a detailed error page in text" do
72
+ get "/error", {}, "HTTP_ACCEPT" => "application/json"
73
+
74
+ expect(last_response.status).to eq 500
75
+
76
+ expect(last_response.body).to include "RuntimeError at /error"
77
+ expect(last_response.body).to match %r{App backtrace.+app/actions/error.rb}m
78
+ end
79
+
80
+ it "renders a detailed error page in text and returns a 404 status for a not found error" do
81
+ get "/__not_found__", {}, "HTTP_ACCEPT" => "text/html"
82
+
83
+ expect(last_response.status).to eq 404
84
+
85
+ expect(last_response.body).to include "Hanami::Router::NotFoundError at /__not_found__"
86
+ end
87
+ end
88
+
89
+ describe "render_detailed_errors config disabled" do
90
+ def before_prepare
91
+ write "config/app.rb", <<~RUBY
92
+ require "hanami"
93
+
94
+ module TestApp
95
+ class App < Hanami::App
96
+ config.logger.stream = File.new("/dev/null", "w")
97
+ config.render_detailed_errors = false
98
+ end
99
+ end
100
+ RUBY
101
+ end
102
+
103
+ it "raises errors from within the app" do
104
+ expect { get "/error" }.to raise_error(RuntimeError, "oops")
105
+ end
106
+ end
107
+ end