Skip to content

Commit c97d4d1

Browse files
committed
fix up page finder
1 parent c0a7f60 commit c97d4d1

2 files changed

Lines changed: 58 additions & 44 deletions

File tree

app/services/alchemy/wildcard_url_matcher.rb renamed to app/services/alchemy/page_finder.rb

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
11
# frozen_string_literal: true
22

33
module Alchemy
4-
class WildcardUrlMatcher
5-
attr_reader :page, :params
4+
class PageFinder
5+
attr_reader :parent_page, :params
66

7-
def initialize(parent_page = Current.language.root_page)
7+
def initialize(parent_page = Current.language.root_page, params: ActionController::Parameters.new)
88
@parent_page = parent_page
9-
@params = ActionController::Parameters.new
10-
@page = nil
9+
@params = params
1110
end
1211

1312
def call(urlname)
14-
@page = find_page(urlname)
15-
self
13+
return if parent_page.nil? || urlname.blank?
14+
15+
find_by_urlname(urlname) || find_by_wildcard(urlname)
1616
end
1717

1818
private
1919

20+
# Finds a page by exact urlname match within the current language.
21+
#
22+
# @return [Alchemy::Page]
23+
def find_by_urlname(urlname)
24+
parent_page.children.contentpages.find_by(urlname: urlname)
25+
end
26+
2027
# Walks the page tree level by level, matching URL segments against page slugs or wildcard URLs.
2128
#
22-
# @return [Alchemy::Page, nil] the matched page or nil if no match
23-
def find_page(urlname)
24-
return if urlname.blank? || @parent_page.nil? || wildcard_layout_names.empty?
29+
# @return [Alchemy::Page] the matched page or nil if no match
30+
def find_by_wildcard(urlname)
31+
return if wildcard_layout_names.empty?
2532

2633
segments = urlname.split("/")
27-
current_parent = @parent_page
34+
current_parent = parent_page
2835
path_prefix = nil
2936
page = nil
3037

spec/services/alchemy/wildcard_url_matcher_spec.rb renamed to spec/services/alchemy/page_finder_spec.rb

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require "rails_helper"
44

55
module Alchemy
6-
RSpec.describe WildcardUrlMatcher do
6+
RSpec.describe PageFinder do
77
let(:language) { create(:alchemy_language) }
88
let!(:language_root) do
99
create(:alchemy_page, :language_root, language: language)
@@ -48,70 +48,77 @@ def create_page(name:, layout: "standard", parent: language_root)
4848
let!(:users_page) { create_page(name: "Users") }
4949
let!(:user_profile_page) { create_page(name: "User Profile", layout: "user_profile", parent: users_page) }
5050

51+
let(:params) { ActionController::Parameters.new }
52+
5153
it "defaults to Current.language.root_page" do
52-
matcher = described_class.new.call("products/123")
53-
expect(matcher.page).to eq(product_detail_page)
54-
expect(matcher.params[:id]).to eq("123")
54+
page = described_class.new(params: params).call("products/123")
55+
expect(page).to eq(product_detail_page)
56+
expect(params[:id]).to eq("123")
57+
end
58+
59+
it "finds a page by exact urlname" do
60+
page = described_class.new(params: params).call("products")
61+
expect(page).to eq(products_page)
5562
end
5663

5764
it "returns nil when the parent page is nil" do
58-
expect(described_class.new(nil).call("products/123").page).to be_nil
65+
expect(described_class.new(nil, params: params).call("products/123")).to be_nil
5966
end
6067

6168
it "returns nil for a blank path" do
62-
expect(described_class.new(language_root).call("").page).to be_nil
69+
expect(described_class.new(language_root, params: params).call("")).to be_nil
6370
end
6471

6572
it "returns nil for a path with no matching prefix" do
66-
expect(described_class.new(language_root).call("other/123").page).to be_nil
73+
expect(described_class.new(language_root, params: params).call("other/123")).to be_nil
6774
end
6875

6976
context "single-segment pattern with integer constraint" do
7077
it "matches /products/123" do
71-
matcher = described_class.new(language_root).call("products/123")
72-
expect(matcher.page).to eq(product_detail_page)
73-
expect(matcher.params[:id]).to eq("123")
78+
page = described_class.new(language_root, params: params).call("products/123")
79+
expect(page).to eq(product_detail_page)
80+
expect(params[:id]).to eq("123")
7481
end
7582

