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
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
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
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
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.
script/plugin install http://svn.munitic.com.hr/plugins/acts_as_commentable
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
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.
script/plugin install http://svn.munitic.com.hr/plugins/highlight_fu
<%= highlight_stylesheet_link_tag -%> and <%= highlight_javascript_include_tag -%><%= highlight @post.body -%><%= 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
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,
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
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
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
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
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
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
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
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 :)

Full name: Ivica Munitic
Age: 28
Profession: Developer
Email: ivica@munitic.com.hr