Hacking Rails Plugins

Using the Acts as Taggable On plugin to add categories to a model class, I wanted to override the to_param method and place the name attribute in the url. The plugin, installed as a gem, source shouldn’t need to be hacked in order to accomplish this. The solution is to add a plugin into the RAILS_ROOT/vendor/plugins directory and append the plugin name with _hack. This will, because of an alphabetical load order, allow you to reopen the any class in the plugin. In my example

# RAILS_ROOT/vendor/plugins/acts_as_taggable_on_hack/init.rb
Tag.class_eval do
  to_param
    "#{id}-#{name.gsub(/[^a-z0-9]+/i, '-'/)
  end
end

Got this tip from http://errtheblog.com/posts/67-evil-twin-plugin.

Rails Plugin Acts as Taggable on Steriods

You can download it here http://github.com/suitmymind/acts-as-taggable-on-steroids as well as read usage info (which is for the most part reprinted here).

./script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids
./script/generate acts_as_taggable_migration
rake db:migrate

Then in your model

class Post < ActiveRecord::Base
    acts_as_taggable
 end

And usage is as follows

p = Post.find(:first)
p.tag_list # []
p.tag_list = "Funny, Silly"
p.save
p.tag_list # ["Funny", "Silly"]
p.tag_list.add("Great", "Awful")
p.tag_list.remove("Funny")

#to find...
Post.find_tagged_with('Funny, Silly')
Post.find_tagged_with('Funny, Silly', :match_all => true)

To use this in a form and let users enter a comma separated list of tag names…

form_for @post do |f|
f.text_field :tag_list

And to get a tag cloud

  #controller
  class PostController &lt; ApplicationController
    def tag_cloud
      @tags = Post.tag_counts
    end
  end

  # and in view...
  &lt;style&gt;
  .css1 { font-size: 1.0em; }
  .css2 { font-size: 1.2em; }
  .css3 { font-size: 1.4em; }
  .css4 { font-size: 1.6em; }
  &lt;/style&gt;

  &lt;% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %&gt;
    &lt;%= link_to tag.name, { :action =&gt; :tag, :id =&gt; tag.name }, :class =&gt; css_class %&gt;
  &lt;% end %&gt;

*Note. If you have a controller “tags_controller.rb” and the auto generated (if you used ./script/generate) helper file “tags_helper.rb” you’ll need to make sure to copy the contents of the plugin lib module of the same name, into the helper file. You’ll get an error otherwise.

TinyMCE Rich Text Editor: HELLO EDITOR Plugin Tutorial and Example

I wanted to create a button for the TinyMCE Rich Text Editor for WordPress. It was tough to find good docs on the subject. There are a couple of useful posts out there but in general I found them lacking.

http://codex.wordpress.org/TinyMCE_Custom_Buttons#Creating_an_MCE_Editor_Plugin

The above resource has a good section on the PHP code needed to write your own callbacks but there is no mention of the JavaScript required to create a button and interact with it. Links are available for more reading but why not also post a code sample to get up and running?

Anyway, I made a simple plugin after hobbling a bunch of resources together to get a button in the text editor that when clicked triggers an alert to the window. The code isn’t fancy but it might provide a starting off point for a larger project or be useful as a resource for some of the basic ideas involved. I read through a couple of the sample plugins included with WordPress along with the Viper Video Quicktags Plugin, which makes extensive use of the feature. However, they were too complex for a quick, basic understanding.

This plugin doesn’t do a lot. I’ll probably flesh it out a bit more when I have a clearer understanding of how it all works. For now here are some code samples and at the bottom of this post there is a link to the .zip file for the plugin which you can install on your own site.

In wp-content/plugins/hello_editor/index.php

&lt;?php
/*
Plugin Name: Hello Editor
Plugin URI: http://seanbehan.com/wordpress/tinymce-rich-text-editor-hello-editor-plugin-tutorial-and-example/
Description: A simple plugin showing how to add a button to the rich text editor in WordPress.
The plugin doesn't do anything except place a button and respond to onclick event, with "Hello Editor".
Most of the editor plugins available are too 'functional' to quickly read and see how to do the
basics. This plugin doesn't 'do' anything, rather it makes it simple to read what is going on.
Version: 0.1
Author: Sean Behan
Author URI: http://seanbehan.com
*/

