Episode 36: Subversion on Rails

Posted almost 7 years back at Railscasts

This episode will walk you through setting up a Rails project on subversion. It covers some helpful tips and gotchas you may experience along the way.

DataGrid - handy hints

Posted almost 7 years back at work.rowanhick.com

Okay the orders list. It's a plain vanilla DataGrid component bound to the result from an HTTPService call to get an XML representation of an ActiveRecord orders collection. This all pretty easy to get up and running, but I ran into two immediate snags. Requirement #1 - Multicurrency in the totals column. Our system deals with orders in currencies all over the world, so we have price types and associate each order with a price type. Fair enough. Now we don't store price type symbol with the total (naturally) so how do we get it to appear in the same column in the the datagrid. Option 1 (on the Rails side) would be to add some faux accessor and iterate over the orders populating it before we handed it to the to_xml function sending it to Rails (that's not without it's problems). OR Option 2 (on the Flex side) we manipulate it over here. First off we create a little function that takes our current item (row) and the column we're manipulating, then prepends the symbol and appends the code to the amount. private function formatCurrency(item:Object, column:DataGridColumn):String { var field:String = column.dataField; return item.price_type_dollar_sign+' '+item[field]+' '+item.price_type_symbol; } Then in the mxml code for the datagrid <mx:DataGridColumn headerText="Total Amount" dataField="total_amount_payable" labelFunction="formatCurrency"/> Requirement #2 Sorting a field in an order different to that displayed So we have an order status (Draft, Pending Review, Review, Payment Pending, In Manufacturing, Shipped etc). Great, now you put that straight into your datagrid, hit a column header to sort, and realise it's doing an alpha sort on the status name. As you expect (but not as you wanted). Well again there's two ways to accomplish this - one is to pass in custom sort function to figure out the order. OR The other option, is to pipe down in your xml along with the order status name, a sort order numeric value , so Draft is 1 Shipped is 1000, everything in between gets a number etc. Then just as you do for the currency symbol, change the formatting of the column - so we bind our column to the order status sort_order value, but we display the order status name. When the datagrid sorts, it sorts on the bound value, not on the formatted value. Very nifty and simple. Just like this... private function formatStatus(item:Object, column:DataGridColumn):String { return item.order_status_name; } <mx:DataGridColumn headerText="Status" dataField="order_status_id" labelFunction="formatStatus"/> No nasty sort function, and does the trick. That's it for now... till next time.

Javascript: Event.onElementReady

Posted almost 7 years back at Revolution On Rails

We deal with a lot of Javascript at RHG. Rather then create functional one-offs that become hard to maintain and very duplicative, we prefer to have collections of behavior that can be assigned to parts of the document. Our CSS designers especially like this because it makes our markup clean and easy to read. It also means we can have tests that exercise a series of interactions.

However, there is a problem we run into when loading the page over a slow connection: the Javascript is not run until window.onload, meaning our users suffer as they can see part of the page rendered but not use it until it's fully loaded. For those who don't know, window.onload will not execute until all images, CSS, and Javascript files have loaded. Our first solution to this problem was to use onDOMReady. This has worked fairly well and kept our site running reasonably quickly. We've run into problems with onDOMReady in IE6 however, and as a result disabled it in favor of having our pages render all the time. In the IE family of browsers if a script tries to access a part of the DOM before it has been completely processed the browser will raise an exception "operation aborted". After this alert message pops up, the web page becomes unusable and the browser could even crash.

After some careful thinking we decided we would attach our behavior objects in smaller chunks of the DOM. Rather then wait until the window.onload or onDomReady events fire we can use a few inline script tags that call a function that figures out how to attach itself to its most immediate parentNode.

For example, we do this:


<div class="parent">
<div class="bvr-blah">
</div>
<script type="text/javascript">
RHG.Behavior.attach();
</script>
</div>


There could be multiple behaviors in the above block of markup that are all contained within the hypothetical div.parent element.

Now, to allow this to work the div.parent must be ready. Otherwise, IE will cancel the whole page rendering with the "Operation Aborted" alert.

