Tuesday, November 29, 2005

Now for something completely different...

For a change of pace, here's a picture riddle for you:

The first one to post with the right answer gets -- Well, nothing actually. Respect maybe? Uh, sure, why not.

Monday, November 14, 2005

Rails Theme Support Plugin

Update: I changed the svn URL to point to it's new home!

Currently, to add support for themes into your rails application, you have to install the theme_generator. The generator will then create the folder structure for themes, and it also creates the plugin files for theme support in rails.

The more I've thought about it, the less I like this approach. I'd rather have a fully encapsulated plugin that would add support for themes into rails and define a simple generator that would merely create the boilerplate theme structure.

To that end, I present the Rail Theme Support plugin. To install the plugin execute the following in the root of your application (requires rails 0.14.3+):

./script/plugin install http://mattmccray.com/svn/rails/plugins/theme_support

In the next couple of days I'll have a trac installation up and running for it too.

You can, of course, still get the theme_generator from RubyForge. But I think the plugin will be the approach I will recommend moving forward.

Try it out and let me know what you think!

Saturday, November 12, 2005

Theme Generator Shapes Up

I've released theme_generator version 1.3.0. It's shaping up rather nicely, if I say so myself.

The theme support is now completely encapsulated in a plugin -- it was split across a plugin and a component.

Like Typo, it fully supports custom application views, of course. (It is based on the Typo theme system, after all.)

It also supports using liquid templates (if you have the liquid plugin installed). You can also lock down the themes to only allow the rendering of liquid templates.

And it's dead simple to use. Once you have the theme_generator installed, here are the steps to make your application support themes...

Step One: Generate a theme named 'default'

In your command terminal, cd into your Rails (0.14.3+) application folder and execute:

./script/generate theme default

