Friday, 25 April 2008

When Git tells you it failed to push some refs

I received an unhelpful error while trying to push to a repository on Github today:

git push
To git@github.com:user/repo.git
! [rejected] branchname -> branchname (non-fast forward)
error: failed to push some refs to 'git@github.com:user/repo.git'

In case you ever have the same problem, all you have to do is a quick git pull first, then you can carry on as normal. Easy when you know how...

Thursday, 17 April 2008

Git clone vs Git submodule

Having recently made the switch from svn to git, I wanted to achieve what svn externals did (and what Piston did better). Turns out this is pretty simple, for example to get rails on edge:

cd your_git_dir
git submodule add git://github.com/rails/rails.git vendor/rails

A couple of other default submodules you'll want:

git submodule add git://github.com/dchelimsky/rspec.git vendor\plugins\rspec
git submodule add git://github.com/dchelimsky/rspec-rails.git vendor\plugins\rspec-rails

What submodule does is to check out the submodules as their own repositories, so they are tracked independently of the repository you made them submodules of. The submodules you have are tracked in the .gitmodules file, which might look something like this:

[submodule "vendor\rails"]
path = vendor/rails
url = git://github.com/rails/rails.git
[submodule "vendor/plugins/rspec"]
path = vendor/plugins/rspec
url = git://github.com/dchelimsky/rspec.git
[submodule "vendor/plugins/rspec-rails"]
path = vendor/plugins/rspec-rails
url = git://github.com/dchelimsky/rspec-rails.git

Or at least that's how it should look, Windows seems to mess this up into looking something like the following:

[submodule "vendor\rails"]
path = vendor\\rails
[submodule "vendor\rails"]
url = git://github.com/rails/rails.git
[submodule "vendor\plugins\rspec"]
path = vendor\\plugins\\rspec
[submodule "vendor\plugins\rspec"]
url = git://github.com/dchelimsky/rspec.git
[submodule "vendor\plugins\rspec-rails"]
path = vendor\\plugins\\rspec-rails
[submodule "vendor\plugins\rspec-rails"]
url = git://github.com/dchelimsky/rspec-rails.git

Note especially that you need to remove the \\'s and replace all \'s with /'s. If you don't git will give a fail message like:

fatal: bad config file line 2 in .gitmodules
No submodule mapping found in .gitmodules for path 'vendor/plugins/attachment_fu'

I don't know why it's doing that, maybe it's something I'm doing wrong but you'll need to tidy it up to make it look more like the first example in order for it to work properly.

One final thing to be aware of is that when you clone onto a new machine you'll need to run the following commands:

git submodule init
git submodule update

This will initialise the submodules that are referenced in the .gitmodules file, then pull them down. By default cloning doesn't seem to do that.

Wednesday, 16 April 2008

Useful Rails javascript expansions for EXTJS

If you're using Edge Rails (or > 2.1, which isn't out at time of writing), and are using the EXT JS framework anywhere, here are a couple of handy javascript include tag expansions to clean up your views. Just chuck them into any file in your config/initializers directory:

ActionView::Helpers::AssetTagHelper.register_javascript_expansion :ext => ['ext/adapter/ext/ext-base', 'ext/ext-all']

ActionView::Helpers::AssetTagHelper.register_javascript_expansion :ext_grid_filter => ['ext/ux/menu/EditableItem', 'ext/ux/menu/RangeMenu', 'ext/ux/grid/GridFilters', 'ext/ux/grid/filter/Filter', 'ext/ux/grid/filter/StringFilter', 'ext/ux/grid/filter/DateFilter', 'ext/ux/grid/filter/ListFilter', 'ext/ux/grid/filter/NumericFilter', 'ext/ux/grid/filter/BooleanFilter']

