Skip to content

Commit 012ffdb

Browse files
authored
Merge pull request #187 from joyofrails/feat/next-article
Prepare for publishing next article
2 parents 5d0bcae + 22faa70 commit 012ffdb

File tree

6 files changed

+55
-11
lines changed

6 files changed

+55
-11
lines changed
Binary file not shown.
376 KB
Loading

app/content/models/article_page.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@ class ArticlePage < Sitepress::Model
77

88
delegate :mime_type, :handler, to: :page
99

10-
def self.published
11-
all.filter(&:published?)
10+
def self.published(params = {})
11+
all
12+
.filter { |article| article.published?(preview: params[:preview]) }
1213
.sort { |a, b| b.published_on <=> a.published_on } # DESC order
1314
end
1415

1516
def self.draft
1617
all.filter(&:draft?)
1718
end
1819

19-
def published?
20-
published_on.presence && published_on <= Date.today
20+
# Consider an article published if it has a published date prior to today.
21+
# If preview is true, consider the article published regardless of the date.
22+
def published?(preview: false)
23+
published_on.presence && (preview || published_on <= Date.today)
2124
end
2225

2326
def draft? = !published?

app/content/pages/articles/how-to-render-css-dynamically-in-rails.html.mdrb

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ summary: Rails is not just for HTML over the wire. This post demonstrates how an
66
description: Rails is not just for HTML over the wire. This post demonstrates how and why you might use Rails for delivering CSS on the fly too.
77
published: '2024-08-12'
88
uuid: 3be769c4-6a2a-4cfb-8008-94046b952aa6
9-
image: articles/how-to-render-css-dynamically-in-rails/placeholder.jpg
10-
meta_image: articles/how-to-render-css-dynamically-in-rails/placeholder.jpg
9+
image: articles/how-to-render-css-dynamically-in-rails/style-rainbow.jpg
10+
meta_image: articles/how-to-render-css-dynamically-in-rails/style-rainbow.jpg
1111
tags:
1212
- Rails
1313
---
@@ -120,15 +120,33 @@ irb> Mime::SET.collect(&:to_s)
120120
["text/html", "text/plain", "text/javascript", "text/css", "text/calendar", "text/csv", "text/vcard", "text/vtt", "image/png", "image/jpeg", "image/gif", "image/bmp", "image/tiff", "image/svg+xml", "image/webp", "video/mpeg", "audio/mpeg", "audio/ogg", "audio/aac", "video/webm", "video/mp4", "font/otf", "font/ttf", "font/woff", "font/woff2", "application/xml", "application/rss+xml", "application/atom+xml", "application/x-yaml", "multipart/form-data", "application/x-www-form-urlencoded", "application/json", "application/pdf", "application/zip", "application/gzip", "text/vnd.turbo-stream.html"]
121121
```
122122

123-
In Rails `mime_types.rb`, we can see the registered MIME types with each of their recognized extensions.
123+
In Rails `mime_types.rb`, we can see the registered MIME types with each of their recognized extensions. Here‘s the original `mime_types.rb`, added to Rails on Dec 2, 2006 ([source](https://github.com/rails/rails/blob/5410f2cb74737bd6d96c226230c2b9c2bfe1d80b/actionpack/lib/action_controller/mime_types.rb 'Source code on Github')).
124+
125+
```ruby:{"show_header": false}
126+
Mime::Type.register "*/*", :all
127+
Mime::Type.register "text/plain", :text
128+
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
129+
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
130+
Mime::Type.register "text/calendar", :ics
131+
Mime::Type.register "text/csv", :csv
132+
Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
133+
Mime::Type.register "application/rss+xml", :rss
134+
Mime::Type.register "application/atom+xml", :atom
135+
```
136+
137+
Support for CSS was added a short time later on Feb 17, 2007 ([commit](https://github.com/rails/rails/commit/392c7f7314d196c54912a65981d79002d032f896 'Commit on Github')).
138+
139+
```diff:{"show_header": false}
140+
+ Mime::Type.register "text/css", :css
141+
```
124142

125-
That `:css` in the MIME type registry is one way to instruct Rails controller behavior with `respond_to`.
143+
We can take advantage of these registered MIME types to instruct Rails controller behavior with `respond_to`.
126144

127145
Using `respond_to` in a controller allows you to define responses and logic based on the requested format. Here‘s how I take advantage of this behavior in `ColorSchemesController#show`:
128146