define( "HELLO_EDITOR_PLUGIN_DIR", "hello_editor" );
define( "HELLO_EDITOR_PLUGIN_URL", "/wp-content/plugins/" . HELLO_EDITOR_PLUGIN_DIR );

// Register the external pllugin from the .js file
function hello_editor_register_external_plugin($plugin_array) {
  $plugin_array['HELLO_EDITOR'] = HELLO_EDITOR_PLUGIN_URL . '/editor_plugin.js';
  return $plugin_array;
}

// Add the button to the array ***NOTE*** The name here "HELLO_EDITOR" must
//match the name in the editor plugin file for the addButton(name_of_button) function!
function hello_editor_register_button($buttons) {
  array_push( $buttons, "|", "HELLO_EDITOR" );
  return $buttons;
}

// Filters which will call our functions
function hello_editor_init(){
  add_filter("mce_external_plugins", "hello_editor_register_external_plugin");
  add_filter('mce_buttons', 'hello_editor_register_button');
}

// When the editor is initialized
add_action('init', 'hello_editor_init');

In wp-content/plugins/hello_editor/editor_plugin.js

(function() {
  //Init the plugin
  tinymce.create('tinymce.plugins.HELLO_EDITOR', {
    init : function( ed, url ) {
    //addButton name needs to match $plugin_array['HELLO_EDITOR'] in add_filter function
    ed.addButton('HELLO_EDITOR', {
      title : 'Hello Editor',
      image : url + "/buttons/hello.png",
      onclick : function(){
        alert("HELLO EDITOR!");
      }
    });
  },
  //Info about the plugin
  getInfo : function() {
    return {
      longname : "Sean Behan's Sample Hello Editor Plugin",
      author : 'Sean Behan',
      authorurl : 'http://www.seanbehan.com/',
      infourl : 'http://seanbehan.com/wordpress/tinymce-rich-text-editor-hello-editor-plugin-tutorial-and-example/',
      version : "0.1"
    };
  }
});
tinymce.PluginManager.add('HELLO_EDITOR', tinymce.plugins.HELLO_EDITOR);
})();

You’ll also need a directory called buttons inside the hello_editor plugin directory with a hello.png file which will be displayed. Without it you’ll just get a blank button. You can rename this but remember to change the name in the editor_plugin.js file as well.

You can download the sample plugin here http://seanbehan.com/wp-content/uploads/2009/11/hello_editor.zip

Rails Paperclip Plugin Options for Attaching Files

I usually change some of the default settings when I use the Paperclip plugin. For most of my projects I don’t like having separate directories for each image that is uploaded. I prefer, in this instance, to put avatars of different sizes together under one directory and differentiated based on the style size of the image. To do this just set the path and url options like so…

  has_attached_file :avatar,
    :styles => {:thumb => "75x75", :medium => "150x150", :large => "500x500"},
    :default_url => "/images/default_avatar.png",
    :url => "/system/avatars/:id/:style_:basename.:extension",
    :path => ":rails_root/public/system/avatars/:id/:style_:basename.:extension"

Also, when you set up the database your model will need to have the following columns for Paperclip to work properly

      t.column :avatar_file_name, :string
      t.column :avatar_content_type, :string
      t.column :avatar_file_size, :integer

Don’t forget that to handle file uploads in Rails you need to set the form with

form_for current_user, :html =>{:multipart => true} do |f|

