Huzzah! I finally wrestled my Recall code into a legit Ruby Gem!
Big thanks to Flatiron alum Kevin Curtin for his help organizing and connecting the various files.
Here’s a quick summary of the rather arduous process I went through to get the working script into a gem. This outline may not be exactly correct, as it took me a little over a week to finally whip my directory structure and
require_relatives into proper order, but it is roughly how this went down.
1. Bundler’s Skeleton
I ended up using Bundler’s built-in gem skeleton generator. I ran
bundle gem recall in a fresh, empty directory and got a scaffolded-out directory for the gem. But I still had some big hurdles to get over.
2. Gemspec File
Next, an easy bit. I filled out the gemspec file that Bundler gave us with our personal information.
3. The Transfer
I then transferred code (models, a runner, and a template) into the appropriate folders inside the skeleton of directories that Bundler created for me. This involved a lot of guess-and-gem-build.
Models and template: I put both of my model files and the templates folder (containing the one template) into
Runner: My “runner” went into
bin/ and I renamed it
recall with no extension.
That weird recall.rb file: Bundler created a module called
lib/recall.rb for me. I guess I’ve come to think of this file as the bridge between my runner (
bin/recall) and my
lib/recall/ directory, which has all my models and templates.
Why a module? The reason for making it a module is to protect it from naming conflicts. For example, Recall has a model called Results. We wouldn’t want anyone using my gem to have a conflict when they call their Results class. So now my Results model effectively becomes Recall::Results (the :: is the scope operator).
Connecting the “bridge” file to my models and templates: The
lib/recall.rb (again, the “bridge” file) has a
require_relative pointing to each model– Results and SiteGenerator– after the module definition. Here’s the entire file in all its weirdness:
require "recall/version" require 'erb' module Recall end require_relative 'recall/results.rb' require_relative 'recall/site_generator.rb'
The SiteGenerator model calls the template with the
expand_path method, so we didn’t need to
require_relative it here.
Connecting the runner to the “bridge” file (scope operators all over): In
bin/recall, the models Results and SiteGenerator are called as
Recall::SiteGenerator, respectively, to associate them with the
Recall module declared in
lib/recall.rb. Oddly, we also found that we had to change their declarations to include the Recall module and the scope operator.
A Hiccup When Connecting One of the Models to the Template and the Output File
In addition to the problem of my
bin/recall runner not being able to find my Recall module until we put scope operators all over the place, it was also difficult to connect my
SiteGenerator model to the files it needed to access.
SiteGenerator needs access to both the .rb.erb template, located in
lib/recall/templates, to know how to render the search results, as well as the temporary file where it dumps the search results. Kevin recommended I try File’s expand_path method for these connections and that did the trick. Here’s how we connected to the template:
template_file = File.expand_path("../templates/sublime.rb.erb", __FILE__)
And here’s how we connected the SiteGenerator model to the output file it writes to:
output_file = File.expand_path("../_site/ruby_file.rb", __FILE__)
(I’m still not 100% why this was necessary.)
As a recap, here is the current directory structure:
4. Building the Gem (over and over again)
Now that we’ve got the directory structure and file connections just right, it’s time to build our gem locally and see if it works! (OK so in actuality I attempted and failed to build this gem correctly numerous times. Each time I made a change to the code, I’d have to re-build it and re-install it to see what affect the change had on how the gem performed.)
To build the gem locally, you run the following command:
gem build recall.gemspec
This creates a new .gem file (with the version number appended) in your gem’s root directory. You then can run
gem install recall to install the gem locally and then you can try it out. Whenever I needed to make another change, I’d gracefully
.gem file, re-build and re-install the gem locally, and try again. Thankfully, you are able to keep the same version number throughout this process so the number of times I had to do this will be forever unknown to others.
5. Publishing the Gem to RubyGems.org
Once I successfully built the gem locally, and liked how it functioned, publishing the thing was surprisingly easy. As per the RubyGems’ publishing guide, I simply created an account at RubyGems.org then and ran
gem push recall-0.0.2.gem in my local directory. To test it, I first uninstalled my local copy of version 0.0.2, then ran
gem install recall and lo and behold it seems to work!
In preparation for its public debut, I added a good amount of flow control to the runner to allow users to set and change the search directory as they please. Their search directory choice is stored in a .txt file called search_map, which I put in
lib/recall. If that file is empty or for some reason doesn’t exist, users are prompted to enter the absolute path of their desired search directory when they first call the gem. After that it’s set, although users can edit the search directory by “searching” for
I think that about covers it. Give it a try? You can always uninstall it!
For more info be sure to visit the GitHub repo. As usual, there’s plenty of improvements to be pull-requested and merged.