Skip to content

Commit 8cd38f3

Browse files
committed
Refactor Code Block amd Markdown
Decompose the Code Block Article component into Header, Body, Code, Footer components etc. Add new AppFileRun component as a separate object from Code Block Article. Adds an AtomAware mixin for components to render simplified Code Block when rendered inside an atom feed.
1 parent bae24d3 commit 8cd38f3

26 files changed

+257
-180
lines changed

app/content/pages/articles/custom-color-schemes-with-ruby-on-rails.html.mdrb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ CSS variables make it easy to change repeated CSS in a lot of places. You can se
5454

5555
Here’s a simplified a view of how I used CSS variables to define the main background color of the `<body>` element. Using the pseudo-class `:root` means the CSS variable can be accessed from any scope in CSS.
5656

57-
```css:{"show_header": false}
57+
```css:{"header": false}
5858
:root {
5959
--theme-color-50: var(--my-color-50, var(--default-color-50));
6060
--theme-color-100: var(--my-color-100, var(--default-color-100));
@@ -73,13 +73,13 @@ Most of the buttons in the color scheme demo are connected to `<form>` elements
7373

7474
When you click the "Save" button, [the application stores the `id` of the chosen color scheme in your Rails session](https://github.com/joyofrails/joyofrails.com/blob/a08589e1cbe2accf4a20713829df56533e31755e/app/controllers/settings/color_schemes_controller.rb#L31):
7575

76-
```ruby:{"show_header":false}
76+
```ruby:{"header":false}
7777
session[:color_scheme_id] = @color_scheme.id
7878
```
7979

8080
When the page is rendered, the application checks for the presence of the session data to query for the desired color scheme:
8181

82-
```ruby:{"show_header":false}
82+
```ruby:{"header":false}
8383
session[:color_scheme_id] && ColorScheme.find(session[:color_scheme_id])
8484
```
8585

@@ -128,7 +128,7 @@ I’m not using any hooks to manage local state. I’m not making any explicit c
128128

129129
In fact, I wrote only a one line of custom JavaScript to make the color scheme preview selection work: on an `onchange` handler:
130130

131-
```html:{"show_header": false}
131+
```html:{"header": false}
132132
<select onchange="this.form.requestSubmit()">
133133
<!-- options -->
134134
</select>

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ irb> Mime::SET.collect(&:to_s)
124124

125125
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')).
126126

127-
```ruby:{"show_header": false}
127+
```ruby:{"header": false}
128128
Mime::Type.register "*/*", :all
129129
Mime::Type.register "text/plain", :text
130130
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
@@ -138,7 +138,7 @@ Mime::Type.register "application/atom+xml", :atom
138138

139139
Support for CSS was added a short time later on Feb 17, 2007 ([commit](https://github.com/rails/rails/commit/392c7f7314d196c54912a65981d79002d032f896 'Commit on Github')).
140140

141-
```diff:{"show_header": false}
141+
```diff:{"header": false}
142142
+ Mime::Type.register "text/css", :css
143143
```
144144

@@ -158,7 +158,7 @@ Below you‘ll see an `iframe` with `src` set to request the **Blue Chill** colo
158158

159159
Here’s the code for the `iframe`:
160160

161-
```erb:{"show_header": false}
161+
```erb:{"header": false}
162162
<%= tag.iframe src: color_scheme_path(@color_scheme_blue_chill) %>
163163
```
164164

@@ -170,7 +170,7 @@ And here you‘ll see an `iframe` with `src` set to request the **Blue Chill** c
170170

171171
Here’s the code for the `iframe`:
172172

173-
```erb:{"show_header": false}
173+
```erb:{"header": false}
174174
<%= tag.iframe src: color_scheme_path(@color_scheme_blue_chill, format: :css) %>
175175
```
176176

app/content/pages/articles/introducing-joy-of-rails.html.mdrb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Let’s say I wanted to describe how to build a basic counter with Rails and Hot
9393

9494
I can also link to and embed the code that makes this work, like the <%= link_to_app_file("app/controllers/examples/counters_controller.rb", "counters controller") %> that saves the count in your session and renders the HTML for this counter dynamically:
9595

96-
<%= render_code_block_app_file("app/controllers/examples/counters_controller.rb", language: "ruby", revision: "504f449e34232fc5299da78056344fffe04460de") %>
96+
<%= render CodeBlock::AppFile.new("app/controllers/examples/counters_controller.rb", language: "ruby", revision: "504f449e34232fc5299da78056344fffe04460de") %>
9797

9898
It would be harder to accomplish this with a WordPress blog or static website.
9999

app/helpers/articles_helper.rb

Lines changed: 0 additions & 10 deletions
This file was deleted.

app/views/components/atom/entry_content.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
# Atom::EntryContent knows how to render a Sitepress::Model as an Atom feed entry.
24
# It uses a different Markdown component to avoid rendering "fancy" elements in the atom feed.
35
# It post-processes the rendered HTML to
Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,30 @@
11
class CodeBlock::AppFile < ApplicationComponent
2-
include InlineSvg::ActionView::Helpers
2+
prepend CodeBlock::AtomAware
3+
4+
attr_reader :app_file, :lines, :attributes, :language
35

46
# @param filename [String] the file path or an Examples::AppFile.
57
# @param file [String] the file path or an Examples::AppFile.
6-
def initialize(filename, lines: nil, revision: "HEAD", **attributes)
8+
def initialize(filename, language: nil, lines: nil, revision: "HEAD", **attributes)
79
@app_file = Examples::AppFile.from(filename, revision: revision)
810
@lines = lines
11+
@language = language
912
@attributes = attributes
1013
end
1114

1215
def view_template
13-
# When an AppFile code block renders within an Atom feed, we want to render the basic code block
14-
if content_type?("application/atom+xml")
15-
return render ::CodeBlock::Basic.new(source, **attributes)
16-
end
17-
18-
render ::CodeBlock::Article.new(**attributes) do |code_block|
19-
code_block.title do
20-
link(app_file.repo_url, "Source code on Github", class: "nc") {
21-
plain app_file.app_path.to_s
22-
plain inline_svg_tag("external-link.svg", class: "icon icon-sm", height: 12, width: 12)
23-
}
24-
end
16+
render CodeBlock::Container.new(language: language) do
17+
render CodeBlock::AppFileHeader.new(app_file)
2518

26-
code_block.body do
27-
source
19+
render CodeBlock::Body.new do
20+
render CodeBlock::Code.new(source, language: language)
21+
whitespace
22+
render ClipboardCopy.new(text: source)
2823
end
2924
end
3025
end
3126

32-
private
33-
34-
attr_reader :app_file, :lines, :attributes
35-
3627
def source
3728
app_file.source(lines: lines)
3829
end
39-
40-
def content_type?(type)
41-
helpers.headers["Content-Type"].to_s =~ %r{#{Regexp.escape(type)}}
42-
end
4330
end

app/views/components/code_block/app_file_basic.rb

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class CodeBlock::AppFileHeader < ApplicationComponent
2+
include InlineSvg::ActionView::Helpers
3+
4+
attr_reader :app_file
5+
6+
def initialize(app_file)
7+
@app_file = app_file
8+
end
9+
10+
def view_template
11+
render CodeBlock::Header.new do
12+
link(app_file.repo_url, "Source code on Github", class: "nc") do
13+
plain app_file.app_path.to_s
14+
plain inline_svg_tag("external-link.svg", class: "icon icon-sm", height: 12, width: 12)
15+
end
16+
end
17+
end
18+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
class CodeBlock::AppFileRun < ApplicationComponent
2+
prepend CodeBlock::AtomAware
3+
4+
attr_reader :app_file, :lines, :language
5+
6+
# @param filename [String] the file path or an Examples::AppFile.
7+
# @param file [String] the file path or an Examples::AppFile.
8+
def initialize(filename, lines: nil, revision: "HEAD", language: nil, **attributes)
9+
@app_file = Examples::AppFile.from(filename, revision: revision)
10+
@lines = lines
11+
@attributes = attributes
12+
@language = language
13+
end
14+
15+
def view_template
16+
render CodeBlock::Container.new(
17+
language: language,
18+
data: code_example_data
19+
) do
20+
render CodeBlock::AppFileHeader.new(app_file)
21+
22+
render CodeBlock::Body.new do
23+
render CodeBlock::Code.new(source, language: language)
24+
render ClipboardCopy.new(text: source)
25+
end
26+
27+
render CodeBlock::Footer.new do
28+
actions
29+
end
30+
end
31+
end
32+
33+
def actions
34+
div(class: "code-actions") do
35+
button(class: "button primary", data: {action: "click->code-example#run", code_example_target: "runButton"}) { "Run" }
36+
button(class: "button secondary hidden", data: {action: "click->code-example#clear", code_example_target: "clearButton"}) { "Clear" }
37+
span(class: "code-action-status", data: {code_example_target: "status"})
38+
end
39+
pre(class: "code-output hidden", data: {code_example_target: "output"}) do
40+
code
41+
end
42+
pre(class: "code-result hidden", data: {code_example_target: "result"}) do
43+
code
44+
end
45+
end
46+
47+
def code_example_data
48+
{controller: "code-example", code_example_vm_value: :rails, language: language, lines:}
49+
end
50+
51+
def source
52+
app_file.source(lines: lines)
53+
end
54+
end
Lines changed: 22 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,24 @@
1-
class CodeBlock::Article < Phlex::HTML
2-
include InlineSvg::ActionView::Helpers
1+
class CodeBlock::Article < ApplicationComponent
32
include Phlex::DeferredRender
4-
include Phlex::Rails::Helpers::ClassNames
3+
prepend CodeBlock::AtomAware
54

6-
def initialize(
7-
source = "",
8-
language: nil,
9-
filename: nil,
10-
run: false,
11-
show_header: true,
12-
enable_copy: true,
13-
**options
14-
)
15-
@source = source || ""
16-
@language = language.presence
17-
@filename = filename.presence
18-
@enable_run = run
19-
@show_header = show_header
20-
@enable_copy = enable_copy
21-
@options = options
5+
attr_reader :source, :language, :filename, :enable_run
6+
7+
def initialize(source = "", language: nil, filename: nil, header: true, **)
8+
@source = source
9+
@language = language
10+
@filename = filename
11+
@header = header
2212
end
2313

2414
def view_template
25-
div(
26-
class: class_names("code-wrapper", "highlight", "language-#{language}", *options[:class]),
27-
data: code_example_data.keep_if { enable_run }.merge(data)
28-
) do
29-
if @show_header
30-
div(class: "code-header") do
31-
plain inline_svg_tag("app-dots.svg", class: "app-dots mr-4")
32-
span(class: "code-title", &title_content)
33-
end
34-
end
35-
36-
div(class: "code-body") do
37-
render CodeBlock::Basic.new(source, language: language, data: {code_example_target: "source"})
38-
render ClipboardCopy.new(text: source) if enable_copy
39-
end
15+
render CodeBlock::Container.new(language: language) do
16+
render CodeBlock::Header.new(&title_content) if show_header?
4017

41-
if enable_run
42-
div(class: "code-footer") do
43-
div(class: "code-actions") do
44-
button(class: "button primary", data: {action: "click->code-example#run", code_example_target: "runButton"}) { "Run" }
45-
button(class: "button secondary hidden", data: {action: "click->code-example#clear", code_example_target: "clearButton"}) { "Clear" }
46-
span(class: "code-action-status", data: {code_example_target: "status"})
47-
end
48-
pre(class: "code-output hidden", data: {code_example_target: "output"}) do
49-
code
50-
end
51-
pre(class: "code-result hidden", data: {code_example_target: "result"}) do
52-
code
53-
end
54-
end
18+
render CodeBlock::Body.new do
19+
render CodeBlock::Code.new(source, language: language)
20+
whitespace
21+
render ClipboardCopy.new(text: source)
5522
end
5623
end
5724
end
@@ -65,22 +32,15 @@ def body(&)
6532
@source = capture(&)
6633
end
6734

35+
def data = {language: language, lines:}
36+
37+
def lines = source.scan("\n").count + 1
38+
6839
def title_content
6940
@title || ->(*) { filename || language }
7041
end
7142

72-
protected
73-
74-
private
75-
76-
attr_reader :source, :language, :filename, :enable_run, :enable_copy, :options
77-
78-
def data = {language: language, lines:}
79-
80-
def code_example_data = {
81-
controller: "code-example",
82-
code_example_vm_value: :rails
83-
}
84-
85-
def lines = source.scan("\n").count + 1
43+
def show_header?
44+
@title || @header
45+
end
8646
end

0 commit comments

Comments
 (0)