Archives For Rails

Its a good idea to use an alternate cache directory in rails (in your environment.rb):

config.action_controller.page_cache_directory = RAILS_ROOT + "/public/cache/"

This makes it easier to sweep. To setup nginx to pick up from a rails alternate cache directory like public/cache, I used:

if ($http_user_agent ~* "(iPhone|iPod)") {
  rewrite ^/$ /iphone break;
  proxy_pass http://mongrel-pt;
  break;
}

if (-f $request_filename) {
  break;
}

if (-f $document_root/cache/$uri/index.html) {
  rewrite (.*) /cache/$1/index.html break;
}

if (-f $document_root/cache/$uri.html) {
  rewrite (.*) /cache/$1.html break;
}

if (-f $document_root/cache/$uri) {
  rewrite (.*) /cache/$1 break;
}

if (!-f $request_filename) {
  proxy_pass http://mongrel-pt;
  break;
}

It was a little tricky to figure out to use the $uri variable. The first rewrite is used for iphone requests so it doesn’t get the non-iphone cached page.

I needed to use the trace_func to figure out a bug, and I came up with:

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
  class Tracer
    
    class << self
      
      def on(io = STDERR, regex = nil)
        set_trace_func proc { |event, file, line, id, binding, classname|
          if !regex.blank?
            s = format("%8s %s:%-2d %10s %8sn", event, file, line, id, classname)
            io.printf(s) if s =~ regex            
          else
            io.printf("%8s %s:%-2d %10s %8sn", event, file, line, id, classname)
          end
        }
      end
    
      def off
        set_trace_func nil
      end
    
      def trace(io = STDERR, regex = nil, &block)
        on(io, regex)
        retval = yield if block_given?
        off      
        retval
      end
      
    end
  end
  
end

Then Tracer.trace { 1 + 1 } or specify a regex filter:

1
2
3
4
5
>> Tracer.trace(STDERR, /Fixnum/) { 1 + 1 }
  c-call (irb):2           +   Fixnum
c-return (irb):2           +   Fixnum
=> 2
>> 

GC and ObjectSpace

August 24, 2007 — Leave a comment

I saw this at the Rails Edge conference and thought it was cool; aside from its really expensive to implement in JRuby.

