Boost PC performance: How more available memory can improve productivity
Creating effective ruby gems
1. SF Software Design in Ruby Meetup Group
Ruby Gem Internals by Examples
Ben Zhang, 1/14/2014
benzhangpro@gmail.com
http://www.meetup.com/Software-Design-in-Ruby-Study-Group/
2. Types of Ruby Gem Features
● Global Singleton
○ typhoeus, twitter
● Single Web Feature (MVC method adding)
○ will_piginate, simple_form
● Mountable Engine
○ rails_admin, resque
● Global Observers
○ airbrake, newrelic_rpm
● Command Line Tool
○ rspec, html2haml
3. Choose a Good Interface
will_paginate
kaminari
Post.paginate(:page => params[:page],
Post.order(:name).page(params[:page]).
:per_page => 30)
class Post < ActiveRecord::Base
self.per_page = 10
per_page(30)
class Post < ActiveRecord::Base
paginates_per 50
end
end
<%= will_paginate @posts %>
<%= paginate @users %>
5. Configuration Options: The How
module Kaminari
def self.configure(&block)
yield @config ||= Kaminari::Configuration.new
end
def self.config
@config
end
class Configuration
include ActiveSupport::Configurable
config_accessor :default_per_page {}
config_accessor :max_per_page {}
end
end
6. Gem Initialization I
#lib/kaminari.rb
require 'kaminari/config'
module Kaminari
require 'kaminari/helpers/paginator'
end
require ‘...’
if defined? Rails
begin
require 'rails'
rescue LoadError
#do nothing
end
require 'kaminari/railtie'
require 'kaminari/engine'
end
#lib/kaminari/railtie.rb
module Kaminari
class Railtie < ::Rails::Railtie
if !defined?(Rails) && !defined?(Sinatra)
initializer 'kaminari' do |_app|
Kaminari::Hooks.init
$stderr.puts “no framework detected.”
end
end
end
end
7. Gem Initialization II
module Kaminari
class Hooks
def self.init
ActiveSupport.on_load(:active_record) do
require 'kaminari/models/active_record_extension'
::ActiveRecord::Base.send :include, Kaminari::ActiveRecordExtension
end
begin; require 'data_mapper'; rescue LoadError; end
if defined? ::DataMapper
require 'kaminari/models/data_mapper_extension'
::DataMapper::Collection.send :include, Kaminari::DataMapperExtension::Collection
end
end
end
end
8. Add methods to controllers
module YourGem
class Feature
def user_agent(request)
end
end
end
ApplicationController.send :include, YourGem::Feature
class UsersController < ApplicationController
def index
user_agent(request)
end
end
9. Add methods to models
# in your code
class Artist
include Mongoid::Document
field :name, type: String
embeds_many :instruments
end
# in your gem
module Mongoid
class Document
def self.embeds_many(association)
end
def self.field(field, options={})
end
end
end
10. Add methods to views
#lib/kaminari.rb
#lib/kaminari/hooks.rb
require 'kaminari/helpers/action_view_extension'
module Kaminari
class Hooks
def self.init
#lib/kaminari/helpers/action_view_extension.rb
ActiveSupport.on_load(:action_view) do
module Kaminari
::ActionView::Base.send :include,
module ActionViewExtension
Kaminari::ActionViewExtension
def paginate(scope, options = {}, &block)
end
end
end
end
end
end
end
11. Add a rake task
#lib/erb2haml.rb
#lib/erb2haml/railties/erb2haml.rake
require 'erb2haml/railtie' if defined?(Rails)
require 'find'
#lib/erb2haml/railtie.rb
require 'erb2haml'
require 'rails'
namespace :haml do
desc "Perform bulk conversion of all html.
erb files to Haml in views folder"
task :replace_erbs do
module ERb2Haml
Find.find("app/views/") do |path|
class Railtie < Rails::Railtie
#convert files
rake_tasks do
load 'erb2haml/railties/erb2haml.rake'
end
end
end
end
end
end
13. Gem customization example
class RegistrationsController < Devise::RegistrationsController
protected
def after_update_path_for(resource)
user_path(resource)
end
end
devise_for :users, :controllers => { :registrations => :registrations }
14. A Good Open Source Contributor
●
●
●
●
●
●
follows conventions others established
cares about backward compatibility
has 100% test coverage of his code
knows when to meta-program
excels at object oriented design
is familiar with various design patterns