Archive for the ‘Rails’ Category

ASProject and Flash debugging to Firebug console

Thursday, April 12th, 2007

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.

Rake test task for a custom FileList

Friday, April 6th, 2007

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

Monday, February 26th, 2007

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.

Fragment Cache Store with TTL

Wednesday, February 21st, 2007

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

Creating iCalendar files in Ruby

Friday, February 16th, 2007

Using the iCalendar ruby library (gem install icalendar):

From the Show model, I added a method to create an event:

1
2
3
4
5
6
7
8
9
10
11
class Show < ActiveRecord::Base
...
  def to_ical_event
    event = Icalendar::Event.new    
    event.dtstart = time ? time.to_datetime : date
    event.dtstamp = created_at.to_datetime
    event.summary = "The title"
    event.description = "The description"
    event.uid = "show-#{id}"   
  end
end
  • dtstart takes a Date or DateTime object (see Ruby cookbook recipe chapter 3.9 for to_datetime method).

From the Venue model, I added a method to create the calendar and add all the show events:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Venue < ActiveRecord::Base
  has_many :shows  
...
  def to_ical
    ical = Icalendar::Calendar.new
    ical.product_id = "-//dclicio.us//iCal 1.0//EN"
    ical.custom_property("X-WR-CALNAME;VALUE=TEXT", name)
    ical.custom_property("X-WR-TIMEZONE;VALUE=TEXT", "US/Eastern")
    shows.each do |show|
      event = show.to_ical_event
      ical.add_event(event)
    end
    ical
  end
end
  • The X-WR custom properties are read by iCal and by google calendar.

Then a task to iterate through the venues creating the ics files:

1
2
3
4
5
6
Venue.find(:all, :include => [ :shows ]).each do |venue|
  ical = venue.to_ical       
  file = File.new("/tmp/#{venue.name}.ics", "w")
  file.write(ical.to_ical) # Allow myself to ... introduce ... myself :/
  file.close
end

An ics file looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BEGIN:VCALENDAR
X-WR-CALNAME;VALUE=TEXT:Black cat
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//dclicio.us//iCal 1.0//EN
X-WR-TIMEZONE;VALUE=TEXT:US/Eastern
BEGIN:VEVENT
UID:dclicio.us-243
DESCRIPTION:with \nhttp://dclicio.us
SUMMARY:
DTSTART:20070117
DTSTAMP:040425
SEQ:0
END:VEVENT
END:VCALENDAR

I then surface these files in public/ical/ and add a ProxyPass /ical ! so apache can serve em up straight without hitting mongrel.

Check out dclicio.us to see it in action.