Otherwise, your upload won’t work :(

Build Your Own Calendar in Rails without any Plugins in less than 10 lines of Ruby Code

There are a number of terrific calendar plugins for Rails. But it’s almost as easy, if not easier, to implement your own calendar.

The steps are pretty simple. First get the @beginning_of_month and @end_of_month and iterate over the days in between. In the loop we check if the day matches the @beginning_of_month and if it does we get the offset ( 0 – 6 ) because if the month doesn’t start on a Sunday all our days won’t match up correctly. We print out the offset number as table cells <td class=’offset’></td>. We then need to consider that weeks ‘restart’ and restart our row if we’re at the beginning of the week… this is accomplished with </tr><tr>. Then we just output the date contained in table cells <td>#{d.day}</td>. The code is below and it’s pretty simple.

In this example I use haml, but you could just as easily use ERB (at the bottom). If you’re not familiar with haml, check it out at http://haml-lang.com/ Haml automatically handles wrapping your html so you don’t have to! The end result is that it cuts your html in half and makes it look pretty, which in turns makes it a lot more manageable long term.

#app/views/calendars/show.html.haml

%table#calendar
  %tr
    %th
      Sunday
    %th
      Monday
    %th
      Tuesday
    %th
      Wednesday
    %th
      Thursday
    %th
      Friday
    %th
      Saturday
  %tr
    - @beginning_of_month = Date.civil(2009,12,1)
    - @end_of_month       = Date.civil(2009, 12, -1)

    - (@beginning_of_month..@end_of_month).each do |d|
      - if d == @beginning_of_month
        - (d.wday).times do # offset beginning of calendar
         &lt; td class='offset'&gt; &lt;/td&gt;
      -if d.wday == 0 #restart the week
        &lt;/tr&gt;&lt;tr&gt;

      == &lt;td class='#{d}'&gt; #{d.day} &lt;/td&gt;

Here it is in ERB

&lt;<span>table</span><span> id</span>=<span>'calendar'</span>&gt;
 &lt;<span>tr</span>&gt;
   &lt;<span>th</span>&gt;Sunday&lt;/<span>th</span>&gt;&lt;<span>th</span>&gt;Monday&lt;/<span>th</span>&gt;
   &lt;<span>th</span>&gt;Tuesday&lt;/<span>th</span>&gt;&lt;<span>th</span>&gt;Wednesday&lt;/<span>th</span>&gt;
   &lt;<span>th</span>&gt;Thursday&lt;/<span>th</span>&gt;&lt;<span>th</span>&gt;Friday&lt;/<span>th</span>&gt;
   &lt;<span>th</span>&gt;Saturday&lt;/<span>th</span>&gt;
  &lt;/<span>tr</span>&gt;
  &lt;<span>tr</span>&gt;
  &lt;% @beginning_of_month = Date.civil(2009, 12, 1) %&gt;
  &lt;% @end_of_month = Date.civil(2009, 12, -1) %&gt;

  &lt;% (@beginning_of_month..@end_of_month).each do |d| %&gt;
    &lt;% if d == @beginning_of_month %&gt;
      &lt;% d.wday.times do %&gt; &lt;td class='offset'&gt;&lt;/td&gt; &lt;% end %&gt;
    &lt;% end %&gt;

    &lt;% if d.wday == 0 %&gt; &lt;/tr&gt;&lt;tr&gt; &lt;% end %&gt;
    &lt;td&gt; &lt;%= d.day %&gt; &lt;/td&gt;
  &lt;% end %&gt;
  &lt;/tr&gt;
&lt;/table&gt;

Acts_as_versioned Rails Plugin

Versioning models with the acts_as_versioned plugin

cd rails/app
./script/plugin install git://github.com/technoweenie/acts_as_versioned.git
./script/generate model post title:string body:text

In your model

class Post < ActiveRecord::Base
  acts_as_versioned
end

In db/migrate/****_create_posts.rb

  def self.up
    create_table :posts do |t|
      t.string :title
      t.text :body
      t.timestamps
    end
    Post.create_versioned_table
  end

  def self.down
    drop_table :posts
    Post.drop_versioned_table
  end

Migrate your db

rake db:migrate

Usage

p = Post.create :body => "hello world"
p.body = "HELLO WORLD"
p.save

p.versions.size
p.versions.last
p.revert_to(p.versions.first)
p.body # => hello world

*Quick Note If you want to revert to an older version in a controller or something, don’t do

@post = @post.revert_to(2)

Revert_to method will return a TrueClass, Boolean type. Instead use

@post.revert_to(2)

This method will update the attributes for you and when you call them you’ll get that version.

More information is available here http://ar-versioned.rubyforge.org/ and http://www.urbanhonking.com/ideasfordozens/archives/2006/02/learns_to_use_a_1.html

using rails paperclip plugin on ubuntu

paperclip is an awesome plugin for rails. it let’s you attach an image to any of your models. installation and usage more information is available at http://jimneath.org/2008/04/17/paperclip-attaching-files-in-rails/

by default paperclip uses the image magick library for image manipulation/resizing. you’ll need to install this library as well as the ruby api to it in order to use it. on ubuntu using apt get here are the commands

apt-get install imagemagick
apt-get install librmagick-ruby1.8