Winsor Test Event

June 26th, 2009

Dealing with Adobe and Serving Socket Policy Servers via NGiNX and 10 lines of code

June 10th, 2009

Ok, I don’t even know where to start, but I guess I’ll start by thanking Adobe for a such a high quality product (</sarcasm>).  So we are working towards our new Vokle product, dubbed Vokle Everywhere (by me, just now).

So our new product allows for embeddability of chats and to do this we use ActionScript 3.  I wouldn’t say that AS3 has its short comings, except of course, Flex, but from time to time we have some issues with it.  Like not being able to override the ‘Accept’ header in a UrlRequest and other various dumb things.  Instead we moved to a mildly liberating open source AS3 HTTP Client.

AS3HTTPClientLib is pretty sweet, the only thing that sort of sucked was that we needed a Socket Policy Server, and the last thing I want to do is run another server (already have nginx, merb, wowza, yadayadayada) like this butthole suggests.

Instead I figured, there has to be a way to just serve the crossdomain.xml file without being a giant hassle, especially since I am already serving it over HTTP for the same domain.  So I said, frig it, I’ll do it with NGiNX since NGiNX is already serving my static files.  So the upside is when my servers are running, my ‘policy server’ is serving, the downside is, I had to finagle it so it wasn’t a pain in the ass.  The problem is that ActionScript makes a ‘weird’ request when its looking for a policy server, and NGiNX doesn’t understand it, so it servers a 400 Bad Request, well, it turns out that you can just do it hackety-hack and send your crossdomain.xml file as the 400 Error Page.  This works because AS isn’t doing an HTTP Request, so it DOESNT CARE about the HTTP Status.  Bam, NGiNX Flash Socket Policy Server in 10 lines of code.

To enable ’socket policy server’ functionality in NGiNX simply add another ’server’ directive to your nginx.conf and drop your crossdomain.xml file in whatever folder your NGiNX server is serving its statics from.


    server {

      listen 843;

      server_name  localhost;

      location / {

        rewrite ^(.*)$ /crossdomain.xml;

      }

      error_page 400 /crossdomain.xml;

 

      location = /crossdomain.xml {

        root html;

      }

    }

 

Sweet, start your nginx server and use the only useful thing from the Adobe blog post:


perl -e 'printf "<policy-file-request/>%c",0' | nc 127.0.0.1 843

And you should see your policy file:


<?xml version="1.0"?>

<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">

<cross-domain-policy>

  <allow-access-from domain="*" secure="false" to-ports="*"/>

  <site-control permitted-cross-domain-policies="master-only" />

</cross-domain-policy>

MovingParts: Auto versioning your Javascript and CSS

March 17th, 2009

This tutorial has a few moving parts.  It could be easily recreated with any stack, but for this post I used Nginx and Merb.

Why?

We like to keep things fast.  Two ways we do that is compress the hell out of our js/css and set the cache time on our assets to max.  This is cool to do and obvious, the thing that sucks is when its time to roll out new javascript or css making sure the clients get the updates.

What?

For this example I am using merb and merb-assets (w/ my latest merb patch), nginx and ruby-yui.

Step1 - Add version info to assets with merb-assets

In our code base we have a version for our site, set in a variable like WWW_VERSION.  Using this variable it is easy (with the newest merb-assets plugin) to modify the path of all of your assets on the fly.

Stick this in your init.rb…


WWW_VERSION = "0.1.0"

# this will make require_js "application"

# => /javascripts/application.0.1.0.js

# instead of /javascripts/application.js

Merb::Plugins.config[:asset_helpers][:js_suffix] = ".#{WWW_VERSION}"

Merb::Plugins.config[:asset_helpers][:css_suffix] = ".#{WWW_VERSION}"

 

At this point in time merb is going to start outputting modified paths to your assets that contain version numbers.  This is cool because now when we modify javascript or css and bump the version number it will cause the clients that have cached the static assets to redownload them since the file path is different.  The part that sucks is renaming a bunch of javascript and css files.

Step2 - Lying about file names

Renaming a bunch of files does suck!  So don’t do it.  Instead have that sneaky russian Nginx lie about it for you.

Stick this in you nginx.conf…


location ~ ^/(javascripts|stylesheets) {

  rewrite ^/(javascripts|stylesheets)\/([^\d]+)(?:\.[0-9]+)*\.(js|css)$ /$1/$2.$3;

  expires max;

  break;

}

This rewrite rule will make nginx route any javascript/stylesheet to the original file if it contains a version number.  So a request like to ‘/javascripts/application.0.1.0.js’ will be rewritten to ‘/javascripts/application.js’.  Nifty.

 

Step3 - Compress those assets

You can compress your assets in a number of ways, I’ll go with shameless self promotion and recommend RubyYUI.  RubyYUI is a ruby wrapper for the Java-based YUI  Compressor.  It lets you glob paths instead of doing one file at a time and add suffixes to files.  All sorts of things.

