This is a first part of my Ruby Tricks & Quirks serie. I’ll present some non-obvious techniques available in beloved Ruby.

As you probably know in Ruby one can inspect almost everything. Available variables, methods or constants to name a few. But is it possible to check the number of method arguments? Sure it is! Hey, it’s ruby impossible is nothing ;)

The trick is achieved by using arity method.

class Logger

    def log(message)
        $stderr.puts message
    end

end

puts Logger.instance_method(:log).arity # => 1

The most popular use of arity is to check the number of arguments accepted by block.

proc { |a, b, c| a + b + c }.arity # => 3

However now we know that arity applies to methods as well.

Let’s digg deeper and see what does arity return for blocks and variable length of arguments.

class Logger

    def initialize(stream, *args)
        @stram = stream
        @args  = args
        @silent = false
    end
    
    def log(*messages)
        @stream.write messages.join("\n") unless self.silent
    end

    def silence(&block)
        old_silent = @silent
        @silent = true
        yield
    ensure
        @silent = old_silent
    end
end

(Logger.instance_methods - Object.instance_methods).each do |method|
    puts "#{method} takes #{Logger.instance_method(method).arity} arguments"
end

Output of the code above:

    silence takes 0 arguments
    log takes -1 arguments
    initializer takes -2 arguments
  • block doesn’t count as argument so arity of silence equals 0
  • log method has variable length of arguments and it’s arity equals -1
  • if method accepts both mandatory arugments and varaible length of them then
  • arity equals (-1 - NUMBER_OF_MANDATORY_ARGUMENTS). In that case arity of initialize equals -2
Filed under: ruby and tricks-and-quirks
Comments

AutomaticForeignKey allows you to create foreign keys easier than ever.

Particularly lots of migrations doesn’t need any change. Let’s dive into it quickly.

class CreateGroups < ActiveRecord::Migration
  def self.up
    create_table :groups do |t|
      t.string :name, :null => false, :limit => 50
      t.integer :user_id, :null => false

      t.timestamps :null => false
    end
  end

  def self.down
    drop_table :groups
  end
end

In that case we’ll have foreign key on groups(user_id) referencing users(id).

But wait a moment. This one looks like ordinary migration. Where did you define foreign key? Well, automatic_foreign_key is smart enough to be predict that user_id column should be referenced to users(id).

Obviously we could customize the behaviour.

    # force user_id to reference admins table
    t.integer :user_id, :null => false, :references => :admins
    # don't create foreign key on application_id column
    t.integer :application_id, :references => nil

It’s dead easy, isn’t it?

If you want to see more just go to github page or start using it now!

$ gem install automatic_foreign_key

Let’s take a look at what brings version 1.2 to us.

Rails 3 generator

New generator creates automatic_foreign_key initializer:

$ rails g automatic_foreign_key:install
  create  config/initializers/automatic_foreign_key.rb

Currently 3 options are supported:

  • on_update - default ON UPDATE action (available options are :cascade, :restrict, :set_null, :set_default, :no_action)
  • on_delete - default ON DELETE action (available options as above)
  • auto_index - automatically create indexes on foreign keys

Fixed auto-index when restoring schema

Previously restoring schema may have led to duplicated indexes. Currently the problem is solved.

Upgraded to RSpec 2

Specs are compatible with Rspec 2.4.0.

Bundler instead of Jeweler

Gemspec is maintained by bundler. You can read more about this change on my blog post.

Filed under: automatic_foreign_key and rails
Comments

Recently I’ve changed some of my gems to use bundler in favor of jeweler. In short both make generating gemspec easier (obviously bundler can do way more than that). Actually I use bundler anyway so why have another dependency in form of jeweler? To compare both let’s take a look at how they work for us. We’ll use jeweler 1.5.2 and bundler 1.0.7.

Creating files structure

$ jeweler mygem
	create	.gitignore
	create	Rakefile
	create	Gemfile
	create	LICENSE.txt
	create	README.rdoc
	create	.document
	create	lib
	create	lib/mygem.rb.rb
	create	test
	create	test/helper.rb
	create	test/test_mygem.rb.rb

Creating basic structure is actually the biggest strength of jeweler.

 $ jeweler --help 

Typing that will list dozens of available options. Choosing test framework, initializing git repo or specyfing github credentials to name a few.

How that compares to bundler?

