User group Rails 2.0 presentation

Posted on Fri Feb 22 @ 03:53:57

Yesterday (21.02.2008) I’ve gave a rather unusual talk about Rails 2.0 for the SoftDevNet User Group. The event was held in a Irish pub called “Harpo”. Beside us geeks the pub was full of people that had no interest in listening to a bunch of geeks talking gibberish.

The talk went good and I think people actually listening to what I was saying were satisfied. I just regret I wasn’t able to finish the presentation with the testing & deployment demo because the laptop died suddenly (not mine and windows based) and it was already 11:30 PM.

All in all it was enjoyable!

Enjoy!

Posted in Rails, 0 Comments Comments

Refactored comment management

Posted on Sat Nov 10 @ 15:15:23

I’ve finally got some time to work on my blog application. I always wanted to remove duplicate code for comment management from the post controller and photo controller. So today I sat down and extracted common code form both controllers and “CommentController” was born.

Here is the code if you are curious.


class CommentController < ApplicationController
    include Akismet

    def add_comment
        if request.post?
           params[:comment][:ip_address] = request.remote_ip
           comment = Comment.new(params[:comment])
           commentable_type = find_commentable_type(
                                  comment.commentable_type, 
                                  comment.commentable_id)
           if comment.valid?
             params[:comment][:is_spam] = 
                      comment_is_spam?(comment)
             commentable_type.comments.create(params[:comment])
           end
        end

        count = comment.errors.count
        unless count == 0
         flash[:errors] = comment.errors.full_messages.join('|')
        end

        redirect_to_commentable_type(commentable_type)
    end

    def delete_comment
        if request.post?
            comment = Comment.find(params[:id])
            comment.destroy
        end
        redirect_to_commentable_type(
               find_commentable_type(comment.commentable_type,
                            params[:commentable_id].to_i))
    end

    def comment_report_spam
        comment = Comment.find(params[:id])
        comment.is_spam = true
        comment.save
        submit_spam(:comment_author => comment.author_name,
                    :comment_content => comment.comment,
                    :user_ip => comment.ip_address,
                    :user_agent => request.user_agent,
                    :referer => request.env['HTTP_REFERER'])

        redirect_to_commentable_type(
              find_commentable_type(comment.commentable_type,
                                    comment.commentable_id))
    end

   protected
   def comment_is_spam?(comment)
     is_spam?(:comment_author => comment.author_name,
              :comment_content => comment.comment,
              :user_ip => comment.ip_address,
              :user_agent => request.user_agent,
              :referrer => request.env['HTTP_REFERER'])
   end

    private
    def find_commentable_type(commentable_type, commentable_id)
        case commentable_type
        when Post.class_name
            result = Post.find(commentable_id)
        when Photo.class_name
            result = Photo.find(commentable_id)
        else
            throw "Wrong commentable type" 
        end
        result
    end

    def redirect_to_commentable_type(commentable_type)
        case commentable_type.class.class_name
        when Post.class_name
            redirect_to article_comments_url(commentable_type)
        when Photo.class_name
            redirect_to :action => "show_comments", 
                        :controller => "photo", 
                        :id => commentable_type
        else
            throw "Wrong commentable type" 
        end
    end

    def article_comments_url(post)
        "#{article_url(:year => post.created_on.year,
                       :month => post.created_on.month, 
                       :day => post.created_on.day,
                       :permalink => post.permalink)
        }#comment_form" 
    end 
end
 

Posted in Rails, 0 Comments Comments

Theme support

Posted on Sat Jul 28 @ 21:50:20

Today I managed to implement a simple theme support for my blog. The result is that now the site has a new look and feel.

The implementation was simple. First I overwritten the template_path class method of ActionController::Base class in my ApplicationController like so:


    class ApplicationController < ActionController::Base
        def self.template_root
          theme = Preference.get('site_theme')
          if theme.size > 0
            "#{RAILS_ROOT}/themes/#{theme}" 
          else
            "#{RAILS_ROOT}/app/views" 
          end
        end
    end

