Vixiom Axioms

September 27, 2006

Ruby on Rails Order by Associated Model

Filed under: Ruby on Rails Alastair @ 1:46 pm

Grouping information helps users traverse your app in a much faster manner (and thus be more productive which leads them to rave about your mad skills to their friends and refer you more work!). Today I was building a website for a gallery and needed to show the list of artwork entered into the system. At first I was ordering the artwork by ‘artist_id’ and ‘title’ but since ‘Artists’ aren’t necessarily going to be entered in alphabetical order the list doesn’t make sense ordered by ‘artist_id’ (sense to a human expecting alphabetical ordering).

After a couple of failed attempts I remembered the ‘:include’ argument for the ‘find’ method. Once I included artists into the artwork result I could order by ‘artist.last_name’.

    @artworks = Artwork.find(:all, :include => :artist, :order => “artists.last_name, title”)

Like most things in rails ordering by associations is easy once you know how.

Digg! submit Ruby on Rails Order by Associated Model to stumbleupon.com submit Ruby on Rails Order by Associated Model to del.icio.us submit Ruby on Rails Order by Associated Model to reddit.com Like this post? subscribe to the feed.

September 26, 2006

Yahoo! Ruby Developer Center

Filed under: Ruby on Rails Alastair @ 10:38 am

Not sure how I missed it but on the same day Yahoo! launched the Flash Developer Center the also launched a Ruby Developer Center as well!

Digg! submit Yahoo! Ruby Developer Center to stumbleupon.com submit Yahoo! Ruby Developer Center to del.icio.us submit Yahoo! Ruby Developer Center to reddit.com Like this post? subscribe to the feed.

September 23, 2006

How to Yahoo! with Flash & Flex

Filed under: ActionScript, Flash, Flex Alastair @ 10:20 am

Yahoo! has a set of tutorials in their Flash Developer Network

The tutorials include

• performing a Yahoo! Search with ActionScript 2 and 3
• embed a Yahoo! Map with ActionScript 2
• create a Yahoo! Music Jukebox plugin
• create a mash up of Flash and upcoming.org

The Yahoo! Maps API is very well documented and they have a ton of cool examples in their Maps Application Gallery, including a video tour of San Francisco.

Digg! submit How to Yahoo! with Flash & Flex to stumbleupon.com submit How to Yahoo! with Flash & Flex to del.icio.us submit How to Yahoo! with Flash & Flex to reddit.com Like this post? subscribe to the feed.

September 22, 2006

FlexFlash/Bitmap Tricks and Techniques

Filed under: Flex Alastair @ 8:06 am

Andrew Tice shows off some cool examples of the Bitmap API.

The first set of examples fall into the ‘tricks’ category, but the last one is a very useful technique exporting a screen shot of the flex app’s current state and saving it to the server!

More crazy ActionScript effects from The Flash Blog shows off a video streak effect that can be manipulated to produce some live ‘After Effects’.

Digg! submit FlexFlash/Bitmap Tricks and Techniques to stumbleupon.com submit FlexFlash/Bitmap Tricks and Techniques to del.icio.us submit FlexFlash/Bitmap Tricks and Techniques to reddit.com Like this post? subscribe to the feed.

September 20, 2006

Flash/Flex Remoting and Disabling Rails Sessions

Filed under: Flash, Flex, Ruby on Rails Alastair @ 4:06 pm

The things that make web development with Ruby on Rails so easy, and dare I say fun, can also sometimes get in the way or cause unexpected problems (not fun). Rails sessions are generated automatically which is cool if you need sessions for say a shopping cart app. However if you don’t need sessions, or you want to handle data tracking in a different way with Flex/Flash’s Shared Objects, Rails will still chug along and output a session unless you tell it not to.

Here’s how to stop the session madness. Open up the WebORB controller and add this line ‘session :disabled => true‘.

class WeborbController < ApplicationController

  session :disabled => true

This is not Remoting specific and works for any controller in a Rails app. Rails sessions will keep multiplying like those furry things from that infamous Star Trek episode (Tribbles, thanks Google). eventually you’ll need a CRON job on your server to delete sessions from the tmp/sessions folder (or the database if you’re using ActiveRecord Store), so you might as well not create them in the first place and avoid a mess to clean up!

Here’s a good article on Rails sessions.

Digg! submit Flash/Flex Remoting and Disabling Rails Sessions to stumbleupon.com submit Flash/Flex Remoting and Disabling Rails Sessions to del.icio.us submit Flash/Flex Remoting and Disabling Rails Sessions to reddit.com Like this post? subscribe to the feed.

September 14, 2006

Mask Credit Card Numbers with Regular Expressions in Rails

Filed under: RegEx, Ruby on Rails Alastair @ 10:03 am

