http://github.com/ruboss/ruboss4ruby/wikis/getting-started-with-ruboss-and-ruby-on-rails
Introduction
In this tutorial we will build a new Rails application from scratch using the Ruboss Framework. Our application should look like this:
Assumptions
The following steps have been tried with Flex Builder 3 (and command line tools) on Mac OS X.
You should have Ruby on Rails 2.1 (or higher) and MySQL 5.0 or higer installed for the Ruby on Rails portion of this tutorial and Flex Builder 3 if you intend to edit your Flex code using it.
1. Make sure you have added Flex SDK bin directory to your $PATH variable so that you can invoke mxmlc from the command line) if necessary.
Developing a Flex application that talks to Ruby on Rails with Ruboss Framework
This application will be called pomodo. First, we’ll create the standard RubyOnRails application.
$>rails -d mysql pomodo
$>cd pomodo
Next, we will install the ruboss4ruby gem:
$>sudo gem install ruboss4ruby
Now, we need to go and set-up our application to use the ruboss4ruby gem we just installed.
Edit config/environment.rb and add:
config.gem "ruboss4ruby"
Next, we run the ruboss_config generator to create the appropriate Flex directories and create default ruboss configuration:
$>./script/generate ruboss_config
Next, we proceed to scaffold our application.
Using YAML to scaffold your Flex + Rails application
Scaffolding is a cool way to get started with a Rails (and now Flex) applications quickly. Unfortunately, things tend to become quite cumbersome once you get beyond 5 or so models. You have to individually run scaffolding for each model and then go and edit relationships in both rails and flex code. This approach just doesn’t scale. You might tolerate doing all this manual work for a few models, but what are you going to do when you have to run pretty much the same command 30 or 40 times and then remember how all these things relate to each other? Wouldn’t it be better if you could specify the bulk of your data model in some easy to read file and just run that once?
This is basically the intuition behind a generator that comes with ruboss4ruby gem. It’s called ruboss_yaml_scaffold.
Now comes the cool stuff. Let’s create a file called db/model.yml that contains the following:
project:
- name: string
- notes: text
- start_date: date
- end_date: date
- completed: boolean
- belongs_to: [user]
- has_many: [tasks]
location:
- name: string
- notes: text
- belongs_to: [user]
- has_many: [tasks]
task:
- name: string
- notes: text
- start_time: datetime
- end_time: datetime
- completed: boolean
- next_action: boolean
- belongs_to: [project, location, user]
note:
- content: text
- belongs_to: [user]
user:
- login: string
- first_name: string
- last_name: string
- email: string
- has_many: [tasks, projects, locations]
- has_one: [note]
This should be fairly self explanatory except for a few details you might not have seen in YAML documents before.
1. You can specify most of the aspects of the models (including relationships) directly in the YAML file:
1. Use belongs_to: [<references here>] notation (e.g. belongs_to: [user]) to refer to the belongs_to end of the relationship.
2. has_one: following by an array of model names to denote has_one end of relationship (e.g. has_one: [note])
3. has_many: works the same way has_one and belongs_to do.
2. - in front of every attribute line preserves the exact order of elements in generated code. Make sure you add it!
That’s pretty much all there is to it. If your db/model.yml file contains the text above, you can run:
$>./script/generate ruboss_yaml_scaffold
And watch scaffolding fly by on the console.
Check out the Flex and Rails code after you run the command. It should have all the fields and relationships set up. This means no more extra manual labour to get your application into a runnable state. Just load some data by running:
$>rake db:refresh
If you have added Flex SDK bin directory to your $PATH variable run:
$>rake ruboss:flex:build
This will compile your new Flex application and move generated .swf file into the public/bin directory.
If you DON’T have mxmlc executable accessible from the command line you’ll have to open this project in Flex Builder and compile it.
Next, start the server by running:
$>./script/server
And point your browser at http://localhost:3000. It’s not going to be the greatest Flex application ever written but for a 5 minute scaffolding job it’s definitely not bad.
Porting Pomodo to AIR
ruboss_config generator you’ve just seen above actually takes an optional argument, which is quite handy for converting our Flex project into an AIR project. Make sure you say Y when prompted to overwrite .actionScriptProperties, .project and Pomodo.mxml files:
$>./script/generate ruboss_config -a
If you don’t want to bother with opening Flex Builder you can compile and run your new AIR application by doing this:
$>rake ruboss:air:build
$>rake ruboss:air:run
If you are running Flex Builder make sure you do the following:
1. It is recommended that you shutdown your Flex Builder before running the generator above. This generator will change a few Flex Builder specific files (such as .actionScriptProperties and .project) to include AIR specific information. This is how Flex Builder itself knows that it’s dealing with an AIR project as opposed to a Flex one. It’s not a very good idea to be changing Flex Builder specific files while it’s running.
2. It’s also a good idea to remove the Pomodo Run definition from Flex Builder (if you have it). To do that open Run Dialog... and delete Pomodo definition. Why is this a good idea? Well, we’ve previously run our application in Flex Builder as a Flex application. Flex Builder has saved that definition in its cache and is now convinced Pomodo is a Flex application. Converting this project to an AIR project makes the stuff Flex Builder has in its cache out of date. Our application is now going to be an AIR app. As far as Flex Builder is concerned these are not the same thing and they are run differently.
OK, let’s open Flex Builder again. With Flex Builder pacified we can now get back to somewhat more intersting stuff. The generator command above will also convert your main application file to something like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:components="pomodo.components.generated.*"
layout="vertical"
styleName="plain"
initialize="init()">
<mx:Script>
<![CDATA[
import org.ruboss.services.AIRServiceProvider;
import pomodo.controllers.ApplicationController;
private function init():void {
ApplicationController.initialize([AIRServiceProvider], AIRServiceProvider.ID, "pomodo");
}
]]>
</mx:Script>
<mx:TabNavigator width="100%" height="100%">
<!-- For a simple demo, put all the components here. -->
<generated:ProjectBox/>
<generated:TaskBox/>
</mx:TabNavigator>
</mx:WindowedApplication>
Basically the only thing the generator changed in the code is the way our main application controller is initialized during application start-up.
OK, you are all done, hit the run button and you should see the same application now running in AIR. It will have no data of course because there is no fixtures for AIR. So go ahead and create a few tasks and projects. They are now being saved to your local AIR database called “pomodo”.
To recap: converting between a Flex application that talks to Rails server using XML-over-HTTP into an AIR application that’s using local SQL database is just a matter of running one command. A command that only changes the way our application is initialized, NOT any of the code used to actually manipulate projects and tasks.
Getting Pomodo AIR application to talk to Ruby on Rails again!
One nice thing about having much of the plumbing abstracted away is that you can now have your brand new AIR application talking to the Rails server again.
If you simply call ApplicationController.initialize() with no arguments then XML-over-HTTP is going to be the default service provider. So let’s change our Pomodo.mxml code to this:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:components="pomodo.components.generated*"
layout="vertical"
styleName="plain"
initialize="init()">
<mx:Script>
<![CDATA[
import org.ruboss.Ruboss;
import pomodo.controllers.ApplicationController;
private function init():void {
Ruboss.httpRootUrl = "http://localhost:3000/";
ApplicationController.initialize();
}
]]>
</mx:Script>
<mx:TabNavigator width="100%" height="100%">
<!-- For a simple demo, put all the components here. -->
<generated:ProjectBox/>
<generated:TaskBox/>
</mx:TabNavigator>
</mx:WindowedApplication>
As you might have guessed this will tell the app to stop using local AIR database and start talking to the remote Rails server again. Remember to start your Rails server using “script/server” before running this app.
Finally, to convert our AIR application back into the Flex application it used to be run rconfig generator with no arguments again:
$>./script/generate ruboss_config
Again, remember to delete the Pomodo Run target and shutdown Flex Builder before you do that.
Check your console or development.log if you have any problems
Here’s what you should have been seeing if all went well:
[ttys000][Stingray]$ rails -d mysql pomodo
create
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create config/initializers
create db
create doc
create lib
create lib/tasks
create log
create public/images
create public/javascripts
create public/stylesheets
create script/performance
create script/process
create test/fixtures
create test/functional
create test/integration
create test/unit
create vendor
create vendor/plugins
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create Rakefile
create README
create app/controllers/application.rb
create app/helpers/application_helper.rb
create test/test_helper.rb
create config/database.yml
create config/routes.rb
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/new_rails_defaults.rb
create config/boot.rb
create config/environment.rb
create config/environments/production.rb
create config/environments/development.rb
create config/environments/test.rb
create script/about
create script/console
create script/dbconsole
create script/destroy
create script/generate
create script/performance/benchmarker
create script/performance/profiler
create script/performance/request
create script/process/reaper
create script/process/spawner
create script/process/inspector
create script/runner
create script/server
create script/plugin
create public/dispatch.rb
create public/dispatch.cgi
create public/dispatch.fcgi
create public/404.html
create public/422.html
create public/500.html
create public/index.html
create public/favicon.ico
create public/robots.txt
create public/images/rails.png
create public/javascripts/prototype.js
create public/javascripts/effects.js
create public/javascripts/dragdrop.js
create public/javascripts/controls.js
create public/javascripts/application.js
create doc/README_FOR_APP
create log/server.log
create log/production.log
create log/development.log
create log/test.log
[ttys000][Stingray]$ cd pomodo/
[ttys000][Stingray]$ ls
README app db lib public test vendor
Rakefile config doc log script tmp
[ttys000][Stingray]$ vi config/environment.rb
[ttys000][Stingray]$ ./script/generate ruboss_config
fetching 1.0.5 framework binary from: http://ruboss.com/releases/ruboss-1.0.5.swc ...
done. saved to lib/ruboss-1.0.5.swc
create lib/tasks/ruboss_tasks.rake
create .flexProperties
create config/ruboss.yml
create .actionScriptProperties
create .project
create html-template/history
create html-template/index.template.html
create html-template/AC_OETags.js
create html-template/playerProductInstall.swf
create html-template/history/history.css
create html-template/history/history.js
create html-template/history/historyFrame.html
create app/flex/pomodo/components
create app/flex/pomodo/controllers
create app/flex/pomodo/commands
create app/flex/pomodo/models
create app/flex/pomodo/events
create app/flex/pomodo/components/generated
create public/javascripts/swfobject.js
create public/expressInstall.swf
overwrite public/index.html? (enter "h" for help) [Ynaqdh] Y
force public/index.html
dependency ruboss_controller
create app/flex/pomodo/controllers/ApplicationController.as
create pomodo.tmproj
create app/flex/Pomodo.mxml
create app/flex/Pomodo-config.xml
[ttys000][Stingray]$ mate .
[ttys000][Stingray]$ ./script/generate ruboss_yaml_scaffold
running: ruboss_scaffold User
dependency scaffold
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/users
exists app/views/layouts/
exists test/functional/
exists test/unit/
exists public/stylesheets/
create app/views/users/index.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/edit.html.erb
create app/views/layouts/users.html.erb
create public/stylesheets/scaffold.css
create app/controllers/users_controller.rb
create test/functional/users_controller_test.rb
create app/helpers/users_helper.rb
route map.resources :users
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
create app/flex/pomodo/models/User.as
create app/flex/pomodo/components/generated/UserBox.mxml
force app/controllers/users_controller.rb
force app/models/user.rb
force test/fixtures/users.yml
create schema/migration
create db/migrate
create db/migrate/20081119102430_create_users.rb
dependency ruboss_controller
force app/flex/pomodo/controllers/ApplicationController.as
done ...
running: ruboss_scaffold Project
dependency scaffold
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/projects
exists app/views/layouts/
exists test/functional/
exists test/unit/
exists public/stylesheets/
create app/views/projects/index.html.erb
create app/views/projects/show.html.erb
create app/views/projects/new.html.erb
create app/views/projects/edit.html.erb
create app/views/layouts/projects.html.erb
identical public/stylesheets/scaffold.css
create app/controllers/projects_controller.rb
create test/functional/projects_controller_test.rb
create app/helpers/projects_helper.rb
route map.resources :projects
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/project.rb
create test/unit/project_test.rb
create test/fixtures/projects.yml
create app/flex/pomodo/models/Project.as
create app/flex/pomodo/components/generated/ProjectBox.mxml
force app/controllers/projects_controller.rb
force app/models/project.rb
force test/fixtures/projects.yml
exists schema/migration
exists db/migrate
create db/migrate/20081119102431_create_projects.rb
dependency ruboss_controller
force app/flex/pomodo/controllers/ApplicationController.as
done ...
running: ruboss_scaffold Task
dependency scaffold
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/tasks
exists app/views/layouts/
exists test/functional/
exists test/unit/
exists public/stylesheets/
create app/views/tasks/index.html.erb
create app/views/tasks/show.html.erb
create app/views/tasks/new.html.erb
create app/views/tasks/edit.html.erb
create app/views/layouts/tasks.html.erb
identical public/stylesheets/scaffold.css
create app/controllers/tasks_controller.rb
create test/functional/tasks_controller_test.rb
create app/helpers/tasks_helper.rb
route map.resources :tasks
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/task.rb
create test/unit/task_test.rb
create test/fixtures/tasks.yml
create app/flex/pomodo/models/Task.as
create app/flex/pomodo/components/generated/TaskBox.mxml
force app/controllers/tasks_controller.rb
force app/models/task.rb
force test/fixtures/tasks.yml
exists schema/migration
exists db/migrate
create db/migrate/20081119102432_create_tasks.rb
dependency ruboss_controller
force app/flex/pomodo/controllers/ApplicationController.as
done ...
running: ruboss_scaffold Note
dependency scaffold
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/notes
exists app/views/layouts/
exists test/functional/
exists test/unit/
exists public/stylesheets/
create app/views/notes/index.html.erb
create app/views/notes/show.html.erb
create app/views/notes/new.html.erb
create app/views/notes/edit.html.erb
create app/views/layouts/notes.html.erb
identical public/stylesheets/scaffold.css
create app/controllers/notes_controller.rb
create test/functional/notes_controller_test.rb
create app/helpers/notes_helper.rb
route map.resources :notes
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/note.rb
create test/unit/note_test.rb
create test/fixtures/notes.yml
create app/flex/pomodo/models/Note.as
create app/flex/pomodo/components/generated/NoteBox.mxml
force app/controllers/notes_controller.rb
force app/models/note.rb
force test/fixtures/notes.yml
exists schema/migration
exists db/migrate
create db/migrate/20081119102433_create_notes.rb
dependency ruboss_controller
force app/flex/pomodo/controllers/ApplicationController.as
done ...
running: ruboss_scaffold Location
dependency scaffold
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/locations
exists app/views/layouts/
exists test/functional/
exists test/unit/
exists public/stylesheets/
create app/views/locations/index.html.erb
create app/views/locations/show.html.erb
create app/views/locations/new.html.erb
create app/views/locations/edit.html.erb
create app/views/layouts/locations.html.erb
identical public/stylesheets/scaffold.css
create app/controllers/locations_controller.rb
create test/functional/locations_controller_test.rb
create app/helpers/locations_helper.rb
route map.resources :locations
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/location.rb
create test/unit/location_test.rb
create test/fixtures/locations.yml
create app/flex/pomodo/models/Location.as
create app/flex/pomodo/components/generated/LocationBox.mxml
force app/controllers/locations_controller.rb
force app/models/location.rb
force test/fixtures/locations.yml
exists schema/migration
exists db/migrate
create db/migrate/20081119102434_create_locations.rb
dependency ruboss_controller
force app/flex/pomodo/controllers/ApplicationController.as
done ...
identical pomodo.tmproj
overwrite app/flex/Pomodo.mxml? (enter "h" for help) [Ynaqdh] Y
force app/flex/Pomodo.mxml
identical app/flex/Pomodo-config.xml
[ttys000][Stingray]$ rake db:refresh
(in /Users/Dima/Projects/experiments/pomodo)
== 20081119102430 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0034s
== 20081119102430 CreateUsers: migrated (0.0037s) =============================
== 20081119102431 CreateProjects: migrating ===================================
-- create_table(:projects)
-> 0.0045s
== 20081119102431 CreateProjects: migrated (0.0050s) ==========================
== 20081119102432 CreateTasks: migrating ======================================
-- create_table(:tasks)
-> 0.0044s
== 20081119102432 CreateTasks: migrated (0.0046s) =============================
== 20081119102433 CreateNotes: migrating ======================================
-- create_table(:notes)
-> 0.0037s
== 20081119102433 CreateNotes: migrated (0.0041s) =============================
== 20081119102434 CreateLocations: migrating ==================================
-- create_table(:locations)
-> 0.0038s
== 20081119102434 CreateLocations: migrated (0.0041s) =========================
[ttys000][Stingray]$ rake ruboss:flex:build
(in /Users/Dima/Projects/experiments/pomodo)
Compiling /Users/Dima/Projects/experiments/pomodo/app/flex/Pomodo.mxml
Loading configuration file /Applications/Adobe Flex Builder 3/sdks/3.0.0/frameworks/flex-config.xml
Loading configuration file /Users/Dima/Projects/experiments/pomodo/app/flex/Pomodo-config.xml
/Users/Dima/Projects/experiments/pomodo/app/flex/Pomodo.swf (378010 bytes)
Moving /Users/Dima/Projects/experiments/pomodo/app/flex/Pomodo.swf to
/Users/Dima/Projects/experiments/pomodo/public/bin
Done!
[ttys000][Stingray]$ ./script/server
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails 2.1.2 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart).
** Rails signals registered. HUP => reload (without restart). It might not work well.
** Mongrel 1.1.5 available at 0.0.0.0:3000
** Use CTRL-C to stop.
Processing LocationsController#index (for 127.0.0.1 at 2008-11-19 12:25:45) [GET]
Session ID: 190716e0211d29be9dcd36c51d322a0e
Parameters: {"format"=>"fxml", "action"=>"index", "controller"=>"locations"}
SQL (0.000226) SET NAMES 'utf8'
SQL (0.000078) SET SQL_AUTO_IS_NULL=0
Location Load (0.000365) SELECT * FROM `locations`
Location Columns (0.001900) SHOW FIELDS FROM `locations`
Completed in 0.12109 (8 reqs/sec) | DB: 0.00257 (2%) | 200 OK [http://localhost/locations.fxml]
Processing NotesController#index (for 127.0.0.1 at 2008-11-19 12:25:45) [GET]
Session ID: 5a883bd95d84ab8890a05029203f5294
Parameters: {"format"=>"fxml", "action"=>"index", "controller"=>"notes"}
Note Load (0.000300) SELECT * FROM `notes`
Note Columns (0.001426) SHOW FIELDS FROM `notes`
Completed in 0.01247 (80 reqs/sec) | DB: 0.00173 (13%) | 200 OK [http://localhost/notes.fxml]
Processing TasksController#index (for 127.0.0.1 at 2008-11-19 12:25:45) [GET]
Session ID: 87dd200501748fddcaf98cfffbb0e654
Parameters: {"format"=>"fxml", "action"=>"index", "controller"=>"tasks"}
Task Load (0.000325) SELECT * FROM `tasks`
Task Columns (0.001808) SHOW FIELDS FROM `tasks`
Completed in 0.01924 (51 reqs/sec) | DB: 0.00213 (11%) | 200 OK [http://localhost/tasks.fxml]
Processing UsersController#index (for 127.0.0.1 at 2008-11-19 12:25:45) [GET]
Session ID: 6ea5349eab5dea59b8fcdff5cf34e3dc
Parameters: {"format"=>"fxml", "action"=>"index", "controller"=>"users"}
User Load (0.000304) SELECT * FROM `users`
User Columns (0.002440) SHOW FIELDS FROM `users`
Completed in 0.01688 (59 reqs/sec) | DB: 0.00274 (16%) | 200 OK [http://localhost/users.fxml]
Processing ProjectsController#index (for 127.0.0.1 at 2008-11-19 12:25:45) [GET]
Session ID: BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo
SGFzaHsABjoKQHVzZWR7AA==--33a1d0f56f69993bc1d69552fd372a4f9a83fa44
Parameters: {"format"=>"fxml", "action"=>"index", "controller"=>"projects"}
Project Load (0.000349) SELECT * FROM `projects`
Project Columns (0.001927) SHOW FIELDS FROM `projects`
Completed in 0.01663 (60 reqs/sec) | DB: 0.00228 (13%) | 200 OK [http://localhost/projects.fxml]