RobL

How can you get a flash movie to accept parameters from the query string?

I have been using JWPlayer for a while to dynamically load videos into Facebook. Later versions of JWPlayer completely disable this ability because it can leave the player open to abuse from sites using your hosted player. How rude of them. Consequently I’ve been stuck on a quite an old version of JWPlayer just to retain this ability. However, this appears to have stopped working in Facebook recently and I need to re-implement this in a more recent version of JWPlayer.

I don’t know any ActionScript at all, but hunting through it appears to me that the config for setting up the player is here. In this case loading the config via Flash Vars passed to the movie via Javascript. So in theory I should optionally allow the config to be loaded from params around here.

https://github.com/braindeaf/jwplayer/blob/master/src/flash/com/longtailvideo/jwplayer/utils/Configger.as

private function loadExternal():void {
  if (ExternalInterface.available) {
    try {
      var flashvars:Object = ExternalInterface.call("jwplayer.embed.flash.getVars", ExternalInterface.objectID);
      if (flashvars !== null) {
        for (var param:String in flashvars) {
          setConfigParam(param, flashvars[param]);
        }
        dispatchEvent(new Event(Event.COMPLETE));
        return;
      }
    } catch (e:Error) {}
  }
  if (Security.sandboxType == Security.LOCAL_WITH_FILE) {
    dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Error loading player: Offline playback not supported"));
  } else {
    dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Error loading player: Could not load player configuration"));
  }
}

Ok, So it would appear there are copies of the JWPlayer Subversion repository floating around which show how configs are loaded in via RootReference. So in theory its just a question of putting this back in and rebuilding the player.swf. Might be nice to pull remote JSON instead of XML configs which would make things a bit more secure.

https://bitbucket.org/pombredanne/http-developer.longtailvideo.com-svn-tags/src/a84e56c637af0d725f9625c769bc715bb7dd88f3/mediaplayer-5.9.rc1/src/com/longtailvideo/jwplayer/utils/Configger.as?at=master

/**
 * @return
 * @throws Error if something bad happens.
 */
public function loadConfig():void {
  loadCookies();
  if (this.xmlConfig) {
    loadXML(this.xmlConfig);
  } else {
    loadFlashvars(RootReference.root.loaderInfo.parameters);
  }
}

/** Whether the "config" flashvar is set **/
public function get xmlConfig():String {
  return RootReference.root.loaderInfo.parameters['config'];
}

/**
 * Loads a config block from an XML file
 * @param url The location of the config file.  Can be absolute URL or path relative to the player SWF.
 */
public function loadXML(url:String):void {
  var xmlLoader:URLLoader = new URLLoader();
  xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, xmlFail);
  xmlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, xmlFail);
  xmlLoader.addEventListener(Event.COMPLETE, loadComplete);
  xmlLoader.load(new URLRequest(url));
}

/**
 * Loads configuration flashvars
 * @param params Hash map containing key/value pairs
 */
public function loadFlashvars(params:Object):void {
  try {
    for (var param:String in params) {
      setConfigParam(param, params[param]);
    }
    dispatchEvent(new Event(Event.COMPLETE));
  } catch (e:Error) {
    dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, e.message));
  }
}

Cross Domain Fonts with Nginx?

Having seen a few issues with cross domain web fonts with Firefox and a report of the same in Chrome. This small addition to an Nginx server config allows access from any origin, in this case for a fonts in a Rails asset pipeline. This config solves the problem of another site pinching our fonts. However, more importantly this allows cloudfront to sit in front of our origin server and for our site to serve fonts over cloudfront.

location ~ "^/assets/(.*/)*.*-[0-9a-f]{32}\.(woff|ttf|eot)$" {
  gzip_static on;
  expires     1h;
  add_header  Cache-Control public;
  add_header  Access-Control-Allow-Origin *;
  # debug to check this is being caught.
  add_header  X-Braindeaf-Pipeline true;
}

With the right syntax, we should probably limit the origin to *.cloudfront.net domains, and to our specific distrubitions in a more complete solution.

Static Google Maps

Since we’re heading off to Germany in the morning, here’s a quick stab at the Google Maps Image APIs Static Maps . Looks fairly straight forward if you have the Lat/Long of the places you want to create markers for.

https://maps.googleapis.com/maps/api/staticmap?zoom=6&size=900×500&maptype=roadmap&markers=color:blue|label:Brighton|50.84274,-0.106829&markers=color:green|label:Essen|51.450001,7.016700

Redirect / to another path in Nginx

I needed to tweak my Nginx config to redirect / (slash) only on a Wordpress site to a completely different location but leave all other routing intact. There we have it.

server {
  listen 80;
  server_name here.robl.me;
  root /var/www/wordpress;
  index index.html index.htm index.php;

  location = / {
    rewrite ^ http://there.robl.me permanent;
  }

  location / {
    try_files $uri $uri/ /index.php?$args;
    # auth_basic "Restricted";
    # auth_basic_user_file /etc/nginx/.htpasswd;
  }

  location ~ \.php$ {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
  }
}

Wrote some PHP for the first time in about 8 years.

I think I liked it. Maybe. Associative Arrays and the like

<?php
$array = array(
  'units' => array(
    '1117411' => array(
      'is_shipped_at' => 'true',
      'shipped_at'    => '2014-10-14 T14:56:55+00:00'
    )
  )
);
var_dump($array);
?>
Robs-iMac:snarf rl$ php -f data.php
array(1) {
  ["units"]=>
  array(1) {
    [1117411]=>
    array(2) {
      ["is_shipped_at"]=>
      string(4) "true"
      ["shipped_at"]=>
      string(26) "2014-10-14 T14:56:55+00:00"
    }
  }
}

JBuilder extensions

class Jbuilder

  # don't repeat yourself. when WillPaginate
  def paginate!(collection, &block)
    if collection.respond_to?(:page)
      set! :total_entries, collection.total_entries
      set! :page, collection.current_page
      set! :per_page, collection.per_page
    else
      set! :total_entries, collection.count
      set! :page, 1
      set! :per_page, collection.count
    end
    set! :entries, collection, &block
  end

  # don't return null, should should be an empty string
  def _set_value(key, value)
    raise NullError, key if @attributes.nil?
    unless @ignore_nil && value.nil?
      if value.nil?
        @attributes[@key_formatter.format(key)] = ''
      else
        @attributes[@key_formatter.format(key)] = value
      end
    end
  end
  
  # when you want to skip a record when iterating over a collection
  def _map_collection(collection)
    return [] if collection.nil?

    collection.map do |element|
      _scope{ yield element }
    end.reject(&:blank?)
  end

end

Resetting counter cache

Ran into a problem whereby the counter cache on one of our associations was out of sync. Simple …just update the counter cache with the count of the association?

rails >> u User.find(1)
rails >> u.update_attribute(:roles_count, 1)
ActiveRecord::ActiveRecordError: roles_count is marked as readonly
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/activerecord-3.2.18/lib/active_record/persistence.rb:194:in `update_column'
	from (irb):17
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:47:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:8:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands.rb:41:in `<top (required)>'
	from /data/app/current/script/rails:6:in `require'
	from /data/app/current/script/rails:6:in `<main>'
rails >> u.update_column(:roles_count, 1)
ActiveRecord::ActiveRecordError: roles_count is marked as readonly
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/activerecord-3.2.18/lib/active_record/persistence.rb:194:in `update_column'
	from (irb):17
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:47:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:8:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands.rb:41:in `<top (required)>'
	from /data/app/current/script/rails:6:in `require'
	from /data/app/current/script/rails:6:in `<main>'
rails pro >> User.readonly_attributes
=> #<Set: {"roles_count"}>

Seems you can’t update the value, I was surprised that update_column wouldn’t work either. I guess it makes sense to protect this value since it’s supposed to be kept in sync and only in rare occasions should it be out of sync. However, you can use the reset_counters class method.

rails >> u = User.find(1)
rails pro >> User.reset_counters u.id, :roles
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
   (0.4ms)  SELECT COUNT(*) FROM `roles` INNER JOIN `role_users` ON `roles`.`id` = `role_users`.`role_id` WHERE `role_users`.`user_id` = 1
   (0.9ms)  UPDATE `users` SET `roles_count` = 1 WHERE `users`.`id` = 355411
=> true

Calling super with no arguments

Suppose I want to overwrite a method an apply a optional argument that the original method didn’t accept.

class Something < ActiveRecord::Base

  def to_s(or_other = nil)
      if or_other
        Other.new.to_s
      else
        super
      end
  end

end

Seems fine. However, when you call to_s you’d get an error like wrong number of arguments (1 for 0). The reason is because without supply arguments to super they, by default, use the arguments passed to the overridden method. Since the original method didn’t accept any arguments previously then this fails. In order to avoid this you must call super() to deliberatly call it without arguments.

class Something < ActiveRecord::Base

  def to_s(or_other = nil)
      if or_other
        Other.new.to_s
      else
        super()
      end
  end

end

Created a new OmniAuth Strategy for PledgeMusic

Just wanted to test out OmniAuth by plugging into PledgeMusic authentication.

Well it works and you get an access token out, nice and simple. Wouldn’t mind re-styling the OmniAuth::Form to be a bit more in keeping with the decor.

Extending RedCloth to use custom tags.

After finding the GitHub jQuery Repo Widget I want to embed the widget in my blog posts. The body of which uses RedCloth So I needed to add a custom tag which could turn.

github. JoelSutherland/GitHub-jQuery-Repo-Widget

into

<div class="github-widget" data-repo="JoelSutherland/GitHub-jQuery-Repo-Widget"></div>

You can extend RedCloth’s HTML formatter by placing something like this in config/initializer/redcloth_extensions.rb for example.

require 'redcloth'

module RedCloth
  module Extensions
    def github(options)
      html = %Q{<div class="github-widget" data-repo="#{options[:text]}"></div>\n}
    end
  end
end
RedCloth::Formatters::HTML.send(:include, RedCloth::Extensions)

And there you have it.