After that I also wrote two helpers that compute the correct path to javascripts and stylesheets for the selected theme. In app/helpers/application_helper.rb I wrote these two methods:


    def theme_stylesheet_link_tag(*styles)
      theme = Preference.get('site_theme')
      if theme.size > 0
        styles.collect do |style|
          source = compute_public_path(style, 
                    "themes/#{theme}/stylesheets", 'css')
          tag("link", {"rel" => 'stylesheet', 
              "type" => 'text/css', 
              "media" => 'screen', "href" => source})
        end
      else
        styles.collect do |style|
          stylesheet_link_tag style
        end
      end
    end

    def theme_javascript_include_tag(*scripts)
      theme = Preference.get('site_theme')
      if theme.size > 0
        scripts.collect do |script|
          source = compute_public_path(script,
                   "themes/#{theme}/javascripts", 'js')
          content_tag("script", "", { "type" => "text/javascript",
                      "src" => source})
        end
      else
        scripts.collect do |script|
          javascript_include_tag script
        end
      end
    end

The Preference.get(‘site_theme’) is a simple implementation of preferences I found looking through the simplelog code. That part of the code can be made more general but for this site it sufficed.

And that was it. Now you can add theme views and layouts in #{RAILS_ROOT}/themes/#{theme} folder and assets in #{RAILS_ROOT}/public/themes/#{theme} folder.

I was aware of theme_support plugin but it needed to be patched to work with rails 1.2.3 and I needed a simpler solution.

I hope someone will find this helpful. I will make a plugin out of this and make it available to the general public.

One last thing. When you change the theme you need to restart your rails application.

Enjoy!

Posted in Rails, 2 Comments Comments

Capistrano deployment on litespeed

Posted on Sun Jul 08 @ 13:37:47

The other day a friend of mine recommended me litespeed web server for rails application instead of the apache, mongrel cluster combination I’ve used. So I listened to him an installed litespeed following these instructions written by PickledOnion.

After the installation I needed to customize my previous capistrano deployment script. I use capistrano 2 for deployment. Here is the code of the script:


    set :application, "munitic.com.hr" 
    set :repository,  "svn://munitic.com.hr/#{application}/trunk" 
    set :deploy_to, "/home/ivica/public_html/#{application}" 
    set :svn_username, 'ivica'

    role :app, "www.munitic.com.hr" 
    role :web, "www.munitic.com.hr" 
    role :db,  "www.munitic.com.hr", :primary => true

    namespace :deploy do
      desc "Restart litespeed web server" 
      task :restart, :roles => :app do
        sudo "/opt/lsws/bin/lswsctrl restart" 
      end

      desc "Start litespeed web server" 
      task :start, :roles => :app do
        sudo "/opt/lsws/bin/lswsctrl start" 
      end

      desc "Stop litespeed server" 
      task :stop, :roles => :app do
        sudo "/opt/lsws/bin/lswsctrl stop" 
      end

      task :after_update, :roles => :app do
        run "ln -s #{shared_path}/photos #{current_path}/public/photos" 
        run "ln -s #{shared_path}/munitic.com.hr.xml 
                   #{current_path}/config/munitic.com.hr.xml" 
        run "ln -s #{shared_path}/.httpasswd 
                   #{current_path}/config/.httpasswd" 
        sudo "chmod -R 2770 /home/ivica/public_html" 
        sudo "chgrp -R www-data /home/ivica/public_html" 
        run "chmod 600 #{current_path}/.msmtprc" 
      end
    end

The main part of the script are the restart, stop, and start tasks. They as their name says restart, stop and start litespeed. I have one additional task called after_update which I use to do some of my custom stuff like linking my gallery photos to /public/photos folder of my application, or linking the configuration file of the litespeed virtual host (munitic.com.hr.xml) and linking the .httpasswd file which is used to enable authorization for awstats. The chmod and chgrp commands are used to ensure that litespeed will have enough permissions to serve my application. The last chmod is to ensure that msmtp program (used to send exception mail via gmails smtp server) which demands that its configuration file (.msmtprc) has 600 permissions has them.

