Rails Plugin Development 101 (...and some...)
Upcoming SlideShare
Loading in...5
×
 

Rails Plugin Development 101 (...and some...)

on

  • 2,633 views

A basic introduction of Ruby on Rails plug-in development which goes on to more advanced plug-in development.

A basic introduction of Ruby on Rails plug-in development which goes on to more advanced plug-in development.

Statistics

Views

Total Views
2,633
Views on SlideShare
2,620
Embed Views
13

Actions

Likes
2
Downloads
24
Comments
1

2 Embeds 13

http://www.slideshare.net 10
http://coderwall.com 3

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Rails Plugin Development 101 (...and some...) Rails Plugin Development 101 (...and some...) Presentation Transcript

  • Ruby on Rails: Plug-in Development 101 ...and some... http://jimeh.me/ by Jim Myhrberg
  • The Basics Helpers Controller Methods Controllers, Helpers & Views
  • The Basics to get you started
  • Generate a Plug-in $ script/generate plugin hello_world
  • install.rb is executed once during installation. init.rb is the only file included by Rails. The lib folder is your sanctuary. Aside from hello_world.rb, place all Ruby source files in lib/hello_world/ to avoid naming collisions.
  • Example: Collecta_ruby
  • Single-file Plug-in install.rb copies collecta.yml to Rails’ config folder during installation. init.rb requires lib/collecta.rb, loads settings from installed collecta.yml and applies them to the Collecta class. lib/collecta.rb is the ‘heart’ of the plug-in.
  • install.rb require "rubygems" require "fileutils" dir = File.dirname(__FILE__) templates = File.join(dir, "templates") files = [ File.join("config", "collecta.yml") ] files.each do |file| if !File.exist?(File.join(RAILS_ROOT, file)) FileUtils.cp File.join(templates, file), File.join(RAILS_ROOT, file) end end
  • init.rb if defined? Rails require "collecta" config_file = File.join(RAILS_ROOT, "config", "collecta.yml") if File.exist?(config_file) config = YAML.load_file(config_file) if !config[RAILS_ENV.to_s].nil? && !config[RAILS_ENV.to_s]["api_key"].nil? Collecta.api_key = config[RAILS_ENV.to_s]["api_key"] end end end
  • collecta.rb require "rubygems" require "net/http" require "uri" require "cgi" require "json" require "xml" class Collecta @@api_key = nil @@api_url = "http://api.collecta.com/search" # rest of the class... end
  • Example: Facebooker Plus
  • Multi-file Plug-in init.rb requires all needed files from lib folder, and calls an init method too boot the plugin. Notice how all files are located under lib/facebooker_plus/. This avoids any naming collisions from other plug-ins, gems, or system.
  • init.rb if defined? Rails if defined? Facebooker require 'facebooker_plus/facebooker_plus' require 'facebooker_plus/rails/fb_sig_add' require 'facebooker_plus/rails/controller' require 'facebooker_plus/rails/helper' require 'facebooker_plus/extensions/action_controller' require 'facebooker_plus/extensions/action_view' require 'facebooker_plus/extensions/session' FacebookerPlus::Base.init(defined?(config) ? config : nil) else STDERR.puts "** [FacebookerPlus] ERROR: Please load Facebooker before Facebooker Plus.n" end end
  • Helpers create or overload helpers
  • One of the simplest things to implement in a Plug-in.
  • action_view.rb
  • action_view.rb ActionView::Base.send(:include, FacebookerPlus::Rails::Helper)
  • action_view.rb
  • helper.rb module FacebookerPlus module Rails module Helper def url_for(options = {}) options.is_a?(Hash) ? super(options) : fb_sig_add(super(options)) end def form_for(record_or_name_or_array, *args, &proc) args[0][:url] = fb_sig_add(args[0][:url]) if !args[0][:url].nil? super(record_or_name_or_array, *args, &proc) end end end end
  • Controller Methods access custom methods in all controllers
  • Makes it easy to control different aspects of your plug- in from within controllers. Easily create global before/after filters which run from your plug-in. Create class methods to enable/disable your plugin on a per-controller basis.
  • action_controller.rb
  • action_controller.rb module ::ActionController class Base def self.inherited_with_facebooker_plus(subclass) inherited_without_facebooker_plus(subclass) if subclass.to_s == "ApplicationController" subclass.send(:include, FacebookerPlus::Rails::Controller) end end class << self alias_method_chain :inherited, :facebooker_plus end end end
  • controller.rb
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods end def send_p3p_headers if !params[:fb_sig_in_iframe].blank? headers['P3P'] = 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"' end end def url_for(options = {}) fb_sig_add(super(options)) rescue super(options) end module ClassMethods def init_facebooker_plus(options = {}) before_filter :send_p3p_headers end end end end end
  • Fancy initialization
  • application_controller.rb class ApplicationController < ActionController::Base init_facebooker_plus(:app_class => "App") end
  • application_controller.rb class ApplicationController < ActionController::Base init_facebooker_plus(:app_class => "App") end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods end def set_facebooker_plus_options(options = {}) @facebooker_plus_options = options end def apply_facebooker_options(options = {}) if @facebooker_plus_options.has_key?(:app_class) then end end def create_session_cookie_if_needed # magic happens here end module ClassMethods def init_facebooker_plus(options = {}) before_filter { |controller| controller.set_facebooker_plus_options(options) } before_filter :create_session_cookie_if_needed before_filter :apply_facebooker_options end end end end end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods end def set_facebooker_plus_options(options = {}) @facebooker_plus_options = options end def apply_facebooker_options(options = {}) if @facebooker_plus_options.has_key?(:app_class) then end end def create_session_cookie_if_needed # magic happens here end module ClassMethods def init_facebooker_plus(options = {}) before_filter { |controller| controller.set_facebooker_plus_options(options) } before_filter :create_session_cookie_if_needed before_filter :apply_facebooker_options end end end end end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods end def set_facebooker_plus_options(options = {}) @facebooker_plus_options = options end def apply_facebooker_options(options = {}) if @facebooker_plus_options.has_key?(:app_class) then end end def create_session_cookie_if_needed # magic happens here end module ClassMethods def init_facebooker_plus(options = {}) before_filter { |controller| controller.set_facebooker_plus_options(options) } before_filter :create_session_cookie_if_needed before_filter :apply_facebooker_options end end end end end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods end def set_facebooker_plus_options(options = {}) @facebooker_plus_options = options end def apply_facebooker_options(options = {}) if @facebooker_plus_options.has_key?(:app_class) then end end def create_session_cookie_if_needed # magic happens here end module ClassMethods def init_facebooker_plus(options = {}) before_filter { |controller| controller.set_facebooker_plus_options(options) } before_filter :create_session_cookie_if_needed before_filter :apply_facebooker_options end end end end end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods end def set_facebooker_plus_options(options = {}) @facebooker_plus_options = options end def apply_facebooker_options(options = {}) if @facebooker_plus_options.has_key?(:app_class) then end end def create_session_cookie_if_needed # magic happens here end module ClassMethods def init_facebooker_plus(options = {}) before_filter { |controller| controller.set_facebooker_plus_options(options) } before_filter :create_session_cookie_if_needed before_filter :apply_facebooker_options end end end end end
  • Controllers, Helpers & Views full controllers, helpers and views in your plug-in
  • Very useful in some scenarios when complex functionality is needed. New Relic’s RPM plug-in uses it to display application performance under http://localhost:3000/newrelic. Decently complex to setup.
  • init.rb if defined? Rails if defined? Facebooker require 'facebooker_plus/facebooker_plus' require 'facebooker_plus/rails/fb_sig_add' require 'facebooker_plus/rails/controller' require 'facebooker_plus/rails/helper' require 'facebooker_plus/extensions/action_controller' require 'facebooker_plus/extensions/action_view' require 'facebooker_plus/extensions/session' FacebookerPlus::Base.init(defined?(config) ? config : nil) else STDERR.puts "** [FacebookerPlus] ERROR: Please load Facebooker before Facebooker Plus.n" end end
  • init.rb if defined? Rails if defined? Facebooker require 'facebooker_plus/facebooker_plus' require 'facebooker_plus/rails/fb_sig_add' require 'facebooker_plus/rails/controller' require 'facebooker_plus/rails/helper' require 'facebooker_plus/extensions/action_controller' require 'facebooker_plus/extensions/action_view' require 'facebooker_plus/extensions/session' FacebookerPlus::Base.init(defined?(config) ? config : nil) else STDERR.puts "** [FacebookerPlus] ERROR: Please load Facebooker before Facebooker Plus.n" end end
  • facebooker_plus.rb
  • facebooker_plus.rb module FacebookerPlus class Base def self.init(rails_config) controller_path = File.join(facebooker_plus_root, 'lib', 'facebooker_plus', 'rails', 'app', 'controllers') helper_path = File.join(facebooker_plus_root, 'lib', 'facebooker_plus', 'rails', 'app', 'helpers') $LOAD_PATH << controller_path $LOAD_PATH << helper_path if defined? ActiveSupport::Dependencies ActiveSupport::Dependencies.load_paths << controller_path ActiveSupport::Dependencies.load_paths << helper_path elsif defined? Dependencies.load_paths Dependencies.load_paths << controller_path Dependencies.load_paths << helper_path else to_stderr "ERROR: Rails version #{(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : ''} too old." return end if rails_config rails_config.controller_paths << controller_path else current_paths = ActionController::Routing.controller_paths if current_paths.nil? || current_paths.empty? to_stderr "WARNING: Unable to modify the routes in this version of Rails. Developer mode not available." end current_paths << controller_path end end # more code here end end
  • facebooker_plus.rb controller_path = File.join(facebooker_plus_root, 'lib', 'facebooker_plus', 'rails', 'app', 'controllers') helper_path = File.join(facebooker_plus_root, 'lib', 'facebooker_plus', 'rails', 'app', 'helpers') $LOAD_PATH << controller_path $LOAD_PATH << helper_path
  • facebooker_plus.rb if defined? ActiveSupport::Dependencies ActiveSupport::Dependencies.load_paths << controller_path ActiveSupport::Dependencies.load_paths << helper_path elsif defined? Dependencies.load_paths Dependencies.load_paths << controller_path Dependencies.load_paths << helper_path else to_stderr "ERROR: Rails version #{(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION : ''} too old." return end
  • facebooker_plus.rb if rails_config rails_config.controller_paths << controller_path else current_paths = ActionController::Routing.controller_paths if current_paths.nil? || current_paths.empty? to_stderr "WARNING: Unable to modify the routes in this version of Rails. " + "Developer mode not available." end current_paths << controller_path end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods view_path = File.join(File.dirname(__FILE__), "app", "views") if controller.public_methods.include?("append_view_path") # rails 2.1+ controller.append_view_path(view_path) elsif controller.public_methods.include?("view_paths") # rails 2.0+ controller.view_paths << view_path else # rails <2.0 controller.template_root = view_path end end end end end
  • controller.rb module FacebookerPlus module Rails module Controller def self.included(controller) controller.extend ClassMethods view_path = File.join(File.dirname(__FILE__), "app", "views") if controller.public_methods.include?("append_view_path") # rails 2.1+ controller.append_view_path(view_path) elsif controller.public_methods.include?("view_paths") # rails 2.0+ controller.view_paths << view_path else # rails <2.0 controller.template_root = view_path end end end end end
  • The Plug-in “app” folder
  • Collecta_ruby source: http://github.com/jimeh/collecta_ruby Facebooker Plus source: http://github.com/jimeh/facebooker_plus Railscast: Making a Plug-in: http://railscasts.com/episodes/33-making-a-plugin email: contact@jimeh.me — twitter: @jimeh slideshare: http://www.slideshare.net/jimeh