It spits out the folder structure for a theme named 'default' (it doesn't actually have to be called 'default', call it what you like).

Since it's the first time we've used it for this application, it'll also create the theme_support plugin files for us.

Step Two: Update your ApplicationController

Now we update the ApplicationController (application.rb) to tell Rails to use our new theme.

classApplicationController< ActionController::Base
  layout 'default'# You may need to modify this, the generator will
# create a layout named 'default' by, uh, default
  theme 'default'# Tell Rails to use the theme named default
end

Step Three: There is no step three

Your app is now ready to support themes! You can create whatever theme-specific stylesheets, images, javascripts, layouts, and application views you'd like and Rails will pick them up.

Note: To reference theme-specific content you'll want to use the theme_* helper tags. See the docs for more.

Credit where credit is due...

Of course, this generator and plugin wouldn't be as remotely robust if it wasn't for the excellent work of Tobias, Scott, and the rest of the Typo guys.

Installation

Want it? Just gem it:

sudo gem install theme_generator

Or, if you already have it:

sudo gem update theme_generator

If you are on Windows, you can leave out the sudo part.

In the near future I'll be posting a tutorial explaining how to implement user and/or application level themes... In the meantime, if you have any suggestions or questions, feel free to send 'em my way.

Wednesday, October 26, 2005

Full Theme Support in Rails, Revisited

OK, so after a short discussion and some experiments, I've realized that the Typo approach to themes has a lot more merit than I initially thought... For general use it still has some trouble areas, but that's nothing that can't be fixed.

I've created a new theme_generator that takes their general approach and makes it easy to leverage in any application. Even an existing one.

How to use it...

Usage is extremely easy. First, use the generator to create a default theme.

  ./script/generate theme default

(It doesn't have to be called 'default', it can be whatever you'd like)

This will create the main themes folder structure, the initial theme files for the theme ('default' in this case), and it creates the plugins/components needed to support themes.

Now you can use themes as easily as you do layouts. The following will use a layout named 'main' in the 'default' theme.

class ApplicationController < ActionController::Base
  layout 'main'
  theme 'default'
end

Here's an example of per-user themes:

class ApplicationController < ActionController::Base
  layout 'default'
  theme :get_user_theme

  def get_user_theme
    # This assumes, of course, your User has a 'theme' field
    return @session[:user].nil? ? 'default' : @session[:user].theme
  end
end

Differences from Typo themes...

I did take the liberty of tweaking of couple of things (beyond setting the theme in your controller). The main difference is that I changed the cached theme structure.

Before:

/public
  /images
    /theme
  /stylesheets
    /theme
  /javascript
    /theme

After:

/public
  /themes
    /[THEME_NAME]
      /images
      /stylesheets
      /javascript

What does this mean? For one thing, it's a lot easier to remove cached theme files... Just delete the public/themes folder.

Also, you should be using relative paths to images in your CSS. For example:

BODY {
  background: #FFF url(../images/page-bg.jpg) top left;
}

Requirements...

Update: theme_generator is now hosted on RubyForge.

The theme_generator requires Rails >= 0.14. The easiest way to install the theme_generator is by running:

  sudo gem install theme_generator

It's that simple. Feel free to install it and let me know what you think.

Monday, October 24, 2005

Supporting Themes In Your Rails Application

Update: There's an updated version of the theme_generator. See the blog post or download it.

I was pretty impressed after looking at the theme support in Typo 2.5. However, I don’t think it’s a system that you should replicate if you want your application to support simple themes.

Before I get too far along let me make it clear that it’s not my intention to cast aspersions on Typo, it’s an excellent tool. In fact, I plan on switching to it from WordPress in the near future.

My issue with Typo’s theme scheme (heh) is that it doesn’t leverage the web-server as it should. Rails is impressive, and it’s caching is quite good. But web-servers like Apache have been around for many years. They are really good at what they do, so let them do it! Rails can’t compete with the server’s ability to deliver static content like CSS and graphics.

Following is how I’ve been implementing themes in Rails applications. In addition to relying on the web-server to provide static files, it also leverages existing Rails infrastructure wherever possible. Bear in mind, I’m not suggesting the following to be an end-all solution. Nor is it a drop-in replacement for Typo’s theme support. Merely think of it as a good starting point.

I’ve been using this approach enough that I’ve created a generator for it.

theme_generator

Usage

After you’ve installed the generator, just call it with the name of the theme you’d like to create.

./script/generate theme blue-bird

It will then add theme_engine.rb to your lib/ folder and create the required file structure.

Note: On Windows, it looks more like:

ruby script\\generate theme blue-bird

File Structure

Let’s start by having a look at the file structure it creates. Here’s an example for a theme named ‘blue-bird’.

app/
  views/
    layouts/
      themes/
        blue-bird/
  public/
    themes/
      blue-bird/
        images/
        javascripts/
        theme.css

Just from looking at that, you can probably get a good idea where I’m going.

Static Files

All of the static files, CSS, Images, and JavaScripts are under the app/public/themes directory, as they should be. Apache (or lighttpd, or whatever) will happily serve these up for us.

To reference theme-specific images from the CSS it’s as simple as:

BODY {
  background: url( images/my-bg.png );
}

Be sure to use a relative path to the image. CSS will look for images relative to the CSS file, not the page, remember?

Layouts

You’ll notice under the app/views/layouts folder, I’ve added a theme structure similar to the one under app/public/. As an example, app/views/layouts/themes/default/main.rhtml is the main layout for the ‘default’ theme.

In your controller, you’ll tell Rails that you want a themed layout like this:

  class MyController << ApplicationController
    layout :themed_layout
  end

Current Theme

The only thing that’s left to your controller is indicating which theme is currently active. It’s as simple as overriding the current_theme method.

class MyController << ApplicationController
  layout :themed_layout

  def current_theme
    'blue-bird'
  end

end

To set the theme for the entire application, you can add that to your ApplicationController instead (in app/controllers/application.rb).

Tag Helpers

To aid in retrieving themed media, helper methods have been added to the ActionView::Helpers::AssetTagHelper module.

  • theme_image_path
  • theme_stylesheet_path
  • theme_javascript_path
  • theme_stylesheet_link_tag
  • [More to come…]

Usage is generally quite simple. In your view, if you want to reference a themed image:

<img  src="<%= theme_image_path 'my-image.png' % />" />

Last, But Not Least

This needs to be included at the bottom of your environments.rb:

require_dependency 'theme_engine'

That’s it really—pretty straight forward. If you look at the theme_engine.rb you’ll see it’s a less-is-more type solution.

In the next few days I’ll polish the theme_engine some more, based on the feedback y’all provide, and refactor it into a plugin.

Thursday, October 20, 2005

Simple Row Style Alternator

In your view (rhtml):

<table>
  <thead>
    <tr>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
  <% for user in @users %>
    <tr class="<%= row_style %>">
      <td><%= user.name %></td>
    </tr>
  <% end %>
  </tbody>
</table>

In your helper (rb): Updated!

  def row_style
    @row_index ||= 0
    @row_index += 1
    @row_index % 2 == 0 ? "Even" : "Odd"
  end

Saturday, September 24, 2005

Rails on Windows (and Apache2)

First of all, I would never recommend using a Windows server -- for anything, really. However, I understand that there are times when you have no choice. I, for instance, have a server that's running Windows (although if asked later, I will deny it). Sadly, because of the web-applications I'm hosting, a *nix system isn't an option. Fortunately I am able to side-step IIS. I'm running Apache 2 instead. (Actually, I am running IIS, but on a different port -- I use Apache as a proxy)

Hooray for a small victory!

Anyway, I -- as well as many others -- have not had a great time trying to get Rails applications running efficiently on Win/Apache. Getting FCGI working is a bear. Getting it working correctly is nigh impossible. CGI is sooo slow. And if, by some miracle, the planets are aligned and the server is working right -- ruby forgets how to talk to MySQL. *sigh*

After consulting everyone's best friend, Mr. Google, I found that a lot of people seem to be having problems on Windows. Some like mine, others not. None of the posted suggestions seem to work.

So what did I do? Well, after my impersonation of a Tourette's syndrome sufferer, I rolled up my sleeves and dug in. After messing around with it for far too long, I finally found a combination that works! (For me, at least.) Believe it or not, Rails apps are most stable/fast using the new SCGI Rails Runner.

Overall, it was actually quite painless to install. Just follow the installation instructions, configure apache, start the scgi_rails script (using the -D flag), restart apache, and your almost ready to go. In my case, at least. I was still having the mysterious ruby error (GPF) stating it couldn't load libmysql.dll.

To get it to work correctly, I had to do one more thing: Move the libmysql.dll (for me it was at C:\Program Files\MySQL\MySQL Server 4.1\bin) to the ruby/bin folder (C:\ruby\bin).

And now, finally, everything is right as rain. In fact, the two Rails applications I've tested are running faster on that machine than I've ever seen.

Hopefully SCGI will be as easy for you to install as well. But of course, with Windoze, your results may vary.

Friday, August 26, 2005

Forget Me Not

For TaskTHIS, I’ve implemented a simple authentication cache. The “Remember me…” checkbox on the login form. I’m going to present to you, good or bad, how I decided to design it.

After looking at how it’s done in other technologies and how secure (or actually insecure) it is, I decided to try something a little different. My goals were to 1) avoid setting cookies with any personal information, and 2) make it perishable. Meaning, I wanted the cookie value itself to expire.

High Level

 Gallery Data Thumbnails 3 Psss82-1

When “Remember Me” is selected on the login screen the application creates a unique token and saves it into a cookie. The token is called a remembrall. The remembrall is essentially an SHA hash of the user email and the exact time the remembrall was created. In addition, the application also generates an expiration date for the remembrall. So it’s only a valid token for a short while, satisfying my second goal.

It may appear that I have already broken my first goal, I have used the user’s email address in the token. Looking back, I probably should have used the USER record’s ID (usually just an integer that’s unique within the system) as the information that’s unique to the user. However, since it’s hashing the email and timestamp, I believe it satisfactorily meets the first goal.

