I’ve just released a very simple Rails plugin that gives you the ability to transparently cache single active record objects using memcached.

The purpose is to cache by fields that are being validated for unique values.

Cache gets refresh after save, and expire after destroy or after a logical delete.

It is less than 100 lines of code, so don’t be shy to look at the source for full understanding.

http://github.com/arydjmal/model_cached

Looking at the Google Analytics stats I realized that a lot of people are still reading my blog post about how to export to Excel in Rails.

IMO I prefer the csv method because it provides the same + more at no extra cost, but given people still wants xls files I wrote another rails plugin: to_xls.


UPDATE: SuperSaaS (Thanks!) mentioned that Excel does not interpret UTF8 characters in CSV files. So if that is a concern to you, maybe you should stick with XLS.

Using this plugin you don’t need the builder views, so it makes it as easy as the to_csv plugin I wrote. In fact it works the same.

In config/initializers/mime_types.rb register the custom mime type.


Mime::Type.register "application/vnd.ms-excel", :xls

In the controller where you want to export to excel, add the format.xls line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class UserController < ApplicationController

        def index
                @users = User.all

                respond_to do |format|
                        format.html
                        format.xml { render :xml => @users }
                        format.xls { send_data @users.to_xls }
                end
        end

        def show...
        def new...
        def edit...
        def create...
        def update...
        def destroy...

end

That’s it!

Install


./script/plugin install git://github.com/arydjmal/to_xls.git

TextAreaMate is a simple javascript library on top of Prototype, that turns a textarea into a simple code editor with a TextMate feeling.

For now it’s working on FF & Safari, Mac only. As soon as I get it to work cross-browser I will put it on GitHub for further development.

Features

tab key support
command + / for commenting lines
command + [ for indenting less (one line or a block of code).
command + ] for indenting more (one line or a block of code).
Dependencies

Prototype JavaScipt Library

Install

Get the beta here and copy both files to the appropriate directory.

Usage

Include prototype.js and textareamate.js:

1
2
<script src="prototype.js" type="text/javascript" charset="utf-8"></script>
<script src="textareamate.js" type="text/javascript" charset="utf-8"></script>

For railers, something like:


<%= javascript_include_tag :defaults, 'textareamate' %>

Now you can start using it:

1
2
<textarea name="editor" id="editor" onkeydown="return TextAreaMate.onKeyPress(this,event)">
</textarea>

It plays well with rails:


<%= text_area :pastie, :body, :onkeydown => 'return TextAreaMate.onKeyPress(this,event)'  %>

Demo

CSS Tip: Three-State Buttons

August 12th, 2008 by ary

Let’s say you have a text button that highlights when you hovered. You can add a third state, when clicked, by specifying a:active the same way you use a:hover. No javascript needed.

In your html:

1
2
3
<a href="#" class="button">
  Some Text
</a>

In your css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a.button:link, a.button:visited, a.button:hover {
  padding: 15px;
  text-decoration: none;
  color: #666;
  display:block;
  border-bottom: 1px solid #CCC;
  font-size: 12px;
}
a.button:hover {
  background-color: #F9F9F9;
}
a.button:active {
  background-color: #FFF;
  color: #333;
}

Demo

CSS Tip: Two-State Images

August 5th, 2008 by ary

Let’s say you have an image that when you hovered you want to highlight (ie. make it brighter). Instead of dealing with two different images you can just use opacity.

In your html:

1
2
3
<a href="#" class="image">
  <img src="/some_image.jpg" />
</a>

In your css:

1
2
3
4
5
6
7
8
9
10
11
a.image img {
  opacity: 0.60;
  -moz-opacity: 0.60;
  filter:alpha(opacity=60);
}

a.image:hover img {
  opacity: 1;
  -moz-opacity: 1;
  filter:alpha(opacity=100);
}

Demo

Recommend Me

I forked the in_place_editing plugin from Github and modified it so it validates your model before saving.

If you are interested, check it out.

A couple of days ago I blogged about how to export to MS Excel. I took an example from a client’s app, in which they are the final-users, and I know for sure they only use Excel 2003.

But if your app needs some sort of export feature, and you don’t know how the users are going to use the data, I would go with CSV. Why? As far as I know every spreadsheet app can open it, including Numbers and Office 2000 (which don’t handle XML-based xls files), plus they might use it with report tools unrelated to excel. And as a bonus it is much easier to use.

The Solution

Last night I wrote a very simple plugin called to_csv that gives you the ability to call to_csv to a collection of activerecords. The builder options are the same as to_json / to_xml, except for the :include.

Usage

1
2
3
4
5
6
7
8
9
10
11
@users = User.all

#
# defaults are export header and all fields except id, and timestamps
#

@users.to_csv
@users.to_csv(:only => [:last_name, :role])
@users.to_csv(:timestamps => true, :id => true, :header => false)
@users.to_csv(:except => [:last_name, :role])
@users.to_csv(:except => :role, :methods => [:name, :admin?])