One last thing. To make deploy:web:enable and deploy:web:disable tasks work login into your litespeed administration web site, on the “Configurations” menu choose “Virtual hosts” and click on the virtual host you want to edit. Now click on the Rewrite tab and then click on the “edit” link for Rewrite Control and set Enable Rewrite to yes. Click “save”. After that click “edit” for Rewrite Rules and add these lines:


    RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
    RewriteCond %{SCRIPT_FILENAME} !maintenance.html
    RewriteRule ^.*$ /system/maintenance.html [L]

Click “save”. Now apply the changes and restart the server.

Enjoy!

Posted in Rails, 3 Comments Comments

ActsAsCommentable plugin

Posted on Thu Jul 05 @ 00:56:37

ActsAsCommentable

ActsAsCommentable is an acts_as plugin that is used to enable commenting on your ActiveRecord models. The plugin is intentionally simple because I wanted this plugin to be as customizable as possible without the user ever touching the plugins code.

Installation


    script/plugin install http://svn.munitic.com.hr/plugins/acts_as_commentable

Usage

In the model you want to be commentable add acts_as_commentable


    class Post < ActiveRecord::Base
        acts_as_commentable
        # or if you want to add some methods to the comments association
        # acts_as_commentable do
        #     def find_by_date(date, options = {})
        #         with_scope :find => options do
        #             # CODE GOES HERE
        #         end
        #     end
        # end
        ...
    end

Create the comments table. The only requirement are the commentable_type and commentable_id fields. On my blog I have something like this


  create_table "comments", :force => true do |t|
    t.column "author_name",      :string
    t.column "comment",          :text
    t.column "ip_address",       :string
    t.column "is_spam",          :boolean,  :default => false
    t.column "commentable_type", :string
    t.column "commentable_id",   :integer
    t.column "created_on",       :datetime
  end