The top one includes the relevant EXT base files and the second one includes all the Grid Filters from the excellent Filter Grid plugin (see http://ccinct.com/lab/filter-grid/.

Include them as usual like this:

javascript_include_tag :ext, :ext_grid_filter, :cache => 'ext_javascripts'

EXT remote-loading forms with Combo boxes

Something that's harder than it should be is populating an EXT edit form with form data, where one of the form fields is a select box. If there is a specific set of values that this select can take, then you can hard code that using a SimpleStore, like this:

var exampledata = [['AL', 'Alabama'],
['AK', 'Alaska'],
['AZ', 'Arizona'],
['AR', 'Arkansas'],
['CA', 'California']];

var store = new Ext.data.SimpleStore({
fields: ['abbr', 'state'],
data : exampleData
});

var combo = new Ext.form.ComboBox({
store: store,
displayField: 'state',
valueField: 'abbr',
... etc ...
});

form = new Ext.form({
items: [combo],
... etc ...
});

form.load(url_to_load_from);

So that will populate the select box with the static values you've defined (the 5 states above), then when the form loads it will select the appropriate option automatically.

So far so good, but what if you need to load what goes into the select box dynamically? Well, first you'll need to set up your remote data store (my server is sending back JSON data, hence the JsonReader):

store = new Ext.data.Store({
url: 'url_to_load_combo_data_from',
reader: new Ext.data.JsonReader(
{root: 'states',totalProperty: 'results'},
[
{name: 'name', type: 'string', mapping: 'state.name'},
{name: 'abbr', type: 'string', mapping: 'state.abbr'}
]
)
});

This will consume data like this:

{"states": [{"state": {"name": "Alabama", "abbr": "AL"}}, {"state": {"name": "Alaska", "abbr": "AK"}}], "results": "2"}

And populate the store with a collection of state records which can then be loaded into the combobox.

Then all you need to do is load the store before loading the form data, and your comboboxes will be correctly populated, displaying the correct option. Here's the full example:

store = new Ext.data.Store({
url: 'url_to_load_combo_data_from',
reader: new Ext.data.JsonReader(
{root: 'states',totalProperty: 'results'},
[
{name: 'name', type: 'string', mapping: 'state.name'},
{name: 'abbr', type: 'string', mapping: 'state.abbr'}
]
)
});

var combo = new Ext.form.ComboBox({
store: store,
displayField: 'state',
valueField: 'abbr'
... etc ...
});

form = new Ext.formPanel({
items: [combo]
});

store.load();
form.load(your_form_data_url);

Be wary using the pagination options on the combobox here (see http://extjs.com/deploy/dev/docs/?class=Ext.form.ComboBox) - the reason being if your state's 'abbr' features on the second page of the results it won't populate the correct options into the combo box.

Saturday, 12 April 2008

Getting EXT PagingToolbars to save state

A problem that has recently had me pulling my hair out is how to save state in an EXT PagingToolbar.

Ext makes it easy to save the state of most of its components - by default it does this by setting a cookie with the relevant configuration info, then just reading it back when you load the component again. I've been using it to save the state of a few EXT grids I've been using on a recent project, this saves config such as which columns you have visible, which column you're sorting by, and how the columns are ordered.

That works great, and is trivial to implement - just set your provider (see http://extjs.com/deploy/dev/docs/?class=Ext.state.CookieProvider) and be sure to give your grid an id in its config - this is used as the key in the state provider and needs to be unique for each component.

The problem comes when you're using a paging toolbar though, as this does not save state, so every time you view the grid you're back to page 1. You can add state behaviour to the paginator by piggybacking the grid's state store, here's how it's done:

Ext.PagingToolbar.override({
init : function (grid) {
this.grid = grid;
this.grid.on("beforestatesave", this.saveState, this);
Ext.util.Observable.capture(grid.store, this.onStateChange, this);
},
saveState : function(grid, state) {
state.start = grid.store.lastOptions.params.start;
},
onStateChange : function(ev, store, records, options) {
if (ev == "load") {this.grid.saveState(); };
}
});

Basically we're intercepting the attached Grid's saveState() event and appending the current start value as stored in the Grid's DataStore (e.g. if you're looking at page 3 with 25 rows per page then start = 50). If you examine the contents of your state provider using Firebug (Ext.state.Manager.getProvider().state, then look for the key that matches the id of your grid), you'll see that there is now a record for 'start', which grabbed the correct value from the Grid's store.

All you need to do then is retrieve that value from the state provider and load your store accordingly:

store = new Ext.data.Store({... your store config ...});

grid = new Ext.grid.GridPanel({
id: 'unique_grid_id',
store: store,
... other grid config ...
});

//shorthand way of retrieving state information
var state = Ext.state.Manager.getProvider();

var start = state.get(options.id).start || 0);
store.load({params: {start: start, limit: 25}});

If the start value for this grid has never been set it'll default to zero - e.g. the first page. Next time you come back to this grid it'll take you right back to where you were, including all column setup and sorting behaviour you have specified.

Rails asset tag expansions

If you're using edge rails you may have noticed that you can now define your own JavaScript expansions (if you're not on edge this will be included in the imminent 2.1 release). The default expansion that comes with rails looks like this:

javascript_include_tag :defaults
Which grabs application.js as well as the prototype/scriptaculous javascripts and includes them all (only do that if you need it all - it adds ~150kb to your page). But say you've got a line which looks like this:

javascript_include_tag 'my_js_file', 'another_js_file', 'and_another'
And say you want to include the same set of files on a different page - it turns out Rails makes it really easy to DRY this up. Make a new file in the config/initializers directory (I call my asset_tag_expansions.rb) and add a line like the following (don't forget to restart your server afterwards):

ActionView::Helpers::AssetTagHelper.register_javascript_expansion :my_js=> ['my_js_file', 'another_js_file', 'and_another']
Now in your views you can simply put:

javascript_include_tag :my_js
You can of course register as many of these as you like, and include as many of your own expansions on the same javascript_include_tag line as you want, e.g.:

javascript_include_tag :my_js, :another_expansion, :and_another
The same applies to stylesheets:

ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :public_styles=> ['reset', 'layout', 'home']
stylesheet_link_tag :public_styles

Finally, although you're getting all that onto one line, each asset file is still being requested separately by your browser, each time making another nasty expensive HTTP request. Rectify that:

stylesheet_link_tag :public_styles, :cache => 'public'
javascript_include_tag :my_js, :another_expansion, :and_another, :cache => 'public'

This bundles up your three stylesheets and concatenates them into a single file, which is called public.css in this case. In the example above this means two less trips to the server to retrieve the stylesheet files, therefore a faster loading page. This is helpful because it enables you to maintain small, targeted stylesheets in development which makes finding the relevant CSS declarations easier, without the performance hit of all those HTTP requests when in production.

One final option is to use the :all expansion, which just grabs everything in the stylesheets or javascripts directory. Be careful with that though as you've got to be sure assets are being loaded in the right order (especially for JavaScript), and that you really need all that asset weight on each page.