Project Management Tutorial Part 2, First Iteration

Here’s our goals for the first iteration.

  • Task Database Object
  • Add Task via Xul Client

It’s small, but it gets us started on all the overhead of both rails and xul.

First is first, creating the rails project in RadRails.
New Project, new Rails Project, we’re gonna call it pm404 because I always lose my other task lists.

The default options are fine. This gives us the default files and directories we need. I normally place the Xul client in another directory called “xul” which needs several underneath it. Should look like this:


xul
--> chrome
--> content
-----> xul
-----> misc
-----> js
----------> lib
--> defaults
----> preferences
--> xulrunner (this is where the xulrunner package gets unzipped to)

Let’s get the xul stuff in order first, as Rails did 99% of the setup for us already.

Here’s our first files. Most of these files are explained elsewhere on the web, and are quick and easy anyways.


xul/application.ini

[App]
Vendor=Joseph Guhlin
Name=pm404
Version=0.1
BuildID=2006103001

[Gecko]
MinVersion=1.8
MaxVersion=1.9

xul/chrome.manifest

content	pm404 content/

xul/defaults/preferences/prefs.js

pref("toolkit.defaultChromeURI", "chrome://pm404/content/xul/main.xul");
pref("javascript.options.showInConsole", true);
pref(“javascript.options.strict”, true);
pref(“browser.dom.window.dump.enabled”, true);
pref(“nglayout.debug.disable_xul_cache”, true);
pref(“nglayout.debug.disable_xul_fastload”, true);
pref("pm404.debug", true);

The pm404.debug is the only thing added here that is specific to the app, the rest is general for xul apps and is either required(defaultChromeURI) or helpful(all of the rest of them) to help with debugging the app. The pm404.debug is a switch I use to enable debugging in the xul app only.

For those new to XUL, look for my older tutorials or devmo or just remember this, it’s basically XML, defining the user interface statically in files. Very DOM like if you’re familiar with that. Also, please refer to the XUL Periodic table of elements as it’s the greatest resource in the world for any xul devs. Of course, you need to be running FF to view it, or another gecko engine with XUL support, but you already are, aren’t you?


xul/content/xul/main.xul

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window
    id="pm404-main-window"
    title="PM404"
    orient="horizontal"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    
<script type="application/x-javascript" src="chrome://pm404/content/js/lib/JaxaJax.js" />

<hbox flex="1">

<tree id="projects_tree" flex="1" hidecolumnpicker="true" seltype="single" onselect="alert('placeholder');" >
	<treecols>
		<treecol id="title"  flex="1" label="Project" />
	</treecols>

	<treechildren>
		<treeitem>
			<treerow>
				<treecell label="Placeholder" />
			</treerow>
		</treeitem>
	</treechildren>
</tree>

<vbox flex="8">
	<label value="Task List and descriptions will go here" />
</vbox>

</hbox>

</window>



There’s a lot there, it should be pretty self explanatory though, and it’s not complete enough for this iteration, but we’ll get there.

JaxaJax is from my previous tutorial, and will be changed for this as appropriate.


xul/content/js/lib/JaxaJax.js

var processlist = new Array();
var ror_server = "http://localhost:3000/server"; // We'll redefine the list, probably. And definately before release.

// Action is the action on the remote server to call
// Data is the data we pass along with it, in the POST or the GET request
// Callback is the function to call and return the data to upon completion of the call...

function JaxaJax(action, data, callback, method) {
    var uniq = new Date();
    uniq = uniq.getTime();
    uniq = action + uniq + Math.round(10000*Math.random());
    // Should be good enough...

    processlist[uniq] = new XMLHttpRequest();
    processlist[uniq].onreadystatechange=function() {
        if(processlist[uniq].readyState == 4) {
            eval("var data = " + processlist[uniq].responseText + ";");
            eval(callback + "(data)");
        }
        // Otherwise it's not ready yet....
    };

    var querystring = createQueryString(data);
    if (method == "GET") {
        processlist[uniq].open("GET", ror_server + "/" + action + "?" + querystring, true);
        processlist[uniq].send(null);
    } else {
        processlist[uniq].open("POST", ror_server + "/" + action, true);
        processlist[uniq].setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        processlist[uniq].send(querystring);
    }

}

function createQueryString(obj) {
    var a = new Date();
    var when = Math.floor(a.valueOf()/1000);
    queryString = "when=" + when; // Just so we know what time, if we have to do any debugging, etc...
    for (key in obj) {
        queryString = queryString + "&" + escape(key) + "=" + escape(obj[key]);
    }
    return(queryString);
}

function echo(data) {
    alert(data);
}

data = {"echo":"Hello Echo!"};
JaxaJax("echo", data, "echo", "GET");

Of course, JaxaJax.js right now is made for testing, but let’s go ahead and leave it.