If you want to add methods to the comment model you can do this using mixins. Here is what I use:


    module Mixins; module Comment; end; end;
    module Mixins::Comment::Spamable
      def self.included(base)
        base.extend(ClassMethods)
      end

      module ClassMethods
        def count_by_spam(spam)
          count :all, :conditions => ['is_spam = ?', spam]
        end

        def count_by_spam_and_commentable(spam, type, id)
          count :all, :conditions => ['is_spam = ? and 
               commentable_type = ? and commentable_id = ?', 
                   spam, type, id]
        end

        def find_for_post_comments_feed(options = {})
          with_scope :find => options do
            find :all, :conditions => ['is_spam = ? and 
                 commentable_type = ?', false, Post.class_name], 
                 :order => 'created_on DESC'
          end
        end
      end
    end
    Comment.send :include, Mixins::Comment::Spamable
    # If you want to add validation you can do that like this:
    Comment.send :validates_presence_of, :author_name, :comment

I have this code in lib/mixins/spammable.rb and i require it in the config/environment.rb file. After that i have these methods in the comment model.

UPDATE: I’ve moved the code to google. So now you can get the latest version with:


    script/plugin install http://acts-as-commentable.googlecode.com/svn/trunk

Enjoy!

Posted in Rails, 0 Comments Comments

HighlightFu syntax highlighting plugin

Posted on Thu Jul 05 @ 00:27:56

HighlightFu

HighlightFu is a syntax highlighting plugin that adds helper methods to your views. It searches for a <pre lang=”[lang]>[code]</pre> html tag and replaces it with the syntax highlighting code. The syntax highlighting is done using dp.SyntaxHighlighter. I’ve developed HighlightFu because I was not satisfied with the previous syntax highlighting plugin I made.

Installation instructions


    script/plugin install http://svn.munitic.com.hr/plugins/highlight_fu

Usage

  1. run rake highlight_fu:install (this installs all necessary javascripts and stylesheets)
  2. include stylesheets and javascripts with
    <%= highlight_stylesheet_link_tag -%>
    and
    <%= highlight_javascript_include_tag -%>
  3. for the text you want to highlight call the highlight helper
    <%= highlight @post.body -%>
  4. IMPORTANT – at the bottom of the page put the following
    <%= highlight_initialize -%>

UPDATE: I refactored the plugin, especially the private make_options function and merged stylesheet and javascript helper into one called highlight_include_assets. The result is that the code is cleaner and the plugin is easier to use. Now step 2 in the usage paragraph is just one line of code and that is:

<%= highlight_include_assets -%>

UPDATE2: I’ve moved the code to google. So now you can get the latest version with:


    script/plugin install http://highlightfu.googlecode.com/svn/trunk

Posted in Rails, 0 Comments Comments

Syntax highlight plugin using CodeRay

Posted on Sun Jul 01 @ 15:10:45

A couple of days ago I’ve implemented a simple syntax highlighting plugin using CodeRay. It is implemented as an after filter. Here is how you use it. In the controller add the after filter using highlight_coderay.


       class PostController < ApplicationController
           #highlight_with_coderay :only => ['show']
           highlight_with_coderay :except =>
               ['posts_feed','comments_feed',
                'comment_report_spam',
                'add_comment', 'delete_comment']

            ...
        end

After that when you add your content in the html just add <pre lang=”[language]”>[code]</pre> and your done. The after filter will replace this pre tag with the correct highlighting.

You can also customize the style coderay uses by adding this line of code to config/environment.rb:



    SyntaxHighlight::Filters::CodeRay.stylesheet = :bright


There are three styles you can use and they are cycnus, murphy which are part of coderay or bright which is part of the plugin. Also included is a javascript scanner i found here,

Installlation


     script/plugin install http://svn.munitic.com.hr/plugins/syntax_highlight

Enjoy!

UPDATE: I now added helper methods too. So now you can choose if you want to use the after filter or the view helpers. The view helpers are:



    <%= coderay_stylesheet_tag :bright %>


This prints out the stylesheet coderay uses.



    <%= highlight_with_coderay post.body %>


This actually does the highlighting

Posted in Rails, 1 Comments Comments

Finally capistrano deployment

Posted on Thu Jun 21 @ 16:00:13

Today I managed to get capistrano deployment finished. I’ve never used capistrano before and it took me a few hours to set it up but at the end I managed it. :)

I’m using bzr as my VCS and mongrel_cluster behind nginx as web server so I had to customize the config/deploy.rb script. Also I’m using capistrano 2.0. Here is my config/deploy.rb:


    set :application, "munitic.com.hr" 
    set :repository,  "/opt/repo_mirror/#{application}" 
    set :deploy_to, "/var/www/apps/#{application}" 
    set :scm, :bzr
    set :checkout, "branch" 

    role :app, "www.munitic.com.hr" 
    role :web, "www.munitic.com.hr" 
    role :db,  "www.munitic.com.hr", :primary => true

    namespace :deploy do
      desc "Restart mongrel cluster" 
      task :restart, :roles => :app do
        run <<-CMD
          cd #{current_path} &&
          mongrel_rails cluster::restart
        CMD
      end

      task :start, :roles => :app do
        run <<-CMD
          cd #{current_path} &&
          mongrel_rails cluster::start
        CMD
      end

      task :stop, :roles => :app do
        run <<-CMD
          cd #{current_path} &&
          mongrel_rails cluster::stop
        CMD
      end
      task :before_update_code do
        `bzr push sftp://munitic.com.hr/opt/repo_mirror/#{application}`
      end

      task :after_update, :roles => :app do
        run "ln -s /var/www/apps/#{application}/shared/photos 
              #{current_path}/public/photos" 
      end
    end

I’ve used tips from here. I hope this helps someone!

Posted in Rails, 0 Comments Comments

Comments feed, looking for ideas

Posted on Thu May 17 @ 21:42:01

Today I managed to implement comments feed for the blog in about 5 minutes including deployment and mongrel cluster restart :) Now I’m looking for ideas what to implement next. If you have any suggestions please do drop a comment.