Sometimes you need to display sensitive information in a browser, such as the credit card a customer has on file. Obviously you don’t want to show the entire card number in case the customer leaves there browser open on a public computer, or even worse someone hacks into their account. However, you do need to show a piece of the information otherwise the customer would have no idea which credit card was on file. How to mask the credit card number? Regular Expressions to the rescue!

When I first searched for a way to do this I was surprised that I couldn’t find any examples, there’s a ton of regex tutorials for checking if emails are valid but none for masking credit card numbers. Here’s my solution in Rails.

Let’s say the customer’s card number is 5555-4444-3333-2222 (@customer.card_number = 5555-4444-3333-2222). First strip everything but the numbers.

    card_masked ||= @customer.card_number.gsub(/[^0-9]/, )

Then mask all but the last four digits.

    @card_masked = card_masked.sub(/^([0-9]+)([0-9]{4})$/) { ‘*’ * $1.length + $2 }

That’s it! @card_masked will out put as ************2222

Digg! submit Mask Credit Card Numbers with Regular Expressions in Rails to stumbleupon.com submit Mask Credit Card Numbers with Regular Expressions in Rails to del.icio.us submit Mask Credit Card Numbers with Regular Expressions in Rails to reddit.com Like this post? subscribe to the feed.

September 13, 2006

Apollo Features

Filed under: AIR Alastair @ 5:40 pm

Aral Balkan live blogs an Adobe Apollo demonstration

Digg! submit Apollo Features to stumbleupon.com submit Apollo Features to del.icio.us submit Apollo Features to reddit.com Like this post? subscribe to the feed.

2 Billion Spam Emails a Year

Filed under: Misc. Alastair @ 5:26 pm

And that’s just one dude…

Danyel Molenaar, a project manager for the Dutch Independent Regulator of Post and Telecommunications, said the man had rented 35 servers for around 14,000 Australian dollars (US$10,493; euro8,256) each per month from a small Internet service provider in the Netherlands to carry out the alleged spam campaign.

So the spammer gets arrested but what happens to the hosting company that let him do it for years?

Digg! submit 2 Billion Spam Emails a Year to stumbleupon.com submit 2 Billion Spam Emails a Year to del.icio.us submit 2 Billion Spam Emails a Year to reddit.com Like this post? subscribe to the feed.

September 12, 2006

Visual Web App Design

Filed under: Flash, Flex, Misc., Ruby on Rails Alastair @ 12:59 pm

My web application planning usually includes the outline from the client proposal as a feature list and a series of hand drawn diagrams (with many eraser marks), sometimes the diagrams where converted to adobe illustrator documents if the app was complex enough that the client needed to see the app flow before we begin building. However actually designing an app diagram in illustrator rather than tracing one makes my head hurt.

In search of brain-pain free visual design process I fired up google to see what I could find. There’s a product called Inspiration which started out as a brain-storming tool but also makes good application flow diagrams. They had a free 30 day trial so I downloaded it and started plotting my app, the first time I launched the program it worked fine, the second time I had Photoshop open as well and Inspiration crashed itself and Photoshop (and I thought the days of conflicting apps where long gone with OS 9 extensions). From then on even with no other apps running Inspiration would crash on launch.

Back the drawing board.

Next I tried Visual Paradigm for the Unified Modeling Language (UML) it runs on OS X and Windows and unlike Inspiration is designed just for visual application design. There’s a free community edition which lets you have one type of diagram per project, and there are many types (Class Diagrams, Business Work flow, Deployment Diagrams, etc.). The diagram I used to design my app is the Business Work flow which is one of the more basic ones, you’re not focusing on classes yet just the overall flow of the app.

Even with such a basic diagram you start to see potential problems in the design of the app before you put any hackery to paper, and no more erasing :)

Business Workflow

Digg! submit Visual Web App Design to stumbleupon.com submit Visual Web App Design to del.icio.us submit Visual Web App Design to reddit.com Like this post? subscribe to the feed.

September 8, 2006

Multiple File Upload with Flash and Ruby on Rails

Filed under: ActionScript, Flash, Ruby on Rails Alastair @ 8:17 pm

I’m going to prefix this tutorial by declaring my love for AJAX to preempt a Flash vs Ajax war. I use AJAX daily, it’s a great tool, the sun shines out it’s arse, etc. But sometimes Flash is just the better tool for the job. I’m going off on a tangent here but many problems with AJAX, e.g. breaks the back button, were the exact same arguments people made about Flash years ago.

ALAX is cool but can you play MP3’s with it? No.

Can you Play Video with AJAX? No.

Upload files with AJAX? No*.

And most importantly can you make a 3D spinning LED thingy in AJAX? No :P

