Creating effective ruby gems

845 views

Published on

from Software Design in Ruby Meetup Group

http://www.meetup.com/Software-Design-in-Ruby-Study-Group/

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Creating effective ruby gems

  1. 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. 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. 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 %>
  4. 4. Configuration Options: Outcome Kaminari.configure do |config| config.default_per_page = 25 config.max_per_page = nil end
  5. 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. 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. 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. 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. 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. 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. 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
  12. 12. Gem customization approaches ● YAML file ○ locale files ● DSL block ○ before {}, after {}, scriptorator ● Subclassing ○ devise ● Method Override(reopen gem class) ○ less preferred
  13. 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. 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
  15. 15. Recommended Readings ● ● ● ● http://api.rubyonrails.org/classes/ActiveSupport/Configurable/ClassMethods.html http://edgeapi.rubyonrails.org/classes/Rails/Railtie.html http://edgeapi.rubyonrails.org/classes/Rails/Engine.html http://railscasts.com/episodes/299-rails-initialization-walkthrough

×