mDialog just introduced their brand spanking new embed player and I was fortunate enough to have the opportunity of helping them build it. Although you may be deceived, this player is NOT FLASH based. It uses fancy technologies including (but not limited to) Javascript, transparent overlays, Browser-specific CSS, CSS3, HTML5 and last but not least, Apple's Quicktime.

Go ahead, try it out :) ...


One huge benefit to using this embed over other flash based ones is the exposure to iPhone users. Flash based embeds have a big problem on the iPhone but Quicktime obviously doesn't so mDialog's embed works really well on the iPhone.

Since Safari is the only major browser at this point in time to support CSS3 (somewhat), this player is best viewed in Safari.

One small issue with the current version (0.4.0) of the AWS/S3 ruby library is that there is no way to determine the name or path for an instance of an s3 log file (AWS::S3::Logging:Log) …

    1 require 'aws/s3'
    2 s3_logs = AWS::S3::Bucket.logs_for AWS_S3_BUCKET
    3 s3_logs.each do |s3_log|
    4   # This will raise an exception b/c AWS::S3::Logging:Log class has no path attribute!
    5   puts "Loading log file: #{s3_log.path}"
    6 end

Line 5 above will result in an exception b/c that attribute does not exist … In fact the only thing you can get from an instance of this object is a collection of the lines within the log file. In my use of this library, one of the things I needed was the relative path of the log file, so that after processing it, I could record it in the application database.

If you look in the logging.rb file, you will see something like (note I’ve removed some lines of code) …

    1 class Log
    2   def initialize(log_object) #:nodoc:
    3     @log = log_object
    4   end
    5   # ...
    6   # some other stuff here that I've removed b/c it's not important 
    7   # ...
    8   def inspect #:nodoc:
    9     "#<%s:0x%s '%s'>" % [self.class.name, object_id, log.path]
   10   end
   11   
   12   private
   13     attr_reader :log
   14 
   15   # ... some other stuff ...
   16 end

So the log instance variable is made private but it has a .path attribute being used by the inspect method. That makes life easy… all we have to do to expose the .path attribute is create the following monkey patch:

    1 class AWS::S3::Logging::Log
    2   def path
    3     return log.path
    4   end
    5 end

Save it as a .rb file in your application’s lib directory (eg: aws_patch.rb) and require it before you make the call:

    1 require 'aws/s3'
    2 require 'aws_patch'
    3 s3_logs = AWS::S3::Bucket.logs_for AWS_S3_BUCKET
    4 s3_logs.each do |s3_log|
    5   # This will no longer raise an exception
    6   puts "Loading log file: #{s3_log.path}"
    7 end

Remember that (for obvious reasons) you cannot apply this fix before requiring ‘aws/s3’ , only after (see example above).

Cheers

In the aws/s3 ruby library you will find the following piece of code under logging.rb:

datetime.sub!(month, Date::ABBR_MONTHS[month.downcase].to_s)

It will result in the following exception for some versions of Ruby, but apparently not all versions

NameError: uninitialized constant Date::ABBR_MONTHS

Oddly enough, according to the Standard Ruby Documentation, this constant should exist right in the Date class

But, If you do some digging you will find that the actual place for the constant is:

Date::Format::ABBR_MONTHS

I came across this error when trying to call the attributes method on AWS::S3::Logging::Log::Line

So that leaves me with a few questions:

  • How did the aws/s3 code work for the developers that wrote it ? The most plausible explanation, especially given the rDocs, is that perhaps they were using a different version of Ruby …
  • If so, how come the constant has been moved around?
  • How do I get around this issue without monkey patching the AWS/S3 Gem ?

Resolution

Instead of overwriting the faulty function in the AWS/S3 Ruby library, I decided instead to add the constant to the Date class.

  • Create a file under lib folder called date_fix.rb
  • Place the following code within it:
    1 class Date
    2   ABBR_MONTHS = Date::Format::ABBR_MONTHS
    3 end
  • Require the file in your environment.rb or if you want the change to be less global, require the file where ever you are using code that needs the patch:
    require 'date_fix'

I think this approach is better than the other alternatives (upgrading Ruby from patchlevel 111 to patchlevel 114 in hopes of fixing the issue (it probably won’t) or monkey patching the aws/s3 library) because it is non-intrusive. We’re simply adding a constant to a class so no code dependencies can be broken by the change.

However, in case you want you change fix the AWS/S3 function instead, do this instead:

  • Create a file under lib folder called s3_aws_fix.rb
  • Please the following code within it:
    1 class AWS::S3::Logging::Log
    2   class Line 
    3     class << self
    4       # Time.parse doesn't like %d/%B/%Y:%H:%M:%S %z so we have to transform it unfortunately
    5       def typecast_time(datetime) #:nodoc:
    6         month = datetime[/[a-z]+/i]
    7         datetime.sub!(%r|^(\w{2})/(\w{3})|, '\2/\1')
    8         datetime.sub!(month, (Date.constants.member?('ABBR_MONTHS') ? Date::ABBR_MONTHS : Date::Format::ABBR_MONTHS)[month.downcase].to_s)
    9         datetime.sub!(':', ' ')
   10         Time.parse(datetime)
   11       end 
   12     end
   13   end
   14 end
  • Require the file in your environment.rb after the require ‘aws/s3’ call or any where else.

I was using:

  • AWS / S3 Ruby Gem Version: 0.4.0
  • Ruby version 1.8.6 patch level 111 (the one that comes with Mac OSX I believe)