Event.onElementReady checks for features of the DOM element to determine whether or not the element is really ready to be manipulatd. Thankfully, IE doesn't mind if you read from an element before it's ready only if you try and modify it. This method will poll the DOM element until either the nextSibling or the textContent is non null.


Object.extend(Event,{
// check whether or not the DOM element is ready
onElementReady: function(element,callback)
{
if( element && (element.nextSibling || element.textContent) ){
callback();
}
else{
setTimeout( this.onElementReady.bind(this,element,callback), 1 );
}
}
});


Now we can attach our behaviors as the page is rendering and avoid "Operation Aborted" alerts from IE.

Javascript: Event.onElementReady

Posted almost 7 years back at Revolution On Rails

We deal with a lot of Javascript at RHG. Rather then create functional one-offs that become hard to maintain and very duplicative, we prefer to have collections of behavior that can be assigned to parts of the document. Our CSS designers especially like this because it makes our markup clean and easy to read. It also means we can have tests that exercise a series of interactions.

However, there is a problem we run into when loading the page over a slow connection: the Javascript is not run until window.onload, meaning our users suffer as they can see part of the page rendered but not use it until it's fully loaded. For those who don't know, window.onload will not execute until all images, CSS, and Javascript files have loaded. Our first solution to this problem was to use onDOMReady. This has worked fairly well and kept our site running reasonably quickly. We've run into problems with onDOMReady in IE6 however, and as a result disabled it in favor of having our pages render all the time. In the IE family of browsers if a script tries to access a part of the DOM before it has been completely processed the browser will raise an exception "operation aborted". After this alert message pops up, the web page becomes unusable and the browser could even crash.

After some careful thinking we decided we would attach our behavior objects in smaller chunks of the DOM. Rather then wait until the window.onload or onDomReady events fire we can use a few inline script tags that call a function that figures out how to attach itself to its most immediate parentNode.

For example, we do this:


<div class="parent">
<div class="bvr-blah">
</div>
<script type="text/javascript">
RHG.Behavior.attach();
</script>
</div>


There could be multiple behaviors in the above block of markup that are all contained within the hypothetical div.parent element.

Now, to allow this to work the div.parent must be ready. Otherwise, IE will cancel the whole page rendering with the "Operation Aborted" alert.

Event.onElementReady checks for features of the DOM element to determine whether or not the element is really ready to be manipulatd. Thankfully, IE doesn't mind if you read from an element before it's ready only if you try and modify it. This method will poll the DOM element until either the nextSibling or the textContent is non null.


Object.extend(Event,{
// check whether or not the DOM element is ready
onElementReady: function(element,callback)
{
if( element && (element.nextSibling || element.textContent) ){
callback();
}
else{
setTimeout( this.onElementReady.bind(this,element,callback), 1 );
}
}
});


Now we can attach our behaviors as the page is rendering and avoid "Operation Aborted" alerts from IE.

Screenshot #1 - Orders list

Posted almost 7 years back at work.rowanhick.com

Right here's our first look at the prototype app. Nothing too dramatic but gives you an idea of where we're headed with this. As you can see we've got some fairly standard controls, a datagrid, combo boxes, and buttons. Tomorrow we'll show how this is structured, and how I managed to get some fairly neat stuff with the datagrid working that demonstrates the polish already in Flex.

Episode 35: Custom REST Actions

Posted almost 7 years back at Railscasts

REST adds many constraints. It restricts your controllers to seven actions. Normally this is okay, but sometimes you need to add your own custom actions. Learn how in this episode.

DRYing Up Polymorphic Controllers

Posted almost 7 years back at Revolution On Rails

Polymorphic routes allow drying up the controller implementation when functionality is identical, regardless of entry point. A good example is comments for articles and blogs. There is a challenge to balance the implementation of the comments controller reflecting the multiple incoming routes. Let's look at the way it could be written.

Routing is straightforward with blogs and article models acting as commentable and both the comment model and comment controllers being polymorphic:

ActionController::Routing::Routes.draw do |map|
map.resources :articles, :has_many => [ :comments ]
map.resources :blogs, :has_many => [ :comments ]
end


This means that a comment can be created via post to either /articles/1/comments/new or /blogs/1/comments/new. The comments controller can be implemented to handle both:

class CommentsController < ApplicationController

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to parent_url(@parent)
else
render :action => 'new'
end

end

private

def parent_object
case
when params[:article_id] then Article.find_by_id(params[:article_id])
when params[:news_id] then News.find_by_id(params[:news_id])
end
end

def parent_url(parent)
case
when params[:article_id] then article_url(parent)
when params[:news_id] then news_url(parent)
end
end

end


This method works fine and there is not much drive to start refactoring it right away. This changes, though, if there is a need to add another commentable or allow some other polymorphic route. Instead of adding more 'when' clauses the whole functionality can be extracted and abstracted based on the idea of having fixed naming conventions for resources that allow movement from a controller name to a model. The refactored example has the parent functionality extracted to the application controller to share it as-is with other polymorphic routes:

class ApplicationController < ActionController::Base

protected

class << self

attr_reader :parents

def parent_resources(*parents)
@parents = parents
end

end

def parent_id(parent)
request.path_parameters["#{ parent }_id"]
end

def parent_type
self.class.parents.detect { |parent| parent_id(parent) }
end

def parent_class
parent_type && parent_type.to_s.classify.constantize
end

def parent_object
parent_class && parent_class.find_by_id(parent_id(parent_type))
end

end

class CommentsController < ApplicationController

parent_resources :article, :blogs

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to send("#{ parent_type }_url", @parent)
else
render :action => 'new'
end

end

end

The parent_resources call declares resources that are parent for a current controller. An alternative approach is to guess such parent resources from the request URI and routes. Aaron is currently working on a patch on Edge implementing it. We'll update this post later.

If you currently use multiple polymorphic resources and have if clauses in the controller code, you might want to rethink how it could be DRYed up using this approach. In some cases views are very parent type specific. Then it might be better to have different templates and partials rendered via render :template => "/controller/#{ parent_type }_action".

DRYing Up Polymorphic Controllers

Posted almost 7 years back at Revolution On Rails

Polymorphic routes allow drying up the controller implementation when functionality is identical, regardless of entry point. A good example is comments for articles and blogs. There is a challenge to balance the implementation of the comments controller reflecting the multiple incoming routes. Let's look at the way it could be written.

Routing is straightforward with blogs and article models acting as commentable and both the comment model and comment controllers being polymorphic:

ActionController::Routing::Routes.draw do |map|
map.resources :articles, :has_many => [ :comments ]
map.resources :blogs, :has_many => [ :comments ]
end


This means that a comment can be created via post to either /articles/1/comments/new or /blogs/1/comments/new. The comments controller can be implemented to handle both:

class CommentsController < ApplicationController

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to parent_url(@parent)
else
render :action => 'new'
end

end

private

def parent_object
case
when params[:article_id] then Article.find_by_id(params[:article_id])
when params[:news_id] then News.find_by_id(params[:news_id])
end
end

def parent_url(parent)
case
when params[:article_id] then article_url(parent)
when params[:news_id] then news_url(parent)
end
end

end


This method works fine and there is not much drive to start refactoring it right away. This changes, though, if there is a need to add another commentable or allow some other polymorphic route. Instead of adding more 'when' clauses the whole functionality can be extracted and abstracted based on the idea of having fixed naming conventions for resources that allow movement from a controller name to a model. The refactored example has the parent functionality extracted to the application controller to share it as-is with other polymorphic routes:

class ApplicationController < ActionController::Base

protected

class << self

attr_reader :parents

def parent_resources(*parents)
@parents = parents
end

end

def parent_id(parent)
request.path_parameters["#{ parent }_id"]
end

def parent_type
self.class.parents.detect { |parent| parent_id(parent) }
end

def parent_class
parent_type && parent_type.to_s.classify.constantize
end

def parent_object
parent_class && parent_class.find_by_id(parent_id(parent_type))
end

end

class CommentsController < ApplicationController

parent_resources :article, :blogs

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to send("#{ parent_type }_url", @parent)
else
render :action => 'new'
end

end

end

The parent_resources call declares resources that are parent for a current controller. An alternative approach is to guess such parent resources from the request URI and routes. Aaron is currently working on a patch on Edge implementing it. We'll update this post later.

