Ruby: How to Write a Gem

Ruby : How to write a gem?

Rubygems.org's Guides is one of the best resources for writing your own gem.

If you're using Bundler in your app, you might want to look at Ryan Bigg's guide to Developing a RubyGem using Bundler and the Railscast on creating gems with Bundler.

If you're interested in tools to help you write gems:

  • Jeweler - Opinionated tool for creating and managing Rubygem projects. There's also a Gemcutter and Jeweler Railscast.
  • Hoe - From the guys at seattlrb.
  • gem-this adds a bunch of helpful rake tasks.

Some tutorials/guides:

  • Creating Your First Gem
  • Using bundler and rvm to build a rubygem - Using bundler and rvm to create a gem
  • Gem Packaging: Best Practices
  • Ruby Gem Recipe - Intro guide to creating a gem using bundler and jeweler
  • How to build a ruby gem and host it on gemcutter - tutorial using echoe and gemcutter
  • The Truth About Gemspecs - goes over gemspecs and tips for dealing with them
  • Packaging with RubyGems - a quickstart guide for Jeweler
  • gem that - James Adam - reviews tools that help build gems (hoe, newgem, echoe, gemhub, jeweler, gem this)
  • Using Gemcutter's Api from the Commandline
  • New Gem with Bundler – Sample Rakefile - Useful rakefile for deploying and publishing a gem
  • Let's Write a Gem
  • How To Build A Ruby Gem With Bundler, Test-Driven Development, Travis CI And Coveralls, Oh My!

How to write a gem? Preparing with 'lib' directory

You can check out a gem I made if you like

--

Gems

Rails gems are basically just zip-files for your Rails app

When you "install" a rails gem, the gem files inside the gem will actually be put into your Rails application. Take our gem as an example:

Sample Image

The folders marked in Red will be inserted into your Rails application when you install the gem. You can't see them, but they'll be there


Engine

It's my opinion that the majority of Rails gems will be engines:

This means that whenever you create a gem, all you're doing is creating a specific piece of functionality which can be applied to your application as required

A gem will create a module, which will wrap all your gem's functionality. This module will be defined in the lib file you created, and therefore allow you to extend your gem's functionality by inheriting from this module


Views / Controllers

To create views / controllers in your application, you need to remember what I said - the files from your gem will be placed into your app

With that in mind, the way to do it is to create an /app folder, and then put the views or controllers directories in there:

Sample Image

You basically need to create directories which inherit from the module you define in the lib directory of your gem:

-app
|-controllers
|--exception_handler
|---exception_controller.rb

|- views
|-- exception_handler
|--- exception
|---- show.html.erb

-lib
|-exception_handler.rb #-> ExceptionHandler module

This is exactly the same as if you namespace your routes

--

As I've created a gem, you may be best talking to me directly about the process. You can post a comment if you'd like to talk about it further

How to create a Gemfile?

The Gemfile is just a text file within your project which contains a list of gems for use in your application.

If you do not have one in your application, you can create the file using an editor of your choice, saving it as Gemfile (with no extension), and in your example, containing:

source 'https://rubygems.org'

gem 'rspec'

How to create a new Ruby gem?

Use Bundler

From the command line:

bundle gem your_new_gem

This will create a directory called your_new_gem with just a basic set of files and directory structure that are now considered best-practice. It's quick, easy, and a great place to start.

How to create a Rails gem with conditional dependency installation

I wasn't able to really figure out the dependency injection for now. So I tried to take what @engineersmky suggested and created an install for the gems. So for right now I have this as my install generator

require 'generators/base_generator'

module MyGem
module Blueprinter
class InstallGenerator < BaseGenerator
source_root File.expand_path("../../../templates", __FILE__)

# Add blueprinter gem to gemfile after my_gem declaration and bundles the newly declared gem
def install_blueprinter
remove_other_supported_gems('ActiveModelSerializers', 'FastJsonapi')
puts 'Installing Blueprinter...'
insert_into_file('Gemfile',
"\ngem 'blueprinter'",
after: "gem 'dry_serialization', source: 'https://gem.fury.io/my_private_gems/'")
run 'bundle install'
end


def helper_include
copy_api_controller
gsub_file(API_CONTROLLER_PATH, /^\t*(include MyGem::.*)\n/, '')
puts 'Adding include statement to ApiController'
insert_into_file(API_CONTROLLER_PATH,
"\n\tinclude MyGem::Blueprinter",
after: 'class ApiController < ActionController::API'
)
end

end
end
end

Gets called with rails g my_gem::blueprinter::install
This creates an ApiController that inherits from ActionController::Base unless one already exists, inserts gem 'blueprinter' into the gemfile after my_gem's declaration, removes references to other serializer gems, then bundles to install the gem.

I like to separate my controllers into api and application to separate the concerns if I decide on a monolith app :D

Then you can use the dryed up methods that I'm creating in each file for each respective gem :D

How to make a Ruby gem executable

I actually had my executable in the /bin folder.

Turns out my issue was that bundler's gem template is too smart for it's own good, and only includes files that have been committed to git. I hadn't actually committed the executable yet, so it wasn't picking it up:

# gemname.gemspec
gem.files = `git ls-files`.split($\)

Create a Gem for Models to be shared between two applications

How you structure the gem is pretty much up to you, but here's how I would start:

name-of-my-gem/
.. lib/
.... name-of-my-gem.rb
.... models/
...... all_of_your_models

Your models gem should have a .rb file that loads any models stored in it. If this file is named after the gem and placed in the lib/ directory of the gem, it should be auto-loaded by bundler. This file might look something like this:

require "models/customer"
require "models/order"
# ...

You might also do something fancier here, like grab the list of files in the models directory and load each one so you don't have to add each new model to this file. Up to you.

Each models/ file would look exactly the same as a model in your rails app:

class Customer < ActiveRecord::Base
has_many :orders
# ...
end

You can copy any model classes you want to share out of app/models/ in the rails app they currently reside in and paste them into models/ in your gem. Then, you simply need to update the gem in each app (run bundle update name-of-my-gem in each) and you should be able to take the model out of app/models/ and it should still work since it will be loaded from your gem.

One downside of this approach is that rails can no longer reload the model after you make changes. After changing a model in your gem you will need to run bundle update name-of-my-gem, and restart the rails server. I would suggest fleshing out most of a model's functionality in one app first to take advantage of rails code-reloading, and when its pretty stable move it into the gem.

You could also give your gem code-reloading powers, but that gets rather complicated and I've never done that.

Write new gem for using command line on terminal

You might not want to make a gem just yet. what you want - I think - is to make an executable. This does not require a full GEM.

I'll be making 2 assumptions here, you are on a UNIX system, and ~/bin is in your PATH.

First make a file with the name of your program (for this example call proga) in your ~/bin folder.

Start this file with (only first line matters)

#!/usr/bin/env ruby

# your code here
# p gets.strip

We just want to make it executable: chmod +x ~/bin/proga

If you really want to make a command line gem, you can find a good start here: http://robdodson.me/how-to-write-a-command-line-ruby-gem/ and http://blog.excelwithcode.com/build-commandline-apps.html



Related Topics



Leave a reply



Submit