1
2
3
4
5
6
7
8
9
10
>> GC.disable
=> false
>> instances = 0
=> 0
>>  ObjectSpace.each_object(Artist) { |a| instances += 1 }
=> 0
>> Artist.find(:all, :limit => 5)
=> [#<Artist:0x327d348 @attributes={....
>>  ObjectSpace.each_object(Artist) { |a| instances += 1 }
=> 5

I should be using ERB instead of eval. But because you can’t use <% inside a string block in your view (that I can figure out; <<EOF didn’t seem to be working in a template). And if you don’t want to specify the code string in your controller you can use [% ... %]. Here is the source I am using for our code helper now:

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
  def erb_code(code) 
    ERB.new(code).result(binding)    
  end
  
  def render_code(code, options = {}) 
    %{ <pre class="helper">n<code class="ruby">n#{h(code)}n</code>n</pre> }
  end
  
  def render_markup(html)
    doc = REXML::Document.new(html)
    markup = ""
    doc.write(markup, 2)
    
    %{ <pre class="markup">n<code class="html">n#{h(markup)}n</code>n</pre>n }    
  end
  
  def render_eval(html)
    %{ <div class="eval">#{html}</div> }
  end
    
  def code_helper(code)
    code.gsub!(/[%/, "<%").gsub!(/%]/, "%>")    
    html = erb_code(code)
    render_code(code) + render_markup(html) + render_eval(html)
  end

Then you can do things like:


  <%= code_helper %{ [%= rating_field(:rating, :effectiveness) %] } %>

and you could also dump it in a helper:

1
2
3
4
5
6
7
8
9
def code_rating_form_field 
  <<-EOF
   <% form_for :rating, @rating2 do |f| %>
     <%= f.rating_field(:effectiveness) %>
   <% end %>
  EOF
end

  // And in your view: <%= code_helper(code_rating_form_field) %>

At work, we have a catalog of markup and CSS and any ruby helpers. I wrote this helper for it which takes a code string and spits it out, evals it, and provides the eval’ed html wrapped and indented. The code-highligher does all the highlighting.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def code_helper(code) 
   help_block = %{ <pre class="helper">n<code class="ruby">n<%= #{h(code)} %>n</code>n</pre> }
    
    result = instance_eval code, "generated code (#{__FILE__}:#{__LINE__})"
    result_block = %{ <div class="eval">#{result}</div> }
    
    doc = REXML::Document.new(result)
    markup = ""
    doc.write(markup, 2)
    
    markup_block = %{ <pre class="markup">n<code class="html">n#{h(markup)}n</code>n</pre>n }
    
    %{ #{help_block} #{markup_block} #{result_block} }
end

And usage:


<%= code_helper %{ will_paginate(@count, @per_page, @page_num, { :extra_param => "extra1" }) } %>

would display the code, the (escaped) markup that it generates, and then the straight up html.

I came up with a hack to render content inside a layout, from the controller:

1
2
3
4
5
6
7
8
9
10
  def content_with_layout(content_for_layout, controller, layout = "the_layout")
    locals = { :params => controller.params }
    template = controller.class.view_class.new(controller.class.view_root, locals, controller)
    template.instance_variable_set("@content_for_layout", content_for_layout)    
    
    # This might work to get assigns? untested...
    #template.assigns = controller.template.instance_variable_get("@assigns") 

    template.render_file("layouts/#{layout}", true)    
  end  

You have to pass in any locals you want, and if you want the use assigns in your layout you would have to set the template.assigns. There was some plugin to give you extra layouts in the view but I forget what it was called. Anybody know where that is?

Update: The inside layout is at: http://fora.pragprog.com/rails-recipes/write-your-own/post/144

Publishing, testing, and running Actionscript projects is a pain.

“AsProject automates a variety of tasks including the creation of projects, classes, test cases, test suites, and swfmill libraries. It automates the download, installation and configuration of the debug flash player and many open source tools. AsProject also includes sophisticated build tools written in rake to automate build processes.”

Video demo or Project page


gem install asproject

Also, a friend Corey pointed out that you can debug flash using Actionscript ExternalInterface to log to Firebug console.log instead of using ASUnit’s debug console. In your actionscript:


 ExternalInterface.call("console.log", "FLASH: " + s);

And make sure to allowScriptAccess.

1
2
3
var so = new SWFObject("flash/xxx.swf", "myswf", "680", "200", "8", "#FFFFFF");
 so.addParam("allowScriptAccess", "always");
 so.write("xxx");

And you could call any javascript from it. ExternalInterface is a replacement for fscommand. I don’t remember but I don’t think ExternalInterface works in all browsers.

I work on a very large rails project, and rake test has become unruly mostly due to the size of our codebase. Anyway, I wrote this task to run only the tests in the products/namespace I work on.

rake test:products PRODUCTS=product1,product2

Runs all unit tests in test/**/product1/*test*.rb, test/**/product2/*test*.rb

The task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace :test do
  task :products do      
    raise "Usage: rake test:products PRODUCTS=product1,product2" if ENV['PRODUCTS'].blank?
    
    products = ENV['PRODUCTS'].split(/,/)
    files = []
    
    products.each do |product|
      dir = "#{RAILS_ROOT}/test/**/#{product}"
      files += FileList["#{dir}/*test*.rb"]
    end    
    
    test_task = Rake::TestTask.new("test_products") do |t|
      t.libs << "test"
      t.test_files = files
      t.verbose = true
    end
    
    task("test_products").execute           
  end
end

There is probably an easier way, but this works for me. Also I wouldn’t mind someone patching rake to accept opts style arguments, like –opt=blah, or -o blah. The ENV stuff seems really not so friendly.

Javascript IE PNG fix

February 26, 2007 — Leave a comment

I adapted the pngfix.js to be rails friendly. Not that I should be using image tags anyway.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var arVersion = navigator.appVersion.split("MSIE");
var version = parseFloat(arVersion[1]);

if ((version >= 5.5) && (document.body.filters)) {        
  var i;
  var length = document.images.length;
    for(i = 0; i < length; i++) {
      var img = document.images[i];
      var re = /w+.[Pp][Nn][Gg]w*/;
      if (img && re.exec(img.src)) {
        var imgTitle = img.title ? "title='" + img.title + "' " : "title='" + img.alt + "' ";
        imgStyle = img.parentElement.href ? "cursor:pointer;" : "";
        imgStyle += "width:" + img.width + "px; height:" + img.height + "px;";
        imgStyle += "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src='" + img.src + "', sizingMethod='scale');";
                
        var strNewHTML = "&lt;span " + imgTitle + " style="display:inline-block; " + imgStyle + " "></span>";

        img.outerHTML = strNewHTML;
        i = i - 1;
    }
  }
}

download

Then add this somewhere in your <head>

1
2
3
<!--[if lt IE 7]>
<script defer type="text/javascript" src="/javascripts/pngfix.js"></script>
<![endif]-->

The only other requirement is you specify a width and height on your image_tags


<%= image_tag("fam/date.png", :alt => "ical", :width => "16px", :height => "16px") %>

Though you should use CSS for most images, this is a fast way to make your png’s work in IE6.

I wanted to use a fragment cache, but I didn’t want to deal with expiring it. So I just made a self-expiring cache based on a TTL.
The fragment cache store just wants 4 methods: read, write, delete, delete_matched

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
32
33
34
35
36
37
38
class Cache::MemStore

  attr_reader :ttl, :auto_expire
    
  def initialize(options = {})
    @ttl = options[:ttl] || 10.minutes
    @cache = {}
  end
    
  def expire(pattern)
    @cache.each { |key, val| delete key if key =~ pattern }
  end
  
  def read(key, options)
    delete(key, options) and return if is_expired?(key)
    return @cache[key][:content] if @cache.has_key? key
  end
  
  def write(key, content, options)
    @cache[key] = { :content => content, :created_on => Time.now }
  end
  
  def delete_matched(pattern, options)
    expire pattern
  end
  
  def delete(key, options)
    @cache.delete key
  end
  
  def is_expired?(key)
    if @cache.has_key? key
      created_on = @cache[key][:created_on]
      return Time.now > (created_on + @ttl)
    end
  end
  
end

Then in your environment.rb put:


ActionController::Base.fragment_cache_store = Cache::MemStore.new(:ttl => 10.minutes)

railsbench gave me the following results:

page request                                      total  stddev%     r/s    ms/r
with cache: /                                   7.23471   2.8012   13.82   72.35
nocache:    /                                  25.81057   0.6966    3.87  258.11