At this point, we need a way to recognize the remembrall on pages that are protected, and automatically log the user into the system if the remembrall is still ‘fresh’. To accomplish this, TaskTHIS uses a filter that runs before every page is loaded. The filter checks to see if there’s a remembrall set and if it’s valid. The flow looks a little something like this:

TaskTHIS Autologin Process

  1. Is there a remembrall cookie set? Yes…
  2. Is there a currently logged in user? No…
  3. Is there a user in the system with this remembrall? Yes…
  4. Has the remembrall expired? No…
  5. Log the user in!

Whew, that was a mouthful.

Less High Level

So let’s look at some code, eh?

The initial user authentication was created with the excellent login_generator. What follows build upon that foundation.

We need to modify the User so that the remembrall can be saved. So, in Migration format, the user gets:

add_column :users, :remembrall, :string, :length=>40
add_column :users, :remembrall_expires, :datetime

One of the results of having an automatic login process is that the login procedure can be called from two different places. Each with a different destination after the successful login. What does that mean? It means I needed to abstract the code for logging in a user. For TaskTHIS, I put that into the AutoLogin module. I wouldn’t really recommend handling it this way, in the future TaskTHIS won’t. I think it should really be a protected method in your ApplicationController.

In the login process, TaskTHIS keeps track of the last time a user logs in. Also, each user’s settings are stored as a YAML string. The login code needs to handle all of this, the code looks like this:

def handle_login( user )
  @session[:user] = user
  @session[:user_prefs] = YAML::load(user.prefs)
  user.last_login = Time.now
  user.save
end

The following is the filtering code used with :before_filter in the ApplicationController:

def login_from_cookie
  user = User.find_by_remembrall( cookies[:remembrall] ) if cookies[:remembrall] and @session[:user].nil?
  if user and active_remembrall? user
    handle_login user
  end
end

It’s using a helper method in the same module for testing the remembrall timestamp:

def active_remembrall?( user )
  return Time.now < user.remembrall_expires unless user.remembrall_expires.nil?
  false
end

From here, it’s just a matter of hooking it all up. In the Controller::login, we’ll need to make sure it calls our custom handle_login method. For clarity, here’s the entire login method:

def login
  case @request.method
    when :post
      if user = User.authenticate(@params[:user_login], @params[:user_password])
        handle_login( user )
        if @params[:remember_me]
          create_remembrall( current_user )
        end
        flash['notice']  = “Login successful”
        redirect_back_or_default :action => “welcome”
      else
        flash.now['notice']  = “Login unsuccessful”
        @login = @params[:user_login]
      end
  end
end

You’ll notice that it uses some things we haven’t discussed: currentuser and createremembrall. They can be found in the lib/auto_login.rb file.

Now, in our ApplicationController, we’ll need to add the call for the :before_filter:

class ApplicationController < ActionController::Base

  include LoginSystem
  include AutoLogin

  before_filter :login_from_cookie

  # the handle_login method and other stuff here...

end

So that, in a nutshell, is how TaskTHIS handles cached user authentication. I’ve not shown all of the code. For example, I didn’t show the code that actually creates the remembrall. For all of that, you can download the code and have a look for yourself.

Let me know what you think or what you would have done differently.

Technorati Tags:

Thursday, August 25, 2005

TaskTHIS.com

Update: Well, DreamHost wasn't my, uh, dream host for RoR applications. So taskthis.com is down for now. Sorry for any inconvenience this causes. taskthis.darthapo.com is still available.

TaskTHIS.com is alive! TaskTHIS is now being hosted at DreamHost (yes, that link has a referral code in it, every little thing I can do to keep eating.)

DreamHost will be whole lot more stable than my development server. I'm going to retire the old demo instance of TaskTHIS (taskthis.darthapo.com) very soon. But, in the meantime, I have upgraded it to 0.5 so that you can export all of your tasklists and import them into taskthis.com.

I've also created a TaskTHIS development blog at blog.taskthis.com. From this point on, information specific to TaskTHIS will be posted there. My general posts and other Rails posts will still be here.

If you need to move your tasklists from the demo instance to taskthis.com, there's a quick tutorial available.

Technorati Tags:

Sunday, August 21, 2005

TaskTHIS 0.5.1

TaskTHIS 0.5.1 is ready for download. It implements the following:

  • Addresses some PostgreSQL compatibility issues
  • Includes ruby-json dependency in vendor folder
  • New configuration option: send_emails

That's pretty much it. If you have any issues, please feel free to post a comment here and I'll try and address them as soon as I can.

Update: Changed the URL to the download page. The link should work again.

Technorati Tags:

Saturday, August 20, 2005

TaskTHIS 0.5

Taskthis-Logo

TaskTHIS 0.5 is officially released! You can download it from here. I don't have it hosted yet, but I'm working on that. I was able to get the taskthis.com domain name, I just haven't had the time to set it all up yet.

This new release contains:

Taskthis Screenshot

  • Cleaner views (rhtml)
  • Enhanced AJAX support
  • New GUI
  • “Remember Me” login support
  • “Send List To...” -- Allows you to send a list to as many as 10 people via email
  • Public lists (readonly)
  • RSS feeds for public lists
  • Better task item reordering
  • Moved the database schema to a Migration script
  • And much more...

It leverages the following technologies:

It also contains an early release of my theme engine work. Check back soon for an article explaining how I implement themes in Rails applications.

Enjoy!

Update: Updated the URL for downloading the source...

Technorati Tags:

Friday, August 19, 2005

Testicular Abuse

 Sin City

Sin City is a dark, extremely stylized movie. Very noir with a modern twist.

Overall, I like this movie. It is very true to it's source material. However, after watching it again on DVD, I think Frank Miller has some issues that Freud would have had a field day with.

Sunday, August 14, 2005

Do The Math

I read this and laughed. It's actually quite amusing. A guy does the math on why he will never have a girlfriend.

