03 Jul 2023
I know people say never to use floats anymore, but surely they can still be used for whatever they were originally intended for… right?
The float CSS property places an element on the left or right side of its container, allowing text and inline elements to wrap around it. The element is removed from the normal flow of the page, though still remaining a part of the flow (in contrast to absolute positioning).
— MDN Docs
Seems unique enough. I don’t think flexbox or CSS grid (the modern layout techniques) do a better job at this.
The float property was introduced to allow web developers to implement layouts involving an image floating inside a column of text, with the text wrapping around the left or right of it. The kind of thing you might get in a newspaper layout.
So the original purpose was to position images within text in a few specific ways.
<div>
<div>Float me</div>
As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill.
</div>
More depth on floats here.
30 Jun 2023
When using a URL helper you can specify a fragment with the anchor
option:
post_path(post.root, anchor: "post_#{post.id}")
# => "/posts/42#post_69
Previously, I was manually doing something similar like:
"#{post_path(post.root)}#post_#{post.id}"
# => "/posts/42#post_69
26 Jun 2023
Stimulus is part of Hotwire, an alternative approach of building modern web apps. Turbo supposedly takes care of 80% of the usual JS required, while Stimulus should be able to handle the majority of other custom use cases.
The Goals of Stimulus
Unlike most other JavaScript frameworks, Stimulus, takes an HTML centered approach to handling state and page updates. The idea is to manipulate your existing HTML, instead of say, making an AJAX request and parsing JSON to determine which elements on the page should re-render. You use Stimulus to “sprinkle” interactive behavior into static/server-rendered HTML pages.
Stimulus is concerned with manipulating this existing HTML document. Sometimes that means adding a CSS class that hides an element or animates it or highlights it. Sometimes it means rearranging elements in groupings. Sometimes it means manipulating the content of an element, like when we transform UTC times that can be cached into local times that can be displayed.
There are cases where you’d want Stimulus to create new DOM elements, and you’re definitely free to do that. We might even add some sugar to make it easier in the future. But it’s the minority use case. The focus is on manipulating, not creating elements.
The Dos and Don’ts
Do
- make your controllers small, concise, and re-usable.
- combine controllers to create more complex behavior and interactions.
- use as much as possible with Turbo.
Don’t
- make controllers too specific (with naming or functionality).
- try to recreate your entire frontend with Stimulus (i.e. creating “view components”).
- fall into old habits of using JQuery.
Basics
The important things I forget often. Otherwise, look at the official reference for details.
Mainly be aware of:
- Controllers
- Actions
- Targets
Controllers
Controllers are the JavaScript classes that house all the actual behavior of the HTML.
Actions
Actions are how you handle DOM Events in your controllers. Specifically, you can specify which DOM Event will execute a specified function inside your Stimulus controller.
For example:
<div data-controller="gallery">
<button data-action="click->gallery#next">…</button>
</div>
// controllers/gallery_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
next(event) {
// …
}
}
- You can have multiple actions tied to one HTML element.
- Prefer naming action methods based on what will happen when they’re called (i.e.
click->profile#showDialog
)
- Actions can have parameters that are be passed from the submitter element.
- They use the format
data-[identifier]-[param-name]-param
.
- Values will be typecast, so you can pass Arrays and Hashes.
- Retrieve them in a controller with
event.params
.
Targets
Targets let you reference important elements by name.
23 Jun 2023
Philosophy
- Views are for presentation.
- There should be no
ActiveRecord
queries in views.
- Most logic (
if
statements) should be excluded from views.
Why Not Helpers?
Helpers are better to use if you have a global formatting method that you re-use in different views.
Things like:
- rendering markdown
- showing dates in a specific format
- removing specific words from text
Helpers are not great to overuse because they lack organization and are difficult to reuse across your app.
Using Presenter Objects
- create
app/presenters
.
- name after model,
app/presenters/post_presenter.rb
.
class PostPresenter
def initialize(post)
@post = post
end
def title_without_forbidden_words
@post.title.gsub("forbidden word", "")
end
def css_color
@post.draft? ? "orange" : "green"
end
end
<% presenter = PostPresenter.new(post) %>
<p>
Post title: <%= presenter.title_without_forbidden_words %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
</p>
This accomplishes a few things:
- it removes logic from views
- creates meaningful names for methods and logic
- allows logic to more easily be reused in other views and mailers
Rails Design Patterns: Presenter & Service Objects
22 Jun 2023
TIL something weird.
I expected this to print “Integer”, but it doesn’t:
mystery_class = Integer
case mystery_class
when Integer
p "Integer"
else
p "Unknown"
end
# => "Unknown"
This was weird because a similar example appears to behave differently:
mystery_instance = 0
case mystery_instance
when 0
p "Y"
else
p "N"
end
# => "Y"
Apparently this is because []case
uses ===
under the hood](https://stackoverflow.com/a/3908411) and ===
has kind of silly behavior when comparing classes.
For example:
Array === Array # false
0 === 0 # true
Class === Array # true
apparently case
also works different with ActiveRecord
classes, since they’ll use is_a?
instead which might also behave differently??? 😱
Anyway, if you want to use case
with classes for ActiveRecord
object you need to do this:
mystery_class = User.last
case mystery_class
when User
p "User"
else
p "Unknown"
end
# => "User"