Vidar
20 December 2009
Custom libraries with rails helpers
After upgrading from Rails 2.3.2 to 2.3.3 through 2.3.5 I had a lot of strange errors in my console, the latest being Read error: and it was really annoying as you might figure. If I set
config.cache_classes = false
to
it would work so I figured it had something to do with rails being unable to to unload or reload classes or constants. After some debugging I found out that the problem was in my custom helper file:
include ActionView::Helpers
module HtmlHelper
def self.htmlify(text)
html = strip_tags(text)
html = auto_link(html, :link => :urls, :href_options => {:target => '_blank'})
html = simple_format(html)
return html
end
end
In this file I am using the built-in helpers in rails to format input from the user into html for the web application. I accessed it statically like this from my model
result = HtmlHelper::htmlify(text)
Rails does not like this even if it works in the console and if not caching classes (aka in production). I don’t know if it’s a bug or if I’m doing it wrong. I googled around and found this guy which led me onto the
class which lets you instantiate one instance of the class, preventing loading of unloadable classes, making sure there is only one instance of the class.
My helper now looks like this
module HtmlHelper
class Transformer
include Singleton
include ActionView::Helpers
def htmlify(text)
html = strip_tags(text)
html = auto_link(html, :link => :urls, :href_options => {:target => '_blank'})
html = simple_format(html)
return html
end
end
end
And I’m calling it like this
result = HtmlHelper::Transformer.instance.htmlify(text)
or of course
helper = HtmlHelper::Transformer.instance
result = helper.htmlify(text)
0 comments
Edit
Delete
Vidar
22 October 2009
Simple format and auto link images
I used to use Redcloth and textilize for all my apps until I found out that it allows too much and I wanted something simpler. The built-in simple_format would convert all newlines to break and paragraph tags, but I needed to be able to insert links and images as well. The auto_link builtin text helper in rails converts all links starting with http or www to clickable links. But what about the images? I needed a way to check if the link was to an image, convert it to an image tag and also extract the height and the width of the image to avoid the web-page jumping around when loading big images.
First I tried doing it server side with regular expressions but I couldn’t find a good way of extracting the image height and width, also I would have to redo it every time the page was edited. So I just used Javascript. This is how I did it using JQuery.
On the server to prepare the text and autolink.
def self.htmlify(text)
html = strip_tags(text)
html = auto_link(html, :link => :urls, :href_options =>{:target => '_blank'})
html = simple_format(html)
if html[0..2] == "<p>" then html = html[3..-1] end
if html[-4..-1] == "</p>" then html = html[0..-5] end
return html
end
On the client
$.fn.showImageLinks = function(){
links = $(this).find("a").filter(function(){return this.href.match(/(png||gif||jpe?g||bmp)$/i)});
$.each(links, function(index, element){
var image = $("<img>").attr("src", element.href);
image.attr({height: image[0].height, width: image[0].width});
$(element).replaceWith(image);
});
return $(this);
}
// replace core with the element containing the links
$('#core').showImageLinks();
1. Find all the links ending with image extensions.
2. Replace each of them with image tags.
3. Done. Remove RedCloth/Textile. It’s not worth it.
The image[0].width and image[0].height magically contains the image info after you set the src attribute.
0 comments
Edit
Delete
Vidar
26 June 2009
Batch resizing images
Today I needed to send a bunch of images to my friend, but each of them were over 2MB in size and wouldn’t fit in one email. Here is how I resized all the images in a directory from the command line using the convert command shipped with the image manipulation library Imagemagick
for i in *; do convert $i -resize 50% ${i/\.*/}.jpg; done
You can play with the options, just check the man page for convert. Change the .jpg with another extension to convert to another format simultaneously.
0 comments
Edit
Delete
Vidar
22 May 2009
Custom counter_cache column
Ever since I created my own blogging engine, I have been struggling with enormous amounts of comment spam. I don’t have time to manually delete the spam, so I installed a spam checking service to figure out if the comments are spam.
My post model has a counter_cache column used to keep track on how many comments each post has so I have that information handy when needed and don’t need to ask the database each time. Using rails, you define the coulumn in a belongs_to associative declaration and the counter is updated automatically in the parent model (google counter_cache rails if you want to know about it). The only problem is that the counter_cache column in rails does not support conditional tracking. I found the same discussion on this guy Douglas’ website and a couple of work-arounds can be found there.
What I ended up doing was almost the same but I didn’t created a second column since I only need to know how many non-spam comments I have. I just disabled the automatic counter cache and decided it’s better to just keep track manually. My comment model now looks like this:
belongs_to :post # removed counter_cache => true
named_scope :approved, :conditions => {:approved => true}
after_save :update_comments_count
after_destroy :update_comments_count
def update_comments_count
self.post.update_attributes :comments_count => self.post.comments.approved.count
end
1 comment
Edit
Delete
Vidar
26 April 2009
Blogging engine updated
I updated the blogging engine and comments are now re-enabled. The comments are spam checked using the Akismet web service. I also installed Coderay for highlighting code and updated to rails 2.3.2. The corner jQuery plugin is also updated and now displays corners correctly in Safari.
I updated the restful authentication plugin to reflect the most recent changes. Found a gotcha when installing the plugin: When you clone another repository into an existingo repository, github will not save the files on the server but rather try to point to wherever you cloned it from. When using automated deployment with vlad or capistrano, the files will not be deployed as they are ignored when pushed to github. To resolve the problem, delete the .git and .gitignore files in the cloned repository before pushing.
0 comments
Edit
Delete
Vidar
29 March 2009
Rails 2.3 nested attributes with Ajax
Rails 2.3 was recently released and with it came a nice feature called nested attributes or nested model mass assignment. It allows you to specify code like the following in your your view (taken from the release notes ):
-form_for @customer do |customer_form|
= customer_form.label :name, 'Customer Name:'
= customer_form.text_field :name
- customer_form.fields_for :orders do |order_form|
= order_form.label :number, 'Order Number:'
= order_form.text_field :number
// The allow_destroy option in the model enables deletion of child records
= order_form.check_box :_delete
= customer_form.submit
But what if you need to submit a form or some data using Ajax? Then you can’t use the rails helpers. As it turns out it’s pretty easy once you understand how the helpers generate the form data.
The above code indicates we have a Customer model with a one-to-many relationship to Order. If you run the code you will see that the first level generates input fields with a name attribute like this
just like we are used to from before using normal mass assignment. However on level 2 it looks like this
# When new, you only get the attribute
customer[orders_attributes][0][name]
# When an id exists, you also get a hidden field
# to indicate which record we are working with
customer[orders_attributes][0][id]
This can nest infinitely deep. Let’s say we also have a model LineItems with a one-to-many association to Order, you’ll get
customer[orders_attributes][0][line_items_attributes][0][some_field]
customer[orders_attributes][0][line_items_attributes][1][some_field]
The [0] indicates that this is the first element in the list of many records ie customers.first.orders.first, so a [1] would indicate customers.first.orders.second and so on. This preserves the stored order of elements.
Lastly, it’s now possible to mark for deletion. Just do this
customer[orders_attributes][2][_delete]
to delete the third element in customer.orders.
So what’s so ingenious about this? My models in the project I’m working on are nested 5 levels deep. Using this technique this is my controller code
# in create
@product = Product.create(params[:product])
# in update
@product = Product.find(params[:id])
@product.update_attributes(params[:product])
3 lines of code to create and update 5 levels deep nested associations. I get validations for each model, atomic saves and get to remove 100 lines of code in models. Use these conventions to assemble parameters when you are using Ajax and it’ll just work. Beautiful.
5 comments
Edit
Delete
Vidar
09 February 2009
Custom script/console
UPDATE: This is a somewhat stupid way of doing it since all you need to do is
irb -r application_name.rb
from the root of your sinatra application. You will then have full access to database and everything else in your application in the console. Read on if you want a pure database console.
I recently started my first Sinatra project and I’m using datamapper as an ORM. Using the script/console in Ruby on Rails to manipulate the database and do manual test for my models is something I have come to depend on. Using the bare bone web framework Sinatra, you don’t have this privelege, or do you?
Using Sinatra requires you to manually load all gems and libs that your application needs. In my application I have the following code (init_datamapper.rb):
require 'dm-core'
require 'dm-validations'
DataMapper.setup(:default, “sqlite3:///#{Dir.pwd}/dev.db”)
DataMapper::Logger.new(STDOUT, :debug)
%w( user post comment tag ).each do |lib|
require File.join(File.dirname(FILE), ‘lib’, lib)
end
The code will import the datamapper libraries and setup the connection, load your models (from my lib directory). What’s nice is that this is exactly what I need to create my fully operational database console!
irb
load 'init_datamapper.rb'
Fire up irb and load the file. Done.
0 comments
Edit
Delete
Vidar
05 February 2009
Merging models with meta
This might not be the cleanest rest technique, but it will replace 4 controllers with 1 and gain DRY leverage. In my application I have 4 models that have the same validations, attributes and consequently all of those models have their own restful controllers. The code in the controllers are more or less identical except for the model name which is Color, Brand, Category or Size. Here is how I created a single controller to handle all of these models restfully.
I added a parameter[:type] to each request which contains “color”, “brand”, “category” or “size”. From that string, all I have to do is to go from here:
@color = Color.find(params[:id])
to
@model = model_name.find(params[:id])
To do that I defined
before_filter :find_class
attr_accessor :model_name, :type
def find_class
self.type = params[:type]
self.model_name = params[:type].camelize.constantize
end
This will take the param[:type], which is a lowercase string initially, camel-case it, and turn it into an ActiveRecord constant you can use for database queries. 4 controllers in one.
0 comments
Edit
Delete
Vidar
03 February 2009
Haml and pre tag indention
I use Haml in most of my applications. It’s a great tool since it makes it so easy to read and develop your view code. Haml uses indentation similar to Python. However, today, I ran into problems. In this blog, if I write some example code, I use <pre> tags to preserve white space. Using Haml, the <pre> code will automatically be indented in addition to white space inside the tag. I used to have this code in my view:
.post_content
= @post.body_html
This code will give unwanted indentation. I changed it to this, using the :preserve filter in Haml (read about the Haml Filters ) and it behaves as expected.
.post_content
:preserve
#{@post.body_html}
Back to what I was supposed to do today…
0 comments
Edit
Delete
Vidar
01 February 2009
Ajax, JSON and Rails
I’ve heard about JSON for a long time, but I wasn’t really sure how and why to use it, I tried to as far as possible to avoid Ajax or javascript if I could, it’s too time consuming. But in my current project, I actually have a use case for it. Here is how I use Ajax to get a bunch of objects in JSON format and insert their data into the current DOM.
# In my controller I have this method
def get_data
colors = Color.all
respond_to do |format|
format.js{ render :json => colors.to_json }
end
end
# in my view I have this element hook
<div id="inventory" />
# in my javascript file I have this code (jQuery)
$.fn.fillSizes = function(){
element = $(this);
select = $('<select>')
.addClass('color')
.append("<option>Wait...</option>");
element.prepend(select);
$.getJSON('/products/get_data', null, function(data){
$.each(data, function(){
select.append('<option value="' + this.color.id + '">'
+ this.color.name + '</option>');
});
select.find("option:first").remove();
});
return element;
}
// Invoke the above function on this object
$('#inventory').fillSizes();
This will create a select box populated with the data from the JSON returned from the server. It will also display a “Wait…” message while loading the data. The cool thing about it is that it is compact. There is only data, no tags.
1 comment
Edit
Delete
Vidar
29 January 2009
Dragon fruit and mandarins
Every day I have some fruit while I’m working and sometimes it strikes me how lucky I am because it’s just so fresh and good and sweet :)

