Hello! Welcome back to ramesh.tv!

Today I'll talk about the updates that have been made over the past week and a little more.

What's New?

  • Dark mode, based on user preference (prefers-color-scheme) and manually settable from the header.
  • Command-line interface with Thor.
  • (CLI) Post file generation, in the style of a Rails generator: thor post:create
  • (CLI) A live, autoupdating blog for debugging and viewing your posts while you write them with just one command: thor render:live
  • SCSS and SASS support using the Ruby sassc module.
  • Stylesheet cleanup with SCSS. I think the end result is much more easily maintainable because of the nesting in SCSS.
  • Turbolinks for being able to load pages without actually having a browser reload, making things seemingly a little faster and a little nicer to look at.
  • Render.rb code cleanup and more manually-controllable commands for rendering parts of the blog
  • RSS feed support if you want notifications on the latest blog posts.
  • Minor post parser updates since I'm rolling my own markup language.
  • The logging library in lieu of messy puts statements.

Most of the changes you see here aren't so user-facing, but you (whoever you are) can always fork my blog, which is licensed under the AGPLv3. You can access the source code here. If you do this, know that my code is going to be quite inflexible for the foreseeable future, since the website's purpose is solely to be a static blog. That might change eventually, though.

There are probably some more minor changes, but these are the major ones I can think of off the top of my head. I'll be describing my experiences implementing some of the larger features.

RSS Feed

Well, this was the first thing I added in the many changes I made. It was one of the easier changes, and surprisingly, the rss library for Ruby is developed by the Ruby developers themselves. I simply had to add a class that took my posts and generated an RSS feed. I chose RSS version 2.0, over something else like Atom. I don't exactly remember why I made this decision but I think it'll be okay.

Also, it seems it's mandatory to include certain pieces of information to the RSS feed, like the title of the feed, the link to the website, and a description. I didn't really realize it was that way until I actually tried it. I've always just used RSS readers blindly in the past.

Anyway, if you want to access it, the link is https://ramesh.tv/rss.xml. The RSS feed is also acessible from the header, which is where I decided to put it for now.

Dark Mode

Initially, I just used the @media (prefers-color-scheme: dark) to change the styles for the website when the user requests dark mode. But I also wanted to have the website change to dark mode manually if a user "preferred" light mode but wanted an override here. The annoyance here, though, was that I had to include JavaScript, and I wasn't too keen on adding JavaScript to my page. Unfortunately, it seemed that adding JS was the only way to go, and because I also had plans to add Turbolinks to the blog, and Turbolinks was JS-based, I bit the bullet and did it. I really don't think a few lines of simple JavaScript will slow down the blog that much.

Adding JS also meant that I had to not use a media query in CSS, and that meant adding a separate class for dark mode to the <html> element. Which also meant I could no longer nest the overrides inside the media query. I had used SCSS before, and knowing it had this feature, I had to add the SASS compiler to the blog (meaning doing the ordeal of editing render.rb to check the static directory for SCSS or SASS files and compiling them as necessary). It does minify the CSS, though, which means that the page will probably load a little faster. I'm not completely sure, though, given that I don't really know too much about website optimization and all that.

SCSS made it easy for me to nest my dark mode overrides in the .darkmode selector that is used to apply dark mode to the page. It also meant I could just select the html tag and add/remove the darkmode class in JS, which wasn't a lot of work. In the end, everything came out okay, except the animations on the header buttons, where I had to make a new animation especially for dark mode. I should also mention that the dark mode wasn't persistent in the beginning, but after adding Turbolinks, it persisted since the page wasn't being reloaded by the browser. I still want it to be persistent across sessions, but that will probably require cookies, which is sort-of a mess.

I think there's still a bug where the dark mode toggler doesn't have the right label sometimes. I don't think it's a huge deal but I'll get around to fixing it sometime.

Turbolinks