$ bundle gem mygem
  create  mygem/Gemfile
  create  mygem/Rakefile
  create  mygem/.gitignore
  create  mygem/mygem.gemspec
  create  mygem/lib/mygem.rb
  create  mygem/lib/mygem/version.rb
Initializating git repo in /home/snatcher/temp/garbage/mygem

Slighty less files were created. We don’t have LICENSE and test framwork files included by default. List of available options is also very thin.

$ bundle help gem  
  -b, [--bin=Generate a binary for your library.]  
      [--no-color=Disable colorization in output]  
  -V, [--verbose=Enable verbose output mode] 

Bundler’s approach is to generate required files only. Tasks not related directly to gemspec are up to developer.

Handling gem version

Jeweler prefers to keep version number in text file named VERSION

$ cat VERSION
0.0.1

On the other hand bundler creates version.rb file and assignes number to VERSION constant

module Mygem
    VERSION = "0.0.1"
end 

In my opinion bundler approach is way better as gem version is available always when the code is loaded.

puts Mygem::VERSION # => 0.0.1 

Of course using Jeweler we’re also able to have version as constant but bundler gives us it for free.

Gem metadata

With jeweler things like summary, description and author are written to Rakefile. Basing on data in Rakefile jeweler creates gemspec. In that case gemspec is meant to readonly and we put all metadata into Rakefile. It leads to duplication as we have the same parts in both Rakefile and gemspec.

Jeweler::Tasks.new do |gem|
  gem.name = "redhillonrails_core"
  gem.summary = %Q{Adds support in ActiveRecord for foreign_keys 
 and other database-related stuff}
  gem.email = "michal.lomnicki@gmail.com"
  gem.homepage = "http://github.com/mlomnicki/redhillonrails_core"
  gem.authors = ["Michał Łomnicki"]
end

To generate gemspec rake task needs to be invoked

$ rake gemspec
Generated: redhillonrails_core.gemspec
redhillonrails_core.gemspec is valid.

Bundler prefers slightly different approach. We edit gemspec itself

Gem::Specification.new do |s|
  s.name        = "exceptioner"
  s.version     = Exceptioner::VERSION
  s.platform    = Gem::Platform::RUBY
  s.authors     = ["Michał Łomnicki"]
  s.email       = ["michal.lomnicki@gmail.com"]
  s.homepage    = "https://github.com/mlomnicki/exceptioner"
  s.summary     = "Stay notified of exceptions raised by 
 your application."
end

No additional work is needed. Gemspec is first class citizen of our gem.

Dependencies

Jewler encurages us to use bundler so we define dependencies using beloved Gemfile.

source :rubygems

gem "activerecord"

group :development, :test do
  gem "jeweler", "~> 1.5"
  gem "rspec", '~> 1.3'
  gem "pg"
  gem "mysql"
end

That’s very familiar technique for everyone who is used to bundler.

On the other hand content of Gemfile created by bundle gem command is somehow surprising.

source "http://rubygems.org"

gemspec

So what is a preferred place to set gem dependencies? Actually bundler follows its philosophy and encurages us to put dependencies in gemspec file.

Gem::Specification.new do |s|
  # metadata goes here

  s.add_dependency("mail", ["~> 2.2"])
  s.add_dependency("xmpp4r", ["~> 0.5"])

  s.add_development_dependency("rack")
  s.add_development_dependency("mocha")
end

Invoking bundle install will still work as expected.

$ bundle install
Using rake (0.8.7) 
Using activesupport (3.0.3) 
Using i18n (0.5.0) 
Using mime-types (1.16) 
Using polyglot (0.3.1) 
Using treetop (1.4.9) 
Using mail (2.2.14) 
Using xmpp4r (0.5) 
Using exceptioner (0.0.5) 
Using mocha (0.9.10) 
Using rack (1.2.1) 
Using bundler (1.0.7) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

Releasing

Both jeweler and bundler has similar functionality here.

$ rake release

Invoking the task above will:

  • build a gem from gemspec
  • tag and push code to the repo
  • push gem to rubygems

Summary

Both jeweler and bundler do great job doing tedious work for us. Bundler is younger brother of jeweler which introduced some very good improvements. Jeweler is still more powerful and flexible. Bundler is thinner and relies more on gemspec itself. Personally I’ve changed from Jeweler to Bundler and I’m pretty satisfied with that.

Filed under: ruby and rubygems
Comments
WrocLove.rb