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.
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.
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!
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.
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.
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.
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
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.
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?
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
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).
To aid in retrieving themed media, helper methods have been added to the ActionView::Helpers::AssetTagHelper module.
Usage is generally quite simple. In your view, if you want to reference a themed image:
<img src="<%= theme_image_path 'my-image.png' % />" />
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.
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
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.
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
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:
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
, 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: rails
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: rails
TaskTHIS 0.5.1 is ready for download. It implements the following:
vendor
foldersend_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: rails
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:
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: rails
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.
I read this and laughed. It's actually quite amusing. A guy does the math on why he will never have a girlfriend.
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).
What are your additions to the list?
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:
Let me know what features you'd like added to the list.
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?’
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.
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
The new gem of Rails with the AJAX enhancements and lot of other stuff is out! Get it while it's hot!
I'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?
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!
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?
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.
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.
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.
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.
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.
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. :-)
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!
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:
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.
Of course the Brits think there's a link between finger length and heart attacks, too.
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.
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:
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…
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 > "
Now my prompt looks like
~/Desktop > |
Ahhhh, much better.
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?
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:
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]
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:
Huh. Weird.
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.
Funny, huh?
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?
Ta-da is an interesting app built with Ruby on Rails, check it out: Ta-da List: Simple sharable to-do lists.