Monday, July 25, 2005

Progress is Good

Thanks for all the emails asking when the next version of TaskTHIS will be out! I really do appreciate them, it spurs me on to action knowing that you guys find the app useful. I'm sorry I haven't answered them all, it's been kinda crazy lately. I've been trying to sell my house, find another place to live -- Oh, and make a little money so I can eat. ^_^

All that said, there's a new version of TaskTHIS on the horizon! It's going to have a whole new UI, and quite a few tweaks and new features. Plus, I'm cleaning up the view code. It'll be a lot less 'spaghetti' than before. Check back a little later -- I'll post an image of the new UI.

I don't really want to tie myself down with a specific release date... However, if everything progresses as it has been, then it should be ready by August 1 (yes, 2005).

Friday, June 17, 2005

Signs That You Work(ed) in a Sweat Shop

voodoo small.jpg
  • When you're finally ready to go home, you forget where it is
  • You know all of your work-mates ringtones for everyone they know -- or used to know
  • You can never remember what day it is
  • The only sleep you get is in the shower... until the hot water runs out

What are your additions to the list?

Saturday, April 23, 2005

improveTHIS!

I'm happy to report that taskthis! has been well received. It has over 1200 registered users! I've gotten a lot of positive response; bugs, suggestions, and feature requests. I appreciate it all.

Originally it was just a tech demo for illustrating how easy it is to use Ajax in Rails... But from the response, and my own usage, I think I'm going to commit a little time and flesh it out.

Oddly enough, the taskthis.com domain was available. So, of course, I snatched it up! That means in the near future it'll have a new home (a textdrive home, at that).

So what does it need to make it 'full-fledged'? ... That wasn't rhetorical, it was a request for feedback! Here's the list, at the moment:

  1. Various little cleanups in the UI
  2. Fix UTF-8 support
  3. Public tasklists (everyone can see)
  4. Shared tasklists (multiple users can add/complete taskitems)
  5. Pervasive RSS
  6. 'Remember Password'
  7. Taskitem reordering
  8. Tasklist reordering
  9. Tasklist merging
  10. Taskitem due date (I'm rather leery of this one)
  11. Help (seems obvious enough, eh?)

Let me know what features you'd like added to the list.

Sunday, April 3, 2005

Is Hard Work Overrated?

I worked with my last employer for over five years. And getting away from it for awhile affords a new perspective. In these couple of weeks of unemployment, I’ve been analyzing the way things work. At my previous employer, and with life in general. I’ve come to a conclusion: Hard work is overrated. It isn’t directly related to success. Some of the hardest work I’ve done has been on failing/unsuccessful projects.

Now, I’m not condoning a weak work ethic. The point I’m making is more semantic, or perhaps a matter of attitude.

You see, I don’t think that hard work has a bearing on the success of any endeavor. I think that commitment does. Passion does. If you have passion for what you are doing, you will be committed to it. If you are committed, the work won’t be hard. It will simply be whatever is needed, and you won’t think anything of it. It’ll be a labor of love, if you will.

This may be an inevitable conclusion that I’ve been slow on picking up. Or perhaps it’s a bit of an epiphany. Either way, I’m going to be sure that I’m passionate about my next endeavor, whatever it may be. I’ve always said that ‘if you aren’t having fun, why do it?’ Now I think I’ll say ‘if you don’t love what you do, why do it?’

Monday, March 28, 2005

AJAX and Rails demo application: taskTHIS!

I thought that since I was involved with the new AJAX stuff in the latest Rails (0.11.1 as of this writing), that I would create a quick demo application showing how to actually use the stuff.

So I present to you taskthis!

Before you roll your eyes and say: “Ugh! Another todo list example?!”, this one is different! Honestly. Sort of. It's using the new Rails (0.11.1) asynchronous AJAX tools. It's using them a lot. Plus, you can download the source and play around with it.

It's been tested on Safari and Mozilla blends (Firefox/Camino), but not too extensively on Windows/IE. So I'd appreciate a shout out if you find any issues there.

Links

Sunday, March 27, 2005

Rails 0.11.1

The new gem of Rails with the AJAX enhancements and lot of other stuff is out! Get it while it's hot!

Saturday, March 26, 2005

Woo Hoo!

rails_logo_remix_small.gifI'm officially a Ruby on Rails contributor! Very cool.

Well, I thought I'd post an update. My changes were approved and merged into Rails' SVN. So, if you are using the latest-greatest code, then you can add a :position symbol to form_remote_tag or link_to_remote with one of the following string values: before, top, bottom, after, replace (default).

For my next idea... I was thinking of making the AJAX.Updater a little smarter by having it check the type of element it's updating. It should be able to set the value of a form field, if it's the update target.

But that leads to some interesting questions. How do we handle different types of input elements? text and textarea are easy -- just set the value. What about checkboxes, radiobuttons, and listboxes? It seems like you would be changing the selectable values for radiobuttons and selectboxes... And I have no idea what would change on a checkbox. You could change the value, but why? You could have it selected or not, but why would you need AJAX for this?

Why is covered, now for the How

Actually, let's talk about the Where. I've been talking about positionally adding content to DIV's, throwing around symbols like: before_begin, after_begin, before_end, and after_end. But what do they mean? Where is the content going using these different update methods?

I'm glad you asked -- saved me the trouble. It will position the new content relative to the target element. Specifically, in Rails, the element with the id you give as the :update param to form_remote_tag. Example:

<%= form_remote_tag( :url => url_for( :action=>'ajax_method'),
) %>

In this case, the :update_method is relative to an element with the id of 'my-target-div'. OK, so we know where the after_begin method is relative to, where exactly will it put it? Another good question!

insertAdjacentHTML.png

So, the target element is the big gray box in the middle. The blue and red boxes are where the content returned from AJAX.Updater will be put, based on the update_method. Notice the red boxes are not in the target element, but the blue boxes are. That's the reason why append and prepend aren't enough to exactly specify where the content is going. If we wanted to limit ourselves to those two update methods, we'd have to make some up some arbitrary rules stating that the content is always inside the target element. Which could be done, but I'd rather not have that as a limitation.

Although I've gone down the path of making the update_method mimic the IE nomenclature for insertAdjacentHTML, I think different names for the update methods would clear up some of the ambiguity. The right column of the above diagram has my recommended update method names.

In short, before_begin would become before, after_begin would be top, before_end would be bottom, and after_end would be after. If this makes sense, then I think I will rework and resubmit my patch to Rails.

Any thoughts or suggestions?

Friday, March 25, 2005

Do, or do not -- never ask why

So I've been working on enhancing the, already excellent, AJAX support in Rails. But I've never really talked about the Why.

I like that Rails made AJAX so simple to use. But as I watched Sam's excellent demo, I realized that it was refreshing the entire list of 'elements' every time it returned data. Which is, as they've said, the simplest approach... But I've done that kind of thing before, and after a while the browser can bog down moving that much HTML in and out of the DOM. Not that I'm pointing fingers, but IE is especially bad about memory bloat when a lot of HTML flies around in javascript.

So, natch, I thought that it would be a good idea to cut down on as much repetition as possible and stop re-rendering every 'element' when only one has changed. Having said that, I also didn't want to over-complicate it. Especially since I just went off about certain technology groups over-architecting things.

And that is how, rather why, I got this AJAX bug up my, uh -- in my head. :-D

By the way, I'm polishing up an app I've thrown together as an example of using AJAX to only update the data that's changed. Yes, *sigh*, it is yet-another task list. But it's simple, and therefore good for an example. Plus, I'll provide the source so you can pick it apart and dig into the AJAX stuff. Look for that early next week.

The Right Way

rails_logo_remix_small.gif OK, I got the head's up that my AJAX.Updater extension wasn't happy in IE... Not too surprising, really. The XMLHttpRequest is an ActiveXObject in IE, so it doesn't like you adding spurious properties to it in JS -- which I had to do to make my enhancements just 'drop-in'.

As I was thinking about this, something I wrote in that last post annoyed me -- I talked about doing it the right way, and then I went and completely did something different. Man, I was at my last job too long.

Anyway, instead of doing something that I knew wasn't what needed to be done, I rolled up my sleeves and submitted a patch to RoR's dev site.

So, if that patch gets approved you'll be able to use a new parameter, :update_method, to indicate how you want to update the target DIV:

<%= form_remote_tag( :url => url_for( :action=>'ajax_method'), ) %>

If the update method is replace, it will replace the target DIV's content using innerHTML. Otherwise, it will add the new content positionally (without overwriting any content in the target DIV) using either insertAdjacentHTML or a DOM equivalent -- a new JS object, Insert, handles this.

Rails will pass the :update_method ( before_begin, after_begin, before_end, after_end, or replace ) to the AJAX.Updater as an option (method param). Which is the 'right way' to do it. This way we don't need to have that hacky, :loading=>'request.prepend=true;', crap.

Also, the Insert dom helper will now prefer a native implementation of insertAdjacentHTML. If it can't find one, it will revert to the DOM implementation.

Wednesday, March 23, 2005

Enhancing RoR's AJAX.Updater

So, RoR version 0.11 came out and has built-in support for AJAX. It’s very cool stuff.

A Small Bug

There was a bug in the gem I installed that may be fixed now… I’m not sure. You can have a look, the bug was in lines 61 & 62 of prototype.js (in your public/javascripts/ folder):

Toggle = {
  display: function() {
    for (var i = 0; i < elements.length; i++) {
      var element = $(elements[i]);
      element.style.display = (element.style.display == 'none' ? '' : 'none');
    }
  }
}

It should look like this:

Toggle = {
  display: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      element.style.display = (element.style.display == 'none' ? '' : 'none');
    }
  }
}

