Updating a zipfile comments with ruby zip

April 9th, 2012

Recently had a situation where I wanted to update the comment on a zip archive.

Easy enough if you’re on php using ZipArchive.

  $zip = new ZipArchive;
  $res = $zip->open('Archive.zip');
  if ($res === TRUE) {
      $cbi = array();
      $cbi['appID'] = "CBIPHP1.0";
      $cbi['lastModified'] = date('Y-m-d H:i:s O');
      $issue = array();
      $issue['series'] = 'Watchmen';
      $issue['title'] = "At Midnight, All the Agents";
      $issue['publisher'] = "DC Comics";
      $issue['issue'] = "1";
      $cbi['ComicBookInfo/1.0'] = $issue;
      $cbi = json_encode($cbi);
      $zip->setArchiveComment($cbi);
      $zip->close();
  }

I wanted to do the same thing with ruby, so I nosed around and found the ruby gem rubyzip.

Unfortunately, the gem didn’t commit any changes to a zip file unless something was added to the archive.

Posted the issue on SO and Dylan Markow kindly fixed the gem for all to use.

require 'zip/zip'

zf = Zip::ZipFile.open 'Archive.zip'

zf.comment = 'blah blah blah'

zf.commit

Multiple manifests for the asset pipeline

February 7th, 2012

One of the cool features introduced in Rails 3.1 was the asset pipeline (Asset Pipeline). The pipeline allows you to add js/css to manifest files and rails will precompile them into a single file, thus reducing load time.

The docs mention that you can have multiple manifest files:

You can have as many manifest files as you need. For example the admin.css and admin.js manifest could contain the JS and CSS files that are used for the admin section of an application.

This is handy for any number of reasons, allowing you to have as many specific css or js files as needed.

However, one thing missing from the docs is how to tell the precompiler that it needs to look for additional manifest files.

Just add this to your application.rb

config.assets.precompile += %w( admin.css )

will_paginate and acts_as_taggable_on and bootstrap

February 7th, 2012

In the process of updating my site to Rails 3.2 and decided to change up a few things. First, decided to use the bootstrap css framework from twitter (Bootstrap). There are a number of gems that allow you to use the less in rails, but the site actually has a very nice customization tool if you just want to tweak it how you’d like and then download the static css.

In order to get will_paginate to play nicely with bootstrap, you should follow this advice (will_paginate with bootstrap). Just create a will_paginate initializer to generate a new renderer for the link bar.

module WillPaginate
  module ActionView
    def will_paginate(collection = nil, options = {})
      options[:renderer] ||= BootstrapLinkRenderer
      super.try :html_safe
    end
 
    class BootstrapLinkRenderer < LinkRenderer
      protected
 
      def html_container(html)
        tag :div, tag(:ul, html), container_attributes
      end
 
      def page_number(page)
        tag :li, link(page, page, :rel => rel_value(page)), :class => ('active' if page == current_page)
      end
 
      def previous_or_next_page(page, text, classname)
        tag :li, link(text, page || '#'), :class => [classname[0..3], classname, ('disabled' unless page)].join(' ')
      end
 
      def gap
         tag :li, link(super, '#'), :class => 'disabled'
       end
    end
  end
end

I also decided to add tagging using the excellent acts_as_taggable_on gem (Acts As Taggable On). One of the things I wanted to do was not only display all tags available, but all tags available on subsets. And make it play nicely with pagination.

I tried this first:

@worlds = World.tagged_with(params[:tag]).order('title ASC').page(params[:page])
@tags = @worlds.tag_counts_on(:tags)

but the query turns out to be too complex

Mysql2::Error: This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME subquery’

so I had to split out the actions on the relation:

@worlds = World.tagged_with(params[:tag]).order('title ASC')
@tags = @worlds.tag_counts_on(:tags)
@worlds = @worlds.page(params[:page])

and it works like a charm.

Ajax on IE7

April 21st, 2011

Trying to do .post() with jq 1.5 in IE7 and nothing.

Exception message: “Object doesn’t support this property or method”

Solution!

$(function () {
  $.ajaxSetup({ 
    xhr: function() {
      if ($.browser.msie) {
        return new ActiveXObject("Microsoft.XMLHTTP");
      } else {
        return new XMLHttpRequest();
      }
    }
  })
})

goo.gl API with OAuth

January 25th, 2011

Google recently added an API to their goo.gl URL shortening service.

Currently, you can shorten, elongate and list your URLs. There are several ways to authenticate using the API, but I went with OAuth since it allows you to keep a list of unique shortened URLs. I decided to write a little library for use with Alternity, but first there are a few steps.

1. Make sure that the URL shortening service is active in your Google API Console
2. Register your domain with Google here

