Monday, July 16, 2007

Unix File Permissions

A useful tidbit that I just discovered: if you want to set unix file permissions for the group to match the owner it's as simple as:

chmod g=u filename

This just saved me boatloads of time.

Discovered at: comp.unix.questions

-Bill

Thursday, May 17, 2007

Harnessing Capistrano

Harnessing Capistrano - RailsConf2007 Tutorial
Jamis Buck

Slide aand demos available here.

Started out as a Deployment tool

Can also use it for:
+ monitoring tool across all servers (ps, df, uname, etc.)
+ server maintenance (mounts, symlinks, ...)
+ troubleshooting

A basic config file and a demo.

A config using a gateway and a demo.

A cool description and demo of 'cap -e shell' which creates and caches a connection to each deployment server. Can be scoped by role or host.

Cap 2.0 adds namespaces for tasks. Sort of a way of grouping like tasks. (eg. cap tail:showuploadlog groups the showuploadlog task into the tail namespace.

Can do variables.

And transactions, so you can make sure tasks complete on all servers or it rolls them all back - no inbetween. No good way to recover if a rollback fails.

All sort of options for including other cap files. This was always optional in Cap 1.x, but there is no default config in Cap2.0.

You can also script-check dependencies (cap deploy:check). That looks damn useful.

scrpit/spin tell cap how to start your application Many times i's script/process/spawner

cap deploy:cold <- first time deploy

cap deploy:rollback
cap deploy:migrations
cap deploy:web:disable
cap deploy:web:enable
cap deploy:cleanup <- clean up all but the last 5 releases

In addion to standard a version control-based checkout, you can do other types: export, copy, remote cache, or you can roll your own. (Set by :deploy_via option.)

There are all sorts of nifty options to get Cap to work with all sorts of version control systems.

Lots of helpers: run, sudo, stream, connect, find_task, etc.

In the ways of advanced usage, there are:
+ before_ and after_ events (before_deploy: run_tests)
+ custom callbacks: (email notifications, etc.) with complex rules
+ staging environments: you can script deploys to behave differently based on target

JRuby on Rails Tutorial

JRuby on Rails - RailsConf 2007 Tutorial

Advantages:
Take advantages of existing Java libraries
Run on Java infrastructure
Supports Java's multibyte/unicode strings.
Support Ruby's thread API, one native thread = one system thread, good multicore use.
True concurrent threading.

Great progress in the past year. From barely running rails 1.1 last year to running all of rails (except some lowlow demand dark corners) now, with good performance on everything but RDoc.

Most Gems just work. Anything pure Ruby (or with a JRuby port) runs.
Webrick works.
JRuby port of Mongrel works.

Differences:
1-Database support
Pure Ruby drivers work - mySQL
All the JDBC drivers you'd want to use work. (Yay!) (Some need custom coding to support migrations.
JDNI for connection pooling (More yay!)

2-No Native Extensions
Unless there's a port.
Mongrel - done
Hpricot - done
Database support - some done, some in progress
RMagick - in progress

3-Command-line Performance
Very good (possibly faster) once you're running, but typical java-slow startup performance.

Deployment
1. Mongrel works well. No process forking, process management
But why? Use Java App Servers via GoldSpike (or rails-integration) plugin.
2. Build WAR files for Rails apps.
One plugin, pure Ruby, out comes a deployable WAR file.
3. Glassfish server gem. (Sort of a "Pack" in the box implementation.)
Not yet. But soon.

Migrating existing Rails apps to JRuby/Rails
Be aware of the currently unsupported features.
1. Database support
- MySQL is great
- Derby & HSQL work well. Small embedable DB.
- Postgres - Few out of 1000+ tests.
- Oracle - starting to get attention
- MS SQL Server & DB - Need help, haven't really been worked on much.
Migrations mostly work well, tricky on some DBs that don't have all features.
Fixtures work well, parts of rails test. Issues generally YAML rather than DB issues.
2. Native Extentions
Option 1 - Use something else, aka don't do it.
Option 2 - Use an equivalent Java library. (binds you to jruby)
Option 3 - Port the library yourself.
Option 4 - Port by wrapping a java library.
3. Deployment Options
- Mongrel: works, but not the most efficient
- Existing WebApp Server: good concurrency, clustering, resource pooling
- Grizzly/GlassFish v3 option: lightweight, gem mongrel-like install

Monday, January 22, 2007

Cascading Drop-downs in Rails

We were looking for a solid example of cascading dynamic drop-down select lists to use in our rails application, and found the web sorely lacking in solid examples. We found a very good start at http://www.railsweenie.com/forums/2/topics/767 but it wasn't complete enough, or didn't work entirely. So my very good buddy and co-worker Sheri and I figured this out, and got it working for our app, and wanted to document it here in the hopes that it helps someone else. Here's the nutshell version:

It's probably already there, but make sure this line is in your standard_layout.rhtml:

<%= javascript_include_tag :defaults %>

Add this function def to application_helper.rb:

def update_select_box( target_dom_id, collection, options={} )
# Set the default options
options[:text] ||= 'name'
options[:value] ||= 'id'
options[:include_blank] ||= true
options[:clear] ||= []
pre = options[:include_blank] ? [['','']] : []
out = "update_select_options( $('" << onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_5">dom_id.to_s << "'),"
out << "#{(pre + collection.collect{ |c| [c.send(options[:text]), c.send(options[:value])]}).to_json}" << ","
out << "#{options[:clear].to_json} )"
end

This calls update_select_options which needs to go into application.js:

function update_select_options( target, opts_array, clear_select_list ) {

if( $(target).type.match("select" ) ){ // Confirm the target is a select box

// Remove existing options from the target and the clear_select_list
clear_select_list[clear_select_list.length] = target // Include the target in the clear list

for( k=0;k <>
obj = $(clear_select_list[k]);
if( obj.type.match("select") ){
len = obj.childNodes.length;
for( var i=0;i <>
}
}

// Populate the new options
for(i=0;i <>
o = document.createElement( "option" );
o.appendChild( document.createTextNode( opts_array[i][0] ) );
o.setAttribute( "value", opts_array[i][1] );
obj.appendChild(o);
}
}
}

Add something like this to the form.rhtml (changing the name of the observable field as appropriate):

<%= observe_field 'item[facility_id]', :frequency => 0.5,
:update => 'location_id', :url =>
{ :controller => 'item', :action=> 'refreshLocation' },
:with => "'facility_id=' + escape(value)" %>

Add something like this to the controller:

def refreshLocation
@facilities = Facility.find(:all)
@facility = Facility.find(params[:facility_id])
@locations = Location.find_all_by_facility_id(params[:facility_id])
render :update do |page|
page << text =""> :description} )
end
end


This tidbit in the form.rhtml is the ultimate target of all this work (this is the drop down we want to refresh)






<%= select_tag "item[location_id]", options_from_collection_for_select(@locations,:id,:description) %>



If I missed any code attributions from the various sources we pieced this together from, I'm sorry. Write me and I'll make good and give attribution where appropriate.

If you have any questions about this, post 'em and I'll take my best shot at answering.

And finally, thanks Sheri! Couldn't have done it without you!