If you currently use multiple polymorphic resources and have if clauses in the controller code, you might want to rethink how it could be DRYed up using this approach. In some cases views are very parent type specific. Then it might be better to have different templates and partials rendered via render :template => "/controller/#{ parent_type }_action".

Possibilities become real.

Posted almost 7 years back at work.rowanhick.com

A while back, at a previous company, our clients inundated us with feature requests. At one point at the infancy of this software's life, the codeword for "thank you for your request, we'll try to get it into the software" was called Possibilities. It always left the door open, however I'm sure the rate at things went through the door and never came back (well not until years later!) possibilities became a dreaded word... Now however (my personal belief) is between Rails and Flex turning the seemingly impossible to implement round in no time at all. After a rocky road start to Flex, I'm now having the opportunity to develop a full blown B2B application in it. Replacing a PHP/HTML solution. Both exciting and nerve racking - last night a good 2 hours was spent tracking down how exactly to get something shown in the correct order. I am well impressed, and looking forward to the future of possibilities with Flex 2, Apollo (and what Flex 3 may bring). To share my enthusiasm a good portion of this blog is going to be dedicated to documenting the rebuild of the previously mentioned B2B application. Currently I have client buy in, after ~ 6 hours of knocking up simple interface showing off a very 'windows like' application within a web browser he's sold on it, so it's now the big re-write. Here's the starting point for it: - A rails app with database migrations to take the v1 app's database and migrate it into a rails friendly naming scheme. - Basic objects replicating core functionality within Rails - Two flex front ends, one for general order management, and the other a tailored interface specifically for manufacturing. So far, here's my pro's and con's list Pro + It's going to work once, anywhere. Flash bugs cross browser are fee and far between. (compare that with XHTML/CSS/JS) + More native application functionality (drag/drop, grids etc) + Less network traffic per session + More responsive application Cons - Write, compile, write, compile, write, compile (although Flex Builder/Flex compiler shell reduces the pain) - A lot more code in building the interface - Guess work figureing out how to do complex stuff - Loosing some of Rails niceties (or duplication of things like validations) Next up some screenshots....

RailsConf 2007 - Ruby on Rails Podcast

Posted almost 7 years back at Ruby on Rails Podcast

Interviews from RailsConf 2007 in Portland.

Episode 34: Named Routes

Posted almost 7 years back at Railscasts

When you add a custom route, make it a named route so you have url helper methods to easily link to that new route. See episode for details.

Railsconf 2007 FTW

Posted almost 7 years back at zerosum dirt(nap) - Home

Had a great time at Railsconf 2007! Just got back. A little jetlagged, and in need of (another) nap. In summary: lots of good presentations, superb microbrews, a hilarious zefrank performance, and a number of lurking lolcats.

Highlights for me included DHH’s keynote, the Rails way ‘live’ performance by Jamis and Koz (although I have to politely disagree with them on the need for private ActiveRecord attributes), Ezra’s deployment and scaling session, and the homesteading talk by Matthew Bass. Thanks to everyone who helped make it happen.

Also got to meet some fellow IRC peeps in person. Now ur tru idinty is nown to me, bewar!

Episode 33: Making a Plugin

Posted almost 7 years back at Railscasts

You can sometimes remove a lot of duplication by generating methods dynamic. In this episode I will show you how to create a plugin which does exactly that.

Only In Kenya

Posted almost 7 years back at Sporkmonger

As a few of you are probably aware by now, I will be taking a break from Ruby, and computing in general for a couple of months. I am getting on a plane headed for Nairobi on May 20th. I won’t be back until August 26th. I’m not taking a computer with me, though I will have limited access through internet cafes, so email should work, but don’t expect quick replies.

For those who are curious, the intent of the trip is humanitarian in nature. There will be trips to Uganda and Tanzania, and some volcano climbing. I’ll be sure to take lots of photos, which will no doubt end up on Flickr.

Update:

In London. About to fall asleep.

Update:

In Kenya. Amazingly still awake.

Episode 32: Time in Text Field

Posted almost 7 years back at Railscasts

Although Rails does allow you to edit time attributes with text fields, it's not very flexible. In this episode you will learn how to use a virtual attribute to format the time to your liking.