Its all about Ruby On Rails
Archive for August, 2008
Hoptoad: A Rails Exception Handling Service
Aug 12th
Many of you guys(as me) may have used Exception Notifier plugin to get Rails app exceptions right into your mailbox, and may also have faced some problem like this.
Also if you have 2-3 or more apps running in production then managing such exception mails is also a big headache. In such case one have to keep track of many things like which type of error is resolved/unresolved for which project etc… .
So, here is a good news for those who don’t know about Hoptoad. It is an hosted service by thoughtbot which receives your exceptions, notify you once per error type by email and keep track(resolved/unresolved, count etc…) of your errors on project basis.
By now its a free service. I’m gonna use this as my next project goes live. What abt you??? ;-P
Configuring TinyMCE SpellChecker with rails application
Aug 8th
Last month I spent much time while configuring tinymce’s spellchecker with my rails application. But finally I got it working and thought I could be a good post. To configure this I took some help form gusto’s blog and google (as usual). So, Lets do it now without spending more time.
First of all create a rails app and install tinyMCE rails plugin:
1) rails tinymce
2) script/plugin install git://github.com/kete/tiny_mce.git
3) rake tiny_mce:scripts:install
We will be using aspell utility to check spellings so install “aspell” first. You can do it by “apt-get install aspell” or “port install aspell”
Once it is done then add following two lines in your layout(application.html.erb):
1) <%= javascript_include_tiny_mce_if_used %>
2) <%= tiny_mce if using_tiny_mce? %>
Now consider that I have a Users controller and User model with a text field ‘about_me’, and I want to use tinymce with spellchecker for that field.
To convert about_me textarea(new user form) to tinyMCE, add following code to users controller:
uses_tiny_mce(:options => {
:theme => ‘advanced’,
:mode => “textareas”,
:height => 100,
:content_css => “/stylesheets/base.css”,
:remove_script_host => true,
:theme_advanced_toolbar_location => “top”,
:theme_advanced_toolbar_align => “left”,
:theme_advanced_buttons2 => %w{ spellchecker },
:editor_selector => ‘mceEditor’,
:plugins => %w{ contextmenu paste spellchecker}
},
nly => [:new])
**Please cross check that you have added(enabled) ‘spellchecker’ in :plugins and added spellchecker button(I have added it in :theme_advanced_buttons2).
Now if you go to new user form you will see that about_me text area is replaced by tinymce editor with spellchecker button.
Now, add two more lines for spellchecker in above tinymce editor configuration:
1) :spellchecker_languages => “+English=en” (english language)
2) :spellchecker_rpc_url => “/users/spellchecker” (rails url where spellchecking will be done)
So our final configuration will be look like:
uses_tiny_mce(:options => {
:theme => ‘advanced’,
:mode => “textareas”,
:height => 100,
:content_css => “/stylesheets/base.css”,
:remove_script_host => true,
:theme_advanced_toolbar_location => “top”,
:theme_advanced_toolbar_align => “left”,
:theme_advanced_buttons2 => %w{ spellchecker },
:editor_selector => ‘mceEditor’,
:spellchecker_rpc_url => “/users/spellchecker”,
:spellchecker_languages => “+English=en”,
:plugins => %w{ contextmenu paste spellchecker}
},
nly => [:new])
Now download Spelling.rb, save it as spelling.rb in your rails lib dir and change ASPELL_PATH at line 7 according to your aspell installation.
Once it is done include this module(“include Spelling”) in application.rb or you controlller (here in our case users controller).
Now, create an action in named spellchecker in you controller(‘users’):
def spellchecker
headers["Content-Type"] = “text/plain”
headers["charset"] = “utf-8″
suggestions = check_spelling(params[:params][1], params[:method], params[:params][0])
results = {“id” => nil, “result” => suggestions, ‘error’ => nil}
render :text => results.to_json
return
end
Now, Type some thing in tinymce editor and click on spellchecker button. It will highlight misspelled words.

And when you click on highlighted misspelled word it will show suggessions.