RoR’s Built-in AJAX

I really love what they’ve done with the AJAX support—it’s especially good for a first-cut. However, I’ve been playing with it and have a couple of suggestions… And code, of course.

The main thing that bothers me is the AJAX.Updater replaces the entire innerHTML of the target DOM object. I’d much rather it use an insertAdjacentHTML equivalent. Why not just use insertAdjacentHTML? Simple. Only Internet Explorer supports it. And I don’t know about you, but I avoid IE like the plague. That said, insertAdjacentHTML is an excellent way to add HTML fragments positionally in the DOM.

The Goal

So my goal was to find a way of doing this without having to hack the Rails code… Don’t wanna do it—wouldn’t be prudent.

So, to start off, what do we need? We need a nice cross-browser way of adding content in a fashion that’s similar to IE’s insertAdjacentHTML. I know it’s easy to extend mozilla’s HTMLElement... But Safari doesn’t support that. And I have to have it working on Safari.

Well, long story short, Safari can do the same thing as mozilla using HTML fragments, we just can’t extend the built-in HTMLElement class. Which is OK, really. After looking at the prototype stuff in RoR, I wanted to follow the same approach. Here’s what I came up with:

Insert = {
  before_begin: function( dom, html ) {
    dom = $(dom);
    var df; var r = dom.ownerDocument.createRange();
    r.setStartBefore(dom);
    df = r.createContextualFragment( html );
    dom.parentNode.insertBefore( df, dom );
  },
  after_begin: function( dom, html ) {
    dom = $(dom);
    var df; var r = dom.ownerDocument.createRange();
    r.selectNodeContents(dom);
    r.collapse(true);
    df = r.createContextualFragment( html );
    dom.insertBefore(df, dom.firstChild );
  },
  before_end: function( dom, html ) {
    dom = $(dom);
    var df; var r = dom.ownerDocument.createRange();
    r.selectNodeContents(dom);
    r.collapse(dom);
    df = r.createContextualFragment( html );
    dom.appendChild( df );
  },
  after_end: function( dom, html ) {
    dom = $(dom);
    var df; var r = dom.ownerDocument.createRange();
    r.setStartAfter(dom);
    df = r.createContextualFragment( html );
    dom.parentNode.insertBefore(df, dom.nextSibling);
  }
};