AJAX file upload is impossible for 99.99% of web users, but Flash file upload is available to 90% of users (the rest are using Gopher on Linux so funk ‘em). As an extra special bonus this example will upload multiple files through Flash, but wait there’s more… it will also include the upload status/progress for each file.


files awaiting upload


uploading with progress


done!

This server side will be Rails but .NET or PHP would work as well. The source for the tutorial is here. Flash files are in the ‘fla’ directory.

As usual start off with a new Rails app

> rails upload

Since we’ll be uploading files we’ll need somewhere to store them preferably in a unique folder so as not to mess anything else up.

> mkdir upload/public/uploads

Change into the ‘upload’ app directory (not the public/uploads directory) so we can generate models and controllers.

> cd upload

Create a ‘File’ model, ‘File’ is a reserved word for Rails so we’ll call it ‘DataFile’

> ruby script/generate model DataFile

In the model file (app/model/data_file.rb) we’re going to create a custom save method. It will have three arguments ‘data’ the file data, ‘name’ what to name the file, and ‘directory’ where to put the file.

class DataFile < ActiveRecord::Base

  def self.save(data, name, directory)
    # create the file path
    path = File.join(directory, name)
    # write the file
    File.open(path,‘wb’) do |file|
      file.puts data.read
    end
  end

end

Next create an ‘Upload’ controller

> ruby script/generate controller Upload

In the controller file (app/controllers/upload_controller.rb) create an index method that takes file information from Flash and passes it to the ‘DataFile’ model. Because there’s no view associated with this controller we add ‘render :nothing => true‘ to avoid any errors. We set the upload directory to the ‘uploads’ directory we created earlier "directory = ‘uploads’".

class UploadController < ApplicationController

  def index
    # capture incoming file params from flash
    data = params[:Filedata]
    name = params[:Filename]
    # the directory in which to save the file
    directory = ‘public/uploads’
    # save the file
    @data_file = DataFile.save(data, name, directory)
    # there’s no view so render nothing
    render :nothing => true
  end

end

Almost done, after bashing AJAX earlier it’s time to bash Flash a bit. Flash file upload is broken when used with Rails, from the Bubble Share folk "the Windows version of Flash Player 8 sends a multipart request with content-length set to zero(0), before sending the real request, if the file you are uploading is bigger than 10K". Long story short it no worky with Rails. Big ups to the bubblers they figured out how to fix it, download the fix here and place it in the Rails plug-in directory (vendor/plugins).

That’s it for the Rails side of things (easy peasy lemon squeezy).

Using the source files you can start a webbrick/lighttpd server and test the multipleUpload.fla file in the Flash IDE.

Now on to Flash…

The multipleUpload.fla file has three components on stage, a DataGrid (files_dg), and two Buttons one for browsing the file system (browse_btn), and one for uploading (upload_btn).

The actions for the .fla are pretty short, just creating an instance of a ‘MultipleUpload‘ class (described below) and hooking up the components to it.

import com.vixiom.utils.MultipleUpload
System.security.allowDomain (“*.localhost.com”);
var MU:MultipleUpload = new MultipleUpload (this.files_dg, this.browse_btn, this.upload_btn);

Below is the ‘MultipleUpload.as’ file. Here’s a quick run down of what’s going on: a FileReferenceList object is created with a listener that ‘listens’ for various user events dealing with files (onSelect, onCancel, onOpen, onProgress, onComplete, onHTTPError, onIOError, onSecurityError). The major ones are onSelect (when a user selects files with the file browser), onProgress (an interval that runs in reference to a particular file being uploaded), and onComplete (when a file has finished uploading). To show the user the status of our file uploads we’re using the DataGrid’s built in ‘editField‘ method to change what the status cells display.

// delegate
import mx.utils.Delegate;
// ui components
import mx.controls.DataGrid
import mx.controls.Button
// file reference
import flash.net.FileReferenceList;
import flash.net.FileReference;

class com.vixiom.utils.MultipleUpload
{

    private var fileRef:FileReferenceList;
    private var fileRefListener:Object;
    private var list:Array;

    private var files_dg:DataGrid;
    private var browse_btn:Button;
    private var upload_btn:Button;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructor (files_dg, browse_btn, upload_btn)
    //
    //////////////////////////////////////////////////////////////////////

    public function MultipleUpload(fdg:DataGrid, bb:Button, ub:Button)
    {
        // references for objects on the stage
        files_dg = fdg;
        browse_btn = bb;
        upload_btn = ub;

        // file list references & listener
        fileRef = new FileReferenceList();
        fileRefListener = new Object();
        fileRef.addListener(fileRefListener);

        // setup
        iniUI();
        inifileRefListener();

    }

    //////////////////////////////////////////////////////////////////////
    //
    // iniUI
    //
    //////////////////////////////////////////////////////////////////////