0 comments
Edit
Delete
Vidar
28 January 2009
DRY and micro frameworks
Today I started working on refactoring my admin interface for e-commerce solution (it’s really just a simple web shop). The admin interface is now a part of the view just turning everything into text fields and select boxes when you are logged in. This is a great approach if you want to have a great admin interface: it forces you to put in time there too.
Moreover, the code is now very compact, but maybe too much so. Edit, new and show actions now share code. Edit and new are identical but I remove the controls with an action/controller test. My models are also identical in that they all just have a “name” attribute and that must be unique. CRUD operations are handled by the same controller on all models. I even use the same javascript.
This has allowed me to reduce the application to very little code, but I wonder if I will need to break it up in the future. Anyway, since I reduced the code base so much I started wondering why I needed Rails at all and checked out Sinatra. It seems interesting, especially since the tests are so tightly integrated and it uses a lot less memory. Think I’ll have to rewrite an application, taking it for a spin :)
“Don’t be a Rails programmer. Be a programmer.”
— By someone I can’t remember
0 comments
Edit
Delete
Vidar
27 January 2009
jQuery filters and plugins, more love
I’m learning more about jQuery and its ingenious selectors. Let’s say you have a single select box with an option value of ‘red’. How do you set that one to selected?
The guys over at Scriptygoddess suggested a plugin. But why use a plugin when all you have to do is this:
$("select option:contains('red')")[0].selected = true
And you also gotta love the plugins. This is all I did to make this window have rounded corners:
$("#core").corner();
By now, in the project I’m working on while learning jQuery, I don’t have any inline javascript. Unobtrusiveness obtained.
0 comments
Edit
Delete
Vidar
26 January 2009
jQuery love
A couple of weeks ago I checked out jQuery. I used it in a couple of applications and removed the old Prototype stuff. I immediately saw that it was smarter than any other library I tried before, but there was a couple of things I didn’t like/find out about.
Until now. One things that always annoyed me was that I had to rebind events to objects whenever I inserted something into the DOM and at the same time being “unobtrusive”. Sometimes it was just a line of code, but it was stupid since I obviously wanted the same event as in ready() on the same objects. Today I was banging my head againts the wall when I saw the “live” function in the API. It does just what I want, binding an event to an object forever, or automatically adds the event to an object of the same class.
I now truly love jQuery.
0 comments
Edit
Delete
Vidar
19 January 2009
Spam and comments disabled
During the course of the holidays I got hundreds of spam comments. The comments have now been disabled before I can enable the spam protection service.
There should be an automatic service to report the ip-addresses of the spam robots and then automatically attack those ip’s and blacklist them. If just everyone can work together on this. Spam breaks my workflow.
0 comments
Edit
Delete
Vidar
26 December 2008
Server transition complete
Today I completed moving all my old repositories from svn to git. Git is a joy working with and it’s ten times more flexible than svn.
All my applications are now running at Slicehost and everything is just great. Running a small bash script I can now push, deploy and restart the server with just one command: “deploy application_name”. This is also due to using vlad the deployer which I find easier to use than capistrano because it does what you need and nothing more.
Now it’s time to hit the beach for a couple of weeks! Happy new year!
0 comments
Edit
Delete
Vidar
25 December 2008
It's Christmas!
I suddenly realized it’s Christmas! I really do not have the Christmas spirit in me, but still it’s here. Should have bought a tree, my bonsai tree isn’t really Christmasy.
I deployed my new blog engine today, simplifying the other ones out there to what I need: Somewhere to jot down random thoughts quickly while they are still in my brain.
Anyway, this post was just to test my blog and to wish everyone a merry Christmas and happy holidays!
0 comments
Edit
Delete