Cool, now we have a nice cross-browser way of adding content to the DOM in a positional way… Now we need to hook up RoR’s AJAX.Updater to use it, instead of the innerHTML approach.

A New Problem

Well, that’s all fine and dandy, but there’s a new problem: We can’t send in extra parameters in the form_remote_tag() helper method. So we have no way of telling it which position we’d like the new content to be added to the DOM.

The Right Way

Well, ideally, I think the form_remote_tag() helper should have a couple of params for handling the content returned from AJAX. Maybe you could set one of the following to true: :replace, :before_begin, :after_begin, :before_end, :after_end. Or, maybe we just have a :content_mode that would define one of the above symbols.

The Now Way

But that’s not important right now, it’s against my goal. I just want a drop-in enhancement.

Since we can’t send any params we want we need another way of telling the AJAX.Updater class how to handle the content, taking into consideration the fact it can be asynchronous… Always a fun little wrinkle.

The only thing I’ve got to work is to set it in javascript on the :loading AJAX event handler. I attach a property to the request object it sends, like so:

<%= form_remote_tag( :url =>; url_for( :action=>;'my_ajax_action' ), ) %>

Using that technique, I just created a custom AJAX.Updater.updateContent method, like this:

Ajax.Updater.prototype.updateContent = function() {
  if( this.request.transport.prepend )
    Insert.after_begin( this.container, this.request.transport.responseText );
  else if( this.request.transport.append )
    Insert.before_end( this.container, this.request.transport.responseText );
else
this.container.innerHTML = this.request.transport.responseText; if (this.onComplete) this.onComplete(this.request);
};

You’ll notice I only support prepending (adding to the beginning of a DOM element) and appending (adding to the bottom of a DOM element). It’d be a simple matter to add the other two types, I just didn’t need them.

The Script

I packaged all of this up into a single file that you can include AFTER you include prototype.js and it will automatically add all of this support.

prototype-ex.js

I’m curious to hear what you think. Plus, I’ve only really tested this on Safari, Firefox (and Camino)... It should work on any DOM compliant browser, but I haven’t gotten around to firing up my PC.

Saturday, March 19, 2005

Why Java Won't Get It Right

When I was reading DHH’spost about Rife I knew it would stir up the Javaites. And, sure enough, the first few posts were by Java trolls, lashing out and showing their ignorance.

I’ve been developing web applications for about ten years. At least four of those years have been exclusively committed to Java work. Java isn’t bad, but it isn’t great. The Sun marketing machine has made a good group of zombies, I will give it that. You’ve heard the expression ‘drinking the kool-aid’, right? You generally see it referring to pro-Microsoft people. Well the Java group are just as bad, it’s just a different flavor of kool-aid.

Now, I have a very good understanding of Java. I know that it’s not, never has been, and never will be a ‘silver bullet’. Nor has it lived up to the claim of ‘write once run anywhere’. And really, it’s no more ‘scalable’ than any other language.

I love that term; ‘scalable’. Certain people seem to think that by using Java, or .Net, or pick-your-technology, that it’s instantly ‘scalable’. Scalability is as scalability does… OK, that was a little too cheesy, but you get the point, right? It’s not the technology, but what you do with the technology that determines whether it’s ‘scalable’.

Technically speaking, everything is ‘scalable’. The question is: “Does your application code scale well when the user load is increased?”. That question is technology agnostic. The same question applies for RoR.

But I think ‘scalability’ is a red herring. You can create an application in RoR that’s as scalable as one written in Java, or .NET, or whatever technology. However, how productively can you create that application? There are people who think that LOC (Lines Of Code) are irrelevant. And, in a way, I agree. When using an IDE like IDEA, or Eclipse, you can create Java code very quickly. To me, it’s about the number of support, or boiler-plate, classes that have to be written. It’s about how much configuration is needed before you can actually use it.

But I digress. I could spend time ripping Java apart and pointing out it’s inadequacies… But that’s not the point. Every language has inadequacies. Every. Single. One. Ruby included. Java is as viable a language as Ruby, or C#, or hell, even C++. They all have their strengths, and weaknesses.

The main reason I don’t think Java will ever really have a good equivalent of RoR is because of the Java developers themselves. Before you read on, understand that these are generalizations. And no generalizations are accurate on the individual level. But, they can be indicative of behavioral patterns of a group. In others words, what I’m about to say may not apply to you—but seems to apply well to the Java community as a whole.

First, they don’t take the time to truly understands Rails. They say they do, but when you look at the ‘framework(s)’ they’ve written, it’s clearly missing the point of it all. Yes, you can do a scaffold knock-off in Java. You know what? I don’t use scaffolds at all. They are a nice add-on to Rails that can allow the developer to focus on the core of the application. Which is usually a lot more than just CRUD screens.

Second, they can’t reign themselves in. They over-architect everything. I’ve actually used a Java framework (I’m not gonna say which) that had XML config files that configured more XML config files! That’s just silly.

Huh. Well, I didn’t mean for this to become a Java bashing post. Because I like Java. I think it’s a good platform—specifically for web development. I’ve enjoyed using it over the years. It’s just a different mindset than the Rails community. And really, that’s why I think there will never be a ‘Java on Rails’. There is a fundamental difference in what each camp thinks, nay believes, is the right way of doing things.

I really meant for this to be a “why can’t we all just get along?” post. Oh well.

Update: Wow, that thread is getting pathetic. From both sides. Jeez, it’s breaking down into a contest of who has the bigger, er, framework.

Friday, March 18, 2005

WhiP it good

Well, comments just stopped working on my install of WordPress 1.2.1—for some reason. I’ve been meaning to upgrade anyway, so viola! I’m now running WP 1.5!

