hanami 2.1.0.rc2 → 2.1.0

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.
@@ -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