1. Skip to navigation
  2. Skip to content

The ELC Community Blog

A knowledge exchange on Ruby on Rails and Agile Development

June 06, 2008

by Jeff Emminger

JS Routes plugin

Here's a little plugin to make working with Rails routing in Javascript easier. It generates jsroutes.js each time your app starts, which allows you to use routes like so:

   1  JSRoutes.get('users_path()')
   2   // returns: /users
   3  
   4  JSRoutes.get('formatted_user_url(1, "js")')
   5   // returns: http://your.server/users/1.js
   6  
   7  JSRoutes.get('this_does_not_exist(1, "2", 3, "xml")')
   8   // throws: JSRoutes::Unknown route: this_does_not_exist(1, "2", 3, "js")

Get it here: https://wush.net/svn/public/jsroutes

June 04, 2008

by Alex Chee

Nathaniel Bibler wrote:
It looks as though Marcel has modified the S3Object.copy method to replace the ad-hoc with...


Tim Trautmann wrote:
Very nice, are you planning to submit this as a patch to Marcel’s main git...


AWS-S3 gem extensions and Amazon's Copy API

One of our projects needed to copy lots of files between different S3 buckets, and Amazon just came out with their beta version for Copying s3 objects. So, we decided it would be handy to use this new feature. Instead of downloading each file then uploading it back to S3, which was the only official way to do this before this feature came out.

We also found that the gem did not include an argument to copy/rename objects between different buckets. So we make a patch to the s3 gem to use the new Copy API and accept an extra argument for the destination bucket. We found it more useful, for us, to have this ability.

copy_patch.diff:
   1  Index: lib/aws/s3/object.rb
   2  ===================================================================
   3  --- lib/aws/s3/object.rb	(revision 1282)
   4  +++ lib/aws/s3/object.rb	(working copy)
   5  @@ -178,19 +178,19 @@
   6             end
   7           end
   8           
   9  -        # Makes a copy of the object with <tt>key</tt> to <tt>copy_name</tt>.
  10  -        def copy(key, copy_key, bucket = nil, options = {})
  11  -          bucket          = bucket_name(bucket)
  12  -          original        = open(url_for(key, bucket))
  13  +        # Makes a copy of the object with <tt>key</tt> in bucket <tt>src_bucket</tt> to <tt>copy_name</tt> in bucket <tt>dest_bucket</tt>.
  14  +        def copy(key, copy_key, src_bucket = nil, dest_bucket = nil, options = {})
  15  +          src_bucket          = bucket_name(src_bucket)
  16  +          dest_bucket          = bucket_name(dest_bucket)
  17  +          original        = open(url_for(key, src_bucket))
  18             default_options = {:content_type => original.content_type}
  19  -          store(copy_key, original, bucket, default_options.merge(options))
  20  -          acl(copy_key, bucket, acl(key, bucket))
  21  +          copy(key, copy_key, src_bucket, dest_bucket, options)
  22           end
  23           
  24  -        # Rename the object with key <tt>from</tt> to have key in <tt>to</tt>.
  25  -        def rename(from, to, bucket = nil, options = {})
  26  -          copy(from, to, bucket, options)
  27  -          delete(from, bucket)
  28  +        # Rename the object with key <tt>from</tt> in bucket <tt>src_bucket</tt> to have a key in <tt>to</tt> in bucket <tt>dest_bucket</tt>.
  29  +        def rename(from, to, src_bucket = nil, dest_bucket = nil, options = {})
  30  +          copy(from, to, src_bucket, dest_bucket, options)
  31  +          delete(from, src_bucket)
  32           end
  33           
  34           # Fetch information about the object with <tt>key</tt> from <tt>bucket</tt>. Information includes content type, content length,
  35  @@ -238,8 +238,35 @@
  36             
  37             put(path, options, data) # Don't call .success? on response. We want to get the etag.
  38           end
  39  +
  40  +        
  41  +        # Copies an object from <tt>source_key</tt> and <tt>source_bucket</tt> to <tt>dest_key</tt> and <tt>dest_bucket</tt> 
  42  +        def copy(source_key, dest_key, source_bucket = nil, dest_bucket = nil, options = {})
  43  +          validate_key!(dest_key)
  44  +          # Must build path before infering content type in case bucket is being used for options
  45  +          path1 = path!(dest_bucket, dest_key, options)
  46  +          path2 = path!(source_bucket, source_key, options)
  47  +          infer_content_type!(dest_key, options)
  48  +          options['x-amz-copy-source'] = path2
  49  +          options['x-amz-metadata-directive'] = 'COPY'
  50  +          put(path1, options) # Don't call .success? on response. We want to get the etag.
  51  +        end
  52  +
  53           alias_method :create, :store
  54           alias_method :save,   :store
  55  +
  56  +
  57  +        def copy(source_key, dest_key, source_bucket = nil, dest_bucket = nil, options = {})
  58  +          validate_key!(dest_key)
  59  +          # Must build path before infering content type in case bucket is being used for options
  60  +          path1 = path!(dest_bucket, dest_key, options)
  61  +          path2 = path!(source_bucket, source_key, options)
  62  +          infer_content_type!(dest_key, options)
  63  +          options['x-amz-copy-source'] = path2
  64  +          options['x-amz-metadata-directive'] = 'COPY'
  65  +          put(path1, options) # Don't call .success? on response. We want to get the etag.
  66  +        end
  67  +
  68           
  69           # All private objects are accessible via an authenticated GET request to the S3 servers. You can generate an 
  70           # authenticated url for an object like this:

Just run this patch in your gem directory and change all references in to copy and rename to include the destination bucket in the arguments. I suggest freezing your gem and executing the patch in the vendor/gems/aws-s3 directory, so you would not be changing your gem for all your previous projects and break them.

Since you're already modifying your aws-s3 gem, it might be worthwhile to also add an Expires and Cache-Control Header to your static assets (images, javascripts, and css). This will make the browser cache files for 3 years (don't worry, if you change the file, S3 will still update the cache-control header) and make YSlow happy.

June 02, 2008

by Dylan Stamat

Warble with Console

By default, creating a war via warble doesn't include script/. I was told that I "shouldn't" need it in production environments, but, I think the positives outweigh the negatives by far. Console can be an excellent diagnostic tool in production... and of course, can cause chaos if used incorrectly (see 'root').

Our custom warbler config is a bit complex due to the nature of our applications directory hierarchy, but here is the small piece that shows the shows the jist of the script/ addition. Some evil twin style approach or mixing in would definitely be preferred, but... alas.

This is a little hack in warbler/lib/warbler/task.rb:

   1  def define_scripts_task
   2    scripts = Dir.glob("script/**/*").map do |f|
   3      define_file_task(f, "#{@config.staging_dir}/#{apply_pathmaps(f, :application)}")
   4    end
   5    with_namespace_and_config do
   6      task "public" => scripts
   7    end
   8  end

And add define_scripts_task to the private define_tasks method. Nothing mind blowing, but, helpful.

Now you can play with script/console on your deployed application, as the full script directory will be added to WEB-INF. And a shout out to Nick for Warble in the first place. It's an excellent package.

June 02, 2008

by Asa Wilson

yawl wrote:
I think this behavior comes fromsolr-ruby, which acts_as_solr depends on: http://svn.apache.org/repos/asf/lucene/solr/trunk/client/ruby/solr-ruby/lib/solr/xml.rb


Scott wrote:
Yep…. REXML will bring any Ruby app to it’s knees. Hpricot is also a good...


Speedy Solr: XML Libraries

Act_as_solr needs to create XML docs to submit to the Solr server, either during initial indexing or on save, delete, etc. Acts_as_solr depends on one of two gems for this xml document creation. It's first choice is libxml and the failover is rexml. There is no message about this and I haven't seen any documentation to this effect on the web but there it is sitting in the source code. Libxml is much much faster than rexml in this situation. When we intalled libxml on our servers the time to create the Solr docs dropped from ~7 seconds to ~1 second and the overall rows/second doubled!! That's an absurd speed difference!

The take away message is, if you are using acts_as_solr you should make sure you have the libxml-ruby gem installed!


home | services | Ruby on Rails Development | code | blog | company