the two alexs

Koala 1.1rc3 released!

Hey everyone,

I’m happy to announce the release of Koala 1.1rc3!  Looking back, the previous release should have been a beta, since we’ve made a number of changes and improvements.

Batch API Interface Change

Most significantly, we’ve made a breaking change to the Batch API interface.  This was not an easy choice, but the original implementation wasn’t thread-safe (class variables and all that), an oversight that’s been resolved with the new batch interface (thanks to spiegela for his help).  It’s very similar, and we hope the change will not cause too much inconvenience.
it "can get two results at once" do
  # batch is now a method on GraphAPI instances,
  # yielding its own GraphBatchAPI object
  me, koppel = @api.batch do |batch_api|
    batch_api.get_object('me')
    batch_api.get_object('koppel')
  end
  me['id'].should_not be_nil
  koppel['id'].should_not be_nil
end
You can check out the updated examples at https://github.com/arsduo/koala/wiki/Batch-requests.

Other Changes

With 1.1rc3, we’ve added a few other improvements to Koala:
  • Added GraphAPI#put_video
  • NetHTTPService now supports global and per-call settings for ca_path, ca_file, and verify_mode (useful to those experiencing SSL problems)
  • We’ve switched from the JSON gem to MultiJson to make Koala compatible with Rubinius
Full documentation is available, as always, on the wiki; the full changelog since RC2 is here.

Installation

Just run [sudo|rvm] gem install koala –pre or add gem “koala”, “~> 1.1.0rc” to your Gemfile.

Final 1.1 Release

Please give 1.1rc3 a try and let us know if you find any issues!  If no big problems come up, we’ll release 1.1 in mid-July.

And don’t forget

To check out Facebook’s new Graph API Explorer, a neat new tool that lets you generate access tokens and play with the Graph API.  (Much like the OAuth Playground, but slicker and without the OAuth components.)


Best,

Alex and Chris
https://github.com/arsduo/koala

Koala 1.1rc released!

Hey everyone,
I’m excited to announce the release of the Koala 1.1 release candidate!
The big news here is support for Facebook’s batch API, which lets you bundle a number of operations into one request from Facebook:
me, friends = Koala::Facebook::GraphAPI.batch do
   @api.get_object('me')
   @api.get_connections('me', 'friends')
end
We’ve also added new convenience methods, improved others, and refactored the HTTP services to be more modular.  All changes are, of course, fully tested and non-breaking (though if you’re interacting directly with Koala’s HTTPServices, you should review the changes).
Documentation is available on the wiki, and the complete diff since 1.0 is available here.

Installation

Just run
[sudo] gem install koala --pre
or add
gem "koala", "~> 1.1.0rc"
to your Gemfile.

Changes

New methods:
  • You can now make batch requests to Facebook; we support the entire API, including file uploads, error handling, and FQL.
  • GraphAPI#get_comments_for_urls (pretty self-explanatory)
  • RestAPI#fql_multiquery, which simplifies both making the requests and using the results
Updated methods:
  • RealtimeUpdates now uses a GraphAPI object instead of its own API
  • RestAPI#rest_call now has an optional last argument for method, for calls requiring POST, DELETE, etc.
  • UploadableIO can take a filename (for the Ads API)
  • get_objects([]) returns [] instead of a Facebook error

Internal improvements:

  • HTTP services are more modular and can be changed on the fly
    • Includes support for uploading StringIOs and other non-files via Net::HTTP even when using TyphoeusService
  • Support for global proxy and timeout settings
  • Support for setting certificate path and file to address Net::HTTP errors under Ruby 1.9.2

Thank to…

We’ve had a lot of great contributions in this release.  Huge thanks to chadk for refactoring the HTTPServices, to seejohnrun for the initial implementation of the Batch API, and to amrnt and aselder, who both contributed several patches.  As always, thanks also to Chris, my codeveloper, and to the people at Context Optional, who have been awesome in supporting Koala’s development.

Note: Facebook now requests access tokens for certain API requests

In case you missed this on the FB developer blog, Graph API requests for PROFILE_ID/feed and PROFILE_ID/posts now requires an access token.  This is a breaking change on Facebook’s end.  If you’ve started to see errors when making anonymous requests for those connections, that’s why.

What’s next

If everything goes well with the release candidate, we’ll release 1.1 mid-month.  In the meanwhile, if you encounter any issues or have any questions, drop me an email or open up an issue.
Enjoy the Batch API and have a great week!
Alex
http://github.com/arsduo/koala

Making Rack::Offline useful in uncached mode

In which we play around with mobile.

I’ve been playing around with mobile websites for the last two months — learning about app caches and localStorage, hacking around with jQuery mobile, discovering the limits of mobile devices.  It’s a huge, complicated, fascinating wilderness, and along my way  I’ve come up with or stumbled upon a few solutions that might be useful anyone else heading the same direction.

Let’s start with the app cache and Rack::Offline, one of my first forays into mobile.

How the App Cache works

Anything I could write would just be a rephrasing of Yehuda Katz’s description in the Rack::Offline readme:

The App Cache allows you to specify that the browser should cache certain files, and ensure that the user can access them even if the device is offline.

[snip]

