Free, private or public SVN with full project development tools…

Misc 6 Comments »

I cannot believe I have not come across assembla sooner !

Assembla is a free service, it provides virtually everything you need to get a project up and running with a distributed team. It is a web front end built round subversion, and provides a whole host of project orientated features.

The features are all modules (termed tools) and can be added and removed from your project at any time.
The tools include:

  • SVN database
  • Online chat that records history
  • Image management for collaborative image design
  • mephisto (blog and site management)
  • Milestones for project management
  • Scrum - for assiting in team meatings
  • Tickets - bug / enhancement request tool

and a whole host of others. Essentially it seems anything you need for product development is provided apart from the development experience and a good idea. They can however provide the development experience if necessary as discussed later.

Within the space of half an hour, I had signed up, had a good look around, created a PRIVATE project, connected by SVN and checked in the first version, invited a friend to join the project by the on-line invite, played with the chat (which doesn’t hold your interest for too long when your the only person in the project chat room) and created a couple of tickets for bits to do in the project. I was nicely suprised to see lots of emails in my inbox when I did things like check code in and add new tickets, keeping my informed even if I was not currently on the site. It’s all very integrated and very well thought out, e.g. the change set notification emails provide links to the changeset online so you can see all the post comments and fiels changed.

Everything you’ll ever need for managing and communicating in a distributed project development seems to be there.

So what do Assembla get out of it? Well it seems their main drive is not orientated around the project provision, you can pay extra for professional services (like team time tracking, off site synchronisation branding, etc. ) which for the service level is in my opinion is a very reasonable price. However, their main income comes from the non-project side of your account. You can choose without cost, to fill in details and publish your skills and profile on their site. This database of highly skilled people can then be tapped into by others who wish to hire developers. Someone who has a good idea can create a project site, source developers, manage the project and it’s progress and pay the developers all with out leaving assembla. The payment scheme operates similar to paypal, moving money into credit and the paying it to the developers assembla account when they have completed the work. There are various mechanisms for getting money in and out of the assembler account, of which assembler take a margin and a reasonable one at that.

So there you have it, all the benefits of sourceforge, etc with a lot more additional tools and integration. The ability to create open and closed source projects under the same user account. The site is responsive, organised, and easy to pickup, but what do you expect from a site written in Ruby on Rails?

Oh, and it’s free. What more could a developer want ?

JQuery and human readable email addresses

JQuery No Comments »

Ive just started playing with the power that is Jquery and came up with this nice use for the Ajax functionality in Jquery.

You want to put your email address on your web site so people can contact you, but don’t want to put a mailto: anchor in the html, because it will be spidered and added to some morons Spam list.

Here is a simple and server independent solution:

Firstly, create a file on your site, it doesn’t have to have a html extension, e.g. email.me
In the content of the file, add the html snippet that you would like to display to the user as your email address (usually an anchor tag with a mailto: href). This is the html that will be inserted into your web page when the user clicks on the Show email address text.

e.g.
[code]
<a href=”mailto:someone@somewhere”>someone@somewhere</a>
[/code]
In your web pages, where you wish someone to be able to see your email address by clicking, enter a span with a class of email.

[code]
<span class=”email”>Click to see email</span>
[/code]

in the ready function for your document enter the following:

[code]
$(’.email’).click(function(el){
$(this).hide().load(’email.me’).fadeIn(1000);
});
[/code]

This will add a click function to all elements assigned the email class, that when clicked, will replace the current content with the content of the email.me file (faded in :) ). In this case your email mailto anchor. This principle can be used to display any content. Maybe you want to hide the comment form for your blog, until a display has been clicked, give it a try.

Script Hightlighter

JavaScript No Comments »

A nice script highlighter that I need to integrate at some point. It is written in javascript so does not rely on server side technologies which is quite nice and will highlight the following languages.

Language Aliases
C++ cpp, c, c++
C# c#, c-sharp, csharp
CSS css
Delphi delphi, pascal
Java java
Java Script js, jscript, javascript
PHP php
Python py, python
Ruby rb, ruby, rails, ror
Sql sql
VB vb, vb.net
XML/HTML xml, html, xhtml, xslt

Produces output that looks like this…

Javascript - Rails like pluralize function

JavaScript No Comments »

Found this on scripts.dzone.com blogged for reference.