Real life example

In the controller where you want to export to csv, add the format.csv line (as of rails 2.1 it is not necessary to register the csv myme_type)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class UserController < ApplicationController

  def index
    @users = User.all
    respond_to do |format|
      format.html
      format.xml { render :xml => @users }
      format.csv { send_data @users.to_csv }
    end
  end

  def show...
  def new...
  def edit...
  def create...
  def update...
  def destroy...

end

That’s it. No myme_type to register, no views to create, just install the plugin, and add format.csv { send_data collection.to_csv } to the controller’s respond_to block that you want to give export capabilities.

Install


./script/plugin install git://github.com/arydjmal/to_csv.git

Ideas

I got ideas and influence from Mike Clarks recipe#35 in Rails Recipes book, some anonymous pastie, and whoever wrote to_xml/to_json builders.

Note

Does not work on a single activerecord, ie, User.first.to_csv. Cannot style output like xls.

Update

I modified the plugin so now it uses the fastercsv library instead of the old CSV library. SO if you don’t have the gem installed type:


sudo gem install fastercsv

Export to Excel in Rails 2

June 8th, 2008 by ary


UPDATE: I released a Rails plugin that makes this much easier, http://arydjmal.com/2009/1/11/to_xls-plugin-export-to-excel-in-rails-the-easy-way.

Rails makes it super-easy to export anything to Excel. I took this example from a client’s project; they wanted to export to excel the data generated from orders/index.

I will summarize this in 3 simple steps:

First, you have to register the mime type in your config/initializers/mime_types.rb. This is needed for the respond_to block in the controller, and of course you have to do it only once.


Mime::Type.register "application/vnd.ms-excel", :xls

Restart the server so the new configuration is loaded.

Now we are ready to respond in xls format; in this example http://localhost:3000/orders will render index.html.erb and http://localhost:3000/orders.xls will create a xls. So in the app/controllers/orders_controllers.rb we can have something like this:

1
2
3
4
5
6
7
8
def index
  @orders = Order.recent

  respond_to do |format|
    format.html
    format.xls
  end
end

Pretty clean, right? Finally, we need to create the excel file, and since Excel can read xml in the app/views/orders/index.xls.builder we have something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8" 
xml.Workbook({
  'xmlns'      => "urn:schemas-microsoft-com:office:spreadsheet", 
  'xmlns:o'    => "urn:schemas-microsoft-com:office:office",
  'xmlns:x'    => "urn:schemas-microsoft-com:office:excel",    
  'xmlns:html' => "http://www.w3.org/TR/REC-html40",
  'xmlns:ss'   => "urn:schemas-microsoft-com:office:spreadsheet" 
  }) do

  xml.Worksheet 'ss:Name' => 'Recent Orders' do
    xml.Table do

      # Header
      xml.Row do
          xml.Cell { xml.Data 'ID', 'ss:Type' => 'String' }
          xml.Cell { xml.Data 'Date', 'ss:Type' => 'String' }
          xml.Cell { xml.Data 'Description', 'ss:Type' => 'String' }
      end

      # Rows
      for order in @orders
        xml.Row do
            xml.Cell { xml.Data order.id, 'ss:Type' => 'Number' }
            xml.Cell { xml.Data order.date, 'ss:Type' => 'String' }
            xml.Cell { xml.Data order.description, 'ss:Type' => 'String' }
        end
      end

    end
  end
end

With this 3 easy steps you can give a export to xls any view.

So my export to excel link for the view would be:


link_to 'Export to Excel', formatted_orders_url(:xls)

Now, the bigger reason I wrote this mini tutorial, was that this set-up gave me some trouble on IE. It was always rendering format.xls so my little-ugly-hack was to make sure that params[:format] is xls

1
2
3
4
5
6
7
8
def index
  @orders = Order.recent

  respond_to do |format|
    format.html
    format.xls if params[:format] == 'xls'
  end
end

Please, let me know if you have a better way to solve this problem.

Now, if you plan to have more than one xls view, I suggest to create this helper in app/helpers/application_helper.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def excel_document(xml, &block)

  xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8" 
  xml.Workbook({
    'xmlns'      => "urn:schemas-microsoft-com:office:spreadsheet", 
    'xmlns:o'    => "urn:schemas-microsoft-com:office:office",
    'xmlns:x'    => "urn:schemas-microsoft-com:office:excel",    
    'xmlns:html' => "http://www.w3.org/TR/REC-html40",
    'xmlns:ss'   => "urn:schemas-microsoft-com:office:spreadsheet" 
  }) do

    xml.Styles do
      xml.Style 'ss:ID' => 'Default', 'ss:Name' => 'Normal' do
        xml.Alignment 'ss:Vertical' => 'Bottom'
        xml.Borders
        xml.Font 'ss:FontName' => 'Arial'
        xml.Interior
        xml.NumberFormat
        xml.Protection
      end
    end

    yield block

  end
