hanami 2.1.0.rc2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@
7
7
  <title>Hanami</title>
8
8
  <style>
9
9
  :root {
10
- --max-width: 1024px;
10
+ --max-width: 768px;
11
11
  --foreground-rgb: 0, 0, 0;
12
12
  --background-rgb: 255, 255, 255;
13
13
  --card-border-rgb: 200, 200, 200;
@@ -84,7 +84,7 @@
84
84
 
85
85
  .grid {
86
86
  display: grid;
87
- grid-template-columns: repeat(4, 1fr);
87
+ grid-template-columns: repeat(3, 1fr);
88
88
  column-gap: 20px;
89
89
  max-width: 100%;
90
90
  margin-bottom: 8vh;
@@ -182,10 +182,6 @@
182
182
  <h2>Guides</h2>
183
183
  <p>Get started with the Hanami guides</p>
184
184
  </a>
185
- <a href="https://docs.hanamirb.org/" class="card">
186
- <h2>API docs</h2>
187
- <p>Learn more through the API docs</p>
188
- </a>
189
185
  <a href="http://github.com/hanami" class="card">
190
186
  <h2>Code</h2>
191
187
  <p>Contribute to the source code</p>
@@ -18,39 +18,17 @@ RSpec.describe "Assets", :app_integration do
18
18
  end
19
19
  RUBY
20
20
 
21
- write "config/assets.mjs", <<~JS
21
+ write "config/assets.js", <<~JS
22
22
  import * as assets from "hanami-assets";
23
23
  await assets.run();
24
24
  JS
25
25
 
26
26
  write "package.json", <<~JSON
27
27
  {
28
- "scripts": {
29
- "assets": "node config/assets.mjs"
30
- }
28
+ "type": "module"
31
29
  }
32
30
  JSON
33
31
 
34
- write "config/routes.rb", <<~RUBY
35
- module TestApp
36
- class Routes < Hanami::Routes
37
- get "posts/:id/edit", to: "posts.edit"
38
- put "posts/:id", to: "posts.update"
39
- end
40
- end
41
- RUBY
42
-
43
- write "app/action.rb", <<~RUBY
44
- # auto_register: false
45
-
46
- require "hanami/action"
47
-
48
- module TestApp
49
- class Action < Hanami::Action
50
- end
51
- end
52
- RUBY
53
-
54
32
  write "app/view.rb", <<~RUBY
55
33
  # auto_register: false
56
34
 
@@ -109,4 +87,69 @@ RSpec.describe "Assets", :app_integration do
109
87
  expect(assets["app.css"].to_s).to match(%r{/assets/app-[A-Z0-9]{8}.css})
110
88
  expect(assets["app.js"].to_s).to match(%r{/assets/app-[A-Z0-9]{8}.js})
111
89
  end
90
+
91
+ describe "slice with assets" do
92
+ def before_prepare
93
+ write "slices/main/view.rb", <<~RUBY
94
+ # auto_register: false
95
+
96
+ module Main
97
+ class View < TestApp::View
98
+ end
99
+ end
100
+ RUBY
101
+
102
+ write "slices/main/views/posts/show.rb", <<~RUBY
103
+ module Main
104
+ module Views
105
+ module Posts
106
+ class Show < Main::View
107
+ end
108
+ end
109
+ end
110
+ end
111
+ RUBY
112
+
113
+ write "slices/main/templates/posts/show.html.erb", <<~ERB
114
+ <%= stylesheet_tag("app") %>
115
+ <%= javascript_tag("app") %>
116
+ ERB
117
+
118
+ write "slices/main/assets/js/app.ts", <<~TS
119
+ import "../css/app.css";
120
+
121
+ console.log("Hello from main slice index.ts");
122
+ TS
123
+
124
+ write "slices/main/assets/css/app.css", <<~CSS
125
+ .btn {
126
+ background: #f00;
127
+ }
128
+ CSS
129
+ end
130
+
131
+ specify "the slice's assets are available in its own and distinct `assets` component" do
132
+ compile_assets!
133
+
134
+ output = Main::Slice["views.posts.show"].call.to_s
135
+
136
+ expect(output).to match(%r{<link href="/assets/_main/app-[A-Z0-9]{8}.css" type="text/css" rel="stylesheet">})
137
+ expect(output).to match(%r{<script src="/assets/_main/app-[A-Z0-9]{8}.js" type="text/javascript"></script>})
138
+
139
+ assets = Main::Slice["assets"]
140
+
141
+ expect(assets["app.css"].to_s).to match(%r{/assets/_main/app-[A-Z0-9]{8}.css})
142
+ expect(assets["app.js"].to_s).to match(%r{/assets/_main/app-[A-Z0-9]{8}.js})
143
+ end
144
+ end
145
+
146
+ describe "slice without assets" do
147
+ def before_prepare
148
+ write "slices/main/.keep", ""
149
+ end
150
+
151
+ it "does not have an assets component" do
152
+ expect(Main::Slice.key?("assets")).to be false
153
+ end
154
+ end
112
155
  end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Cross-slice assets via helpers", :app_integration do
7
+ include Rack::Test::Methods
8
+ let(:app) { Hanami.app }
9
+ let(:root) { make_tmp_directory }
10
+
11
+ before do
12
+ with_directory(root) do
13
+ write "config/app.rb", <<~RUBY
14
+ module TestApp
15
+ class App < Hanami::App
16
+ config.logger.stream = StringIO.new
17
+ end
18
+ end
19
+ RUBY
20
+
21
+ write "config/slices/admin.rb", <<~RUBY
22
+ module Admin
23
+ class Slice < Hanami::Slice
24
+ # TODO: we should update `import` to make importing from the app nicer
25
+ # TODO: this test failed when I tried doing `as: "app"` (string instead of symbol); fix this in dry-system
26
+ import keys: ["assets"], from: Hanami.app.container, as: :app
27
+ end
28
+ end
29
+ RUBY
30
+
31
+ write "config/assets.js", <<~JS
32
+ import * as assets from "hanami-assets";
33
+ await assets.run();
34
+ JS
35
+
36
+ write "package.json", <<~JSON
37
+ {
38
+ "type": "module"
39
+ }
40
+ JSON
41
+
42
+ write "app/view.rb", <<~RUBY
43
+ # auto_register: false
44
+
45
+ require "hanami/view"
46
+
47
+ module TestApp
48
+ class View < Hanami::View
49
+ config.layout = nil
50
+ end
51
+ end
52
+ RUBY
53
+
54
+ write "app/assets/js/app.ts", <<~TS
55
+ import "../css/app.css";
56
+
57
+ console.log("Hello from index.ts");
58
+ TS
59
+
60
+ write "app/assets/css/app.css", <<~CSS
61
+ .btn {
62
+ background: #f00;
63
+ }
64
+ CSS
65
+
66
+ write "slices/admin/assets/js/app.ts", <<~TS
67
+ import "../css/app.css";
68
+
69
+ console.log("Hello from admin's index.ts");
70
+ TS
71
+
72
+ write "slices/admin/assets/css/app.css", <<~CSS
73
+ .btn {
74
+ background: #f00;
75
+ }
76
+ CSS
77
+
78
+ write "slices/admin/view.rb", <<~RUBY
79
+ # auto_register: false
80
+
81
+ module Admin
82
+ class View < TestApp::View
83
+ end
84
+ end
85
+ RUBY
86
+
87
+ write "slices/admin/views/posts/show.rb", <<~RUBY
88
+ module Admin
89
+ module Views
90
+ module Posts
91
+ class Show < Admin::View
92
+ end
93
+ end
94
+ end
95
+ end
96
+ RUBY
97
+
98
+ write "slices/admin/views/context.rb", <<~RUBY
99
+ # auto_register: false
100
+
101
+ require "hanami/view"
102
+
103
+ module Admin
104
+ module Views
105
+ class Context < Hanami::View::Context
106
+ include Deps[app_assets: "app.assets"]
107
+ end
108
+ end
109
+ end
110
+ RUBY
111
+
112
+ write "slices/admin/templates/posts/show.html.erb", <<~ERB
113
+ <%= stylesheet_tag(app_assets["app.css"]) %>
114
+ <%= javascript_tag(app_assets["app.js"]) %>
115
+ ERB
116
+
117
+ before_prepare if respond_to?(:before_prepare)
118
+ require "hanami/prepare"
119
+ end
120
+ end
121
+
122
+ specify "assets are available in helpers and in `assets` component" do
123
+ compile_assets!
124
+
125
+ output = Admin::Slice["views.posts.show"].call.to_s
126
+
127
+ expect(output).to match(%r{<link href="/assets/app-[A-Z0-9]{8}.css" type="text/css" rel="stylesheet">})
128
+ expect(output).to match(%r{<script src="/assets/app-[A-Z0-9]{8}.js" type="text/javascript"></script>})
129
+ end
130
+ end
@@ -147,6 +147,72 @@ RSpec.describe "Hanami web app", :app_integration do
147
147
  expect(last_response.body).to eql("one.two")
148
148
  end
149
149
 
150
+ specify "Setting a middleware that requires keyword arguments" do
151
+ write "config/app.rb", <<~RUBY
152
+ require "hanami"
153
+
154
+ module TestApp
155
+ class TestMiddleware
156
+ def initialize(app, key:, value:)
157
+ @app = app
158
+ @key = key
159
+ @value = value
160
+ end
161
+
162
+ def call(env)
163
+ env[@key] = @value
164
+ @app.call(env)
165
+ end
166
+ end
167
+
168
+ class App < Hanami::App
169
+ config.logger.stream = StringIO.new
170
+
171
+ # Test middleware with keywords inside config
172
+ config.middleware.use(TestApp::TestMiddleware, key: "from_config", value: "config")
173
+ end
174
+ end
175
+ RUBY
176
+
177
+ write "config/routes.rb", <<~RUBY
178
+ require "hanami/router"
179
+
180
+ module TestApp
181
+ class Routes < Hanami::Routes
182
+ slice :main, at: "/" do
183
+ # Also test middleware with keywords inside routes
184
+ use TestApp::TestMiddleware, key: "from_routes", value: "routes"
185
+
186
+ root to: "home.index"
187
+ end
188
+ end
189
+ end
190
+ RUBY
191
+
192
+ write "slices/main/actions/home/index.rb", <<~RUBY
193
+ require "hanami/action"
194
+
195
+ module Main
196
+ module Actions
197
+ module Home
198
+ class Index < Hanami::Action
199
+ def handle(request, response)
200
+ response.body = [request.env["from_config"], request.env["from_routes"]].join(", ")
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ RUBY
207
+
208
+ require "hanami/boot"
209
+
210
+ get "/"
211
+
212
+ expect(last_response).to be_successful
213
+ expect(last_response.body).to eq "config, routes"
214
+ end
215
+
150
216
  specify "Setting a middleware that requires a block" do
151
217
  write "config/app.rb", <<~RUBY
152
218
  require "hanami"
@@ -3,51 +3,77 @@
3
3
  require "hanami"
4
4
 
5
5
  RSpec.describe "App view / Context / Assets", :app_integration do
6
+ subject(:context) { context_class.new }
7
+ let(:context_class) { TestApp::Views::Context }
8
+
6
9
  before do
7
- module TestApp
8
- class App < Hanami::App
9
- end
10
- end
10
+ with_directory(make_tmp_directory) do
11
+ write "config/app.rb", <<~RUBY
12
+ module TestApp
13
+ class App < Hanami::App
14
+ config.logger.stream = File::NULL
15
+ end
16
+ end
17
+ RUBY
11
18
 
12
- Hanami.prepare
19
+ write "app/views/context.rb", <<~RUBY
20
+ # auto_register: false
13
21
 
14
- module TestApp
15
- module Views
16
- class Context < Hanami::View::Context
22
+ require "hanami/view/context"
23
+
24
+ module TestApp
25
+ module Views
26
+ class Context < Hanami::View::Context
27
+ end
28
+ end
17
29
  end
18
- end
30
+ RUBY
31
+
32
+ before_prepare if respond_to?(:before_prepare)
33
+ require "hanami/prepare"
19
34
  end
20
35
  end
21
36
 
22
- let(:context_class) { TestApp::Views::Context }
23
- subject(:context) { context_class.new }
37
+ context "assets present and hanami-assets bundled" do
38
+ def before_prepare
39
+ write "app/assets/.keep", ""
40
+ end
24
41
 
25
- describe "#assets" do
26
- context "without assets provider" do
27
- xit "raises error" do
28
- allow(Hanami).to receive(:bundled?).with("hanami-assets").and_return(false)
42
+ it "is the app assets by default" do
43
+ expect(context.assets).to be TestApp::App[:assets]
44
+ end
45
+ end
29
46
 
30
- expect { context.assets }
31
- .to raise_error(Hanami::ComponentLoadError, /hanami-assets/)
32
- end
47
+ context "assets not present" do
48
+ it "raises error" do
49
+ expect { context.assets }.to raise_error(Hanami::ComponentLoadError, /assets directory\?/)
33
50
  end
51
+ end
34
52
 
35
- context "with assets provider" do
36
- it "is the app assets by default" do
37
- expect(context.assets).to be TestApp::App[:assets]
38
- end
53
+ context "hanami-assets not bundled" do
54
+ def before_prepare
55
+ # These must be here instead of an ordinary before hook because the Hanami.bundled? check for
56
+ # assets is done as part of requiring "hanami/prepare" above.
57
+ allow(Hanami).to receive(:bundled?).and_call_original
58
+ allow(Hanami).to receive(:bundled?).with("hanami-assets").and_return(false)
39
59
 
40
- context "injected assets" do
41
- subject(:context) {
42
- context_class.new(assets: assets)
43
- }
60
+ write "app/assets/.keep", ""
61
+ end
44
62
 
45
- let(:assets) { double(:assets) }
63
+ it "raises error" do
64
+ expect { context.assets }.to raise_error(Hanami::ComponentLoadError, /hanami-assets gem/)
65
+ end
66
+ end
46
67
 
47
- it "is the injected assets" do
48
- expect(context.assets).to be assets
49
- end
50
- end
68
+ context "injected assets" do
69
+ subject(:context) {
70
+ context_class.new(assets: assets)
71
+ }
72
+
73
+ let(:assets) { double(:assets) }
74
+
75
+ it "is the injected assets" do
76
+ expect(context.assets).to be assets
51
77
  end
52
78
  end
53
79
  end
@@ -29,12 +29,16 @@ module RSpec
29
29
 
30
30
  private
31
31
 
32
+ # TODO: make slice-aware
32
33
  def stub_assets(*assets)
33
34
  manifest_hash = assets.each_with_object({}) { |source_path, hsh|
34
35
  hsh[source_path] = {url: File.join("/assets", source_path)}
35
36
  }
36
37
 
37
- write "public/assets.json", JSON.generate(manifest_hash)
38
+ write "public/assets/assets.json", JSON.generate(manifest_hash)
39
+
40
+ # An assets dir is required to load the assets provider
41
+ write "app/assets/.keep", ""
38
42
  end
39
43
 
40
44
  def compile_assets!
@@ -44,7 +48,11 @@ module RSpec
44
48
  require "hanami/cli/command"
45
49
  require "hanami/cli/commands/app/command"
46
50
  require "hanami/cli/commands/app/assets/compile"
47
- assets_compile = Hanami::CLI::Commands::App::Assets::Compile.new(config: Hanami.app.config.assets)
51
+ assets_compile = Hanami::CLI::Commands::App::Assets::Compile.new(
52
+ config: Hanami.app.config.assets,
53
+ out: File.new(File::NULL, "w"),
54
+ err: File.new(File::NULL, "w"),
55
+ )
48
56
 
49
57
  with_directory(Hanami.app.root) { assets_compile.call }
50
58
  end
@@ -62,16 +70,14 @@ module RSpec
62
70
  root = Hanami.app.root
63
71
 
64
72
  with_directory(root) do
65
- write("config/assets.mjs", <<~JS) unless root.join("config", "assets.mjs").exist?
73
+ write("config/assets.js", <<~JS) unless root.join("config", "assets.js").exist?
66
74
  import * as assets from "hanami-assets";
67
75
  await assets.run();
68
76
  JS
69
77
 
70
78
  write("package.json", <<~JSON) unless root.join("package.json").exist?
71
79
  {
72
- "scripts": {
73
- "assets": "node config/assets.mjs"
74
- }
80
+ "type": "module"
75
81
  }
76
82
  JSON
77
83
  end
@@ -106,4 +106,15 @@ RSpec.describe Hanami::Helpers::AssetsHelper, "#asset_url", :app_integration do
106
106
  end
107
107
  end
108
108
  end
109
+
110
+ context "given an asset object" do
111
+ it "returns the URL for the asset" do
112
+ asset = Hanami::Assets::Asset.new(
113
+ path: "/foo/bar.js",
114
+ base_url: Hanami.app.config.assets.base_url
115
+ )
116
+
117
+ expect(asset_url(asset)).to eq "/foo/bar.js"
118
+ end
119
+ end
109
120
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  RSpec.describe "Hanami::VERSION" do
4
4
  it "returns current version" do
5
- expect(Hanami::VERSION).to eq("2.1.0.rc2")
5
+ expect(Hanami::VERSION).to eq("2.1.0")
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.rc2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-08 00:00:00.000000000 Z
11
+ date: 2024-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -162,28 +162,28 @@ dependencies:
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: 2.1.rc
165
+ version: '2.1'
166
166
  type: :runtime
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
- version: 2.1.rc
172
+ version: '2.1'
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: hanami-utils
175
175
  requirement: !ruby/object:Gem::Requirement
176
176
  requirements:
177
177
  - - "~>"
178
178
  - !ruby/object:Gem::Version
179
- version: 2.1.rc
179
+ version: '2.1'
180
180
  type: :runtime
181
181
  prerelease: false
182
182
  version_requirements: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 2.1.rc
186
+ version: '2.1'
187
187
  - !ruby/object:Gem::Dependency
188
188
  name: zeitwerk
189
189
  requirement: !ruby/object:Gem::Requirement
@@ -325,6 +325,7 @@ files:
325
325
  - spec/integration/action/view_rendering/view_context_spec.rb
326
326
  - spec/integration/action/view_rendering_spec.rb
327
327
  - spec/integration/assets/assets_spec.rb
328
+ - spec/integration/assets/cross_slice_assets_helpers_spec.rb
328
329
  - spec/integration/assets/serve_static_assets_spec.rb
329
330
  - spec/integration/code_loading/loading_from_app_spec.rb
330
331
  - spec/integration/code_loading/loading_from_lib_spec.rb
@@ -446,11 +447,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
446
447
  version: '3.0'
447
448
  required_rubygems_version: !ruby/object:Gem::Requirement
448
449
  requirements:
449
- - - ">"
450
+ - - ">="
450
451
  - !ruby/object:Gem::Version
451
- version: 1.3.1
452
+ version: '0'
452
453
  requirements: []
453
- rubygems_version: 3.4.21
454
+ rubygems_version: 3.5.6
454
455
  signing_key:
455
456
  specification_version: 4
456
457
  summary: The web, with simplicity
@@ -466,6 +467,7 @@ test_files:
466
467
  - spec/integration/action/view_rendering/view_context_spec.rb
467
468
  - spec/integration/action/view_rendering_spec.rb
468
469
  - spec/integration/assets/assets_spec.rb
470
+ - spec/integration/assets/cross_slice_assets_helpers_spec.rb
469
471
  - spec/integration/assets/serve_static_assets_spec.rb
470
472
  - spec/integration/code_loading/loading_from_app_spec.rb
471
473
  - spec/integration/code_loading/loading_from_lib_spec.rb