studio-engine 0.134.0 → 0.135.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +1 -0
- data/app/controllers/studio/not_found_controller.rb +54 -0
- data/app/helpers/studio/link_preview_helper.rb +16 -3
- data/app/views/studio/_not_found.html.erb +30 -0
- data/app/views/studio/not_found/show.html.erb +5 -0
- data/app/views/style/_pages.html.erb +19 -0
- data/app/views/style/_tricks.html.erb +3 -0
- data/config/studio_tricks.yml +7 -0
- data/lib/studio/exceptions_app.rb +124 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +1 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1b8bd497e5ba5d6c113a0e6c221b03200f395b1c3d4032aa1327691b7c584c34
|
|
4
|
+
data.tar.gz: 0a64eb7f3a70bcbe03a50550d36a1d8da938498d91d5cf0a1fcb257845a76759
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c325e02d5e2f0c2a9123ab2b342281b59d27635e48cca92396f792cd39a6fc403cf6e79bfc2e920f891ef9dcc30063e0460e393db83e7148470d3895615b4aad
|
|
7
|
+
data.tar.gz: 96424698d12cf9d7d60bfee31c6f8d97f5bb464439bb421d8f47ba3ab78f1ce359e9a01bd543f3baab4c45d502c73e117ed3074d4bc306c40582b9819ab3aa53
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.135.0 — 2026-11-07
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- The not-found page's og:url is the app's home, not the address it was asked for: `link_preview` takes `url:` (a path or an absolute URL), and a 404 at a hidden route no longer names that route in its link-preview tags.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **A friendly not-found page.** `config.exceptions_app = Studio::ExceptionsApp.new` in `config/application.rb` gives a lost visitor the app's name, brand mark, a short message and a button home, inside the app's own layout, for an unknown address and a missing record alike. The status stays 404. A JSON request still gets Rails' 404 JSON, and other statuses keep `public/<status>.html`. If the page cannot render, or the host answers anything but a 404, the static `public/404.html` answers instead, so it cannot loop or change the status. An app with its own wall lists `"studio/not_found" => %w[show]`. See `docs/NOT_FOUND_PAGE.md`.
|
|
16
|
+
|
|
7
17
|
## 0.134.0 — 2026-11-07
|
|
8
18
|
|
|
9
19
|
### Changed
|
data/README.md
CHANGED
|
@@ -45,6 +45,7 @@ resolved.
|
|
|
45
45
|
- **Public user page**: `/u/:username` shows a user's avatar and username (nothing else), and unfurls with their avatar, falling back to the site image. Link any username to it with `link_to_user_profile(user)` or `studio_user_profile_path(user)`. Opt in with `config.draw_public_user_routes = true`. See [`docs/PUBLIC_USER_PAGE.md`](docs/PUBLIC_USER_PAGE.md).
|
|
46
46
|
- **Transactional emails**: `Studio::EmailCatalog` — every email an app sends, its type, a live preview, and its banner — plus the shared `/admin/emails` page. Every app inherits the standard emails and their artwork on day one, and can register its own workflows and upload its own banners. See [Transactional emails](#transactional-emails).
|
|
47
47
|
- **Site footer**: `<%= studio_site_footer %>` in the layout renders the public site's footer. It is on by default: with nothing configured it prints the app's name, logo and the legal links the app has routes for, and no address. `config.site_footer` declares the app's own facts (brand, social profiles, link columns, an address with a live map, legal links), every part optional; `config.site_footer_address` adds a location in one setting; `config.site_footer = false` turns the footer off; `config.site_footer_chat_path` adds a standard Chat link to the Contact column. Leaflet is vendored and served by the engine. See [`docs/SITE_FOOTER.md`](docs/SITE_FOOTER.md).
|
|
48
|
+
- **Not-found page**: `config.exceptions_app = Studio::ExceptionsApp.new` in `config/application.rb` gives a lost visitor a friendly 404 with the app's name, brand mark and a button home, inside the app's layout. The status stays 404, JSON requests keep Rails' 404 JSON, and `public/404.html` is the fallback if the page cannot render. See [`docs/NOT_FOUND_PAGE.md`](docs/NOT_FOUND_PAGE.md).
|
|
48
49
|
- **Booking**: a simple scheduler on Google Calendar appointment schedules, with or without the footer. With `config.booking_url`, `studio_booking_frame` embeds Google's booking page, `studio_booking_link` opens it in a popup, and `config.draw_booking_routes = true` draws `/schedule` (or name the app's own page with `config.booking_path`). `config.booking_crop` crops the frame to the slot picker at rest, from numbers `bin/booking-crop-measure` reads off the app's own schedule. See [`docs/BOOKING.md`](docs/BOOKING.md).
|
|
49
50
|
- **Voice profile**: an opt-in row on `/profile` ("Set up your voice"): the person reads three phrases with a live level meter, their device turns each take into a speaker embedding (WeSpeaker via transformers.js), and only the averaged vector is saved on their user, never audio. `Studio::VoiceMatch.identify(embedding, among: User.all, model:)` answers who is speaking. Turn it on with `config.voice_profile = true` after installing the migration. See [`docs/VOICE_PROFILE.md`](docs/VOICE_PROFILE.md).
|
|
50
51
|
- **Voice capture**: the microphone chip (names the microphone a page records from, picks another, Chrome help dialog), one level meter, and a record button that refuses a silent take and hands a heard one to the page as a `studio:voice-recorder:take` event with the blob; it uploads nothing. Render `studio/voice/microphone_chip` and `studio/voice/recorder`. See [`docs/VOICE_CAPTURE.md`](docs/VOICE_CAPTURE.md).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Studio
|
|
4
|
+
# The friendly not-found page, drawn by Studio::ExceptionsApp for an HTML 404
|
|
5
|
+
# (docs/NOT_FOUND_PAGE.md). It has no route: only the exceptions app calls it.
|
|
6
|
+
#
|
|
7
|
+
# It inherits the host's ApplicationController so the page sits inside the
|
|
8
|
+
# app's own layout, navbar, footer and theme, as Studio::PublicUsersController
|
|
9
|
+
# does. A signed-out visitor must see it, so it skips the engine's sign-in gate.
|
|
10
|
+
# A host with its own wall lists it there (the hub's AdminWall names
|
|
11
|
+
# "studio/not_found" => %w[show]). If the host still answers anything but a
|
|
12
|
+
# 404 here, the exceptions app throws that answer away and serves the static
|
|
13
|
+
# public/404.html, so a wall can cost the page its layout but never its status.
|
|
14
|
+
class NotFoundController < ::ApplicationController
|
|
15
|
+
skip_before_action :require_authentication, raise: false
|
|
16
|
+
# No forgery skip: Rails' ShowExceptions calls the exceptions app as a GET
|
|
17
|
+
# (request_method = "GET"), which never needs the token, so the host's CSRF
|
|
18
|
+
# allow-lists stay as they are.
|
|
19
|
+
|
|
20
|
+
def show
|
|
21
|
+
@studio_not_found = {
|
|
22
|
+
app_name: Studio.app_name,
|
|
23
|
+
logo: not_found_logo,
|
|
24
|
+
home_path: not_found_home_path,
|
|
25
|
+
back_path: not_found_back_path
|
|
26
|
+
}
|
|
27
|
+
render "studio/not_found/show", status: :not_found, formats: [:html], content_type: "text/html"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def not_found_logo
|
|
33
|
+
Studio.navbar_brand&.dig(:logo) || Studio.logo_for("Navbar Logo")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def not_found_home_path
|
|
37
|
+
main_app.respond_to?(:root_path) ? main_app.root_path : "/"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The page the visitor came from, when it is on this app: "Go back" returns
|
|
41
|
+
# there. A link from elsewhere, or none, draws no second link.
|
|
42
|
+
def not_found_back_path
|
|
43
|
+
referer = URI.parse(request.referer.to_s)
|
|
44
|
+
return nil unless referer.host == request.host && referer.path.present?
|
|
45
|
+
# "//other.host/x" is a same-host path a browser reads as another site.
|
|
46
|
+
return nil if referer.path.start_with?("//")
|
|
47
|
+
return nil if referer.path == request.env["action_dispatch.original_path"]
|
|
48
|
+
|
|
49
|
+
[referer.path, referer.query].compact.join("?")
|
|
50
|
+
rescue URI::InvalidURIError
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -10,7 +10,7 @@ module Studio
|
|
|
10
10
|
# reason Studio::GeoHelper gives: every helper module is included into every
|
|
11
11
|
# view, so an unprefixed name would collide with a host's own helper.
|
|
12
12
|
module LinkPreviewHelper
|
|
13
|
-
OVERRIDE_KEYS = %i[image title description image_alt].freeze
|
|
13
|
+
OVERRIDE_KEYS = %i[image title description image_alt url].freeze
|
|
14
14
|
|
|
15
15
|
# THE PAGE OVERRIDE. Call it from any view (the layout renders later, so it
|
|
16
16
|
# sees the call):
|
|
@@ -71,7 +71,7 @@ module Studio
|
|
|
71
71
|
image_height: height,
|
|
72
72
|
image_alt: (overrides[:image_alt].to_s.strip.presence || preview[:title] if preview[:image]),
|
|
73
73
|
site_name: Studio.app_name.to_s,
|
|
74
|
-
url: studio_link_preview_page_url
|
|
74
|
+
url: studio_link_preview_page_url(overrides[:url])
|
|
75
75
|
)
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -127,9 +127,22 @@ module Studio
|
|
|
127
127
|
request_base_url: (request.base_url if studio_link_preview_request?))
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
# The page's og:url: the request's own URL, or the `url:` a page passed to
|
|
131
|
+
# link_preview. A path is built on the same base as the request's; an
|
|
132
|
+
# absolute URL is used as is. The not-found page passes the app's home, so a
|
|
133
|
+
# 404 never advertises the broken address it was asked for (a 404 at a
|
|
134
|
+
# hidden route would otherwise name that route in its og:url).
|
|
135
|
+
def studio_link_preview_page_url(override = nil)
|
|
131
136
|
return nil unless studio_link_preview_request?
|
|
132
137
|
|
|
138
|
+
override = override.to_s.strip
|
|
139
|
+
return override if override.match?(%r{\Ahttps?://}i)
|
|
140
|
+
|
|
141
|
+
if override.start_with?("/")
|
|
142
|
+
return Studio::LinkPreview.page_url(base_url: Studio.link_preview_base_url,
|
|
143
|
+
request_url: "#{request.base_url}#{override}", path: override)
|
|
144
|
+
end
|
|
145
|
+
|
|
133
146
|
Studio::LinkPreview.page_url(base_url: Studio.link_preview_base_url,
|
|
134
147
|
request_url: request.original_url, path: request.path)
|
|
135
148
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<%# locals: (app_name:, home_path:, logo: nil, back_path: nil, heading: :h1) %>
|
|
2
|
+
<%# THE NOT-FOUND PAGE: what a lost visitor sees on an unknown address or a
|
|
3
|
+
record that is not there (docs/NOT_FOUND_PAGE.md). Studio::NotFoundController
|
|
4
|
+
renders it inside the app's own layout, from the app's Studio config:
|
|
5
|
+
|
|
6
|
+
app_name Studio.app_name: named in the button home.
|
|
7
|
+
home_path the app's root: the primary button.
|
|
8
|
+
logo the app's brand mark (Studio.navbar_brand's logo, else the
|
|
9
|
+
Navbar Logo theme image). nil draws no image.
|
|
10
|
+
back_path a page on this app the visitor came from: a second link back.
|
|
11
|
+
nil draws no second link.
|
|
12
|
+
heading the title's tag: :h1 on the page, a lower level where the page
|
|
13
|
+
is shown inside another (the style guide's card).
|
|
14
|
+
|
|
15
|
+
Theme tokens only, so it reads in light and dark. The static last resort,
|
|
16
|
+
public/404.html, carries the same words and the same data-test hooks. %>
|
|
17
|
+
<section class="mx-auto w-full max-w-md px-4 py-16 sm:py-24 text-center" data-studio-not-found data-test="not-found" aria-labelledby="studio-not-found-title">
|
|
18
|
+
<% if logo.present? %>
|
|
19
|
+
<img src="<%= logo %>" alt="<%= app_name %>" width="64" height="64" class="mx-auto mb-6 h-16 w-16 object-contain" data-studio-not-found-logo>
|
|
20
|
+
<% end %>
|
|
21
|
+
<p class="type-fine font-semibold uppercase tracking-wide text-primary-ink mb-2">404 · Not found</p>
|
|
22
|
+
<%= content_tag heading, "This page isn’t here", id: "studio-not-found-title", class: "type-h2 text-heading mb-3" %>
|
|
23
|
+
<p class="type-body text-secondary mb-8">The address may be mistyped, or the page may have moved. Head back home and pick up from there.</p>
|
|
24
|
+
<div class="flex flex-col sm:flex-row items-center justify-center gap-3">
|
|
25
|
+
<a href="<%= home_path %>" class="btn btn-primary w-full sm:w-auto" data-test="not-found-home">Back to <%= app_name %></a>
|
|
26
|
+
<% if back_path.present? %>
|
|
27
|
+
<a href="<%= back_path %>" class="btn btn-outline w-full sm:w-auto" data-test="not-found-back">Go back</a>
|
|
28
|
+
<% end %>
|
|
29
|
+
</div>
|
|
30
|
+
</section>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<%# Studio::NotFoundController#show: the not-found page in the app's layout. %>
|
|
2
|
+
<% content_for(:title, "Page not found · #{Studio.app_name}") %>
|
|
3
|
+
<%# A 404's og:url is the app's home, never the broken address it was asked for. %>
|
|
4
|
+
<% link_preview(url: @studio_not_found[:home_path]) %>
|
|
5
|
+
<%= render "studio/not_found", **@studio_not_found %>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%# The Pages group of the Tricks board (admin/style): whole pages an app
|
|
2
|
+
adopts with one line. Not Found is the friendly 404 (docs/NOT_FOUND_PAGE.md),
|
|
3
|
+
drawn here with THIS app's name and brand mark; the Go back link shows as it
|
|
4
|
+
does when the visitor came from a page on this app. %>
|
|
5
|
+
<section id="pages" class="section-subtarget space-y-5">
|
|
6
|
+
<div class="space-y-1">
|
|
7
|
+
<h3 class="type-h4 text-heading">Pages</h3>
|
|
8
|
+
<p class="text-muted text-sm">Whole pages an app adopts with one line in config/application.rb.</p>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="grid gap-4 sm:grid-cols-2">
|
|
12
|
+
<%= render layout: "style/specimen", locals: { trick: "not_found" } do %>
|
|
13
|
+
<div class="card w-full">
|
|
14
|
+
<%= render "studio/not_found", app_name: Studio.app_name, logo: Studio.navbar_brand&.dig(:logo) || Studio.logo_for("Navbar Logo"),
|
|
15
|
+
home_path: "/", back_path: "#pages", heading: :h4 %>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
</section>
|
data/config/studio_tricks.yml
CHANGED
|
@@ -462,6 +462,13 @@ booking_frame:
|
|
|
462
462
|
note: "Cropped to the slot picker at rest; bin/booking-crop-measure prints yours."
|
|
463
463
|
resolve: [config:booking_crop]
|
|
464
464
|
|
|
465
|
+
not_found:
|
|
466
|
+
name: Not Found
|
|
467
|
+
group: Pages
|
|
468
|
+
token: "config.exceptions_app = Studio::ExceptionsApp.new"
|
|
469
|
+
note: "A lost visitor's 404 page, with the app's brand and a button home."
|
|
470
|
+
resolve: [partial:studio/not_found]
|
|
471
|
+
|
|
465
472
|
board:
|
|
466
473
|
name: Board
|
|
467
474
|
group: Tasks
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_dispatch"
|
|
4
|
+
|
|
5
|
+
module Studio
|
|
6
|
+
# The friendly not-found page, as an exceptions app. An app opts in with one
|
|
7
|
+
# line in config/application.rb (docs/NOT_FOUND_PAGE.md):
|
|
8
|
+
#
|
|
9
|
+
# config.exceptions_app = Studio::ExceptionsApp.new
|
|
10
|
+
#
|
|
11
|
+
# Rails hands this app every exception ShowExceptions renders, with PATH_INFO
|
|
12
|
+
# rewritten to "/<status>" and the method set to GET. That is the path a
|
|
13
|
+
# RoutingError takes (an unknown address) and the path a RecordNotFound takes
|
|
14
|
+
# (Studio::ErrorHandling#handle_not_found re-raises on purpose so it lands here).
|
|
15
|
+
#
|
|
16
|
+
# What it answers:
|
|
17
|
+
#
|
|
18
|
+
# 404, an HTML request Studio::NotFoundController#show: the app's name and
|
|
19
|
+
# brand mark, a short message and a button home,
|
|
20
|
+
# inside the app's own layout. Status 404.
|
|
21
|
+
# 404, a data request ActionDispatch::PublicExceptions, unchanged: a JSON
|
|
22
|
+
# request gets {"status":404,"error":"Not Found"} with
|
|
23
|
+
# status 404, never the HTML page.
|
|
24
|
+
# any other status ActionDispatch::PublicExceptions, unchanged (500,
|
|
25
|
+
# 422 and the rest keep public/<status>.html).
|
|
26
|
+
#
|
|
27
|
+
# IT CANNOT LOOP AND IT CANNOT CHANGE THE STATUS. The page renders through the
|
|
28
|
+
# app's ApplicationController, so the app's before_actions, layout and database
|
|
29
|
+
# all run. If any of them raises, or answers anything but a 404 (a sign-in
|
|
30
|
+
# redirect from a wall that does not list the page, a 406 from allow_browser),
|
|
31
|
+
# the answer is thrown away and PublicExceptions serves public/404.html, the
|
|
32
|
+
# static last resort, with status 404. PublicExceptions only reads a file, so
|
|
33
|
+
# the fallback has nothing left that can fail into this app again.
|
|
34
|
+
#
|
|
35
|
+
# Which requests are HTML is PublicExceptions' own rule, so the split never
|
|
36
|
+
# drifts from Rails': a format whose body PublicExceptions can serialize (JSON,
|
|
37
|
+
# XML) is data, and everything else, `Accept: */*` included, is the page.
|
|
38
|
+
class ExceptionsApp
|
|
39
|
+
NOT_FOUND = 404
|
|
40
|
+
PAGE_PARAMS = { controller: "studio/not_found", action: "show" }.freeze
|
|
41
|
+
# Cached by the failed request; the page's request must not read them.
|
|
42
|
+
STALE_KEYS = %w[action_dispatch.request.path_parameters
|
|
43
|
+
action_dispatch.request.parameters
|
|
44
|
+
action_dispatch.request.query_parameters
|
|
45
|
+
action_dispatch.request.request_parameters
|
|
46
|
+
action_controller.instance].freeze
|
|
47
|
+
|
|
48
|
+
# public_path: where the static pages live. Defaults to Rails.public_path,
|
|
49
|
+
# read on each call, so the line works before the app finishes booting.
|
|
50
|
+
# page: the Rack endpoint that draws the page (a seam for tests).
|
|
51
|
+
def initialize(public_path = nil, page: nil)
|
|
52
|
+
@public_path = public_path
|
|
53
|
+
@page = page
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def call(env)
|
|
57
|
+
return static(env) unless page_request?(env)
|
|
58
|
+
|
|
59
|
+
response = draw_page(env)
|
|
60
|
+
return response if response
|
|
61
|
+
|
|
62
|
+
static(env)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def page_request?(env)
|
|
68
|
+
request = ActionDispatch::Request.new(env)
|
|
69
|
+
return false unless request.path_info.delete_prefix("/").to_i == NOT_FOUND
|
|
70
|
+
|
|
71
|
+
html?(request)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# PublicExceptions renders a body in a format the error hash serializes to
|
|
75
|
+
# (to_json, to_xml) and the HTML file for anything else.
|
|
76
|
+
def html?(request)
|
|
77
|
+
content_type = begin
|
|
78
|
+
request.formats.first
|
|
79
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
|
80
|
+
Mime[:text]
|
|
81
|
+
end
|
|
82
|
+
return true unless content_type
|
|
83
|
+
|
|
84
|
+
!{}.respond_to?("to_#{content_type.to_sym}")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The page's answer, or nil when it raised or came back as anything but a
|
|
88
|
+
# 404 HTML page.
|
|
89
|
+
def draw_page(env)
|
|
90
|
+
page_env = env.except(*STALE_KEYS)
|
|
91
|
+
page_env["action_dispatch.request.path_parameters"] = PAGE_PARAMS.dup
|
|
92
|
+
status, headers, body = page.call(page_env)
|
|
93
|
+
return [status, headers, body] if status.to_i == NOT_FOUND && html_body?(headers)
|
|
94
|
+
|
|
95
|
+
body.close if body.respond_to?(:close)
|
|
96
|
+
log("answered #{status}, not 404")
|
|
97
|
+
nil
|
|
98
|
+
rescue StandardError, ScriptError => e
|
|
99
|
+
log("#{e.class}: #{e.message}")
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def html_body?(headers)
|
|
104
|
+
type = headers["content-type"] || headers["Content-Type"]
|
|
105
|
+
type.to_s.start_with?("text/html")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def page
|
|
109
|
+
@page || Studio::NotFoundController.action(:show)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def static(env)
|
|
113
|
+
ActionDispatch::PublicExceptions.new(public_path).call(env)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def public_path
|
|
117
|
+
(@public_path || Rails.public_path).to_s
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def log(reason)
|
|
121
|
+
Rails.logger&.warn("[studio.not_found] the page fell back to public/404.html: #{reason}")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.135.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
@@ -470,6 +470,7 @@ files:
|
|
|
470
470
|
- app/controllers/studio/local_reviews_controller.rb
|
|
471
471
|
- app/controllers/studio/marketing_kit_controller.rb
|
|
472
472
|
- app/controllers/studio/models_controller.rb
|
|
473
|
+
- app/controllers/studio/not_found_controller.rb
|
|
473
474
|
- app/controllers/studio/onboarding_controller.rb
|
|
474
475
|
- app/controllers/studio/profiles_controller.rb
|
|
475
476
|
- app/controllers/studio/public_users_controller.rb
|
|
@@ -656,6 +657,7 @@ files:
|
|
|
656
657
|
- app/views/studio/_hold_button.html.erb
|
|
657
658
|
- app/views/studio/_hold_button_guard.html.erb
|
|
658
659
|
- app/views/studio/_leveling_activity_assets.html.erb
|
|
660
|
+
- app/views/studio/_not_found.html.erb
|
|
659
661
|
- app/views/studio/_page_header.html.erb
|
|
660
662
|
- app/views/studio/_section_header.html.erb
|
|
661
663
|
- app/views/studio/_session_stamp.html.erb
|
|
@@ -771,6 +773,7 @@ files:
|
|
|
771
773
|
- app/views/studio/models/show.html.erb
|
|
772
774
|
- app/views/studio/newsletter_mailer/subscribed.html.erb
|
|
773
775
|
- app/views/studio/newsletter_mailer/subscribed.text.erb
|
|
776
|
+
- app/views/studio/not_found/show.html.erb
|
|
774
777
|
- app/views/studio/profile_mailer/email_change_notification.html.erb
|
|
775
778
|
- app/views/studio/profile_mailer/email_change_notification.text.erb
|
|
776
779
|
- app/views/studio/profiles/_birthday_fields.html.erb
|
|
@@ -817,6 +820,7 @@ files:
|
|
|
817
820
|
- app/views/style/_loaders_feed_page.html.erb
|
|
818
821
|
- app/views/style/_modal_specimen.html.erb
|
|
819
822
|
- app/views/style/_modals.html.erb
|
|
823
|
+
- app/views/style/_pages.html.erb
|
|
820
824
|
- app/views/style/_scrims.html.erb
|
|
821
825
|
- app/views/style/_site_footer.html.erb
|
|
822
826
|
- app/views/style/_specimen.html.erb
|
|
@@ -884,6 +888,7 @@ files:
|
|
|
884
888
|
- lib/studio/email_smoke.rb
|
|
885
889
|
- lib/studio/engine.rb
|
|
886
890
|
- lib/studio/environment_banner.rb
|
|
891
|
+
- lib/studio/exceptions_app.rb
|
|
887
892
|
- lib/studio/geo.rb
|
|
888
893
|
- lib/studio/geo/countries.rb
|
|
889
894
|
- lib/studio/geo/lookup.rb
|