end

So now the view would be:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
excel_document(xml) do
  xml.Worksheet 'ss:Name' => 'Recent Orders' do
    xml.Table do

      # Header
      xml.Row do
          xml.Cell { xml.Data 'ID', 'ss:Type' => 'String' }
          xml.Cell { xml.Data 'Date', 'ss:Type' => 'String' }
          xml.Cell { xml.Data 'Description', 'ss:Type' => 'String' }
      end

      # Rows
      for order in @orders
        xml.Row do
            xml.Cell { xml.Data order.id, 'ss:Type' => 'Number' }
            xml.Cell { xml.Data order.date, 'ss:Type' => 'String' }
            xml.Cell { xml.Data order.description, 'ss:Type' => 'String' }
        end
      end

    end
  end
end

NOTE: This works with Office 2003+ for windows and Office 2004+ for Mac, it will not work with Office 2000, Numbers or QuickLook. So if this is a problem you could try another solution.

UPDATE: Check out my to_csv plugin for better excel compatibility!

What I've learned at RailsConf '08

June 8th, 2008 by ary

1) The value of open source.

Open source is really amazing. I’m looking forward to contribute to rails, and to write more plugins/gems. Why? It’s very probable that “your plugin (or whatever OSS)” it is going get better as more people use it or contribute to it. You will meet people, you’ll get your name out there, and a job opportunity may arise because of your results.

2) The value of hooking up with other programmers.

First it is fun to have friends like you. It’s also great to have close people to share knowledge, problems, etc. And again you might have job opportunities thanks to these relations.

3) How we (programmers) should work with designers.

There were great talks at RailsConf, but I think this is the only session that taught me something. It was great talk given by Ryan Singer of 37Signals, he is a designer, not a programmer. A lot of people misunderstood his talk, and the purpose of it, eventhough Ryan made it pretty clear. In a nutshell:

(DESIGNERPHOTOSHOP + HTML/CSS + RAILS_SOURCE) + PROGRAMMER = SUCCESS
4) I’m not alone.

The rails community is getting bigger and bigger, it was incredible to see so many people at a conference.

I’m really excited about this coming RailsConf, I’m certain that I’m going to meet a lot of talented people!

Can’t wait…

link_to_tagged_current

April 10th, 2008 by ary

Almost every site has some sort of tabbed navigation. There are a lot of alternatives of how to accomplish it. One way that comes to my mind is Paolo Dona’s widgets plugin. But sometimes I want something lighter, and link_to_unless_current is way too simple. So I created link_to_tagged_current, a very simple rails helper that in difference of link_to_unless_current, when current, it creates the link + it adds the class current to it.

In app/helpers/application_helper.rb

1
2
3
4
5
6
def link_to_tagged_current(name, options = {}, html_options = {}, &block)
  if current_page?(options)
    html_options[:class] = "#{html_options[:class].to_s} current".strip
  end
  link_to name, options, html_options, &block
end

After little use, this was still pretty simple, so I added a :highlights_on option.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def link_to_tagged_current(name, options = {}, html_options = {}, &block)
  current = false
  highlights = html_options[:highlights_on] ? html_options[:highlights_on] + [options] : [options]

  highlights.each do |h|
    if (h[:controller] == @controller.controller_name and !h[:action]) or
       (h[:controller] == @controller.controller_name and h[:action] == @controller.action_name and !h[:id]) or
       (!h.kind_of?(Hash) and current_page?(h))
      current = true
      break
    end
  end

  if current
    html_options[:class] = "#{html_options[:class].to_s} current".strip
  end

  html_options[:highlights_on] = nil
  link_to name, options, html_options, &block
end

Some usage examples are:

1
2
link_to_tagged_current 'Dashboard', dashboard_url
link_to_tagged_current 'Users', users_url, :highlights_on => [:controller => 'admin']

Given this is a very common need I will try to push it to rails.

UPDATE: Rails core member Pratik said that it should stay as a plugin for now. You can see the ticket here.

Sometimes I want to print each error of a form right below the input/select box. And at the same time let the user know that there were problems.

If I use error_messages_for, I will be showing the user each error twice… not a good idea.

So for this case I wrote error_message_for, a method that tells you that there is something wrong whether you have 1 error or more. It also accepts more than one model.

1
2
3
def error_message_for(*args)
    "<p id='errorExplanation'>Oops, enter correct info.</p>" if args.any? {|o| error_messages_for(o).size > 0 }
end

Some usage examples are:

1
2
error_message_for :account
error_message_for :account, :user

Rails built-in timezones are awesome!

February 5th, 2008 by ary

You probably know that rails edge sports some very good goodies. But after using rails time zones, I was really amazed how they solved a not-so-fun “problem” like timezones.

Thank you for making my life easier!

Really looking forward to Rails 2.1.