You can do something like this in a vlad or capistrano task during deployment.


require 'rubygems'

require 'ruby-yui'

# this will cause the files to be compressed in place

Yui.new("./public/javascripts",:stomp => true, :suffix => nil).compress

Yui.new("./public/stylesheets",:stomp => true, :suffix => nil, :type=>:css).compress

 

Now you have compressed assets in place in your deployed app and nginx will rewrite any versioned paths to your compressed files.  Bump your version number and all your clients will start getting the new compressed files.  Wee.

Why IE doesn’t drop Flash NetConnections / NetStreams and how to fix it.

March 10th, 2009

Problem

The Adobe Flash Player ActiveX control for Internet Explorer 7 (I don’t know about IE6) will not disconnect NetConnections if the connection was made by a swf instance loaded in a tab and only that tab was closed. If IE itself is closed though the NetConnection will be dropped. This could lead to hung “ghost” connections where you won’t hear any audio, etc. but your Wowza Media Server (and maybe Flash Media Server, who knows?) will still think the client is connected.

Explanation

Laziness on the part of two large corporations.

Solution

Register a JS handler function for window.onbeforeunload that uses the Flash ExternalInterface to call a function in your swf that manually closes the NetConnection. The event “onbeforeunload” is recognized and executed by IE, Safari and Firefox before the onunload event is fired. It is apparently not handled by Opera AFAIK but that’s ok since IE is the only one that actually needs this fix.

In your ActionScript 3 code somewhere:


import flash.external.ExternalInterface;

....

private someFunction():void{

if( ExternalInterface.available ){

ExternalInterface.addCallback("disconnect", disconnect);

}

}

private function disconnect():void {

_my_net_connection1.close();

}

In your Javascript code for the page that loads your swf:


window.onbeforeunload = function(){

// pure JS

var swf = document.getElementById('mySwf');

swf.disconnect();

// jQuery 1.2.6 version

$("#mySwf")[0].disconnect();

}

The End

(image originally from visualizeus)

Vokle DEMO Follow up

March 6th, 2009

Good Friday Evening,

I hope everyone’s weekend is off to a great start.  I wanted to post a follow up to the newsletter I sent out earlier in the week regarding our DEMO launch.  After a 3 day whirlwind, the team has finally caught up on sleep.  I know some of you have found a few bugs and emailed us or even uploaded screencaps to flickr (thank you for that).  For any specific bugs, please use the feedback button located at the top right of the home page.

If you have any general questions, feedback, or simply want to test out the video chatting, be sure to add me on Vokle (edward) by searching for me in the search bar on the bottom right corner next to your friend button.  Also, please feel free to contact me via email.  I’m generally pretty quick at responding to emails, so please do not hesitate to ask a question or say hello.

I’ll be posting a little more frequently onto the blog, so please feel free to leave any comments if there is something you’d like addressed!

Have a safe weekend,

Edward

Private Beta & Happy Holidays!

December 20th, 2008

Hope everyone has exciting plans this holiday season!

We have begun randomly selecting names from the beta sign up list to come in and scope out Vokle!

In order to efficiently spot and remove bugs without overwhelming the team, we will be steadily opening up access to the website :)
If you receive notice that you have been selected, be sure to surf around the site and give us some feed back!  Below are several notes to help you help us in getting the Vokle experience just right.

You’ll be given a keyword and the address of our development server.  Make sure to copy the keyword before navigating away from your email, as you will need it to gain access to Vokle.

Once you are in the site and have signed up, you will notice a “feedback” button on the top right of every page.  This is there so we can note any thoughts you may have on anything from a function, to an error.  By  clicking feedback you will notice there are several basic questions we ask such as, “what happened, what did you expect to happen, what were the steps you took leading up to the problem”.  This helps us replicate the problem quickly and make adjustments to the system.

Make sure to send me a message or a private video chat request to say hello!  I look forward to hearing thoughts and opinions in the live video chats around the latest news and content!  It is definitely a fun and engaging way to hear different perspectives :-D

I look forward to hearing and seeing all of you soon as we continue to update and open up the site!

Happy Holidays on behalf of the Vokle team!

-Edward

Ruby YUI

December 15th, 2008

Well, ok, it’s not that Yui, but now that I’ve got your attention, I’ve made a ruby wrapper for the YUI JavaScript compressor.  What’s the point in that you say.  Well, I’d ideally like to keep my javascript like I keep my Sass/CSS.  The source I work on in one place, the ‘compiled’ stuff in a public one.  Why?  It just feels cleaner, and I like clean.

So whats it do?  Well essentially the same thing as the YUI compressor command line except that it will process an entire folder structure.  It also has the ability to bundle all the compressed files into one file.  It includes Thor tasks so you can use it from the command line and a ruby Yui class so you can access it programatically.

You can git it (haha, ‘git it’, git it? God damn I’m funny) from: http://github.com/coryodaniel/ruby-yui/tree/master

What using it look like, well:


require 'rubygems'
require 'ruby-yui'

yui = Yui.new "./public/javascripts"

yui.minify #=> true | false
yui.bundle #=> path to bundle or nil
yui.clobber #=> cleans up generated files

#Shortcut
Yui.compress "./public/javascripts" #=> same thing as Yui#minify
Yui.clobber "./public/javascripts" #=> cleans up generated files

Yui#initialize and Yui.compress also take params hashes:


yui = Yui.new "public/javascripts", {

# clobber files before compressing new ones
:clobber        => false,

# change the java command to run it
:java_cli       => "java -jar",

# alternate  YUI jar file
:yui_jar        => File.join(YUI_ROOT,"ext","yuicompressor-2.4.2.jar"),

# change the suffix used on the compressed file
:suffix         => "yui-min",

# change the out path; NOTE: the outpath is String#sub for the input path
# ./public/javascripts/main.js => ./my/out/path/main.js
:out_path       => nil,

# :js or :css
:type           => :js,

# YUI parameters
:charset        => nil,
:preserve_semi  => false,
:disable_opt    => false,
:nomunge        => false
}

DataMapper Remixable Updates

December 5th, 2008

DM Remixables 0.9.7 RC 2 is out.  Includes clean accessor name creation, more specs, and the ability to assign methods from the Remixable to generated and remixing classes.  Woot, woot.  See the specs and readme for examples.

DataMapper is Viewable

December 5th, 2008

Even when I’m panicking I’m a fan of datamapper.  I like how clean it generally makes my code, but I do notice that I tend to have the same queries cropping up from time to time either in the same application or in other apps using my model library.  This just wasn’t dry enough for me, so I made dm-is-viewable.  It gives sql-like view functionality to DataMapper Resources.  It’s a pretty simple plugin, but lets take a look, um kay?

Let’s start with a really simple User class…


class User
  include DataMapper::Resource
  belongs_to :location

  property :id, Serial
  property :name, String
  property :username, String
  property :password, String

  property :gender, String
  property :age, Integer

  property :favorite_color, String
  property :favorite_number, Integer
end

class Location
  include DataMapper::Resource

  has n, :users

  property :id, Serial
  property :name, String
  property :desc, String
end

Making your resource viewable is pretty easy, first grab the dm-is-viewable gem, then say that your resource is viewable. Views take the exact same parameters that you could pass to Resource.all. The only difference is that dm-is-viewable stores them until they are called.


class User
  include DataMapper::Resource
  is :viewable  

  #lets create some views
  create_view :legal_women, :gender => 'female', :age.gt => 18
  create_view :serial_killers, :favorite_number => 666, :favorite_color => 'black'

  #... Resource code *SNIP SNIP*
end

See the befores and afters below:


# Legal Women Before
User.all(:gender => 'female', :age.gt => 18)

# Legal Women After
User.view :legal_women

# Serial killers before
User.all(:favorite_number => 666, :favorite_color => 'black')

# Serial killers after
User.view :serial_killers

# You can also pass further limiting query parameters to #view.
# Legal Local Women Before
User.all(:gender => 'female', :age.gt => 18, User.location.name => 'Los Angeles')

# Legel Local Women After
User.view :legal_women, User.location.name => 'Los Angeles'

# Passing additional parameters FURTHERS the limitation of records, so..
User.view :legal_women, :gender => 'male'
# => Would return nil, the query would essentially be generated as:
#  SELECT * from users where gender = 'male' and gender = 'female'

Simply, clean, useful. Woot.

Update - Too Sexy to be a frontpage…

December 5th, 2008

Hey Everyone!

I got a hold of a draft of a section of Vokle for our sneak peak!  What better image to start us off than the Front Page :D

(Keep in mind we are constantly making adjustments based on testing, so this may not be the EXACT final product…but its close).

On this page you see the current top articles for each category based on votes, views, and rating.  If you click one of the categories, you will see more cards for just that category.  Some of the cards you see on other category pages are on their way to being the next top article for the front page, where you can discuss them live with others.

One of the coolest things to notice with navigation at Vokle, is that it is done with the persistent controller at the bottom of the page, a.k.a. the “Vo-kontroller”. This control bar is context sensitive, which means that it only displays actions that are useful to the page you are currently at.  In other words, you won’t need to see a bunch of links per page that you don’t need to see. Duh, right?

Next button over we have the create a card button, which is a very simple process in which you can create your own content card to submit a link to the community.  You’ll be amazed at how quick and fun it is to submit news to the site and create your own card.

;D

All the way to the right we have our Friend Button.  Click this button and a small menu pops up where you can view online friends.  This is where you’ll be going to setup private video chats with friends, family, or co-workers.

I’ll keep you posted as we progress through the site!  Let ’s see some comments (at least until I can literally hear them after launch)!

-Edward