Techblog

Tech Blog

Our latest geek adventures!

Archive for July, 2008

31 July Lighten up your development with Lighthouse

Posted by Gert-Jan in Development

Over the last year we’ve tried a lot of different applications to keep track of bugs and other issues, but none of those apps sticked with us. They were either not suited for the job (Basecamp) or to difficult and ugly (Trac, Bugzilla). But now I think that we finally have found a solution that just gets the job done: Lighthouse by activereload.

Lighthouse ticket scoreboardLighthouse’s tagline is “beautiful simple issue tracking” and that really sums it up perfectly. It tracks issues, not only a bugs, so you can use it for all the issues that need to be dealt with. It’s simple and it’s easy to use, it just does what it needs to do without loads of unneeded features. A great advantage for us is that our designers can (and want) to use it too. It’s also very nicely priced and it has a sweet API for us geeks to build your own scoreboard :-D (see picture).

There are some minor things that can be improved (for example, it’s somethings a little confusing to work with multiple projects) but it’s by far the best issue tracker I’ve ever worked with. To everybody at activereload, we really love your product and keep up the good work!

No Comments - Tags: , , ,

29 July Active OLAP released

Remember my post about easy OLAP queries in Rails? I rewrote it almost completely and published is as a Rails plugin for anyone to use on github! It is now called: Active OLAP.

Although it is a complete rewrite, the API I demoed in my previous post should still work with some small changes. The most important: you have to enable it for every class you want to use it on with the enable_active_olap method. You can provide a block to this method with dimension definitions, but is not mandatory:

class User < ActiveRecord::Base
 
  enable_active_olap do |olap|
 
    # create a simple dimension on the account_type field
    olap.dimension :account_type
 
    # create a dimension with custom categories
    # the order of the categories will be kept in the results 
    # if you use an array to define the categories.
    olap.dimension :nationality, :categories => [
      [:usa, { :country => 'US' }],
      [:china, { :country => 'CN' }]
      # other is automatically added
    ]
 
    # Easily create a trend dimension
    olap.dimension :created_daily, :trend => {
      :timestamp_field => :created_at,
      :period_length => 1.day, 
      :period_count => 20
    }
  end
end

Now, we can use these dimensions for our OLAP queries. Multiple dimensions are supported too!

# simple query
@result = User.olap_query(:nationality)
# @result[:usa] == 123, @result[:china] == 456, @result[:other] = 789
 
# do drilldown using will_paginate to paginate the results
# olap_drilldown is implemented as a named_scope
@users = User.olap_drilldown(:nationality => :china).paginate(:page => 1)
 
# multiple dimensions!
@result = User.olap_query(:nationality, :created_daily)
@users = User.olap_drilldown(:nationality => :china, 
                        :created_daily => :period_19)

I am working on a generic controller that can easily be added to your Rails project. Just define dimensions for your models and the controller will let you execute OLAP queries and display the results as a table or a graph.

Keep an eye on this weblog or the github project if you want to stay up-to-date! Or, contact me if you have questions, suggestions or want to help out.

5 Comments - Tags: , , ,

26 July Easy search with ActiveRecord

A couple of minutes ago I released scoped_search, a Rails/ActiveRecord plugin that makes it easy to search your models. It is very easy to use:

  1. Install the plugin in your vendor/plugins directory from http://github.com/wvanbergen/scoped_search
    Add the gem to your rails environment.rb:

    config.gem 'wvanbergen-scoped_search', :lib => 'scoped_search', 
        :source => 'http://gems.github.com'

    Call rake gems:install afterwards to ensure the gem is installed.

  2. Define in what fields your model should be searched by calling
    searchable_on :some, :field, :names
  3. Find your records by calling search_for("query keywords")

That’s all! A short example:

class Project < ActiveRecord::Base
  searchable_on :name, :description
end
 
Project.search_for("search keywords").each do |project| 
  puts project.name
end
 
# SELECT * FROM projects WHERE 
#      (name LIKE '%search%' OR description LIKE '%search%') 
#  AND (name LIKE '%keywords%' OR description LIKE '%keywords%')

This functionality is completely build upon named_scope. The search_for method is actually a named scope that was created by the call to searchable_on. Because these scopes can be chained, this offers some great possibilities.

For example, in Floorplanner, we only want you to search on the projects you have access to. We have implemented this access logic in another named scope. The calls can simply be chained:

class Project < ActiveRecord::Base
  searchable_on :name, :description
 
  named_scope :accessible_by, lambda { |user| ... }
  named_scope :published, :conditions => 'published_at IS NOT NULL'
end
 