In short, the App Cache is a much stickier, atomic cache. After storing an App Cache, the browser takes the following (simplified) steps in subsequent requests:

  1. Immediately serve the HTML file and its assets from the App Cache. This happens whether or not the device is online
  2. If the device is offline, treat any resources not specified in the App Cacheas 404s. This means that images will appear broken, for instance, unless you make sure to include them in the App Cache.
  3. Asynchronously try to download the file specified in the manifest attribute
  4. If it successfully downloads the file, compare the manifest byte-for-byte with the stored manifest.
    • If it is identical, do nothing.
    • If it is not identical, download (again, asynchronously), all assets specified in the manifest.

Step #4 is the critical step for today’s post.  After loading content from a previously-stored manifest, the browsers checks to see if the old manifest is identical to the new one downloaded from the server; if not, it updates all the files in the cache.

The Problem

Critically, this comparison doesn’t just happen with locally-stored manifests — after the browser downloads an updated cache, it downloads the manifest a second time to verify nothing’s changed. If the manifest was updated during the download, the whole process starts over.  If the manifest is changed every time, the app cache never works.

This is exactly the problem in Rack::Offline’s “uncached” mode (on by default when using Rails::Offline in development mode).  To keep the cache fresh, the middleware calculates a new hash each time:

Digest::SHA2.hexdigest(Time.now.to_s + Time.now.usec.to_s)

Since the browser downloads the cache twice (once at the beginning, and once at the end for verification), the use of usec (microseconds) means the manifest will always be different, meaning you have no app cache.

(In cached mode, by contrast, the gem calculates a hash of the static files in the manifest, ensuring that the cache is reusable until files change.  If you only want to provide static files offline, you can avoid this problem by initializing Rack::Offline with :cache => true; if you want to serve dynamic assets, such as Rails-generated pages, though, this won’t work for you.  See note at the bottom.)

The Solution

Fortunately, it’s fairly straightforward to patch this behavior, as shown in the embedded gist below (look at the uncached_key method in particular).  The cache-breaking comment in the manifest will now will only change after a configurable interval (by default 10 seconds) to allow the browser to download the cache first. There’s always a risk that the download will start too close to the boundary, but since this is development only, you can lengthen or shorten the period as you want by passing :cache_interval => your_interval when initializing your Rack::Offline block.

I’ve submitted this to the Rack::Offline project as a pull request, so hopefully soon this behavior will be built-in.  In the meanwhile, you can throw this into your initializers folder.

If you’ve been working with mobile and offline content, I’d be glad to hear from you — it’s a fascinating subject, and one I’m just starting on.

require "rack/offline/config"
require "rack/offline/version"
require "digest/sha2"
require "logger"
require "pathname"
require 'uri'

module Rack
  class Offline
    def self.configure(*args, &block)
      new(*args, &block)
    end
    
    # interval in seconds used to compute the cache key when in uncached mode
    # which can be set by passing in options[:cache_interval]
    # note: setting it to 0 or a low value will change the cache key every request
    # which means the manifest will never successfully download
    # (since it gets downloaded again at the end)
    UNCACHED_KEY_INTERVAL = 10

    def initialize(options = {}, &block)
      @cache = options[:cache]

      @logger = options[:logger] || begin
        ::Logger.new(STDOUT).tap {|logger| logger.level = 1 }
      end

      @root = Pathname.new(options[:root] || Dir.pwd)

      if block_given?
        @config = Rack::Offline::Config.new(@root, &block)
      end

      if @cache
        raise "In order to run Rack::Offline in cached mode, " \
              "you need to supply a root so Rack::Offline can " \
              "calculate a hash of the files." unless @root
        precache_key!
      else
        @cache_interval = (options[:cache_interval] || UNCACHED_KEY_INTERVAL).to_i
      end
    end

    def call(env)
      key = @key || uncached_key

      body = ["CACHE MANIFEST"]
      body << "# #{key}"
      @config.cache.each do |item|
        body << URI.escape(item.to_s)
      end

      unless @config.network.empty?
        body << "" << "NETWORK:"
        @config.network.each do |item|
          body << URI.escape(item.to_s)
        end
      end

      unless @config.fallback.empty?
        body << "" << "FALLBACK:"
        @config.fallback.each do |namespace, url|
          body << "#{namespace} #{URI.escape(url.to_s)}"
        end
      end

      @logger.debug body.join("\n")

      [200, {"Content-Type" => "text/cache-manifest"}, body.join("\n")]
    end

  private

    def precache_key!
      hash = @config.cache.map do |item|
        Digest::SHA2.hexdigest(@root.join(item).read)
      end

      @key = Digest::SHA2.hexdigest(hash.join)
    end
    
    def uncached_key
      now = Time.now.to_i - Time.now.to_i % @cache_interval
      Digest::SHA2.hexdigest(now.to_s)
    end
    
  end
end

view raw gistfile1.rb This Gist brought to you by GitHub.

Promised side note: it may seem contradictory to serve dynamically-generated assets in an offline cache; it may even be so.  I’m not sure yet.  How to serve a dynamic, interesting mobile site offline, especially one using jQuery Mobile, seems to be a big blank area in the map — one I’m excited to explore :)

Also: there are some issues caching Rails pages in Rack::Offline — something I’ll cover in another post / pull request.

koppel badge miley badge