Rails and Docker

    Docked

    Setting up Rails for the first time with all the dependencies necessary can be daunting for beginners. Docked Rails uses a Rails CLI Docker image to make it much easier, requiring only Docker to be installed.

    I have never been efficient with Docker in the past, but the brevity of this Docker file and knowing the hoops I had to jump through over the years with homebrew, rbenv, and more over the years is making me think it might be time to revisit a docker setup and work env.

    David Kimura posted a comment with a more elaborate setup, but this still feels much simpler than what I am doing today.

    RailsBytes

    There are many templates to jump-start a project and few options to add something to an existing project.

    Ruby on Rails templates allow you to add features to both old and new apps. Check out our repository of templates for adding everything from authentication to error monitoring to your apps.

    See: RailsBytes

    A Rubyist’s Introduction to Character Encoding, Unicode and UTF-8

    …learn how character encodings work and how they’re implemented in Ruby. By the end, you’ll be able to understand and fix these errors….

    Starting the explanation with Morse Code was a nice touch.

    Adding Paging Titles for Short Micro.blog Posts

    I noticed posts without titles do not have proper HTML Title tags on my new Micro.blog.

    Even as I type this, I kind of get it. How do you have a title without a title? 🤔

    Many services rely on a title element when building links, and seeing “ScottW’s Blog” (or whatever I eventually name this thing) looks silly.

    I checked the source for a couple other Micro.blog templates and found none of them did anything special to handle the missing title. Most looked something like this:

    1
    
    <title>{{ if not .IsHome }}{{ .Title }} - {{ end }}{{ site.Title }}</title>
    

    After some trial and error, I noticed the .Summary value for the default meta description tag and decided to use that. This is the first ‘code’ I have written in Hugo, so it might need some more tweaking in the future:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
      <title>
          {{ if .IsHome }}
            Home
          {{ else if .Title }}
            {{ .Title }}
          {{ else}}
            {{ .Summary }}}
          {{ end }}
          - {{ .Site.Title }}
      </title>
    

    CSS Loaders 100 sample loaders are all done with CSS. I continue to be amazed at what can be done with CSS, even with a personal goal to write as little CSS as possible for the rest of my life. 😄

    Rails Safety Mechanisms

    An overview of some of the ways Rails protects you from yourself.

    Percent notation in Ruby

    You can put this in the “the more you know” category.

    Phlex

    Phlex is a framework for building fast, reusable, testable views in pure Ruby.

    I look forward to trying Phlex for a small project in the next couple of weeks.

    I am still a fan of ViewComponent as well, but the markdown support for Phlex might be a game changer for me.

    ShortRuby - The best part about checking email on Monday morning is finding a new ShortRuby newsletter.

    This is a weekly newsletter about what happens in Ruby’s world (mainly on Twitter) curated by @lucianghinda

    The CodingFont game is excellent, but what if you want to do a quick test drive?

    Well, here you go Programming Fonts.

    Programming Fonts Screenshot

    CodingFont is a fun bracket game that helps you pick a coding font.

    I recommend checking the Hide Font Names option.

    My winner was Azeret Mono. A younger me would have picked a smaller font, but I will give this one a shot for a couple weeks.

    Optimizing ActiveRecord SQL Memory Usage in Rails

    A decent suggestion by Patel Urbanek - Easy to Miss Way to Optimize ActiveRecord SQL Memory Usage in Rails.

    Urbanek recommends using select in your ActiveRecord queries to limit the fields returned and thus save memory. While this certainly will work, I find it messy long term.

    1. It is not obvious what data you have access to. Your code appears to have access to the user object, but most of the data is missing.
    2. Similar to #1, other helpers/decorators/etc. that you have in your codebase may have model requirements that are not obvious as well. You pass along this partial object, and you may have some unintended consequences.
    3. If you use any kind of caching, you may accidentally expose this partial object to completely unaware parts of your app

    I typically use two approaches:

    1. pluck - As suggested in the article, you may be giving up some of your model niceties. Still, it is usually a worthwhile trade-off if the amount of data returned warrants optimization. It can also help expose some code that should not live directly within your model.
    2. pluck and Struct - when using pluck gets too messy, you can always take the data returned and load it into a struct (or a PORO).

    A First Look at Hanami

    I have not spent any time with Hanami, but I am hoping to change that.

    I like Sinatra when your goal is to respond to requests and do not need the full Rails functionality and ecosystem.

    Hanami looks like it fits somewhere between the two.

    Finding Duplicate Charactes in Multiple Arrays with Ruby

    We needed to find duplicate characters in multiple arrays for Saturday’s Advent of Code.

    In the first part of the problem, it was among two arrays, so I did something quick like this:

    1
    
    first_array.select { |char| second_array.include?(char) }
    

    This worked as expected, but in part 2, you needed to perform a similar search across three arrays.

    I could have chained a second select or duplicated the line entirely, but I figured there was a more straightforward way…and of course, in Ruby, there is almost always a more straightforward way.

    I do not have a traditional comp sci background, so my mind rarely jumps to Bitwise operators, but in this case, this is the Bitwise & operator is exactly what we needed.

    @scott pointed this is not Bitwise when dealing with strings:

    It is the unary and operator. On arrays, #& is a method that returns the set union of two arrays. On integers, it is indeed implemented as a bitwise and.

    1
    2
    3
    4
    5
    
    a = "vJrwpWtwJgWrhcsFMMfFFhFp".chars
    b = "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL".chars
    c = "PmmdzqPrVvPwwTWBwg".chars
    
    duplicates = a & b & c
    

    Even better, this easily scales across N arrays.

    I have not had much use for Sizzy in while, but fired it up today and was instantly re-impressed. One of the more indispensable tools for frontend web development.

    Bare Metal FizzBuzz

    If I were deploying this somewhere, I would lean towards something that looks more like the readable_solution below.

    But for the sake of the challenge:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    def solution(number)
     "#{"Fizz" if (number % 3).zero?}#{"Buzz" if (number % 5).zero?}"
    end
    
    raise unless solution(3) == "Fizz"
    raise unless solution(5) == "Buzz"
    raise unless solution(15) == "FizzBuzz"
    
    puts "It works!"
    
    

    If this were a one-liner competition, I would like my chances. 😄

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    def readable_solution(number)
     modulo_3 = (number % 3).zero?
     modulo_5 = (number % 5).zero?
    
     if modulo_3 && modulo_5
       "FizzBuzz"
     elsif modulo_3
       "Fizz"
      elsif modulo_5
        "Buzz"
      end
    end
    

    Heroku + CloudFlare + SSL

    Heroku has had pretty good free’ish SSL support for a couple of years. You add your domain, enable auto certs, and enter a DNS record.

    1
    2
    
    heroku domains add www.mkrank.com
    heroku certs:auto:enable
    

    Heroku makes it super easy, but it does come with one big drawback: Heroku does not support root (apex) level domains. Whether you should use your root domain or subdomain and if it matters is a debate for another day. I only care about the user experience around setting up a root-level SSL cert and whether it is easy to set up and maintain.

    Lack of Heroku certs on root-level domains is usually not a big deal. Most of your traffic likely comes from something other than a person typing a URL directly into a browser (and even then, autocomplete based on browsing history helps guide visitors back). But if we could do it and it was easy to do, then why not?

    In practice, you would not want to CNAME your apex domain since it likely hides their records in the same node1.

    However, a couple of DNS providers support “aliasing” apex-level domains without the side effect of making other records in that node unavailable. As far as I know, this is not an actual spec/standard yet, but its support is pretty solid and has been growing over the years.

    The three I know who support it today are:

    • DNS Made Easy
    • DNSimple
    • CloudFlare

    Both DNS Made Easy, and DNSimple requires you to choose to use this type of alias record explicitly. CloudFlare automatically does this if you add a CNAME for your root domain (regardless of whether it is on accident or purpose). I am not sure which is better, but every six months, I spend 30 minutes trying to remember how to do this in CloudFlare2.

    Anyway, back to CloudFlare, I have been using them more and more for my projects. In addition to great DNS and pretty good support, they have a growing list of exciting features, such as workers.

    So how do we get going? To use CloudFlare for SSL for Heroku:

    1. Add your domain to Heroku (heroku domains add mkrank.com)
    2. Take the CNAME record they give you and add it to CloudFlare3

    SSL will work, but it comes with a minor caveat. The traffic from your clients' browsers to CloudFlare will be secure. However, from CloudFlare to your Heroku app, it will not be. Chances are this is not an issue but a weak link. I did not even notice the weak link until I published a new app, MKRank4.

    I built this app as a fun side project, and I want nothing to do with managing emails/passwords/etc., so I opted to use OmniAuth and enabled signing in with various existing sites.

    Everything worked locally, but when I first deployed MKRank, I could not sign in with any third-party site. After a bit of debugging, I realized the callback URL from my app did not match what I had configured in each third-party app (all are hard-coded to use SSL).

    So what was going on?

    Well, if you remember from above, communications between the client and CloudFlare were happening over SSL. However, CloudFlare was communicating back to my app over HTTP. Since my app received non-SSL traffic, the URLs it generated for OmniAuth did not use HTTPS.

    One fix would be to force OmniAuth somehow always to use SSL in production (this might be a good idea either way).

    However, a better fix would be to enable SSL communication between CloudFlare and my app.

    It turns out this is very simple. All we must do is add the Heroku auto certs feature (heroku certs:auto:enable). Once in place, Heroku will accept SSL traffic for our app.

    The final step is to go back into CloudFlare and enable full SSL.

    Full SSL

    Now, all communication from the client (browser) → CloudFlare → Heroku is secure. Logins with OmniAuth will now generate the proper callback urls, and no additional configuration or monkey-patching was necessary.


    1. Everyone sometimes finds this out the hard way when their email is no longer available because MX records are no longer available. ↩︎

    2. Actually, who am I kidding? I prefer being explicit, but as someone who does product support, I 100% get why they just made it so. ↩︎

    3. you should create a second CNAME on the www domain. Once in place, you can use CloudFlare’s page rules to redirect all www. traffic to the non-www URL. ↩︎

    4. MKRank is an app for the mechanical keyboard community. It allows you to build lists of your favorite mechanical keyboard products. ↩︎

← Newer Posts