So we checked 2 major ways already of creating public (more or less) static pages:
- brute force drop your HTML into the
app/assets/public
- have custom actions in something like a
PublicPagesController
/StaticPagesController
.
Now I wanted to look at another approach.
Originally, I saw this in David Copeland's book Sustainable Web Development with Ruby on Rails.
He suggests modeling public static pages as resources, like you would do with the dynamic pages, to keep things consistent and more sustainable. After all, the suggestion of the book and of Rails is to keep it REST and rather create resources than custom routes.
So, a very simple example would be for you to have a resource :privacy_policy, only [:show]
. The icky thing with that is that while you are suggesting a single resource here, Rails controllers are not smart enough to acknowledge it, so you'd still need to call it from a PrivacyPoliciesController
. But you most probably have just 1 policy 🤪
While we could live with that or work around it, we could still end up with a lot of stuff and tons of resources that would litter our app, like if we had a resource for each public documentation page or user AB experiment page that marketing is throwing at us.
So, in these cases, you might still want to go with our custom routes approach and namespace those pesky marketing and doc pages:
# routes.rb
namespace :marketing do
get :ab_test_feature1
# ...
get :experiment_99
end
What got me inspired by this approach is that I steered myself into a better type of thinking. I went from having a public pages controller where I would throw in my public pages as custom routes:
class PublicPagesController < ApplicationController
def home
# TODO: Create a landing page for the root route.
end
def major_systems
@major_systems = MajorSystem.for_public_view.preload(:pegs)
end
# ... more custom routes ...
end
To a resource each:
class Public::HomeController < ApplicationController
def show
# We'll show the view here.
end
end
class Public::MajorSystemsController < ApplicationController
def index
@major_systems = MajorSystem.original.language(major_systems_params[:language_iso])
end
def show
@major_system = MajorSystem.find(major_system_params[:id])
end
private
def major_systems_params
params.permit(:language_iso)
end
def major_system_params
params.permit(:id)
end
end
Just by thinking about resources, the public pages became more of a full showcase of my feature. Before that, I was trying to cram everything into one route (PublicPagesController#major_systems
) and then somehow dynamically choose which system to display via JavaScript or Turbo. But now on one of the public feature pages, the users can see a list of features in their preferred language (chosen via dropdown). It reduced the number of dropdowns on the page from 2 to 1, thus simplifying the UX.
There's also a detail page that shows a given resource in more detail that was easy to integrate in the Rails way using Rails partials and co.
You'll also notice that my public "static" pages are not completely static, this is because I like to showcase some functionality in there with actual data, which makes them probably rather public dynamic pages 😬 But on the other hand, the data that I'm showing is fairly static, it just happens to come from the database ;))
Anyway, that's kind of a rough outline of the whole public static pages as a resource approach.
I don't think modeling your public pages as resources is widely used or documented anywhere, but I did like what it did in the end to my thinking, the code, and the final result for the user. Win-win-win.