git: what to do if you commit to no branch
October 28, 2009 by Ed Spencer · 2 Comments
Using git, you’ll sometimes find that you’re not on any branch. This usually happens when you’re using a submodule inside another project. Sometimes you’ll make some changes to this submodule, commit them and then try to push them up to a remote repository:
ed$ git commit -m "My excellent commit" [detached HEAD d2bdb98] My excellent commit 3 files changed, 3 insertions(+), 3 deletions(-) ed$ git push origin master Everything up-to-date
Er, what? Everything is not up to date – I just made changes! The clue is in the first part of the commit response – [detached HEAD d2bdb98]. This just means that we’ve made a commit without actually being on any branch.
Luckily, this is easy to solve – all we need to do is checkout the branch we should have been on and merge in that commit SHA:
ed$ git checkout master Previous HEAD position was d2bdb98... My excellent commit Switched to branch 'master' ed$ git merge d2bdb98 Updating 88f218b..d2bdb98 Fast forward ext-mvc-all-min.js | 2 +- ext-mvc-all.js | 2 +- view/FormWindow.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
Once we got onto the master branch, we just called git merge with the SHA reference for the commit we just made (d2bd98), which applied our commit to the master branch. The output tells us that the commit was applied, and now we can push up to our remote repository as normal:
ed$ git push origin master Counting objects: 11, done. Delta compression using up to 2 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 692 bytes, done. Total 6 (delta 4), reused 0 (delta 0) To git@github.com:extmvc/extmvc.git 88f218b..d2bdb98 master -> master
This had me puzzled for a while so hopefully it’ll save someone banging their head against a nearby wall.
Writing Better JavaScript – split up long methods
October 6, 2009 by Ed Spencer · Leave a Comment
For the second time this week I’m going to pick on the usually delightful Ext JS library. Last time we discussed the overzealous use of the Module pattern; this time it’s the turn of bloated methods.
As before, I’m not really picking on Ext at all – this happens all over the place. But again, this is the library closest to my heart and the one I know the best.
The Problem
We’re going to take a look at Ext.data.XmlReader’s readRecords method. Before we get started though, I’ll repeat that this is intended as an example of an approach, not a whine at Ext in particular.
/**
* Create a data block containing Ext.data.Records from an XML document.
* @param {Object} doc A parsed XML document.
* @return {Object} records A data block which is used by an {@link Ext.data.Store} as
* a cache of Ext.data.Records.
*/
readRecords: function(doc) {
/**
* After any data loads/reads, the raw XML Document is available for further custom processing.
* @type XMLDocument
*/
this.xmlData = doc;
var root = doc.documentElement || doc;
var q = Ext.DomQuery;
var recordType = this.recordType, fields = recordType.prototype.fields;
var sid = this.meta.idPath || this.meta.id;
var totalRecords = 0, success = true;
if(this.meta.totalRecords){
totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
}
if(this.meta.success){
var sv = q.selectValue(this.meta.success, root, true);
success = sv !== false && sv !== 'false';
}
var records = [];
var ns = q.select(this.meta.record, root);
for(var i = 0, len = ns.length; i < len; i++) {
var n = ns[i];
var values = {};
var id = sid ? q.selectValue(sid, n) : undefined;
for(var j = 0, jlen = fields.length; j < jlen; j++){
var f = fields.items[j];
var v = q.selectValue(Ext.value(f.mapping, f.name, true), n, f.defaultValue);
v = f.convert(v, n);
values[f.name] = v;
}
var record = new recordType(values, id);
record.node = n;
records[records.length] = record;
}
return {
success : success,
records : records,
totalRecords : totalRecords || records.length
};
}
Anyone care to tell me what this actually does? Personally, I have absolutely no idea. I recently found myself needing to implement an XmlReader subclass with a twist which required understanding how this works, and let’s just say it wasn’t easy!
So what is it that makes the above so terrifyingly hard to understand? Well, in no particular order:
- It’s too long – you’d need to be a genius to easily understand what’s going on here
- The variable names don’t make much sense – some of the oddest include ‘q’, ‘ns’, ‘v’, ‘f’ and ’sv’
- There’s minimal commenting – we’re given a single-line clue at the very top as to what these 40-odd lines do
A Solution
Let’s see how the reworked code below addresses each of the concerns above:
- Although we end up with more lines of code here, no single method is more than around 10 LOC
- No single letter variable names – you no longer have to decode what ’sv’ means
- Constructive commenting allows rapid comprehension by skimming the text
One additional and enormous benefit here comes directly from splitting logic into discrete methods. Previously if you’d wanted to implement your own logic to determine success, get the total number of records or even build a record from an XML node you’d be stuck. There was no way to selectively override that logic without redefining that entire monster method.
With our new approach this becomes trivial:
Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
readRecords: function(doc) {
this.xmlData = doc;
//get local references to frequently used variables
var root = doc.documentElement || doc,
records = [],
nodes = Ext.DomQuery.select(this.meta.record, root);
//build an Ext.data.Record instance for each node
Ext.each(nodes, function(node) {
records.push(this.buildRecordForNode(node));
}, this);
return {
records : records,
success : this.wasSuccessful(root),
totalRecords: this.getTotalRecords(root) || records.length
};
},
/**
* Returns a new Ext.data.Record instance using data from a given XML node
* @param {Element} node The XML node to extract Record values from
* @return {Ext.data.Record} The record instance
*/
buildRecordForNode: function(node) {
var domQuery = Ext.DomQuery,
idPath = this.meta.idPath || this.meta.id,
id = idPath ? domQuery.selectValue(idPath, node) : undefined;
var record = new this.recordType({}, id);
record.node = node;
//iterate over each field in our record, find it in the XML node and convert it
record.fields.each(function(field) {
var mapping = Ext.value(field.mapping, field.name, true),
rawValue = domQuery.selectValue(mapping, node, field.defaultValue),
value = field.convert(rawValue, node);
record.set(field.name, value);
});
return record;
},
/**
* Returns the total number of records indicated by the server response
* @param {XMLDocument} root The XML response root node
* @return {Number} total records
*/
getTotalRecords: function(root) {
var metaTotal = this.meta.totalRecords;
return metaTotal == undefined
? 0
: Ext.DomQuery.selectNumber(metaTotal, root, 0);
},
/**
* Returns true if the response document includes the expected success property
* @param {XMLDocument} root The XML document root node
* @return {Boolean} True if the XML response was successful
*/
wasSuccessful: function(root) {
var metaSuccess = this.meta.success;
//return true for any response except 'false'
if (metaSuccess == undefined) {
return true;
} else {
var successValue = Ext.DomQuery.selectValue(metaSuccess, root, true);
return successValue !== false && successValue !== 'false';
}
}
});
(For brevity I have omitted the existing readRecords comment blocks from the above)
I suggest that you structure your code in this way at least 99% of the time. The one exception is if high performance is an issue. If you are in a situation where every millisecond counts (you probably aren’t), then taking the former route becomes more acceptable (though there’s still no excuse for not adding a few comments explaining what the code actually does).
My refactored code almost certainly runs slower than the original as it doesn’t take as much advantage of cached local variables as the monolithic version does. For library-level code this can make sense if the performance gain is significant, but for the everyday code you and I write it is rarely a good idea.
I’ll be watching.
ExtJS modules and mixins
October 2, 2009 by Ed Spencer · 3 Comments
A few days back Praveen Ray posted about “Traits” in Ext JS. What he described is pretty much what we’d call Modules in the Ruby world, and how to mix those modules into a given class.
Basically, using modules we can abstract common code into reusable chunks, and then include them into one or more classes later. This has several advantages – avoiding code repetition, decoupling code concepts and ease of unit testing among them.
While the idea is good, there is a better way of achieving this than Praveen suggests. Let’s say we define the following modules, which are just plain old objects:
//module providing geolocation services to a class
var GeoLocate = {
findZipLatLng: function(zipCode) {
//does some clever stuff to find a zip codes latitude/longitude
},
getGeoApiKey: function() {
return this.geo_api_key || 'default key';
}
};
//module allowing a class to act as a state machine
var StateMachine = {
transition: function(stateName) {
this.state = stateName;
},
inState: function(stateName) {
return this.state == stateName;
}
};
We’ve got a couple of fictional modules, providing geolocation and state machine functionality. Adding these to an ExtJS class is actually pretty simple:
Ext.override(Ext.form.FormPanel, StateMachine); Ext.override(Ext.form.FormPanel, GeoLocate);
All that happens above is each property of our module object is copied to Ext.form.FormPanel’s prototype, making the functions available to all FormPanel instances.
If we just wanted to mix our modules into a specific instance of a class, we can do it like this:
var myForm = new Ext.form.FormPanel({});
Ext.apply(myForm, StateMachine);
This will only affect the instance we’re applying to, leaving all other FormPanel instances alone. In Praveen’s example this is in fact all we need to do – there is no need to do the constructor definition and Ext.extend call, we can just use Ext.apply.
There’s nothing in the above that’s actually limited to Ext JS – all we’re doing is copying properties from one object to another. Implementing Ext.override and Ext.apply are pretty simple without Ext itself, though Ext.extend is a whole other story (and a blog post in itself).
Finally, beware overwriting existing properties (functions or objects) on the class you are mixing into. If your formpanel already has a ‘transition’ function it will be overwritten by your module, which could lead to unexpected behaviour. At the instance level you could buy some protection against that by using Ext.applyIf instead of Ext.apply, though you might be safer writing a custom mixin function which can provide access to the original function or raise an exception when overwriting an existing property.
Ext.ux.layout.FillContainer
September 28, 2009 by Ed Spencer · 3 Comments
One of the pages on the Ext JS app I’m currently working on has a form with a grid underneath. The page exists as a tab inside an Ext.TabPanel, and uses the border layout, with the form as the ‘north’ component and the grid as ‘center’.
The trouble with this is that the grid shrinks down to an unusable size when the browser window is too small, ending up like this:

