Creating a Ruby Gem

How I solved my (repetitive) Turbolinks issues and created my own rails generator in the process

Posted on November 15, 2015

From the Turbolinks Repo

Turbolinks makes following links in your web application faster. Instead of letting the browser recompile the JavaScript and CSS between each page change, it keeps the current page instance alive and replaces only the body (or parts of) and the title in the head. Think CGI vs persistent process.

That's pretty cool. Turbolinks is doing some fascinating things. But here’s my problem: at this stage in my career as a developer, whether I’m writing the JS or a colleague is…Turbolinks seems to break our code. Well, to give credit where it’s due, Turbolinks is doing what it’s supposed to but, as a result, our code is behaving in an unexpected way. And someday I’ll really grind through the Turbolinks docs and make my JS work with Turbolinks.

But for now, in every rails app I make I follow Steve Klabnik’s blog post to remove Turbolinks. Easy enough when you have to do that once. But I found myself doing that over and over, sometimes forgetting one step, then double checking to make sure I didn’t miss a step. This process is ripe for human error and is not DRY at all. In fact, whenever I do something twice when writing code I immediately think: “John, you’ve done this before. Do you think it’s necessary to do it again?” Most of the time, the answer to that question is a resounding “Nope.”

In the case of removing Turbolinks from my rails apps, it was a matter of writing a shell script (or something similar) to manipulate the files within an app. Basically, automate the three steps Klabnick outlines. I can even take it a step further and write Ruby code that executes terminal commands. For instance, one of my duties as a TA for Cohort 5's Ruby course at The Iron Yard Durham was to grade student challenges. To do so, I had to create executable Ruby files from Gists submitted by students. So rather than doing this:

                

          mkdir challenge_name
          cd challenge_name
          touch studentname_challenge_name.rb
                
              

repeatedly for each student name, day after day, for every challenge that the students completed, I wrote the following code:

              

          def create_files(challenge)
            `mkdir #{challenge}`
            names = [“Bob”, “Sarah”, “Jim”, “Alex”,
                     “Jessie”, “Tim”, “Lauren”]
            names.length.times do |i|
              `touch ./#{challenge}/#{names[i].downcase}_#{challenge}.rb`
            end
          end

          create_files(ARGV[0])
              
            

Then I just execute the file in the terminal, passing it an argument of that day’s challenge name like so:

              

          ruby create_files.rb challenge_name
              
            

And that gives me a folder populated with files ready to have the students’ Gists pasted in so that I can run their code. Of course, having their names in the file is a little code smelly since it’ll need to be changed for a new class of students, or if the current class changes. And I still need to figure out how to automate populating each file with the Gists, but it’s simple enough now and I learned how to pass an argument from the terminal into a method in Ruby. Neat!

But I digress...back to the Gem!

So I began to research shell commands to remove text from files, add text to files, save changed files, etc. And found myself wondering why I was even putting terminal commands into a Ruby file and mixing languages. Can’t Ruby just do that type of stuff for me? Yes. The answer is yes. Here’s how I solved those problems in Ruby.

First I removed references to Turbolinks from views/layout/application.html.erb and commented out the gem in the Gemfile. This basically boils down to opening a file, replacing text with a gsub, and saving the file.

              

          def remove_turbolinks_from_layout
            file_name = './app/views/layouts/application.html.erb'
            text = File.read(file_name)
            new_contents = text.gsub(/, 'data-turbolinks-track' => true/, "")
            #Write changes to the file:
            File.open(file_name, "w") {|file| file.puts new_contents }
          end

          def remove_turbolinks_from_gemfile
            file_name = './Gemfile'
            text = File.read(file_name)
            new_contents = text.gsub(/gem 'turbolinks'/, "# gem 'turbolinks'")
            File.open(file_name, "w") {|file| file.puts new_contents }
          end
              
            

Finally, the more challenging of the three methods, was removing the line in app/assests/javascripts/application.js, as it involved Ruby’s Tempfile object.

              

          require 'tempfile'

            def remove_turbolinks_from_application_js
              # Open temporary file
              tmp = Tempfile.new("extract")
              # Write good lines to temporary file
              open('app/assets/javascripts/application.js', 'r').each { |l| tmp << l unless l.chomp == '//= require turbolinks' }
              # Close tmp, or troubles ahead
              tmp.close
              # Move temp file to origin
              FileUtils.mv(tmp.path, 'app/assets/javascripts/application.js')
            end
              
            

Nothing too crazy here, and after a few test runs I was able to remove Turbolinks from a Rails app by executing this file from the root of the rails app. Simple enough right? Just take this file, copy it into the root of my app, execute the file (with lines added after the methods to call said methods), and then delete it from the app (or add it to the .gitignore). Yikes…I’ll have to do that for every app I make ad nauseam. Which made me think: There must be a better way...a gem! Gems magically do things for you in apps, I’ll make this a gem. How do I make a gem?

So I asked around, and my instructor/mentor/colleague, Mason Matthews, pointed me towards the gem Jeweler. A gem to make a gem. Pretty meta. Reading the Jeweler docs, as well as this article from Rubygems.org, I made a gem. But how do I run that gem once it’s installed? Reading the Rails Docs (hopefully you’re seeing a common theme here…read the docs) I found out that when a Rails generator is run, all public methods in the generator are executed. This solved my problem of executing the code, but I needed to make a Rails generator. Yeah, a generator like rails generate controller, but for my gem. Cool! Here’s how I made the gem a custom generator:

              

          class RemoveTlGenerator < Rails::Generators::Base
            #insert methods here
          end
              
            

That’s it. Just create a class that is named appropriately and let it inherit from the Rails::Generator::Base and you’re all set. As for the magic naming...this allowed me to execute the following command from the command line and run my gem’s code via rails generate remove_tl.

But the naming - remove_tl - really?!? That naming isn’t the best. Well, near the end of the process I searched Rubygems and found that someone else had already created a gem to do the exact same thing as me. And he got the better name! Oh well, I’ve come this far so I’m going to finish and publish my gem to Rubygems. Besides, I learned some cool things along the way.

Jeweler allowed me to locally install my gem and test it before releasing it on Rubygems. And then Jeweler has a rake command to release the gem to the world rake release ...classy.

And voila…my gem to remove Turblinks can be used by anyone. In fact, in the 5 weeks since I released it there have been over 500 downloads. That's pretty cool! Hopefully I'll figure out another gem to write in the future, and next time I'll be ready to write it quicker.