Registering your domain and putting in Target URL path prefix will enable your OAuth consumer key and OAuth consumer secret.

The next few steps will be familiar to anyone who has dealt with OAuth before, but Google has a few twists.

I used ruby’s oauth gem for this, currently version 0.4.4, so if you’re missing it just sudo gem install oauth.

Your first step is to create a consumer with your key and secret:

consumer = OAuth::Consumer.new(ConsumerKey, ConsumerSecret, 
{:site => "https://www.google.com", 
:request_token_path => "/accounts/OAuthGetRequestToken", 
:access_token_path => "/accounts/OAuthGetAccessToken", 
:authorize_path=> "/accounts/OAuthAuthorizeToken", 
:signature_method => "HMAC-SHA1"})

Now you need to get your request tokens. Google adds a scope parameter. This can be a comma separated list of Google services, but I only care about the url shortener.

request_token = consumer.get_request_token({:oauth_callback => "http://www.SOMEURL.com"}, 
{:scope => "https://www.googleapis.com/auth/urlshortener"})

Grab the authorization url

request_token.authorize_url

and paste that into your browser. You’ll be taken to Google where you will be given the choice to either allow or deny access to the service. When you select allow, Google will redirect back to your oauth_callback url and add some parameters:

http://www.SOMEURL.com/?oauth_verifier=big_long_string&oauth_token=big_long_string

You’ll want to note the verifier and the token. They’re url encoded so I decoded them. Just ran them through php’s url_decode on the command line.

php -r “echo url_decode(big_long_string);”

Now you need your access tokens

access_token = request_token.get_access_token(:oauth_verifier =>OAUTHVERIFIER FROM URL ABOVE)

Pull out your access token and access secret with access_token.token and access_token.secret and save them somewhere.

Now when you need to use the api in your web app, it’s a two step process.

1. Get the consumer using the same process as above.
2. Create the access token

access_token = OAuth::AccessToken.new(consumer, ACCESSTOKEN, ACCESSSECRET)

Get your history

response = access_token.get('https://www.googleapis.com/urlshortener/v1/url/history')
history = JSON::parse(response.body)

Shorten a url

h = Hash.new
h["longUrl"] = "http://www.something.com"
j = h.to_json
response = access_token.post('https://www.googleapis.com/urlshortener/v1/url', j, { 'Content-Type' => 'application/json' })
shortURL = JSON::parse(response.body)["id"]

Cloud storage

January 14th, 2011

I’m a big fan of DropBox, ever since I started I using it I don’t remember the last time I had to use a USB drive.

Recently, I tried out SugarSync and I have to say I’m impressed. You can sync any folders you like on any number of computers you want. No more being limited to the dropbox folder. You can email a file to your storage space, push images to facebook and email files directly from the mobile app. The devs really took their time and included a ton of useful features. Their free plan starts at 5 gb with plenty of chances to earn more storage.

Competition is good and I’ll be interested to see what DropBox does in response.

You can sign up here.

MySQL GUI

December 22nd, 2010

I’ve been using mysql for a long time and I’ve always preferred the command line. I’ve used tools before, like navicat, or the mysql tools (which are great for stored procedure creation, though). Even the Textmate interface doesn’t seem to quite fit my needs.

Recently, though, I’ve been using Sequel Pro for some of my quick dips into the database and I have to say that I’m impressed. A clean interface and some nice features for queries make it a viable option. I still like to use command line for any intensive work, because I like to know exactly what’s going on, but for a quick look at a table structure or a simple select query I’m starting to use this tool more and more.

New Captcha on Comments

December 13th, 2010

Added the monetized captchas from Solve Media to the comments.

Interesting idea. I’m curious to see how it works out.

Quick memory game

November 16th, 2010

Threw together a quick memory tile game using jquery and php. Used memotest as a starting point and then upgraded the jquery to the latest version and put in all new php to randomly select images. Once I get it more how I want it, I think I’ll try to incorporate it into my ruby on rails site.

Give it a try!

Memory Game

I resized the images using thumbs up from Devon Technologies

S3′s object limit

November 1st, 2010

This is more to remind myself about the limit than anything else.

Amazon Web Services S3 has a limit of 1000 objects returned by a request. In order to work around this, you simply loop through your objects using a marker, much like using a LIMIT clause in mysql.

The marker is the last key you received.

Since I’m using the aws::s3 gem in Ruby on Rails, here’s my example:

    s3_objects = []
    marker = nil
    begin 
      list = AWS::S3::Bucket.objects(bucket, :marker => marker)
      s3_objects.concat(list)
      marker = list.last.key
    end while list.size == 1000

Even after using it for several years, ruby’s version of a do…while loop still looks weird to a C++ coder.