Bloated RailsConf Presentation Downloader 2
I’ve updated my downloader from earlier to include all sorts of fancy options. It no longer requires wget, it just uses open-uri. It can give the files a fancy name. It can be told where to download the files to. It will skip files that won’t download for some reason. It will even butter your toast if you can find the correct command line switch.
It’s about 3 times bigger than the previous one. But maybe you can learn a little more about optparse, hpricot, file handling, and error handling along the way.
Here it is:
#!/usr/bin/env ruby
require 'optparse'
OPTIONS = { :Verbose => false,
:Force => false,
:DownloadDir => '.',
:DescriptiveFilenames => true
}
OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely, default #{OPTIONS[:Verbose]}") do |verbose|
OPTIONS[:Verbose] = verbose
end
opts.on("-f", "--[no-]force", "Force downloads, default #{OPTIONS[:Force]}") do |force|
OPTIONS[:Force] = force
end
opts.on("-d", "--[no-]descriptive", "Use long descriptive filenames, default #{OPTIONS[:DescriptiveFilenames]}") do |long|
OPTIONS[:DescriptiveFilenames] = long
end
opts.on("-p", "--path PATH", "Path to download to, default #{OPTIONS[:DownloadDir]}") do |path|
OPTIONS[:DownloadDir] = path
end
opts.on_tail("-h", "--help", "Print help message") do |help|
puts opts
exit
end
end.parse!
require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'fileutils'
BASE_URL = 'http://www.web2expo.com'
def log(str)
puts str if OPTIONS[:Verbose]
end
def download(href, filename)
url = "#{BASE_URL}#{URI.escape(href)}"
download_file = File.join(OPTIONS[:DownloadDir], filename)
if OPTIONS[:Force] || !File.exists?(download_file)
log "downloading #{File.basename(href)}..."
begin
File.open(download_file, 'w') { |f| f.write(open(url).read)}
log "\tsaved as #{download_file}"
rescue Object => e
FileUtils.rm(download_file)
$stderr.puts "ERROR downloading #{url}: #{e.message}"
end
else
log "skipping #{File.basename(href)}... already downloaded as #{download_file}"
end
end
FileUtils.mkdir_p(OPTIONS[:DownloadDir])
h = Hpricot(open("#{BASE_URL}/pub/w/51/presentations.html"))
h.search('div.presentation').each do |presentation_node|
href = presentation_node.at('a[@href^="/presentations/rails2007/"]')[:href]
if OPTIONS[:DescriptiveFilenames]
name = presentation_node.at('b a').inner_text.strip
text = presentation_node.inner_text
speaker = text[/Speaker\(s\):\s+(.*)\s*$/, 1]
date = Date.parse(text[/Presentation Date:\s+(.*)\s*$/, 1])
filename = [speaker, date, name, File.basename(href)].compact.map { |s| s.to_s.strip.gsub(/[^\w\.]/, '_').squeeze('_') }.join('-')
else
File.basename(href)
end
download(href, filename)
endDownload RailsConf 2007 Presentations 5
Updated: Now more bloated!
Run this to get the RailsConf 2007 presentations:#!/usr/bin/env ruby
require 'rubygems'
require 'hpricot'
require 'open-uri'
base = 'http://www.web2expo.com'
h = Hpricot(open("#{base}/pub/w/51/presentations.html"))
h.search('div .presentation > a[@href^="/presentations/rails2007/"]').each do |a|
url = "#{base}#{a[:href]}"
if File.exists?(File.basename(url))
puts "skipping #{url}... already downloaded"
else
puts "downloading #{url}..."
`wget --quiet #{url}`
end
end Will Marcel Molina Steal Matz's Ruby Super Powers 3
I didn’t start watching Heroes until after I heard Rich Kilmer and Marcel Molina talking about it while putting the badges together for RubyConf 2006 in Denver. Had I watched it before maybe I would have been a little scared of Marcel (Sylar), but he was very nice and didn’t seem like a threat to the Ruby world at all.
So, why be scared of Mr. Molina? You be the judge.
class MarcelMolina
include Heroes
def <=>(other_hero)
other_hero.name == 'Sylar' ? 0 : 1
end
end
==
# => true
RailsConf 2007 - Day 2 1
Note: Check out the RailsConf Wiki and the presentation page
Chad Fowler is starting the keynote introduction. 1600 developers are at the keynote. He makes a passionate speech about how Rails is changing how we think about software and how Rails impacted the industry. An now Rich Kilmer introduces the person that introduced him to Textmate: DHH.
A peek at Rails 2.0
Celebrating what we have
- > 1 million downloads
- Hundreds of plugins
- ~10k people on rubyonrails-talk
- He asks who is getting paid in one form or another using Rails and a large part the participants raise their hands. ( Books: many Rails books in many languages. Surpassed PHP, Perl and Python (source O’Reilly Radar).
- http://workingwithrails.com/ indicates over 100 countries where people do Rails.
- IDE: CodeGear, Textmate, NetBeans, JetBrains, Aptana
Rails 2.0
- It’s not gonna change everything you know, it’s gonna be humble.
- No new ‘great’ idea.
- The experiment worked! (RESTFul resource)
- The world of resources is a better, greener place.
- Rails 2.0 will focus more on the REST convention
- Routes
map.namespace(:admin) do |admin
admin.resources :products,
:collection => {:inventory => :get},
:member => {:duplicte => :post},
:has_many => [:tags, :images, :variants],
:has_one => :seller
end- David now demos the new scaffolding that use the resource approach of working with things.
./script/generate scaffold persone name:string create_at:time
* He adds new format format.csv to support exporting as comma separated text. You can easily connect to this services:require 'active_resource'
class Person < ActiveResource::Base
self.site = "http://localhost:3000"
end
p = Person.find 1- Beyond CRUD you can custom methods, Person.find :all, :params => {:name => ‘d’}
- This all works today.
- Action Web Service is no longer service with Rails 2.0. ActiveResource is.
- Friends and allies: Ajax, REST, Atom, OpenID
9 other things I like about rails 2:
1) Breakpoints are back (ruby_debug)
2) HTTP Performance
<!-- :cache => true
gzips all javascript and stylesheet in one file
i.e. prototype, css get zipped down to 35K from 200k
-->
<%= javascript_include_tag :all, :cache => true %>
<%= stylesheet_link_tag :all, :cache => true %> 3) Query cache. DHH loves free performance
4) action.mime_type.rendererpeople/index.html.erb
people/index.xml.builder
people/index.rss.erb
people/index.atom.builder
def index
respond_to do |format|
format.html
format.xml
format.rss
format.atom
end
end5) config/initializers. One file per configuration in initializers folder
6) Sexy migrations (just nicer)create_table :people do |t|
t.integer :account_id
t.string :first_name, :last_name
end8) The MIT assumption. ./script/generate plugin now generates a default MIT license file.
9) Spring cleaning. Deprecated features of 1.2 will be removed. In-place editor will be moved to plugins.
That’s pretty much it. Thank you.
Building Community Focused Apps with Rails by Dan Benjamin
Rails is the ideal platform for Web 2.0. Fast prototyping and proof of concept. Prototype becomes the product. Conducive to collaboration with developers and designers. Dan is going to talk about Cork’d. Currently 20000 users.
Make a Plan.
- Treat your application like a product and your idea like a business. Just because it’s a good idea doesn’t mean it’s gonna be automatically successful.
- Stay Agile. Resist big infrastructure.
- Build the Right Team. Keep it light.
- Determine Ownership.
- Have a Revenue Stream (ads don’t count)
- Focus on Simplicity. Don’t build features just because you think they are cool.
- Don’t Release a Public Beta. They had two private betas. They learned from a handful set of people rather than from a large group.
- Know Your Audience. Be your Audience
Build the App.
- Think Like a Designer.
- Consider the Fold.
- Avoid Big Migrations. User entered data can be challenging.
- Collaborate.
- The Rails Layouts Makes Designers Happy.
- Common Rails Collaboration Tools. Subversion, Capistrano, Campfire, Basecamp, Lighthouse.
- Don’t Repeat Yourself: Use Plugins. acts_as_authenticated, attachment_fu, acts_as_taggable, exception_notification, open_id_authentication, ymlr4r geocode
- Take “Code Vacations”
Get Noticed
- “It’s Google’s World, We Just Live in It.”
- Use Smart URLs. /wine/view/5748, /authors/danbenjamn
- Leverage Markup. Google reads metatags.
Recruit Members
- Make It Obvious and Easy to Signup
- Ask Only for What’s Truly Necessary
- Ask for Everything but Require (Almost) Nothing
- Limit Non-Members
Keep Them Coming Back for More
- Make Frequent Improvements
- Respond Positively to Your Members
- Create A Developer Network
- Share Your API
- Find Good Partners
- “If You Do Things Right, People Won’t Know You’ve Done Anything At All.”
- Just Ship It
How We Used Apollo and Rails to (start to) Build an Agile Project Management App by Christopher Haupt and Chris Balley
They are part of Adobe’s Consumer Hosted Applications and Online Services group. They use whatever technology is right for the job, Flex, Rails and dozens of other technologies. They use Scrum and are geographically dispersed teams, in Germany, India, US.
Why Did We Choose Apollo and Rails?
- Learning Exercise
- Offline Support
- Maturing Tools
- Leverage our Rails Experience
- Cross Platform
- Installers
Why not AJAX?
- Offline and Partially Connected
- File System Access
- Drag and Drop with Native OS
Now they are demoing Maptacular, an Apollo applications.
Combining Apollo and Rails: Communication
- Flash Side
- AMF
- SOAP
- REST <== they used that
- HTML Side
- AJAX techniques
- Hybrid
- Ue DOM bridge to use the one you are comfortable with.
Combining Apollo and Rails: Quirks
- Flex’isms
- RESET can be a pain
- Use _method hacks for PUT, DELETE, HEAD
- Pass a body on everything other than GET
- Some HTTP headers can’t be used, or problematic on a GET
- HTTP Status codes not accessible
- Use URLoader to get HTTP Status Codes
- dasherize-is-unhealthy-to-your-actionscript-code
- Migrating to newer Apollo Builds
- ApolloApplication .vs. Application (for transparency)
Now they move on to Code Snippets. The sample code can be found on the Apollo Labs.
- Online/Offline (Event.NETWORK_CHANGE). Notifies if network change but doesn’t tell if network is up or not. Solution: use URLoader to ping the network (they ping google.com)
- Video Capture, File IO, Doing your own Chrome
Chris now demonstrate some community create applications.
RailsConf 2007 - Day 1 3
Here we go, RailsConf 2007, has started. It’s bigger than ever, more tracks, more sessions. This is the first day where they provide full or half-day tutorials sessions. I will try to cover the different sessions I will attend so stay tuned.
Today I will attend: “Scaling a Rails Application from the Bottom Up.” and “Harnessing Capistrano.”
Scaling a Rails Application from the Bottom Up. by Jason Hoffman
Jason Hoffman, CTO of Joyent. Did also form Textdrive.
Six part presentation:
I. Introduction and foundation II. Where do I put stuff III. What stuff? IV. What do I run on this tuf? V. What are the patterns of deployment? VI. Lessons learned
His presentation will answer the following questions:
- What is a “scalalble” application?
- What are some hardware layout?
- Where do you get the hardware?
- How do you pay for it?
- Where do you put?
- Who runs it?
- How do you watch it?
- What do you need relative to an application?
- What are the commonalities of scalable web architectures?
- What are the unique bottlenecks for Ruby on Rails applications?
- What’s the best way to start so you make sure everything scales?
- what are to common mistakes?
Maybe it’s a little early, or I am not awake, but the talk seems a little slow. But Jason seems to do a good job at describing how the different people (developer, sysadmin, ...) see scalability.
Ease of management is on log scale. It’s not just a Rails issue. A $5000 Dell 1850 costs $1850 to power over 3 year.
This is a really good presentation from a point of view of what is involved to build data center. I should have read better the description of the presentation.
So I am going to move over to Thomas Fuchs presentation:
Is JavaScript Overrated? Or: How I Stopped Worrying and Put Prototype and script.aculo.us to Full Use by Thomas Fuchs
I am just tuning in to his presentation and he is showing of how to use selectors with Prototype.
DOM traversal:$('blech').previous('ul').down('.somesuch',2)
$('homo-sapiens').descendantOf('australopothecus')$('a_div').update('blah').show().setStyle({opacity:0.5});
$('myform').focusFirstElement();
$('person-example').serialize();
Element.addMethods('form') {
valid: function(element) {
// code to valid form
}
}- speedier $$
- CSS 3 Selectors
- $(‘form’).request()
- String .includes .times .toPaddedString(8,2)
- JSON support i.e. new Date().toJSON();
- $(‘blah’).firstDescendent()
- throw $continue deprecated use “return” instead
- Safari issues fixed
- YAML compatible
DOM, Events, Forms, Position:
- new Element(tagName, attributes);
- $(‘blech’).insert(html|object, position)
- $(‘blech’).wrap(‘span’);
- $(‘country’).setValue(‘AT’);
Function.prototype: curry(), wrap(), defer(), delay() Q: When is the next release of Prototype? A: When it’s ready.
A 10 minute break now, the Thomas is going to present Scriptaculous Effects.
script.aculo.us adds advanced User Interface interaction to the DOM. Extracted from Real-World applications. Started with Fluxiom. The two main parts are Visual effects and Drag&Drop. Today we will only look at the Visual Effects. They are other parts such as Autocompleter, In-Place Editor, Slider control, DOM Builder, and Unit testing. They won’t be more advanced components.
Effects engine: the ideas behind the engine is timeline based animations.
Core Effects:- Effect.Move
- Effect.Opacity
- Effect.Highlight
- Effect.ScrollTo
- Effect.Morph // 1.7+
- Effect.Parallel
Based on the Effect.Base.prototype class. Effect life cycle: intialize(), setup(), update(), finish(). Each frame calls update().
Effect.DefaultOptions = {
transition: Effect.Transitions.sinoidal,
duration: 1.0,
fps: 100,
sync: false,
from: 0.0,
to: 1.0,
delay: 0.0,
queue: 'parallel'
}Morphing: came out with Scriptaculous 1.7.
$('mydiv').morph('font-size:20px; color:"#abcdef");
$('mydiv').morph('warning'); //limited to top level classnamenew Effect.Blah('element_2')
new Effect.Blah('element_2', {duration:0.6, delay:0.3});new Effect.Blah('element_1', {queue:'end'});
new Effect.Blah('element_2', {queue:'front'});new Effect.Blah('element_1', {queue:{scope:'blech'}});
new Effect.Blah('element_2', {queue:{scope:'blech', position:'end'}});
new Effect.Blah('element_3', {queue:'front'});Element.toggle('element', 'blind');
Element.tagifyText(element);
Element.multiple('element', Effect.Fade, {speed:0.05});Do it yourself: Thomas now shows how to create an Effect programatically.
Future features:- Sound without Flash (it’s already in the beta release). Sound.paly(‘sword.mp3’). It uses native sound implementation with Quicktime as fallback.
- Adjust to new Prototype features. $(‘blech’).fade(); $(‘blech’).slowlyReveal();
Part IV: Testing
Thomas is flying through testing…- assert(true)
- assertEqual(expected, actual)
- assertEnumEqual(expected, actual)
- assertNotEqual(expected, actual)
- assertMatch
- assertIdentical
- assertNotIdentical
- assertType
- assertRaise
- assertRespondTo
- assertVisible(element)
- assertNotVisible(element)
- info(message)
Mostly unit testing, but some functional testing is available. Most assert take a message. I.e. assertXYZ(params, message)
- wait(milliseconds, method) // should be last statement in test, but can be nested.
- rake test:javascripts (browser will popup). Done with the javascript_test plugin. Launches the web server (WEBrick), then controls the browsers (Safari, Firefox, IE), the browser then calls the web server, and list the results (SUCCESS, FAILURE, and ERROR)
- www.prototypejs.org
- wiki.script.aculo.us/scriptaculous/
- Book: Prototype and script.aculo.us by Pragmatic Programmer
- Book: Prototype and Scriptaculous in Action by Manning
- Book: Ajax on Rails by O’Reilly
JRuby talk by Charles Nutter and Thomas Enebo
(note taken by Robert Hall, thanks man!)
- 1.8ish—based on Ruby 1.8.5
- Gems 0.9.1 is pre-installed
- Partially compiled—about 80% of Ruby code compiles…rest is run in JIT mode
- Ruby 1.8 strings supported. Works with ActiveSupport::MultiByte
- Ruby 2.0 String support coming
- Most Ruby apps should work on JRuby —most gems just work —Red Cloth, Blue Cloth —Hpricot
- Typical Rails commands just work
- ports: Mongrel ported, Hpricot ported, RMajick in progress
- Ruby thread API supported —native-threaded 1 JRuby thread=1 system thread —supports thread pooling
- performance comparable to C Ruby impl —Rdoc has issues —CLI performance slow
- JDBC support strong —mySQL support strong —some postgres issues —some Oracle users —many others
- ActiveRecord JDBC
- No native extension support
- Goldspike—JRuby deployment tool —Rails plugin for building WAR files —app server agnostic —Can be deployed to Java app server as WAR file
- Deployment—MOngrel supported..some issues (forking, process management doesn’t work)
- Access to Java EE features (JMS, JPA, JTA)
- Java libraries can be wrapped
- Coming soon —A Grizzly/GlassFish V3 option —Lightweight, gem-installable like Mongrel —Concurrency, pooling mulit-app like WAR
- Mephisto demo
- Main idea—- Ruby as the programming language, Java for the platform and libraries
- Best of all worlds
—Ruby or Rails as the appl layer
—Java libraries alone or as ported/wrapped gems
-Java based services-legacy app integration —The JVM - Acceptable to today’s enterprise —Java to ‘them’, Ruby to you
- Calling Java from Rails demo —RSS reader demo..calling Java library from Rails code
- Tools— Textmate, Emacs, Vi(m), notepad —missing some features —code competion? —jump to declaration? —rename variables? —popup Rdoc?
- Presenters claim Best Ruby IDE Available is NetBeans (milestone 9) —code completion —smart syntax highlightings (for Ruby code, RHTML files, etc) —Rdoc support
- Demoed a cool Ruby shell like IRB implemented in Swing..built into NetBeans
- Jruby 1.0 almost ready
LUNCH: will be back at 1:30pm
Harnessing Capistrano by Jamis Buck
Jamis will focus Capistrano 2.0 today. Some things will not be backward compatible with Capistrano 1.0.
His slides are online at http://presentations.jamisbuck.org/railsconf2007/.
Lee's RailsConfPlan at MyConfPlan
Following Daniel’s lead, here are the talks that I’m thinking of attending. So far I only looked at the titles of the talks, I’m sure I’ll do some switching once I look at them more in depth. Anyway, if you decide to look Daniel up while you’re there, you might want to look me up too, so I can translate his Swiss accent for you.
Chris, Sol and Nick... tag… your turn.
RailsConf 2007 - Here we come!
I just completed a first pass at setting my schedule for RailsConf. Check it out at MyConfPlan. In any case go check out MyConfPlan which is a pretty cool Rails application. As usual I am looking forward meeting many of the people I have met last year. Also if you are from Switzerland and doing Rails work, try to hunt me down, I would love to talk to you. It’s also fun that we are going to meet Kirk again, and “old” mate from Denver that is now leaving in Portland.
Forgot Password? 12
I did it again…forgot my password. Now if everyone could offer an openid login like Highrise. This time it happened on myconfplan, while I was organizing my schedule for next weeks RailsConf. As I didn’t find a link to reset the password on myconfplan, I send an email to their support. Dr Nic replied promptly and said he didn’t implement this yet on this wedsite, but he could manually reset the password. Well, recently I implemented that feature for MySpyder.net (one of our forthcoming web applications). So I send him some code snippets. Not sure if Dr Nic will use them, but maybe some of our blog readers may be interested, so here we go.
They are several ways to implement a “Forgot Password? This time we choose to send out a “reset password” link that is valid for 24 hours. This link lets the user login, bypassing the standard login, and showing the change password screen.
First lets add a migration.
class ForgotPassword < ActiveRecord::Migration
def self.up
add_column :users, :reset_password_code, :string
add_column :users, :reset_password_code_until, :datetime
end
def self.down
remove_column :users, :reset_password_code
remove_column :users, :reset_password_code_until
end
end“Forgot password” form.
Then add a “forgot password” form, allowing the user to submit the email to which the “reset password” link will be emailed. When the form is submitted, the controller creates a ‘reset password code’ that is valid for one day, and sends an email to the user.
def forgot_password
user = User.find_by_email(params[:email])
if (user)
user.reset_password_code_until = 1.day.from_now
user.reset_password_code = Digest::SHA1.hexdigest( "#{user.email}#{Time.now.to_s.split(//).sort_by {rand}.join}" )
user.save!
UserNotifier.deliver_forgot_password(user)
render :xml => "<errors><info>Reset Password link emailed to #{user.email}.</info></errors>"
else
render :xml => "<errors><error>User not found: #{params[:email]}</error></errors>"
end
endSend email with the ‘reset password’ link.
When the user receives the “reset password” email and clicks the link to reset the password, the reset_password method is invoked on the controller. The user associated with the “reset_code” is found, and if the the reset_code is not yet expired the user is automatically logged-in and redirected to the account page where he can change his password. Note that by adding an expiration attribute for the code, we don’t need to run a cleanup batch process to invalidate these codes. Not in the following code we redirect to a ”.swf” file. This was an early experiment where the user interface of the application was written in Flex. We are currently rewriting it to use a more traditional html and css approach.
def reset_password
user = User.find_by_reset_password_code(params[:reset_code])
self.current_user = user if user && user.reset_password_code_until && Time.now < user.reset_password_code_until
redirect_to logged_in? ? "/MySpyder.swf?a=account" : "/MySpyder.swf?a=login"
endThe email is simply send using the following ActionMailer.
class UserNotifier < ActionMailer::Base
def forgot_password(user)
setup_email(user)
@subject += 'MySpyder.net - Reset Password'
@body[:url] = "http://myspyder.net/reset_password/#{user.reset_password_code}"
end
protected
def setup_email(user)
@recipients = "#{user.email}"
@from = "admin@myspyder.net"
@subject = "[myspyder.net] "
@sent_on = Time.now
@body[:user] = user
end
endAnd the view for the UserNotifier is the following
<%= @user.email %>,
You can reset your password by using the following link <%= @url %>
Thank your for using MySpyder.netTextMate filetype detection for script/runner Rails scripts
So you’re building some righteous automation for your killer web 2.0 app, placing scripts in RAILS_ROOT/script that you can call from cron for nightly maintenance, etc. To bootstrap your rails environment, you decide to use the shebang feature of script/runner, available since changeset 5189. When you start to edit the script in TextMate (you are using TextMate, aren’t you?) there is no syntax highlighting to be found! It’s all plain text with no colors, and none of your ever-so-helpful keyboard macros work! Frightful. Well, take a deep breath, because together, we’re going to get the filetype detection magic working for you.
Before we get started, it’s helpful to know how filetype detection works. TextMate does a couple of different types of filetype detection—the first is based off of the extension, so if you named your script with a .rb extension, you are probably wondering what in the world I’m rambling about. Dude. It just works.
However, if you followed the rails convention for scripts, and did not use an extension with your filename, keep reading. The second type of detection works by scanning the so called “shebang” line at the top of the script which tells the shell (and in this case TextMate) which interpreter to use to evaluate your script—this is how we will tell TextMate that script/runner really means ruby.
First of all, you’ll need to fire up the Bundle Editor and select “Languages” from the drop-down filter. Expand the “Rails” node, and then select the “Ruby on Rails” language. On the right side, you should see the definition being used by TextMate to detect the Ruby on Rails scope. If you have not modified your bundle, you’ll probably see that it is using a fileTypes to look for .rxml files. This is where we want to insert the following line:
firstLineMatch = '^#!.*(script/runner)';
Here’s a screenshot of what it should look like when you are done:

Now go back to your script and enjoy all the colorized, scope-aware editing goodness that is TextMate!
Mapping Rails Errors to Flex Fields. 7

We extended the com.wheelerstreet.utils.ValidatorForm to add support for Rails Errors. Saving a form is a two step process. First, client side validation, the Signup button only gets enabled if the form is valid from a client side point of view. Step 2, server side validation, the user press the signup button and invokes the Rails UserController#create method
class UsersController < ApplicationController
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
self.current_user = @user
format.xml { head :created }
else
format.xml { render :xml => @user.errors.to_xml(:dasherize => false) } end
end
end
endIf saving the user fails then Rails return the xml version of the errors:
@user.errors.to_xml(:dasherize => false)Now we need a generic way to deal with these errors. We created the Flex RailsErrors class to manage the returned xml. And we created the RailsValidationForm that extends the com.wheelerstreet.utils.ValidatorForm. The RailsValidationForm class can be bound to a RailsErrors instance. So the result handler of the Flex Cairngorm Flex SignupCommand we just set the errors on the model locatorL
var errors:RailsErrors = new RailsErrors(data.result as XML);
MySpyderModelLocator.getInstance().signupErrors = errors;The signup.mxml page contains the following signup form
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:rails="org.onrails.rails.*"
>
<mx:Panel x="162" y="64" title="Signup - Your Account Details.">
<!-- Instance of org.onrails.rails.RailsValidationForm -->
<rails:RailsValidationForm
id="submitForm"
defaultButton="{signupButton}"
validators="{validators}"
fieldMap="{{Email:email, Password:password}}"
railsErrors="{MySpyderModelLocator.getInstance().signupErrors}"
x="98" y="89">
<mx:FormItem label="email">
<mx:TextInput id="email" />
</mx:FormItem>
<mx:FormItem label="Password">
<mx:TextInput id="password" displayAsPassword="true" />
</mx:FormItem>
<mx:FormItem label="Confirmation">
<mx:TextInput id="passwordConfirmation" displayAsPassword="true" />
</mx:FormItem>
<mx:Button id="signupButton" label="Signup >>" click="signup()" enabled="{MySpyderModelLocator.getInstance().signupButtonEnabled}"/>
</rails:RailsValidationForm>
</mx:Panel>
<rails:RailsErrorBox x="487" y="64" width="301" height="178"
errorMessage="errors prohibited this new account to be created"
errors="{MySpyderModelLocator.getInstance().signupErrors}"
visible="{MySpyderModelLocator.getInstance().signupErrors && MySpyderModelLocator.getInstance().signupErrors.hasErrors()}"
/>
</mx:Canvas>The RailsErrorBox just displays the text of all error messages and is only visible if there are any Rails errors.
Now all the magic happens in the org.onrails.rails.RailsValidationForm railsErrors setter.
public function set railsErrors(errors:RailsErrors):void {
_railsErrors = errors;
if (_railsErrors==null || !_railsErrors.hasErrors()) {
resetErrors();
} else {
for each (var field:String in _railsErrors.fields) {
if (_fieldMap[field])
_fieldMap[field].errorString = field + ' ' + _railsErrors.getFieldErrors(field).join(', ');
}
}
}The key is to associate the Rails error message with the field is simply to set the errorString on the field.
I just created this code this morning at breakfast so it’s really a work in progress. For instance it doesn’t support Rails attributes that are more than one word. But this goes hand in hand with the ActiveResourceClient and can be useful I hope to others trying to integrate Rails and Flex. So we will create a Google project and post the RailsErrors and RailsValidationForm.