I’m still using the default theme—but that’ll change soon enough. Since WP 1.5 supports themes, real themes (finally!), I’ll port my old site layout to an actual WP theme.

It turns out the upgrade process was quite simple. All of the permalinks should still work. In theory. Let me know if you find problems with it…

Update: Well, I think it’s all re-stylized… It’s hard to tell the way they did the theme support. Which is, in fact, one of the main reasons I’m not a fan of PHP—it promotes spaghetti code.

Wednesday, March 16, 2005

So What Have I Been Working On?

I really can’t say enough good things about Ruby on Rails. It. Just. Makes. Sense.

Care to see what I have in the works?

Coming…. Oh, say, soon. :-)

Wednesday, March 9, 2005

AJAX and Rails

rails_logo_remix_small.gif I posted previously about communicating with Rails using XMLHttpRequest, or AJAX, as it’s starting to be called. Since that post, Rail 0.10 (and 0.10.1!) were released with the new Routes functionality.

Anyway, I’ve created a couple of JavaScript classes to aid in using the XMLHttpRequest stuff. I’ve been using them on a little project I’ve been working on (and will soon announce) and they are working great on Safari, Firefox, and Windows IE 6! I updated them for use with Routing and, being the nice guy that I am, I thought I’d share them with the world.

I call them Connectors. They connect the browser to the (Rails) server via JavaScript and XMLHttp. There are two classes, both in the same .js file: RailsConnector, and JsonConnector. They can be used Synchronously or Asynchronously.

In the aforementioned blog post, I talk a bit about the API — but the source is actually fairly well commented. It even has a couple of usage examples. So what are you waiting for? Download it!

connectors.js(8 KB)

You can use this script however you like. So have a look at it, kick the tires. I only ask that if you find any bugs, make any fixes, let me know!

Nothing New Under the Sun...

For my more GUI challenged friends, I was going to write some articles on designing web applications, usability, etc. But in my RoR trolling I came across a couple of articles that sum up, very nicely, my thoughts on design, on how design should be done.

So, without further ado, go forth and be learned:

Tuesday, March 1, 2005

A Must Read For All Web Developers

If you do any web development, you need to pick up this book:

Defensive Design for the Web : How to improve error messages, help, forms, and other crisis pointsSpecific guidelines for preventing errors in web sites and rescuing customers when problems occur, providing hundreds of real-world examples that show the right and wrong ways to handle crisis points. The book will be useful for designers and information architects, developers, copy writers, and project managers and executives.

It's a quick read, but well worth it. It has a lot of pointers that we should all follow, but we generally don't... To our own detriment.

Thursday, February 10, 2005

Experiments

rails_logo_remix_small.gif I’ve been playing around with Rails. So far, I really enjoy it. I was able to throw together a build tool for work in one evening. Which is really impressive considering what all it does. Oh, and I developed it on my Mac, dropped it on my PC server and it just worked! Take that Java.

I’ve been using the XMLHttp stuff for a couple of years at work (long before Google made it popular :-P). But I’ve never really looked at JSON until recently. It seems like a really nice format for use with “rich” web applications. It should be faster than XML and a lot easier to handle.

Along that vein, I’ve been testing some javascript classes I’ve made to help in talking with Rails. The RailsConnector class just calls a Rail Action and returns the results. JsonConnector acts the same, but evaluates the results as JSON. There is also support for asynchronous calls. They’re working great on Safari and Firefox, I haven’t tested on Internet Explorer.

Constructors

JsonConnector(controller, action[, callback])
RailsConnector(controller, action[, callback])

The parameter callback is optional, if you pass it a function the Connector will execute asynchronously.

Methods

.add(key, value)
.get(key)
.remove(key)
.clear()
.execute()

Both classes have the same method signatures. The .add() .get() .remove() and .clear() methods are used for setting parameters to send to the Rails action.

I’ll probably post some examples pretty soon.

Saturday, January 29, 2005

Comic Master

Be a master of your own domain, get Comic Master (bater)!

OK, so I’m going to bite the bullet (and maybe lose my teeth in the process) and learn RubyOnRails. But instead of building another ToDolistapp or another one of countless blogs, I thought I’d do something different. Something that I actually could use. A comic website management application. Thusly, Comic Master was conceived.

Planned features for version 1:

  • Comic strip management (duh)
  • File uploading (for strips and other things… wallpaper, avatar, etc)
  • Setting future dates for strips to be published
  • Allow for grouping individual episodes into issues (Lil Monstas will use this format)
  • Simple content management
  • Simple forums
  • News posting
  • Blog
  • Commenting on all of the above

I’m going to use MySQL as the database. All the content is going to support RedCloth. Plus, I’d like to make it support themes.

I don’t like the name Comic Master, though. Sounds a little like “Be a master of your own domain, get Comic Master (bater)!” Which is just plain wrong. But I can change that when — or if — I think of a better name. Feel free to post any suggestions…

Friday, January 28, 2005

Terminal Woes

I couldn’t stand it any more. My terminal prompt looking like:

Matthew-McCrays-Computer:darthapo ~/Desktop $ |

It was driving me crazy! I wasn’t sure how to change it on my Mac though. Our friend Google showed me that I can change it the same way I would on my linux box—since they both use bash as the shell.

So, I’m much happier now that I’ve updated my ~/.profile with this entry:

export PS1=" \w &gt; "

Now my prompt looks like

 ~/Desktop > |

Ahhhh, much better.

Thursday, January 27, 2005

The Venture Brothers

The Venture Brothers is the best cartoon on Cartoon Network (Adult Swim). It’s got action, humor, and some of the best villains I’ve seen in a long time. I nearly fell off of my couch when I saw Magic 8 Ball man! He, of course, doesn’t speak. He shakes himself and you read his answer on his chest-plate.

Dean: Dad is super serious all of a sudden. Do you really think he’s in as much danger as he says he is? Brock: Oh yeah, he’s screwed. I give him about an hour before he panics and begs for us to haul him up. Dr. Venture: Okay guys, I can hear all this