    private function iniUI()
    {
        // buttons
        browse_btn.onRelease = Delegate.create(this, this.browse);
        upload_btn.onRelease = Delegate.create(this, this.upload);
        // columns for dataGrid
        files_dg.addColumn(“name”);
        files_dg.addColumn(“size”);
        files_dg.addColumn(“status”);
    }

    private function browse()
    {
        trace(“// browse”);
        fileRef.browse();
    }

    private function upload()
    {
        trace(“// upload”);
        // upload the files
        for(var i:Number = 0; i < list.length; i++) {
            var file = list[i];
            trace(“name: “ + file.name);
            trace(file.addListener(this));
            file.upload(“http://0.0.0.0:3000/upload”);
        }
    }

    //////////////////////////////////////////////////////////////////////
    //
    // inifileRefListener
    //
    //////////////////////////////////////////////////////////////////////

    private function inifileRefListener()
    {
        fileRefListener.onSelect        = Delegate.create(this, this.onSelect);
        fileRefListener.onCancel        = Delegate.create(this, this.onCancel);
        fileRefListener.onOpen          = Delegate.create(this, this.onOpen);
        fileRefListener.onProgress      = Delegate.create(this, this.onProgress);
        fileRefListener.onComplete      = Delegate.create(this, this.onComplete);
        fileRefListener.onHTTPError     = Delegate.create(this, this.onHTTPError);
        fileRefListener.onIOError       = Delegate.create(this, this.onIOError);
        fileRefListener.onSecurityError = Delegate.create(this, this.onSecurityError);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onSelect
    //
    //////////////////////////////////////////////////////////////////////

    private function onSelect(fileRefList:FileReferenceList)
    {
        trace(“// onSelect”);
        // list of the file references
        list = fileRefList.fileList;
        // data provider list so we can customize things
        var list_dp = new Array();
        // loop over original list, convert bytes to kilobytes
        for(var i:Number = 0; i < list.length; i++)
        {
            list_dp.push({name:list[i].name, size:Math.round(list[i].size / 1000) + ” kb”, status:“ready for upload”});
        }
        // display list of files in dataGrid
        files_dg.dataProvider = list_dp;
        files_dg.spaceColumnsEqually();
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onCancel
    //
    //////////////////////////////////////////////////////////////////////

    private function onCancel()
    {
        trace(“// onCancel”);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onOpen
    //
    //////////////////////////////////////////////////////////////////////

    private function onOpen(file:FileReference)
    {
        trace(“// onOpenName: “ + file.name);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onProgress
    //
    //////////////////////////////////////////////////////////////////////

    private function onProgress(file:FileReference, bytesLoaded:Number, bytesTotal:Number)
    {
        trace(“// onProgress with bytesLoaded: “ + bytesLoaded + ” bytesTotal: “ + bytesTotal);
        for(var i:Number = 0; i < list.length; i++)
        {
            if (list[i].name == file.name) {
                var percentDone = Math.round((bytesLoaded / bytesTotal) * 100)
                files_dg.editField(i, “status”, “uploading: “ + percentDone + “%”);
            }
        }
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onComplete
    //
    //////////////////////////////////////////////////////////////////////

    private function onComplete(file:FileReference)
    {
        trace(“// onComplete: “ + file.name);
        for(var i:Number = 0; i < list.length; i++)
        {
            if (list[i].name == file.name) {
                files_dg.editField(i, “status”, “complete”);
            }
        }
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onHTTPError
    //
    //////////////////////////////////////////////////////////////////////

    private function onHTTPError(file:FileReference, httpError:Number)
    {
        trace(“// onHTTPError: “ + file.name + ” httpError: “ + httpError);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onIOError
    //
    //////////////////////////////////////////////////////////////////////

    private function onIOError(file:FileReference)
    {
        trace(“// onIOError: “ + file.name);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onSecurityError
    //
    //////////////////////////////////////////////////////////////////////

    private function onSecurityError(file:FileReference, errorString:String)
    {
        trace(“onSecurityError: “ + file.name + ” errorString: “ + errorString);
    }

}

That’s it! I’d love to be proven wrong but that’s impossible to do with AJAX. And if you could do it with AJAX who would want to deal with all the cross browser testing anyways?

*I’ve actually seen a couple of examples of attempts at AJAX file uploading but there’s always some caveat like "only works with FireFox if the user does A, B, and C".

Digg! submit Multiple File Upload with Flash and Ruby on Rails to stumbleupon.com submit Multiple File Upload with Flash and Ruby on Rails to del.icio.us submit Multiple File Upload with Flash and Ruby on Rails to reddit.com Like this post? subscribe to the feed.
Older Posts »

Powered by WordPress