This adventure of learning rails by coding my own blog is really going well. Any comments are welcomed. I’m quite happy with the results so far. I’m also satisfied with the ease of development using rails and the wealth of plugins it has.

Now it would be really nice if I could use it on the job, but that wont happen simply because my firm is M$ only. Well thats life. It sucks I know :) Rails is so much more fun to code with. Testing with rails is simpler and better integrated than in ASP.NET. Also the code is shorter and more to the point and beautiful. All in all it as pure joy to code this blog engine of mine and I’ll continue to do so until I run out of ideas :)

As I said before, any comments, ideas are welcomed.

Posted in Rails, 3 Comments Comments

MetaWeblog API, Exception Notifier

Posted on Sun Apr 29 @ 23:08:08

Just today I implemented the MetaWeblog API for this blog and this post was published using ScribeFire plugin for Firefox. I followed the instructions from this blog post. It was really easy to follow the instructions and implement it into my custom blog. The only problem I’ve had was that I have a model named Category and because of that I had to change the Category ActionWebService::Struct name (I’ve used AWSCategory :).

A week or so ago I also implemented exception notification. Now every time an exception is raised inside my blog a mail is sent to me with the remote ip, user agent, url and the exception backtrace. Because of this I was able to fix some url bugs I didn’t noticed at first.

As for my injury I’m now fine and my foot has recovered. I still feel some small pain but the doctor said it will pass in a couple of days. For now I will not play any football or cageball, but I will do cycling if the weather permits it.

Update:

I’ve replaced acts_as_attachment plugin with attachment_fu. I only needed to replace acts_as_attachment with has_attachment in the model and move the public/#{table_name}/#{photo.id} folders to public/#{table_name}/0000/00#{photo.id}/ folders because of some changes in the attachment_fu namely this from file_system_backend.rb:36 in the backends folder of the attachment_fu plugin:


    def partitioned_path(*args)
        ("%08d" % attachment_path_id).scan(/..../) + args
    end

Posted in Rails, 0 Comments Comments

Photo commenting in the gallery

Posted on Mon Apr 16 @ 12:34:41

Thanks to the amazing rails web framework I was able to add photo commenting in the gallery in less than an hour. And I'm a rails newbie.  Just click on an image in the gallery and when lightbox2 finishes its job you will see a link called "Comments". If you click on it you will be redirected to the commenting page for that picture.

In other news, today I found out I would not go to the WinDays conference held in Opatija, Croatia because of my cageball injury. I have an appointment with the doctor on the 23rd. when I will find out if the casts are history or not. The WinDays conference starts on the 23rd. so no Opatija for me.

So bye bye Opatija see you next year :(

Posted in Rails, 0 Comments Comments

Comment spam protection

Posted on Fri Mar 30 @ 00:39:59

Today I added akismet comment spam protection to my blog thanks to this article by Kommen. Now I'm in the process of deciding if to implement blog apis (blogger etc.).

Any suggestion is appreciated.

Update: I've replaced the akismet library with the akismet plugin :)

Posted in Rails, 0 Comments Comments

Gallery feature

Posted on Thu Feb 15 @ 21:41:02

The adventure of developing my own little blog engine is going better than i previously thought.

Today I finished implementing a simple gallery using acts_as_attachment plugin. I was amazed how easy was to develop it.  Now I'll do code cleanup and refactoring and after that ajax support when viewing photos and commenting posts. That will be version 1.0.

Posted in Rails, 2 Comments Comments

First post, start of an adventure

Posted on Mon Feb 12 @ 09:32:42

Hi ppl,

    After weeks of developing this blog finally it's finished. It is my first rails application and I must say I'm very pleased with the results. I'm still learning, but so far it was a joy working with it.

    This blog will grow with features in time but for now it has an administration site, atom feed, post commenting, sort by categories and archive. Features to add are comment administration, gallery (i'm an amateur photographer :)), ajaxifed comment form, category cloud …

So this is it for now. Finally I'm starting to blog :)

Update: 

Just added in place comment editing for admins.
Let the censorship begin :)

Posted in Rails, 0 Comments Comments