It’s definitely quirky—when one of the main characters is voiced by Patrick Warburton (The Tick, Puddy, et al) you know it’s gonna be a little off. It pokes fun at lot of cartoons from yesteryear. You’ll notice the nod to Johnny Quest right off. A tad of Hardy Boys, a dose of Austin Powers, with the camp of Brisco County, Jr. (how’s that for an eclectic reference) what’s not to like?

Brock is chained up, talking to Hank through his communicator watch
Brock: After the twist, you’ll hear a snap. Then the body goes ragdoll on ya.
Hank: And that will knock him out…even more?
Brock: That’ll kill him
Hank: Do I have to?
Brock: Alright fine, crybaby. Just tie him up and, maybe I guess gag him. But at the first sign of trouble I want you to at least break both his knees.

Check it out on Cartoon Network -- it’s usually on Sunday nights -- I think you’ll be glad you did.

FYI: I feel compelled to mention that this show is on Adult Swim, so it’s not really for little kids. I think it’s rated TV-PG, but still some of them can get a little raunchy.

Mass. Pirate: We need that key to stash your boat, Mr. Big Stuff, let’s have it.
Brock: ... it’s up my ass.
Pirate #1: Are you serious?
Brock: Why don’t you check?
Pirate #1: (to other pirate) Well? Check!
Mass. Pirate: But what if he’s lying?
Pirate #1: If he were telling the truth, that would be better?

Time to Find a New Job

OMG -- I almost feel sorry for this guy. Almost.

Monday, January 24, 2005

Never Say Never

noIE90.png Well, all things being cyclic, it eventually had to come to this; Internet Explorer is not the top dog anymore. When I say this, I don’t mean market-share. They still have a good slice of that (see chart below). However, IE is so far behind it’s competition that you can’t - in good conscience - call it a “modern browser”. It’s slow, buggy, non-compliant… Oh, yeah, and full of security holes. In fact, it’s really just an online virus magnet that allows you to see some web-pages too.

I’ve been playing with Safari, of late—I really enjoy it. I’ll probably wind up going back to FireFox, though. It’s just so darn nice.

Here’s the market-share chart from ZooDotCom:

server.jpg

Adult Swim, Without A Paddle

As most of you know, I have a webcomic. A second one in the works too. I draw a lot of my inspiration from my many, many years of watching cartoons and reading comics. In fact, I still watch cartoons and read comics. However, I’m a lot more selective in my tastes now.

Over the next few weeks I thought I’d post on some of my favorite cartoons and (web)comics.

It’s funny to watch these new cartoons and know that the writers are my age—you can tell by the jokes they tell.

I watched “Without A Paddle” this weekend. It’s a very funny movie. Matthew Lillard did a good job—although I’ll always see him as Shaggy now. The depressing part is that this movie is about a group of guys my age. I’m now old enough to have the ‘letting go of childhood’ movies targeted at me.

[Sigh]

General Weirdness

Hmmm… WordPress doesn’t like Scooby and Shaggy. I don’t know if you noticed, but the last post is really messed up if you select the permanent link, or try and add a comment.

I’m not sure why, either. It seems to only be that post. I’ve added the last few entries with MarsEdit. But if that was the issue, the other posts should have been messed up too—I’m rather baffled by the whole thing.

Are images an issue? Let’s find out, here’s a picture of my car:

2004-tiburon.jpg

Huh. Weird.

Sunday, January 23, 2005

Which Carter is the Carter with all the Pills?

Comments have been re-enabled!

It’s funny, I was perusing the blog of the developer of the AuthImage plugin (that I’m now using the kill some spam) when I saw a comment about him going through more wallets “than Carter has pills”. He mentioned that he thought it referred to ex-President Jimmy Carter—which is amusing, ‘cause I used to think that too. But then I used to wonder, if he had to take so many liver (or little) pills shouldn’t he have died many years ago?

Anyway, it turns out that it’s not about Jimmy Carter. Which really isn’t a surprise. It’s actually referring to a company that made liver pills.

liver-pills.jpg

Funny, huh?

Saturday, January 22, 2005

Software Stereotypes

I don’t generally believe in stereotypes. But occasionally they do seem to be true, in a general sense.

I’ve been an internet software developer for nearly ten years. In those years I’ve worked in various areas of web-development and have noticed certain attitudes that seem to be common amongst certain technology “camps”. For example:

Java people generally over-complicate things. They come up with the most convoluted “solution” to any given problem and call it scalable. Which, by the way, is strictly speculation. Plus, they really go overboard with configuration files. I’ve seen configuration files for configuration files, how silly is that?

Microsoft people not only drink the M$ kool-aid, they like it. They are usually rather weak at OO stuff. And rarely know anything about Design Patterns (which isn’t entirely bad).

Then there are the XML zealots—which are similar to Java people. But they aren’t limited to Java, they exist everywhere, in every technology camp. Words cannot describe these people. The seem to think XML is the solution for everything. I mean everything. I actually saw one guy post that XSLT was the only language he’d ever use again. He claimed it was a full-fledged language that was comparable to C++.

Delusion simply isn’t a strong enough word.

There are other wacky tech “camps” out there, to be sure. But I’ve not really been exposed to them (thank god). So I can’t really say what they’re typically like.

What “camp” am I a part of? Well, frankly, none of them. I’m really a bit of a technology agnostic. They all do the same thing, more or less. They each have their strengths and weaknesses. That having been said, I’m becoming more and more drawn to dynamic languages like Python. Ruby is on my list of things to play with. Primarily because I want to play with Ruby on Rails.

Anyway, I thought I’d just piss off all of my friends at once. o_O

Of which camp are you?

Sunday, January 16, 2005

Stardom

You know what’s cooler than having your own comic?

Getting fan mail for your comic! I got my first fan mail last week, others quickly followed. It’s a very cool feeling. Plus, it’s good motivation to update regularly when I have a “fan-base” (too cool) to think of.