Converting a Ruby Class Library to a Gem
source link: https://fuzzyblog.io/blog/ruby/2020/08/12/converting-a-ruby-class-library-to-a-gem.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Converting a Ruby Class Library to a Gem
Aug 12, 2020
This blog post talks about my experiences converting a handful of Ruby class libraries to gems. As do a lot of software engineers, I have a series of routines that I bring into almost every Ruby project I tackle that deal with what I consider core stuff you always need: url handling, database stuff, time parsing and so on. Normally I just copy these from project to project but the sheer plethora of them has recently made me see the need to go down the gem route:
❯ mdfind -name url_common.rb | wc -l
62
All of these files are named *common.rb so you will see a number of these on my Github page. I don't claim that any of them are particularly wonderful, brilliant, complete or even well coded; I simply find them useful.
In order to figure out which was the right version of the 62 different files I found above, I wrote a separate blog post about using mdfind.
The How
Here is the quick tldr of how to build a gem.
- Create a gems directory where you can group all the gems you have. Once you have one, I suspect you're going to have many.
- Change into that gems directory.
- Create a repo for the gem and clone it locally.
- Change into the repo you just cloned.
- Do a gem signin
- Do a bundle gem project_name
- Edit project_name/project_name.gemspec
- Update your Gemfile with any dependencies.
- Change into project_name
- Do a bundle install
- Do a bundle exec rake build; this gives you the pkg/* stuff below (see next command).
- Do a gem push pkg/url_common-0.1.0.gem
- Oh and write the code and the tests. This exists within the lib directory structure.
Tips and Tricks
1. Change into the project_name/lib Directory to Run bundle install
My first attempt at following the directions gave me this:
❯ bundle install
Could not locate Gemfile
And the easy solution was to change into the project_name/lib directory. So:
cd url_common/lib
Yep. I was an idiot for not realizing this. Sigh.
2. Change into the project_name/lib Directory to Run bundle exec rake build
Similar to 1 above, my attempt to run bundle exec rake build gave me this failure:
❯ bundle exec rake build
Could not locate Gemfile or .bundle/ directory
Again. I was an idiot. And, again, same solution – change to the right directory.
3. Use irb for Debugging
In order to debug the gem you are building:
- Change into the project_name/lib directory
- run irb
- require your gem i.e. require 'url_common'
And now you can execute commands from your gem like:
UrlCommon.foo
4. If You Have a Class Library then You Don't Need class project_name
My prior class libraries were all structured like this:
class ProjectName
def self.foo
end
def self.bar
end
end
That now becomes something like this:
require "url_common/version"
require 'any_gem_you_need'
module UrlCommon
class Error < StandardError; end
def self.foo
end
def self.bar
end
end
Given that modules provide a namespace just as a class does and the . syntax invokes methods uniformly, this lets you invoke your "class methods" the same way you did when they were actually class methods.
5. The .try Method is a Rails Thing Not a Ruby Thing
Even though I like the semantic clarity of .try(:method_symbol), you can use &.method_name instead:
#return parts.hostname.sub(/^www\./, '') + parts.try(:path) + '?' + parts.query
return parts.hostname.sub(/^www\./, '') + parts&.path + '?' + parts.query
This is a Ruby 2.3 change so it should be available to everyone by now.
6. Within Your Gem You Can't Reference the Namespace
I hit this error:
1) UrlCommon.url_base should return the url base w/o the www
Failure/Error: base_domain = UrlCommon.get_base_domain(url)
NoMethodError:
undefined method `get_base_domain' for UrlCommon::UrlCommon:Class
# ./lib/url_common.rb:84:in `url_base'
# ./spec/url_common_spec.rb:97:in `block (3 levels) in <top (required)>'
This came from this line of code:
def self.url_base(url, base_domain=nil)
if base_domain.nil?
base_domain = UrlCommon.get_base_domain(url)
end
#...
end
and the fix turned out to be:
def self.url_base(url, base_domain=nil)
if base_domain.nil?
base_domain = get_base_domain(url)
end
#...
end
despite there being a def self.get_base_domain method defined in the module. Shrug
Conclusion
I haven't tried to build a gem in years. Building a gem is substantially easier in 2020 than it was circa 2007 - 2009. Kudos to the entire Ruby tooling team. Recommended.
Sources
See these sources:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK