Author Archive

Joining the Internet Blackout

Tuesday, February 17th, 2009

zoomin_black

We at ProjectX support the movement to blackout in protest against the Guilt Upon Accusation law Section 92A. In solidarity to the blackout movement, we have “blacked” out our maps on http://www.zoomin.co.nz and http://www.projectx.co.nz.

This blackout will be in effect until February 23rd, 2009.

If you are interested in helping out and have Google maps, here’s the code we used to black out our maps:

//Assume you have a map object
//Create a BlackOutMessageControl
function BlackoutMessageControl() {}
BlackoutMessageControl.prototype = new GControl();
BlackoutMessageControl.prototype.initialize = function(map) {
var container = document.createElement("div");

var src = “http://creativefreedom.org.nz/blackout.html”

var link = document.createElement(“a”);
link.setAttribute(‘href’, src);
link.setAttribute(‘title’, “Internet Blackout NZ”);
link.appendChild(document.createTextNode(‘Why is the map “blacked” out?’));
link.style.color = “#ffffff”;
container.appendChild(link);
this.setButtonStyle_(container);

GEvent.addDomListener(container, “click”, function() {
window.location = src;
});
GEvent.addDomListener(container, “mouseover”, function() {
link.style.textDecoration = “underline”;
});
GEvent.addDomListener(container, “mouseout”, function() {
link.style.textDecoration = “none”;
})

map.getContainer().appendChild(container);
return container;
}
BlackoutMessageControl.prototype.getDefaultPosition = function(){
return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(75, 7));
}
// Sets the proper CSS for the given button element.
BlackoutMessageControl.prototype.setButtonStyle_ = function(button) {
button.style.textDecoration = “none”;
button.style.color = “#ffffff”;
button.style.backgroundColor = “black”;
button.style.border = “1px solid black”;
button.style.padding = “2px”;
button.style.marginBottom = “3px”;
button.style.textAlign = “center”;
button.style.width = “20em”;
button.style.cursor = “pointer”;
}

//Dark maps
var myCopyright = new GCopyrightCollection(“© “);
myCopyright.addCopyright(new GCopyright(‘Demo’,
new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)),
0,’©2008 ProjectX Technology’));
var tilelayer = new GTileLayer(myCopyright);
tilelayer.getTileUrl = function() { return “/images/dark_map.gif”; };  //Modify the image location to suit
tilelayer.isPng = function() { return false;};
tilelayer.getOpacity = function() { return 0.5; }
var myTileLayer = new GTileLayerOverlay(tilelayer);
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
map.addOverlay(myTileLayer);
map.addControl(new BlackoutMessageControl());

You can use the image(dark_map.gif) below

dark_map.gif

Completely custom routes

Thursday, October 30th, 2008

In a recent project for the New Zealand business community, we were required to have some really custom routes. An example of such routes would be geographical routes, e.g. /china, or /china/beijing.

We could try something like this:

map.country ':country', :controller => :locations, :action => :show,
  :country => Regexp.new(Country.all.map(&:name).join('|'))
map.region ':country/:region', :controller => :locations, :action => :show,
  :country => Regexp.new(Country.all.each{|c| "^#{c.name}" }.join('|'))

However we get this error “Regexp anchor characters are not allowed in routing requirements”. So we can’t use requirements. However, we can use route conditions instead. With inspiration from Jamis Buck’s route monkeypatch :

# /lib/routing_extensions.rb
module ThongKuah
module Routing

  module RouteExtensions
    def self.included(base)
      base.alias_method_chain :recognition_conditions, :path_regexp
    end

    # allows recognition for paths only matching the given regexp (conditions[:path])
    # allows recognition for paths not matching the given regexp (conditions[:not_path])
    def recognition_conditions_with_path_regexp
      result = recognition_conditions_without_path_regexp
      result << "conditions[:path] =~ path" if conditions[:path]
      result << "(conditions[:not_path]=~path).nil?" if conditions[:not_path]
      result
    end
  end

end
end

# /config.environment.rb
require 'route_extension'
ActionController::Routing::Route.send :include, ThongKuah::Routing::RouteExtensions

So now we can go :

map.country ':country', :controller => :locations, :action => :show,
  :conditions => {:path => Regexp.new(Country.all.map(&:name).join('|'))}
map.region ':country/:region', :controller => :locations, :action => :show,
  :conditions => {:path => Regexp.new(Country.all.each{|c| "^#{c.name}" }.join('|')) }

Note we can have negative matches as well. I’m sure the Regular expressions gods can do a negative match use Regexp only, but hey.

We found this rails idiom to be quite useful as this allows us full customisation of routing, if we need it. We have used it successfully for Made from New Zealand, so you check it out on pages such as www.madefromnewzealand.com/new-zealand and www.madefromnewzealand.com/new-zealand/auckland.

Pastie of the code here.

Xapian search – acts_as_xapian tip (II)

Saturday, September 20th, 2008

Further to my first post on using acts_as_xapian, I have been trying to make xapian work with pagination and association proxies properly

class Lesson < ActiveRecord::Base
# Index user_id as a term in xapian

    belongs_to :user
    def self.find_with_xapian(search_term, options={})
      ActsAsXapian::Search.new([self], search_term, options).results.collect{|x| x[:model]}
    end
end

class User < ActiveRecord::Base
    has_many :lessons do  #Extend this association
      # Override the method in lessons
      def self.find_with_xapian(search_term, options={})
        ActsAsXapian::Search.new([proxy_reflection.klass],
          "{proxy_reflection.primary_key_name}:#{proxy_owner.id}  #{search_term}", options)
        .results.collect{|x| x[:model]}
      end
    end
end

This will ensure that current_user.locations.find_with_xapian will find the correct number of locations, enabling us to work with pagination, etc. What’s left to do is to get the Search object out so that we can get matches_estimated out. I’ll leave out for next time.

This post was brought to you from Software Freedom Day, New Zealand.

Xapian search – acts_as_xapian tip

Thursday, September 11th, 2008

We use xapian as our offline indexing solution here at ProjectX. Recently, Francis Irving from mySociety has started a fantastic Rails plugin, acts_as_xapian. I’ll leave the introductions to the aforementioned pages. Suffice to say that it is quite easy to install, and it utilizes all the power of Xapian in a very smooth way.

One thing I have not been able to crack is to utilize association proxies on searches. Locomotivation has a solution using named_scope, but that approach removes all ordering, in addition to issuing one more unnecessary SQL statement.

class Lesson < ActiveRecord::Base
    named_scope :find_with_xapian, lambda { |*args| {:conditions => ["lessons.id in (?)", ActsAsXapian::Search.new([Lesson], args.to_s).results.collect{|x| x[:model].id}]}}
end

But if we do this instead, we can now do association proxies

class Lesson < ActiveRecord::Base
    def self.find_with_xapian(search_term, options={})
      ActsAsXapian::Search.new([self], search_term, options).results.collect{|x| x[:model]}
    end
end

current_user.lessons.find_with_xapian "something"

That would automatically scope and return lessons that belong to the current user only. Awesome!

Update: The code is not showing quite as nicely. I have pasted the same code in Pastie – http://pastie.org/270114

ZoomIn Outage – Data Maintenance

Monday, July 7th, 2008

ZoomIn will be down from 6pm today (7th July 2008). We will be performing several data maintenance tasks, including updated addresses and suburbs. We apologise for any inconvenience caused.

The site should be back up within the night, after the tasks have been completed.