Enjoy
Hosting Rails app and WordPress on same domain(as folder instead of subdomain)
Aug 8th
Hey guys, Yesterday I did an interesting server configuration. Actually we had a rails app hosted on server which is using passenger(a.k.a mod_rails). This application can be access by going to http://domain.com . Also we had a wordpress running which could be access by going to http://blog.domain.com.
But, for SEO sake I had to change configuration so that wordpress can be access by http://domain.com/blog instead of http://blog.domain.com/
The problem was if I configure wordpress for http://domain.com/blog and go to this url, the request was handled by rails app because of domain.com virtualhost.
So what I did? I changed apache virtualhost configuration for http://blog.domain.com and http://domain.com as:
<virtualHost *>
ServerName blog.domain.com
DocumentRoot /var/www/html/wordpress/
<directory "/var/www/html/wordpress/">
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</directory>
</virtualHost>
<virtualHost *>
ServerName domain.com
ServerAlias www.domain.com
DocumentRoot /var/www/html/domain/current/public
<directory "/var/www/html/domain/current">
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</directory>
RailsAllowModRewrite on
RewriteRule ^/blog/?(.*)$ http://blog.domain.com/$1 [P,NC,L]
</virtualHost>
Also I created a symbolic link to wordpress installation directory under rails public folder(ln -s /var/www/html/wordpress /var/www/html/railsapp/public/blog).
Now remember to change your wordpress address and blog address options to http://domain.com/blog under settings tab of wp-admin section.(Thanks Amit for pointing this out)
I restarted apache and it worked fine. WordPress was running at http://domain.com/blog and rails app was as http://domain.com/.
Update: If you want to change your wordpress permalink structure in account of SEO please change “AllowOverride None” to “AllowOverride All” as shown in image below.
Leopard sucks while installing/updating gems
Aug 7th
May be you guys have noticed that when you try to install a new gem on your machine it says some thing like “Updating meta data for 500 gems” and display one dot per gem. These dots moves very slowly and stops in some time.
I faced this situation many times and found a solution some where on net. I don’t remember the link but it worked.
According to that, you just need to add:
require ‘resolv-replace’
as the first require in /usr/bin/gem file.
enjoy!!
When Ultrasphinx is used with polymorphic associations…
Aug 7th
Lets first consider simple has_many and belongs_to associations as:
class Article < ActiveRecord::Base has_many :comments end
class Comment < ActiveRecord::Base belongs_to :article end
Now if you wish to index the title of associated article with comment for searching, you just need to add ” is_indexed :fields => :body, :include => [{ :association_name => 'article', :field => 'title', :as=> 'article_title'}] ”
So, your comment model will look like:
class Comment < ActiveRecord::Base
belongs_to :article
is_indexed :fields => :body,
:include => [{ :association_name => 'article', :field => 'title', :as=> 'article_title'}]
end
Setup ultrasphinx by issuing:
rake ultrasphinx:bootstrap
Now at rails console try:
>> article = Article.create(:title => "This is the first article's title", :body => "Here comes first article's body.") => #<article id: 1, title: "This is the first article's title", tagline: nil, type: nil, body: "Here comes first article's body.", created_at: "2008-08-07 07:47:53", updated_at: "2008-08-07 07:47:53"> >> article.comments => [] >> article.comments<<comment.new(:body=>"first comment on first article. which says yahooo !!") => [#<comment id: 1, body: "first comment on first article. which says yahooo !...", article_id: 1, created_at: "2008-08-07 07:48:47", updated_at: "2008-08-07 07:48:47">] >> Comment.find :all => [#<comment id: 1, body: "first comment on first article. which says yahooo !...", article_id: 1, created_at: "2008-08-07 07:48:47", updated_at: "2008-08-07 07:48:47">] >> search = Ultrasphinx::Search.new(:query => "first article's title", :class_names => ['Comment']).run.results => [#<comment id: 1, body: "first comment on first article. which says yahooo !...", article_id: 1, created_at: "2008-08-07 07:48:47", updated_at: "2008-08-07 07:48:47">]
Simple, we have article and comment models with has_many belongs_to associations. Comments are indexed with their associated article title. So it can return comments if query matches with article’s titles.
Now, consider a case when we wish to change comment model to make it polymorphic. In that case our models will be look like:
class Article < ActiveRecord::Base has_many :comments, :as => :commentable, :dependent => :destroy end
class Comment < ActiveRecord::Base belongs_to :commentable, :polymorphic => true end
Check at rails if associations are working fine:
>> article = Article.create(:title => "First article title", :body => "here comes first artile's title") => #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31"> >> comment = Comment.new(:body => "hello there, I am a comment") => #<comment id: nil, body: "hello there, I am a comment", commentable_id: nil, commentable_type: nil, created_at: nil, updated_at: nil> >> comment.commentable = article => #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31"> >> comment.save => true >> comment.reload => #<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29"> >> article.reload => #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31"> >> article.comments => [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">] >> comment = Comment.new(:body => "hi there, this is second comment") => #<comment id: nil, body: "hi there, this is second comment", commentable_id: nil, commentable_type: nil, created_at: nil, updated_at: nil> >> article.comments<< comment => [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">, #<comment id: 2, body: "hi there, this is second comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:15:34", updated_at: "2008-08-07 08:15:34">] >> article.comments => [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">, #<comment id: 2, body: "hi there, this is second comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:15:34", updated_at: "2008-08-07 08:15:34">] >> comment.commentable => #<article id: 1, title: "First article title", tagline: nil, type: nil, body: "here comes first artile's title", created_at: "2008-08-07 08:12:31", updated_at: "2008-08-07 08:12:31">
Now, the point is to index article title with comments. Here we can get associated article using ‘commentable’ i.e. comment.commentable.
So, change ultrasphinx is_indexed code in comment model accordingly:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
is_indexed :fields => :body,
:include => [{ :association_name => 'commentable', :field => 'title', :as=> 'article_title'}]
end
Note that we have changed associan_name to ‘commentable’.
Next, when we reconfigure ultrasphinx using ” rake ultrasphinx:bootstrap”(since we have changed db schema), it starts throwing errors:
** Invoke ultrasphinx:bootstrap (first_time) ** Invoke ultrasphinx:_environment (first_time) ** Invoke environment (first_time) ** Execute environment rake aborted! uninitialized constant Commentable /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:278:in `load_missing_constant' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:467:in `const_missing' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:479:in `const_missing' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/inflector.rb:283:in `constantize' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/core_ext/string/inflections.rb:143:in `constantize' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/associations.rb:19:in `get_association_model' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:128:in `configure' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:124:in `each' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:124:in `configure' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:101:in `each' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/fields.rb:101:in `configure' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/configure.rb:32:in `load_constants' /Users/akhilbansal/work/sti/vendor/plugins/ultrasphinx/lib/ultrasphinx/autoload.rb:8:in `after_initialize' /Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:148:in `process' /Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:93:in `send' /Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/initializer.rb:93:in `run' /Users/akhilbansal/work/sti/config/environment.rb:13 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `require' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:354:in `new_constants_in' /Library/Ruby/Gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require' /Library/Ruby/Gems/1.8/gems/rails-2.1.0/lib/tasks/misc.rake:3 /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:546:in `call' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:546:in `execute' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:541:in `each' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:541:in `execute' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:508:in `invoke_with_call_chain' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `synchronize' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `invoke_with_call_chain' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:518:in `invoke_prerequisites' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `send' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:515:in `invoke_prerequisites' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:507:in `invoke_with_call_chain' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `synchronize' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `invoke_with_call_chain' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:518:in `invoke_prerequisites' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `send' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1183:in `each' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:515:in `invoke_prerequisites' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:507:in `invoke_with_call_chain' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `synchronize' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:501:in `invoke_with_call_chain' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:494:in `invoke' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1931:in `invoke_task' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1909:in `top_level' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1909:in `each' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1909:in `top_level' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1948:in `standard_exception_handling' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1903:in `top_level' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1881:in `run' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1948:in `standard_exception_handling' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake.rb:1878:in `run' /Library/Ruby/Gems/1.8/gems/rake-0.8.1/bin/rake:31 /usr/bin/rake:19:in `load' /usr/bin/rake:19
After spending some time on research, I was able to make it work by making some changes in comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
is_indexed :fields => :body,
:include => [{:class_name => 'Article', :association_sql => 'left outer join articles on articles.id = comments.commentable_id and comments.commentable_type = "Article"', :field => 'title', :as=> 'article_title'}]
end
Lets check it on rails console:
>> search = Ultrasphinx::Search.new(:query => 'first article', :class_name => "Comment").run.results => [#<comment id: 1, body: "hello there, I am a comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:14:29", updated_at: "2008-08-07 08:14:29">, #<comment id: 2, body: "hi there, this is second comment", commentable_id: 1, commentable_type: "Article", created_at: "2008-08-07 08:15:34", updated_at: "2008-08-07 08:15:34">]
In such cases we need to define class_name and association_sql in is_indexed statement instead of association_name.
Hope it helps…