129147
<%= render CodeBlock::AppFile.new("app/controllers/color_schemes_controller.rb", lines: [1, 8..17], language: "ruby", revision: "bd53833b03c7f957546e5a9031643d3beb179beb") %>
130148

131-
This controller action will respond differently for HTML and CSS requests. To demonstrate the difference, I've inserted two iframes for `ColorSchemesController#show` below:
149+
This controller action will respond differently for HTML and CSS requests. Note the `format.html` and `format.css` blocks describe alternate results. To demonstrate the difference, I've inserted two iframes for `ColorSchemesController#show` below:
132150

133151
#### Iframe for HTML request
134152

@@ -162,7 +180,7 @@ We can use this endpoint in a stylesheet `<link>` tag just like any other static
162180
<%= stylesheet_link_tag color_scheme_path(@color_scheme, format: :css) %>
163181
```
164182

165-
A downside of moving our dynamic CSS to a separate controller action is that it requires an additional HTTP request. This could be an issue for a highly interactive user experience. But on the other hand, using a separate request allows you to take advantage of Rails [conditional GET features](https://guides.rubyonrails.org/v3.2/caching_with_rails.html#conditional-get-support)
183+
One downside of moving our dynamic CSS to a separate controller action is that it requires an additional HTTP request. This could be an issue for a highly interactive user experience. But on the other hand, using a separate request allows you to take advantage of Rails [conditional GET features](https://guides.rubyonrails.org/v3.2/caching_with_rails.html#conditional-get-support)
166184

167185
> Conditional GETs are a feature of the HTTP specification that provide a way for web servers to tell browsers that the response to a GET request hasn't changed since the last request and can be safely pulled from the browser cache.
168186
>
@@ -176,6 +194,15 @@ This method will calculate a value for `Etag` or `Last-Modified` response header
176194

177195
In short, dynamic CSS combined with a conditional GET allows you to leverage put Ruby logic behind your stylesheet link tags in a performant manner.
178196

197+
## Which approach is right for you?
198+
199+
There‘s no one right answer here but consider the tradeoffs. Both work well with Hotwire; just like any other element, our style tags and stylesheet link tags can be nested inside a Turbo Frame or can be manipulated with a Turbo Stream if we need some interactivity. With a `<style>` tag, no extra requests to your Rails endpoints are needed to render the page. Separating dynamic css into a controller action can help address bandwidth as response body length as your dynamic styles grow more numerous.
200+
201+
My thought process is usually:
202+
203+
- start with dynamic css straight into HTML within a `<style>`
204+
- move to a controller action when more flexibility is needed or when HTTP caching features are desired
205+
179206
## Recap
180207

181208
Rails provides us with all sorts of [sharp knives](https://rubyonrails.org/doctrine#provide-sharp-knives) especially it comes to dynamic rendering in various formats, like CSS.

app/content/pages/index.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Joy of Rails
44

55
<section style="min-height: 51vh">
66
<div class="main-content container py-gap lg:py-3xl">
7-
<% ArticlePage.published.take(4).zip(%w[left right].cycle).each do |(page, side)| %>
7+
<% ArticlePage.published(params).take(4).zip(%w[left right].cycle).each do |(page, side)| %>
88
<%= render Pages::Summary.from_page(page, side: side) %>
99
<% end %>
1010
</div>

spec/requests/site/articles_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@
66
# Filter out index.html.erb from article pages before selecting first draft
77
let(:first_draft) { ArticlePage.draft.lazy.filter { |article| article.page.request_path != "/articles" }.first }
88

9+
describe "GET /" do
10+
it "lists recent published articles" do
11+
get "/"
12+
13+
expect(response).to have_http_status(:ok)
14+
end
15+
16+
it "supports preview flag" do
17+
get "/", params: {preview: true}
18+
19+
expect(response).to have_http_status(:ok)
20+
end
21+
end
22+
923
describe "GET /articles" do
1024
it "lists published articles" do
1125
get "/articles"

0 commit comments

Comments
 (0)