We could alternatively use a basic container layout, but this limits us to a fixed height for the grid, meaning we waste space at the bottom:

Enter the imaginatively named FillContainer:
new Ext.Panel({
autoScroll: true,
layout: 'fillcontainer',
items : [
{
html : 'Pretend this is a form',
height: 400
},
{
html : 'And this is the grid',
minHeight : 250,
fillContainer: true
}
]
});
If our containing panel shrinks to less than 650px in height, the grid will be automatically sized to 250px and a vertical scrollbar will appear on the panel, like this:

If the panel’s height increases to, say, 900px, the grid gets resized to 500px high. This way we use the space when it’s available, while maintaining a usable interface when height is limited:

Here’s the code that makes it work:
Ext.ns('Ext.ux.layout');
/**
* @class Ext.ux.layout.FillContainerLayout
* @extends Ext.layout.ContainerLayout
* @author Ed Spencer (http://edspencer.net)
* Extended version of container layout which expands a given child item to the
* full height of the container, honouring the item's minHeight property
*/
Ext.ux.layout.FillContainerLayout = Ext.extend(Ext.layout.ContainerLayout, {
monitorResize: true,
/**
* After rendering each item, resize the one with fillContainer == true
*/
onLayout: function(ct, target) {
Ext.ux.layout.FillContainerLayout.superclass.onLayout.apply(this, arguments);
var ctHeight = ct.getHeight(),
itemsHeight = 0,
expandItem;
ct.items.each(function(item) {
if (item.fillContainer === true) {
expandItem = item;
} else {
itemsHeight += item.getHeight();
}
});
//set the expand item's height to fill the container
if (expandItem != undefined && ctHeight > itemsHeight) {
var newHeight = ctHeight - itemsHeight;
expandItem.setHeight(Math.max(newHeight, expandItem.minHeight));
}
}
});
Ext.Container.LAYOUTS['fillcontainer'] = Ext.ux.layout.FillContainerLayout;
As we’re just extending the default container layout, your items will be rendered in the order you specify them. The expanding item doesn’t have to be the last one – we could equally have set fillContainer and minHeight on the form to expand that instead of the grid.
Using the ExtJS Row Editor
September 16, 2009 by Ed Spencer · 14 Comments
The RowEditor plugin was recently added to the ExtJS examples page. It works a lot like a normal Grid Editor, except you can edit several fields on a given row at once before saving.
This neatly solves the problem of adding a new row to an editor grid, entering data into the first field and finding it save itself straight away, which is rarely desired. In this fashion we can provide full CRUD for simple models in a single page.
Installation
You’ll need to get a copy of the javascript, css and images from the server. This is a bit of a pain. If you still have the ExtJS SDK around you can find these in the examples folder, if not you can get each file as follows:
Grab the plugin JS file below and put it where you usually put your .js files:
http://www.extjs.com/deploy/dev/examples/ux/RowEditor.js
This needs to go with your other stylesheets, usually in a directory called ‘css’:
http://www.extjs.com/deploy/dev/examples/ux/css/RowEditor.css
Download these two images and put them into your existing ‘images’ folder (the same place the other ExtJS images live):
http://www.extjs.com/deploy/dev/examples/ux/images/row-editor-bg.gif
http://www.extjs.com/deploy/dev/examples/ux/images/row-editor-btns.gif
Include the .js and .css files on your page and you should be ready to go.
Usage
RowEditor is a normal grid plugin, so you’ll need to instantiate it and add to your grid’s ‘plugins’ property. You also need to define what type of Editor is available (if any) on each column:
var editor = new Ext.ux.grid.RowEditor();
var grid = new Ext.grid.GridPanel({
plugins: [editor],
columns: [
{
header : 'User Name',
dataIndex: 'name',
editor : new Ext.form.TextField()
},
{
header : 'Email',
dataIndex: 'email',
editor : new Ext.form.TextField()
}
]
... the rest of your grid config here
});
RowEditor defines a few events, the most useful one being ‘afteredit’. Its signature looks like this:
/**
* @event afteredit
* Fired after a row is edited and passes validation. This event is fired
* after the store's update event is fired with this edit.
* @param {Ext.ux.grid.RowEditor} roweditor This object
* @param {Object} changes Object with changes made to the record.
* @param {Ext.data.Record} r The Record that was edited.
* @param {Number} rowIndex The rowIndex of the row just edited
*/
'afteredit'
All you need to do is listen to that event on your RowEditor and save your model object appropriately. First though, we’ll define the Ext.data.Record that we’re using in this grid’s store:
var User = Ext.data.Record.create([
{name: 'user_id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'email', type: 'string'}
]);
And now the afteredit listener itself
editor.on({
scope: this,
afteredit: function(roweditor, changes, record, rowIndex) {
//your save logic here - might look something like this:
Ext.Ajax.request({
url : record.phantom ? '/users' : '/users/' + record.get('user_id'),
method: record.phantom ? 'POST' : 'PUT',
params: changes,
success: function() {
//post-processing here - this might include reloading the grid if there are calculated fields
}
});
}
});
The code above simply takes the changes object (which is just key: value object with all the changed fields) and issues a request to your server backend. ‘record.phantom’ returns true if this record does not yet exist on the server – we use this information above to specify whether we’re POSTing to /users or PUTing to /users/123, in line with normal RESTful practices.
Adding a new record
The example above allows for editing an existing record, but how do we add a new one? Like this:
var grid = new Ext.grid.GridPanel({
//... the same config from above goes here,
tbar: [
{
text : "Add User",
handler: function() {
//make a new empty User and stop any current editing
var newUser = new User({});
rowEditor.stopEditing();
//add our new record as the first row, select it
grid.store.insert(0, newUser);
grid.getView().refresh();
grid.getSelectionModel().selectRow(0);
//start editing our new User
rowEditor.startEditing(0);
}
}
]
});
Pretty simple stuff – we’ve just added a toolbar with a button which, when clicked, creates a new User record, inserts it at the top of the grid and focusses the RowEditor on it.
Configuration Options
Although not documented, the plugin has a few configuration options:
var editor = new Ext.ux.grid.RowEditor({
saveText : "My Save Button Text",
cancelText: "My Cancel Button Text",
clicksToEdit: 1, //this changes from the default double-click activation to single click activation
errorSummary: false //disables display of validation messages if the row is invalid
});
If you want to customise other elements of the RowEditor you probably can, but you’ll need to take a look at the source (it’s not scary).
Final Thought
RowEditor is a really nice component which can provide an intuitive interface and save you writing a lot of CRUD code. It is best employed on grids with only a few columns – for models with lots of data fields you’re better off with a full FormPanel.
I’d be pretty happy to see this included in the default ExtJS distribution, as I find myself returning to it frequently.