So far, we’ve implemented the Xul skeletons, with a little bit there for an idea of where we’re going with the interface, although it’ll change, a lot. Let’s keep going with this. You downloaded the xulrunner package, right? Excellent! Unzip it into the xul/xulrunner directory, and copy the xulrunner-stub.exe file to xul/pm404.exe. Note that the xulrunner zip has a xulrunner/ directory, you don’t want xulrunner/xulrunner/ just xulrunner/ and all the files underneath there. At this point, it loads and runs just fine. Just double click the pm404.exe in the xul/ directory and you’re up and going!

Not a big window though, that’s fine, it’ll expand when we fill it with stuff later.

JaxaJax.js is trying to call the echo action, let’s go ahead and create it.

First thing is the generators tab on the bottom of the RadRails screen. select controller, and put in server and click Go. Let it go through it’s paces(pretty quick). Now open up the file app/controllers/server_controller.rb This is what we want it to look like for now.


app/controllers/server_controller.rb

class ServerController < ApplicationController
def echo
    render_text params[:echo].to_json
  end
end

Ok, on the Servers tab click the pm404 project(highlighted if it’s your only one) and click the green arrow. Now run pm404.exe and it should give you an alert that says “Hello Echo!” If not, email me or leave a comment and I’ll help you look into it.

Of course, this isn’t exactly where we want to be. But a quick aside, we’re using JSON and simple GET requests right now, we’ll change this later for security and compatibility, although we’ll continue using JSON, which Rails natively supports(nice!!!!).

Ok, let’s move on. We’re going to need a database, and soon. I hate installing MySQL on Windows. MySQL isn’t made for Windows and Windows XP isn’t made to be a server. Let’s use SQLite. How to Use SQLite with Ruby on Rails is a good article.

Follow that info, the run (something similar to this based on where your stuff is):

sqlite F:\radrails\pm404\db\dev.db

then just .quit

Only worry about the dev in database.yml

I used sqlite3 as well. We don’t create anything as we use migrations here to handle everything for us. If you’re using rails and creating the tables by hand, you’re missing out on some big stuff.

Ok, so back in RadRails, Generators tab, select model, call it task. Now we have a app/models/task.rb and a db/migrate/001_create_tasks.rb. Nice. Nice.


db/migrate/001_create_tasks.rb

class CreateTasks < ActiveRecord::Migration
  def self.up
    create_table :tasks do |t|
      t.column :title, :string, :null => false
      t.column :created_at, :datetime
      t.column :notes, :text
    end
  end

  def self.down
    drop_table :tasks
  end
end

then run rake migrate from the command line, change the paths and everything you need to do.


F:\radrails\pm404>D:\ruby\bin\rake.bat migrate
(in F:/radrails/pm404)
== CreateTasks: migrating =====================================================
-- create_table(:tasks)
   -> 0.0000s
== CreateTasks: migrated (0.0160s) ============================================

Very nice. Now we have a basic table to store the tasks. Very light on the details, but details are for later iterations.

We’re going to start simple.


app/models/task.rb

class Task < ActiveRecord::Base
  validates_presence_of :title
end

And our server_controller


app/controllers/server_controller.rb

class ServerController < ApplicationController
  def echo
    render_text params[:echo].to_json
  end

  def new_task
    @task = Task.new(@params[:task])
    @task.save
    render_text "true".to_json
  end

  def get_tasks
    @tasks = Task.find(:all)
    render_text @tasks.to_json
  end

end

and add the following lines to the end of JaxaJax, go ahead and remove the echo test call.



function fetch_tasks(data) {
    JaxaJax("get_tasks", {}, "echo", "GET");
}

data = {"task[title]":"Test Task"};
JaxaJax("new_task", data, fetch_tasks, "POST");

It just gives us [Object] (or [Object],[Object] depending on how many times you ran it).

What we need to do next is display the data, at least in some meaningful way.

So what we’re gonna do here, is add the following function to the bottom of JaxaJax.js


function displayTasks(data) {
    for (var i in data) {
        alert(data[i]['attributes'].title);
    }
}

And change the fetch_tasks function to have the following line in it instead of the other:


    JaxaJax("get_tasks", {}, "displayTasks", "GET");

And now it’ll loop through each task(since a new one is added each time we run the program) and display the title to us.

Goals for the next tutorial will be:

  • Display the title of each task in the tree
  • Click on the title to bring up additional task information
  • Add more fields to each task
  • Interactive fields to add a task, instead of adding a test task automatically each time it’s run
  • Change “notes” to “description” and create a notes object to add multiple notes to a task

I’m not sure how far I’m going to take this tutorial series, further than my last attempt, I want people to learn how to do a lot of stuff beyond Rails/Xul communications, and show that Rails/Xul is an effective front end, even if it’s not as dynamic as the rest of rails it’s still a powerful way to develop applications rapidly. I’m definitely going to take everyone thru the basic features of the system, as well as adding a web interface to it all, and turning the xul app into a Firefox extension, some tree stuff as well, but past that we shall see how far it goes.

Posted: November 3rd, 2006 under Firefox, Ruby on Rails, Tutorial Series, Tutorials, XUL, XulRunner.
Comments: 1

Comments

Comment from M@
Time: November 7, 2006, 4:11 am

Very nice! I’m looking forward to the next installment.

Write a comment