Thursday, February 3, 2011

creating a gem

this whole post is useless but i already wrote it so i'm posting


this video goes over everything in this post, except for conventional directory and file layout and the bin/ directory. but i didn't find it until i was about to hit publish. oh wells.

scattered notes


it's been difficult to find a tutorial on building a gem. it seems as though many ways have come and gone, and not gone. bundler can create gems. gem can create gems if you set up the files correctly. jeweler. new gem. hoe. echoe... well bundler gem some_gem_name creates a skeleton project for you and makes it into a git repo for you so you can start off clean. that's right, it creates a skeleton and makes it a git repo. awesome. and it uses git to specify which files are included in the gem. check out some_gem_name/some_gem_name.gemspec after running that command. you'll see something like:
s.files         = `git ls-files`.split("\n")
s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables   =
`git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }

so it makes sense that bundler starts off the project under git control. this also forces me to think about what exactly is and isn't in git.

gem structure conventions


from reading through actionmailer and some other gems, i've found that the basic structure is to mirror ruby class and module nesting th directory structure. after running bundle gem some_gem_name a new directory called some_gem_name is created in the current directory. under that is Gemfile, some_gem_name.gemspec, lib/ some other stuff. under lib/ is some_gem_name/ and some_gem_name.rb. in some_gem_name.rb is a skeleton with a module named SomeGemName! i guess bundler just uses the convention of taking out underscores and capitalizing the next letter, and capitilizing the first letter too. so that's cool.

so now we're in lib/some_gem_name/


in lib/some_gem_name/ is a file named version.rb. in that is simply:
module SomeGemName
VERSION = "0.0.0"
end

based on what i've read in actionmailer and thor, this is also where the meat goes. the guts of the code that make the gem do what it does. every file that does something important and lib like should go in here. and it should begin with some requires, and all the code should be between a [module|class] SomeGemName and an end. in addition, any directory under this one (for example lib/some_gem_name/foo_bar) should be the name of a module or class, with the first capital letter made lower, and every following capital made lower and preceded by an underscore. so the directory foo_bar corresponds to the class or module FooBar. furthermore, the nesting of those classes and modules in ruby should reflect the nesting of the directories on the filesystem. for example lib/some_gem_name/foo_bar/ should contain files that begin with some requires, followed by opening the class|module SomeGemName and then opening the class|module FooBar.
for example, inside lib/some_gem_name/foo_bar/foo_bar.rb, you would find something like:
require 'nokogiri'

module SomeGemName
class FooBar
def baz directance
fronlum = turboencabulate directance
fronlum.dipits
end
end
end

another convention seems to be having a name.rb as a sibling to every lib/**/name/. so there's a some_gem_name.rb corresponding to lib/some_gem_name/ and located at lib/some_gem_name.rb. another example, there should be a foo_bar.rb for lib/some_gem_name/foo_bar/ located at lib/some_gem_name/foo_bar.rb

bin/


there's also the option of creating a directory bin/ as a sibling to lib/. this is what bundler does. it looks like the convention is to use a file with no extension. that's how bundler creates the bundle command. so that's cool. within these files, you can just require 'some_gem_name' and use the stuff in your gem libs. the context is already set up so your some_gem_name

rake


right after running bundle gem some_gem_name && cd some_gem_name you can do rake -T to see a list that looks like this:
rake build    # Build template_validator-0.0.4.gem into the pkg directory
rake install  # Build and install template_validator-0.0.4.gem into system ...
rake release  # Create tag v0.0.4 and build and push template_validator-0.0...

so after writing the code into the locations described above, you can run rake build && rake install && irb -r some_gem_name to test out the gem you just created! so that's awesome.

actionmailer has a neat trick

to turn some code i wrote into a gem, i decided to read through other gems for examples of how to do it. while reading through the actionmailer gem installed with rails 3.0.3, i found this /home/ozzloy/.rvm/gems/ruby-1.9.2-p136@rails3tutorial/gems/actionmailer-3.0.3/lib/rails/generators/mailer/templates/mailer.rb:

class <%= class_name %> < ActionMailer::Base
default :from => "from@example.com"
<% for action in actions -%>

# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.<%= file_path.gsub("/",".") %>.<%= action %>.subject
#
def <%= action %>
@greeting = "Hi"

mail :to => "to@example.org"
end
<% end -%>
end

so that's cool! it's using ruby to generate ruby code. metaprogramming, but not by manipulating classes and modules directly. instead generating the source code to create modules and classes, which i presume gets printed to a file with scaffolding. and probably also eval'd so as to keep DRY. anyways, neat.

Wednesday, February 2, 2011

ruby 1.9 gem 1.5 bundler 1.0.9 problem fixed

bundler 1.0.10 fixed whatever problem it had with gem 1.5. just have bundler at or above version 1.0.10 and gem 1.5, or bundler 1.0.9 or below with gem 1.4. and all of this seems to only have been an issue on ruby 1.9.*. i saw no reports of problems on ruby 1.8.?

Where am I?