7683
it "rejects non-integer and falls through to unconstrained sibling" do
77-
matcher = described_class.new(language_root).call("products/some-slug")
78-
expect(matcher.page).to eq(product_by_slug_page)
79-
expect(matcher.params[:slug]).to eq("some-slug")
84+
page = described_class.new(language_root, params: params).call("products/some-slug")
85+
expect(page).to eq(product_by_slug_page)
86+
expect(params[:slug]).to eq("some-slug")
8087
end
8188
end
8289

8390
context "multi-segment pattern" do
8491
it "matches /blog/2024/my-post and extracts both params" do
85-
matcher = described_class.new(language_root).call("blog/2024/my-post")
86-
expect(matcher.page).to eq(blog_post_page)
87-
expect(matcher.params[:year]).to eq("2024")
88-
expect(matcher.params[:slug]).to eq("my-post")
92+
page = described_class.new(language_root, params: params).call("blog/2024/my-post")
93+
expect(page).to eq(blog_post_page)
94+
expect(params[:year]).to eq("2024")
95+
expect(params[:slug]).to eq("my-post")
8996
end
9097

9198
it "does not match with wrong segment count" do
92-
expect(described_class.new(language_root).call("blog/2024").page).to be_nil
99+
expect(described_class.new(language_root, params: params).call("blog/2024")).to be_nil
93100
end
94101
end
95102

96103
context "pattern with static segments" do
97104
let(:uuid) { "550e8400-e29b-41d4-a716-446655440000" }
98105

99106
it "matches /users/:uuid/profile" do
100-
matcher = described_class.new(language_root).call("users/#{uuid}/profile")
101-
expect(matcher.page).to eq(user_profile_page)
102-
expect(matcher.params[:uuid]).to eq(uuid)
107+
page = described_class.new(language_root, params: params).call("users/#{uuid}/profile")
108+
expect(page).to eq(user_profile_page)
109+
expect(params[:uuid]).to eq(uuid)
103110
end
104111

105112
it "does not match without the trailing static segment" do
106-
expect(described_class.new(language_root).call("users/#{uuid}").page).to be_nil
113+
expect(described_class.new(language_root, params: params).call("users/#{uuid}")).to be_nil
107114
end
108115
end
109116

110117
context "hierarchical patterns (grandchild under pattern page)" do
111118
it "matches /products/42/comments through the pattern parent" do
112-
matcher = described_class.new(language_root).call("products/42/comments")
113-
expect(matcher.page).to eq(comments_page)
114-
expect(matcher.params[:id]).to eq("42")
119+
page = described_class.new(language_root, params: params).call("products/42/comments")
120+
expect(page).to eq(comments_page)
121+
expect(params[:id]).to eq("42")
115122
end
116123
end
117124

@@ -122,10 +129,10 @@ def create_page(name:, layout: "standard", parent: language_root)
122129
let!(:child_of_single) { create_page(name: "My Post", parent: single_segment_page) }
123130

124131
it "matches the first pattern sibling even when the other could also match via its child" do
125-
matcher = described_class.new(language_root).call("shared/2024/my-post")
126-
expect(matcher.page).to eq(multi_segment_page)
127-
expect(matcher.params[:year]).to eq("2024")
128-
expect(matcher.params[:slug]).to eq("my-post")
132+
page = described_class.new(language_root, params: params).call("shared/2024/my-post")
133+
expect(page).to eq(multi_segment_page)
134+
expect(params[:year]).to eq("2024")
135+
expect(params[:slug]).to eq("my-post")
129136
end
130137
end
131138

@@ -134,13 +141,13 @@ def create_page(name:, layout: "standard", parent: language_root)
134141
let!(:sku_page) { create_page(name: "SKU Lookup", layout: "product_by_sku", parent: warehouse_page) }
135142

136143
it "matches when the value satisfies the regex" do
137-
matcher = described_class.new(language_root).call("warehouse/AB-1234")
138-
expect(matcher.page).to eq(sku_page)
139-
expect(matcher.params[:sku]).to eq("AB-1234")
144+
page = described_class.new(language_root, params: params).call("warehouse/AB-1234")
145+
expect(page).to eq(sku_page)
146+
expect(params[:sku]).to eq("AB-1234")
140147
end
141148

142149
it "does not match when the value violates the regex" do
143-
expect(described_class.new(language_root).call("warehouse/invalid").page).to be_nil
150+
expect(described_class.new(language_root, params: params).call("warehouse/invalid")).to be_nil
144151
end
145152
end
146153
end

0 commit comments

Comments
 (0)