/*
 * This script depends on no outside libraries.
 */

Inflector = {
    /*
     * The order of all these lists has been reversed from the way 
     * ActiveSupport had them to keep the correct priority.
     */
    Inflections: {
        plural: [
            [/(quiz)$/i,               "$1zes"  ],
            [/^(ox)$/i,                "$1en"   ],
            [/([m|l])ouse$/i,          "$1ice"  ],
            [/(matr|vert|ind)ix|ex$/i, "$1ices" ],
            [/(x|ch|ss|sh)$/i,         "$1es"   ],
            [/([^aeiouy]|qu)y$/i,      "$1ies"  ],
            [/(hive)$/i,               "$1s"    ],
            [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
            [/sis$/i,                  "ses"    ],
            [/([ti])um$/i,             "$1a"    ],
            [/(buffal|tomat)o$/i,      "$1oes"  ],
            [/(bu)s$/i,                "$1ses"  ],
            [/(alias|status)$/i,       "$1es"   ],
            [/(octop|vir)us$/i,        "$1i"    ],
            [/(ax|test)is$/i,          "$1es"   ],
            [/s$/i,                    "s"      ],
            [/$/,                      "s"      ]
        ],
        singular: [
            [/(quiz)zes$/i,                                                    "$1"     ],
            [/(matr)ices$/i,                                                   "$1ix"   ],
            [/(vert|ind)ices$/i,                                               "$1ex"   ],
            [/^(ox)en/i,                                                       "$1"     ],
            [/(alias|status)es$/i,                                             "$1"     ],
            [/(octop|vir)i$/i,                                                 "$1us"   ],
            [/(cris|ax|test)es$/i,                                             "$1is"   ],
            [/(shoe)s$/i,                                                      "$1"     ],
            [/(o)es$/i,                                                        "$1"     ],
            [/(bus)es$/i,                                                      "$1"     ],
            [/([m|l])ice$/i,                                                   "$1ouse" ],
            [/(x|ch|ss|sh)es$/i,                                               "$1"     ],
            [/(m)ovies$/i,                                                     "$1ovie" ],
            [/(s)eries$/i,                                                     "$1eries"],
            [/([^aeiouy]|qu)ies$/i,                                            "$1y"    ],
            [/([lr])ves$/i,                                                    "$1f"    ],
            [/(tive)s$/i,                                                      "$1"     ],
            [/(hive)s$/i,                                                      "$1"     ],
            [/([^f])ves$/i,                                                    "$1fe"   ],
            [/(^analy)ses$/i,                                                  "$1sis"  ],
            [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis"],
            [/([ti])a$/i,                                                      "$1um"   ],
            [/(n)ews$/i,                                                       "$1ews"  ],
            [/s$/i,                                                            ""       ]
        ],
        irregular: [
            ['move',   'moves'   ],
            ['sex',    'sexes'   ],
            ['child',  'children'],
            ['man',    'men'     ],
            ['person', 'people'  ]
        ],
        uncountable: [
            "sheep",
            "fish",
            "series",
            "species",
            "money",
            "rice",
            "information",
            "equipment"
        ]
    },
    ordinalize: function(number) {
        if (11 <= parseInt(number) % 100 && parseInt(number) % 100 <= 13) {
            return number + "th";
        } else {
            switch (parseInt(number) % 10) {
                case  1: return number + "st";
                case  2: return number + "nd";
                case  3: return number + "rd";
                default: return number + "th";
            }
        }
    },
    pluralize: function(word) {
        for (var i = 0; i < Inflector.Inflections.uncountable.length; i++) {
            var uncountable = Inflector.Inflections.uncountable[i];
            if (word.toLowerCase() == uncountable) {
                return uncountable;
            }
        }
        for (var i = 0; i < Inflector.Inflections.irregular.length; i++) {
            var singular = Inflector.Inflections.irregular[i][0];
            var plural   = Inflector.Inflections.irregular[i][1];
            if ((word.toLowerCase() == singular) || (word == plural)) {
                return plural;
            }
        }
        for (var i = 0; i < Inflector.Inflections.plural.length; i++) {
            var regex          = Inflector.Inflections.plural[i][0];
            var replace_string = Inflector.Inflections.plural[i][1];
            if (regex.test(word)) {
                return word.replace(regex, replace_string);
            }
        }
    }
}

function ordinalize(number) {
    return Inflector.ordinalize(number);
}

/*
 * Javascript doesn't have optional parameters or overloading so I had to use
 * the ever popular pseudo options hash object technique.
 * required properties:
 *     count    Number of objects to pluralize
 *     singular Singular noun for the objects
 * optional property:
 *     plural   Plural (probably irregular) noun for the objects
 * examples:
 *      pluralize({ count: total_count, singular: "Issue" })
 *      pluralize({ count: total_count, singular: "Goose", plural: "Geese" })
 */
function pluralize(options) {
    return options.count + " " + (1 == parseInt(options.count) ?
        options.singular :
        options.plural || Inflector.pluralize(options.singular));
}

Oh my god, the future of Rich Internet Applications is here…

JavaScript No Comments »

In my opinion, there are key events that happen that shape the future of web development.  The move to Web 2.0 and ajax, was a little tremor compared by the Factor 10  on the Richter scale announcement of Aptana’s Jaxer server.  I have always found having to develop javascript client side, C# / PHP / ASP or some other technology server side annoying.  Having to replicate the same validation in different languages and having to write data conversion layers to get data in either json or xml format just so the client can use it annoying, a pain to maintain and a waste of development time.

Enter Jaxer, essentially server side javascript processing but on steroids.  With Jaxer, you can write in the single html page, javascript that can run on either the client, the server or both, server side code is never sent to the client browser enabling you to hide your intellectual property and the workings of you API.  Any javascript library that can run client side can be used server side as well and they have added server side libraries for writing to files, socket communications, database access and web based communication. The Client can even make callbacks to server side functions simply by setting function.name.proxy = true, and Jaxer will create an Ajaxy stub client side making the server side call available to the client.

It is implemented using the mozilla firefox 3 javascript and dom engine, and a large proportion of the interpreter is C++ code, so performance should be good.  I would love to see some performance stats that compare it against php / asp / etc , but haven’t come across any yet.  If Aptana have got the performance nail hit on the head, this really will be the future of Rich Internet application development.

Not the best way to start the day…

Misc No Comments »
For some reason this morning, I must have had one of those revelation moments, I woke up thinking about certainties. 

There are only 3 things that are certain in life: death, taxes, and browser incompatibilities…

I think I need a break….

Installing and managing edge Rails

Rails, Ruby 1 Comment »

Great article here:

http://www.sitepoint.com/blogs/2006/07/11/installing-and-managing-edge-rails/

How to stop webrick from the command line

Rails, Ruby 2 Comments »

To create a bash file to stop webrick.

In a terminal type gedit stopWebrick and add the following to the contents of the file and save it.  Give the file execute permission with chmod of using the file properties in nautilus.

#!/bin/bash
kill -9 `ps -o pid,cmd –no-heading -p $(pgrep ruby) | grep script/server | awk ‘{print $1}’`

This will kill the first ruby process it finds running as script/srever

Run it as needed.

UserSumChallenge Module

Rails, Ruby 1 Comment »

The following module is my first ever Ruby/Rails Module.

The UserChallenge module will create a simple random sum, that can be prompted to the user for solving as part as your authentication scheme during a registration process.
Simply create an instance of UserSumChallenge or UserSumImageChallenge and store against the user session. If you use the image challenge, call render to create the image otherwise just add the question attribute to your output.










# UserChallenge provides several mechanisms to aid in establishing a real user is on the end of a submit

# Typically used for user registration processes or commenting, the UserChallenge module will allow you to provide

# a challenge question and validate the repsponse.

# Mixins include

#       * SumChallenge - provides a simple arimethetic sum to be solved

#       * RandomTextChallenge - supplies a random string of characters

# Supporting Classes include

#       * SumTextChallenger - Class that will provide a question as a text string

#       * SumImageChallenger - Class that will create a png image with the sum in it

#       * RandomTextChallenger - Class that will provide a random text string

#       * RandomTextImageChallenger - Class that will create a png image with the random text string in it

module UserChallenge

    

    # This module creates a challenge that is a basic sum, expecting the respose to be the sums result, e.g. 2+6

    module SumChallenge

        #Initailizes the challenge to a sum

        #optionally specify the max values for the left and right 

        def createChallenge(lMax=10, rMax=10)

            lValue = rand(lMax)

            rValue = rand(rMax)

            @solution = lValue + rValue

            @question “#{lValue} + #{rValue}”

        end

        # checks the value passed in against the expected result    

        def checkResponse(value)

            return (@solution == value.to_i)

        end

    end

    # This module creates a challenge that is a series of random characters in a specified range, expecting the respose to be the same string

    module RandomTextChallenge

        #Initailizes the challenge to a sum

        #optionally specify the number of characters to generate and the range of characters to choose from

        def createChallenge(count=6, chars = ‘a’..‘z’)

            @question “”

            count.times do

                index = rand(chars.entries.length-1)

                @question += chars.entries[index]

            end

            @solution @question

        end

        # checks the value passed in against the expected result    

        def checkResponse(value)

            return (@solution == value.to_s)

        end

    end

    

    # A simple class that generates a sum challenge in text form

    class SumTextChallenger

        include SumChallenge

        

        # the sum question as a string

        attr_reader :question

        # the solution as an integer

        attr_reader :solution

        

        def initialize()

            createChallenge()

        end

    end

    

    # A class that generates a sum challenge in image form

    class SumImageChallenger

        require “RMagick”

        include SumChallenge

        # the sum question as a string

        attr_reader :question

        # the solution as an integer

        attr_reader :solution

        

        def initialize()

            createChallenge()

        end

        

        # returns an image blob with the sum question layered ontop of the specified background

        def render(backgroundImageUrl)

            background = Magick::Image.read(backgroundImageUrl).first

                

            croppedImg = background.crop(0,0,128,28)

            

            text = Magick::Draw.new

            text.annotate(croppedImg, 1282800@question) {

              self.gravity = Magick::SouthGravity

              self.pointsize = 24

              self.stroke = ‘transparent

              self.fill = ‘#0000A9

              self.font_weight = Magick::BoldWeight

            }

            

            croppedImg = croppedImg.blur_image

            return croppedImg.to_blob

        end

    end

    # A simple class that generates a random text challenge in text form

    class RandomTextChallenger

        include RandomTextChallenge

        

        # the sum question as a string

        attr_reader :question

        # the solution as an integer

        attr_reader :solution

        

        def initialize()

            createChallenge()

        end

    end

    

    # A class that generates a random text challenge in image form

    class RandomTextImageChallenger

        require “RMagick”

        include RandomTextChallenge

        # the sum question as a string

        attr_reader :question

        # the solution as an integer

        attr_reader :solution

        

        def initialize()

            createChallenge()

        end

        

        # returns an image blob with the sum question layered ontop of the specified background

        def render(backgroundImageUrl, width=128)

            background = Magick::Image.read(backgroundImageUrl).first

            

            croppedImg = background.crop(0,0,width,28)

            

            text = Magick::Draw.new

            text.annotate(croppedImg, width, 2800@question) {

              self.gravity = Magick::SouthGravity

              self.pointsize = 22

              self.stroke = ‘transparent

              self.fill = ‘#0000A9

              self.font_weight = Magick::BoldWeight

            }

            

            croppedImg = croppedImg.blur_image

            return croppedImg.to_blob

        end

    end

end


Here is a simple controller that uses a ImageChallenge.  Notice the background being supplied to the render method

class MainController < ApplicationController require "UserChallenge" def welcome @challenge = UserChallenge::UserSumImageChallenge.new session["uc"] = @challenge end def image storePath = "/home/user/path" filename = storePath + "/Camouflage.png" challenge = session["uc"] session["uc"] = nil send_data challenge.render(filename), :filename => "confirm.png", :type => 'image/png', :disposition => 'inline' end end

Installing RMagick on Ubuntu

Ruby No Comments »

Taken from the rubyonrails wiki, for my reference:

If you need RMagick support for your web app, your going to need to install ImageMagick and RMagick.

sudo apt-get install imagemagickdpkg -l | grep magick

This will install the most current imagemagick and then

list the ‘magick’ packages installed. Look for what version of
libmagick got installed and make sure you specify THAT version number when you install libmagick-dev.

sudo apt-get install libmagick9-devsudo gem install rmagick

After a lengthy build process, you’re ready to edit images in ruby!

WP Theme &Design by minus19.com & Icons
Entries RSS Comments RSS Log in