Given the ease of adding Turbolinks, I wanted to mention how I did it simply to express my support for Turbolinks. Essentially, when you click on a link in your website pointing to another part of your website, Turbolinks will take over, and instead of your browser redirecting you, Turbolinks will use an AJAX request to fetch the other page you clicked on, and replace <body> with that page.

It makes your website load a little fater, and look a little nicer. In my case, I prefer there to be a progress bar every time I click on a link, since it shows some visual feedback, so I set Turbolinks to show the progress bar every time a page loads on ramesh.tv. Normally, Turbolinks will show the progress bar only if fetching the other page is taking a while, but I changed it. Also, Turbolinks's progress bar is usually blue, but that looks pretty out-of-place on ramesh.tv, so I changed the bar to be black/white based on whether you're in light or dark mode.

I really like the addition of Turbolinks, other than that I have to use npm to install it and load it into the website: you'll notice if you inspect the <head> of this page that the path to Turbolinks has node_modules in it. Also, I have to call npm from Ruby, which makes npm's install status show up in the otherwise clean output when rendering ramesh.tv. I could probably fix all of this, but I'll take using npm if it means that I get Turbolinks and it gets updated too, which is my concern about using something like a CDN or manually downloading it.

A Command-Line Interface

It's a website. Why do I need a command line interface? Well, it was getting a little annoying to have to keep running bundle exec ruby render.rb && cd output && http-server && cd .. all the time. Not to mention that my long command didn't reload the the post while I was editing it, meaning I had to keep going back to the terminal to re-render the blog and view it. Which is, in general, slow, and demotivating.

Now, it's just thor render:live. Also, it doesn't depend on a Node module to run the http server, but uses a native Ruby solution instead, which is a lot nicer. I got the idea to use Thor while seeing how nice generators were in Rails, and while having to write one myself. I learned that Rails used Thor, so that was my choice here.

Thor was a little confusing to set up from scratch, because, for example, you can put your classes in files ending in .thor or in a file called Thorfile. If you want to split up your code, like I did, I didn't want use the .thor option since I wanted to be able to import everything. I ended up dividing the files in to .rb files and importing them into the Thorfile, which works well.

As for the classes themselves, it's basically just Ruby, in all its glory (I'll be doing a post on my thoughts on Ruby soon). While doing the refactor to Thor, I did end up with some annoying things happening, like I had classnames that conflicted with each other, causing Ruby to... monkey patch the imported class and Thor tried to import each and every function in my class as a command, which was not so fun.

I ended up refactoring render.rb at this point so I could have more fine-grained control over how everything was being outputted. For example, I added a command called thor render:clean which wipes the output directory. Sure, I could have used FileUtils.rm_rf here, but using render.clean was just nicer and less repetitive. Also, it prints a statement from the logging library, making the output look nicer than what it might have been by manually removing things. It also lets me add stuff later to the clean method more easily in the future.

Going back to the live render feature, I essentially added the listen gem, which lets me run some code in a block every time files in a specified directory changes. In this case, I told Listener to rerender ramesh.tv every time I change any file in the entire project. While it isn't working with Ruby code just yet (I suspect I'll need to require or something like that), it does work to rerender my posts. Live render also runs a web server based on WEBrick, part of the Ruby standard library to server the static files rendered every time.

Right now, every time I make a change, I have to reload the page in order to see it, which is still an inconvenience. However, the difference between having to go between the terminal, the editor, and the web browser and just having to reload the browser is night and day. I'll get it to reload automatically down the line, but what I have now is perfectly satisfactory for me for now.

Conclusion

For now I think I've explained most of what happened. I might come back and elaborate on this post a bit later, but hopefully this gives you a general idea of what's happening behind the scenes. Here's some things that are going to happen in the future:

  • Multi-language support (this one will probably be the hardest!)
  • More mobile optimizations as the number of tags begin to expand and stop fitting on the screen (which might have already happened)
  • Error pages

That's it for the moment! Stay tuned!