@projects = Project.accessible_by(current_user).published.search_for('query')
@projects.each { |project| ... }

This plugin is released under the MIT license, so please use it for any purpose you see fit. There are some TODOs: you currently can not search on fields in other tables, and splitting the search string into keywords is very basic. Please contact me if you have implemented any of these features and you are willing to share them! Do not hesitate to contact me in case or problems either.

Update: I added support for quotes and the minus sign to the query language:
Project.search_for('willem -"van bergen"').count

Update #2: Wes Hays implemented the OR keyword:
Project.search_for('wes OR hays').count.
A big thanks to Wes for helping out on this project!!

10 Comments - Tags: , , , ,

19 July Snack 2.0

Earlier this week we discussed a fast food snack that is available in Rotterdam called “Kapsalon”, literally “Hairdresser’s”. According to the urban legend, the name came into existence after employees from a hairdresser’s composed their favorite meal at the shoarma place next door. The “calorie bomb” contains french fries, shoarma, cheese and lettuce, all thrown together. Unboxing pictures of it can be seen here.

Within a couple of months, it became rather popular in Rotterdam and most shoarma places include the dish on their menu, next to döner kebab and Turkish pizza. At Floorplanner we hope it will spread and become a national phenomenon. Not because the dish is so tasty or healthy, because it is not. It is, however, a very buzzword compliant meal:

  • It could be described as a mashup, as it just consists of some existing dishes trown together in a unique manner.
  • It is user-generated content, as the customers of the shoarma place invented the dish instead of the shoarma shop itself.
  • Its popularity is because of a grassroots campaign, instead of a major marketing undertaking by one of the big Dutch snack producers like Mora or Beckers.

It’s almost a shame that the birth of this phenomenon is taking place in the city of Feyenoord instead of the city of AJAX. ;-)

1 Comment - Tags: , , , ,

16 July Using setInterval in a JavaScript class

Posted by Gert-Jan in Javascript

I just figured out how to use setInterval in a JavaScript class. This little snippet shows how I used setInterval in a recursive way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function MyClass() {}
 
MyClass.prototype.doNext = function( pArray ) {
	clearInterval( this.interval );
 
	if( pArray.length > 0 ) {
		// do something with the array record
		this.doSomething( pArray.pop() );
 
		// call this function again in a couple of milliseconds
		var scope = this;
		var milliseconds = 100;
		this.interval = setInterval( function(){ scope.doNext( pArray ) }, milliseconds );
	} 
} 
 
MyClass.prototype.doSomething = function( pRecord ) {
	// do something
}
 
var myObj = new MyClass();
myObj.doNext( myArray );

No Comments - Tags: ,

14 July Easy OLAP queries in ActiveRecord

Because I love statistics so much, I decided to add some neat statistics functionality to the Floorplanner administration interface, so we can get better insight in what is going on. Instead of writing complete OLAP SQL queries myself and adding a custom interface for each one of them so our management can use them (yes Jeroen, that means you!), I built an ActiveRecord extension to ease the work. Right now, I only have to define some categories, and it automagically generates the right SQL query to generate charts and tables with the number of records that fall in each category. Moreover, by clicking on these numbers, I can drill down to the individual records.

I can define the categories like this:

olap_definition = { :categories => {
  :project_is_private   => { :public => false, :publishd_at => nil },
  :project_is_public    => { :public => true,  :publishd_at => nil },
  :project_is_published => 'projects.published_at IS NOT NULL'
}}

Not too hard, was it? Now, I can easily feed this to Project.olap_query:

@query_result = Project.olap_query(olap_definition) 
# @query_result == {
#   :project_is_private   => 123,
#   :project_is_public    => 456,
#   :project_is_published => 3,
#   :other                => 2
# }

Note that the category other is added automatically, but can be omitted if you wish. (I found that the other-category is nice to spot data integrity problems in your dataset you didn’t think of beforehand). The result can be used to create a table with the results, plot a pie chart with the Google Charts API. Because this setup is completely generic, this functionality only has to be written once. DRY!

The SQL for other-category is “calculated” by OR-ing all the categories and checking whether the result is false, or NULL. The check for NULL is necessary if you have NULL-values in your table: this is a weird characteristic of SQL that defines that TRUE AND NULL equals NULL (see Wikipedia).

The actual SQL query for this example would be:

SELECT 
  SUM(projects.public = 0 AND projects.published_at IS NULL) AS project_is_private,
  SUM(projects.public = 1 AND projects.published_at IS NULL) AS project_is_public,
  SUM(projects.published_at IS NOT NULL) AS project_is_published,
  SUM( NOT (
    (projects.public = 0 AND projects.published_at IS NULL) OR
    (projects.public = 1 AND projects.published_at IS NULL) OR
    (projects.published_at IS NOT NULL)
  ) OR (
    (projects.public = 0 AND projects.published_at IS NULL) OR
    (projects.public = 1 AND projects.published_at IS NULL) OR
    (projects.published_at IS NOT NULL) IS NULL)) AS other
FROM projects

Some notes about this query:

  • It is complety built using the fragments from the categories. The fragment for the other-cagegory is a little verbose, but what do I care? It works and is generated automatically! :-)
  • Note that a record can be in multiple categories, depending on the category definitions. The other category only contains records that conform to none of the provided categories.
  • SUM is used in stead of COUNT. This way, I can query all the categories at once and it solves the problems with NULL-values, while keeping my WHERE and GROUP BY clause nice and clean :-)
  • The query is built completely using ActiveRecords find method by using anonymous scopes. Therefore, Rails 2.1 is required, but this makes some neat tricks possible as well.

I also have a Project.olap_drilldown method that I can use to find the individual projects in a category:

@projects = Project.olap_drilldown(olap_definition, :project_is_public)
# SELECT projects.* FROM projects 
# WHERE (projects.public = 1 AND projects.published_at IS NULL)
 
@projects.each do |project|
  puts project.name
end

Because this functionality is built on anonymous scopes, it offers some interesting additional functionality. You can use your own scopes to limit the input dataset

class Project < ActiveRecord::Base
  named_scope :recent, lambda { { :conditions => 
              ['created_at > ?', Time.now - 7.days]} }
  ...
end
# This will add a WHERE-clause to the OLAP query
results = Project.recent.olap_query(olap_definition)
 
# Or, use :conditions for the same effect
results = Project.olap_query(olap_definition.merge(
            :conditions => ['created_at > ?', Time.now - 7.days]))

As I noted before, the GROUP BY-clause is not used. I already built an extension to use the GROUP BY clause to group the results in periods of a given timestamp field of the model (e.g. created_by). When I pass the result of such a query to the Google Chart API, I can generate trend graphs to see how my dataset is evolving.

If I have time and there is any interest, I may release this extension as a gem or Rails plugin.

UPDATE: I rewrote it and released this project on github.

1 Comment - Tags: , , , ,

14 July Welcome back!

Posted by Gert-Jan in Floorplanner

Welcome to our brand new Floorplanner tech blog! Here we will be posting all our little technical adventures while working on the Floorplanner. This blog focusses mainly on the technical side of the Floorplanner, so you can expect stories about Flash/Flex, Ruby on Rails, Papervision 3D, JavaScript and all other kinds of thingies we run into on a daily basis. 

You might know that we did a similar thing at a tech blog on Suite75, but a couple of things have changed over the last year. As Suite75 we developed rich internet applications for all kinds of clients. Instead of doing a lot of different projects for clients, we wanted to build and sell our own product. Our mission: to be the easiest, quickest and best looking way to create and share interactive floor plans online. Floorplanner was born.

Along with the mission came a huge amount of technical challenges in the world that is know as the World Wide Web. It will be our pleasure to tell you about all the technical problems (and hopefully the solutions) we had to overcome to achieve our goal. So buckle up and join us on the bumpy road!

No Comments -

11 July Using git-svn

I personally am a fan of the git version control system. The best part of git is its speed, and the simplicity of using local branches.

Local branches are very helpful if you are working on different features at the same time but want to keep them apart. An example: it happens all the time that I am working on some feature and than I have to put my current work aside to work on a high priority issue. Once this issue is solved, I need to commit the changes and usually do a deploy of the web application so that the problem is solved as soon as possible. With Subversion, I sometimes commit files that were part of the unfinished feature I was working on before I started on the high priority issue. If I am not careful and deploy those files, unfinished work will be put into production and this can go horribly wrong, like every page request returning a 500-error of our high traffic site :( .

Using git, I can put my current work aside easily by using git stash. When I am finished with the high priority issue, I can revert to my previous work with git stash apply.

Another option: branching the project (using git branch feature) if the feature I am working on is invasive and than switch branches for high priority issues using git checkout master. I can go back to the feature branch with git checkout feature, followed by git merge master to merge back the changes I just made in the master branch. Branching and merging is very fast in git and merging is not the PITA like it is in Subversion.

However, our main code repository will probably remain in SVN for now. Luckily for me, I can use git-svn locally to profit from these advantages. I found an informative page on installing and getting started with git-svn on OSX. If you know Subversion, this page is helpful to translate Subversion commandos to their git alternatives.

No Comments - Tags: , , , ,