Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
pillowfactory committed Jul 30, 2008
0 parents commit fc8432b
Show file tree
Hide file tree
Showing 34 changed files with 1,264 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>rubyamf_quickly</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.rubypeople.rdt.core.rubybuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.rubypeople.rdt.core.rubynature</nature>
</natures>
</projectDescription>
20 changes: 20 additions & 0 deletions MIT-LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2008 Luke Pillow

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
62 changes: 62 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
RubyAMF::Quickly
================

The RubyAMF::Quickly plugin is intended to jump start any Flex on Rails project using RubyAMF.

NOTE: The RubyAMF::Quickly plugin installation appends it's own configuration settings to the config/rubyamf_config.rb file.
The plugin's configuration defaults it's settings for the most natural Rails and Flex development as well as overrides some of the
default RubyAMF settings. All examples and following explanation are given using the RubyAMF::Quickly default configuration.

RubyAMF::Quickly provides the following:

1. ActionScript Model Generator
============================
The action_script_models generator creates ActionScript models from the project's ActiveRecord models.

Given a Rails project with a single ActiveRecord model, Person.rb the following will be created from running
./script/generate action_script_models com.example.models

RAILS_ROOT/app/flex/com/example/models/Person.as => Generated once. Add custom ActionScript model behavior here. Subclasses PersonBase.as
RAILS_ROOT/app/flex/com/example/models/base/PersonBase.as => Generated every time. Contains properties and toParams() method
RAILS_ROOT/app/flex/com/example/models/helpers/Errors.as => ActionScript implementation of the ActiveRecord Errors class
RAILS_ROOT/app/flex/com/example/models/helpers/Hash.as => ActionScript dynamic class with some Ruby Hash-like methods
RAILS_ROOT/app/flex/com/example/models/helpers/RubyAMF.as => Simple remoting helper for Rails controller/action invocation

2. Runtime Assistance
==================
By default, RubyAMF::Quickly adds a couple of helpers to the RubyAMF request cycle.

- Parameter Filtering - This before_filter merges any remoting request parameters into the standard controller params hash.
- Default Exception Handling - Using the Rails 2 rescue_from method, any unhandled Exceptions will be converted to a RubyAMF
FaultObject that will trigger an ActionScript FaultEvent.


Quickly Concepts
================

The main idea behind RubyAMF::Quickly is to keep with the simplicity of Rails RESTful theme while maintaining a natural Flex development environment. The core RubyAMF project allows bidirectional object graph messaging. This method works, but is not conducive to reuse of existing Rails controller logic without "cluttering" things up with is_amf conditional logic. Again... this works, but is not the Rails way.

RubyAMF::Quickly encourages a slightly different approach: Send HTTP hash-like requests from Flex. Let Rails respond to AMF requests with ActiveRecord object graphs.


Example
=======

Given a Person.rb ActiveRecord class, a PeopleController#create Rails controller/action, and the generated ActionScript classes as described above, the following ActionScript code will save a Person to the database.

var myPerson:Person = new Person();
myPerson.name = 'Foo';
myPerson.favoriteNumber = 7;

RubyAMF.call('PeopleController', 'create', {person: myPerson.toParams()}, myPersonResultHandler, myPersonFaultHandler);

This example uses the generated Person.as ActionScript class that corresponds to the ActiveRecord Person.rb with two database attributes: name and favorite_number. The RubyAMF.call() method streamlines the definition and "caching" of Flex RemoteObjects. The first parameter is the class name of the Rails controller we're targeting. The second parameter is the controller action to invoke. The third parameter is an anonymous object that serves as the hash-like "wrapper" that the parameters will be sent in. This is the equivalent of a standard HTML form input names of person[name] and person[favorite_number]. You'll want to note the use of myPerson.toParams() when constructing the request "hash." toParams() is defined on the generated PersonBase.as class that returns another anonymous object with the corresponding attributes and values. The request "hash" could have also been defined without using the toParams() method as {person: {name: 'foo', favoriteNumber: 7}}, but the toParams() method provides additional functionality. toParams() also accepts an array of properties to exclude. For example, {person: myPerson.toParams('name')} would not include the name property and value in the request.


More Documentation
==================

The RubyAMF::Quickly::Config section that was appended to rubyamf_config.rb file has more documentation for configuration options supported by RubyAMF::Quickly.


Copyright (c) 2008 Luke Pillow, released under the MIT license
22 changes: 22 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

desc 'Default: run unit tests.'
task :default => :test

desc 'Test the rubyamf_quickly plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

desc 'Generate documentation for the rubyamf_quickly plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'RubyamfQuickly'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end
Binary file added generators/.DS_Store
Binary file not shown.
11 changes: 11 additions & 0 deletions generators/action_script_models/.svn/all-wcprops
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
K 25
svn:wc:ra_dav:version-url
V 73
/svn/!svn/ver/111/plugins/rubyamf_quickly/generators/action_script_models
END
action_script_models_generator.rb
K 25
svn:wc:ra_dav:version-url
V 107
/svn/!svn/ver/111/plugins/rubyamf_quickly/generators/action_script_models/action_script_models_generator.rb
END
44 changes: 44 additions & 0 deletions generators/action_script_models/.svn/entries
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
8

dir
111
http://www.treetrunkmedia.com/svn/plugins/rubyamf_quickly/generators/action_script_models
http://www.treetrunkmedia.com/svn



2008-07-02T16:04:28.464873Z
111
lpillow


svn:special svn:externals svn:needs-lock











975abe41-da3c-0410-88f3-9b76e4b188a3

action_script_models_generator.rb
file




2008-07-30T17:47:08.000000Z
ff3176acb15c87929d2f65c86ad2883a
2008-07-02T16:04:28.464873Z
111
lpillow
has-props

templates
dir

1 change: 1 addition & 0 deletions generators/action_script_models/.svn/format
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
K 14
svn:executable
V 1
*
END
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
require 'app/configuration'
require 'rubyamf_quickly'

class ActionScriptModelsGenerator < Rails::Generator::Base
include RubyAMF::Quickly

default_options :skip_helpers => false, :force_all => false

# runtime_args[0] = AS3 Package
def initialize(*runtime_args)
super(*runtime_args)
Config.action_script_package = runtime_args[0] unless runtime_args[0].empty?
end

def manifest
record do |m|

helper_package = [Config.action_script_package, 'helpers'].compact.join('.')
skip_or_force = options[:force_all] ? :force : :skip

# HELPERS
unless options[:skip_helpers]
FileUtils.makedirs(helper_dir = File.join(Config.relative_flex_root, helper_package.split(/\./)))

['Hash', 'RubyAMF', 'Errors'].each do |helper_name|
m.template( "as3_helper_#{helper_name.downcase}.erb", File.join( helper_dir, "#{helper_name}.as" ), :collision => skip_or_force,
:assigns => {:package => helper_package })
end
end

# MODELS

# Base
FileUtils.makedirs(base_dir = File.join(Config.relative_flex_root, Config.action_script_package.to_s.split(/\./), 'base'))
m.template( 'as3_base.erb', File.join(base_dir, 'Base.as'), :collision => skip_or_force,
:assigns => {:package => [Config.action_script_package, 'base'].compact.join('.')} )

ar_models = []
Dir.chdir(File.join(RAILS_ROOT, 'app', 'models')) do
Dir["**/*.rb"].each do |entry|
unless Config.ignore_classes.include?( model_class_name = entry.sub(/\.rb$/,'').camelize )
model_class = model_class_name.constantize
ar_models << model_class if model_class < ActiveRecord::Base && !model_class.abstract_class?
end
end
end

ar_models.each do |ar|
as3_class = ActionScriptModel.new( ar, Config.action_script_package )

FileUtils.makedirs(dest_dir = File.join(Config.relative_flex_root, as3_class.package.to_s.split(/\./)))
FileUtils.makedirs(base_dest_dir = File.join( dest_dir, 'base'))

m.template( 'as3_model_base.erb', File.join(base_dest_dir, "#{as3_class.name}Base.as"), :collision => :force,
:assigns => { :as3_class => as3_class,
:use_helpers => !options[:skip_helpers],
:helper_package => helper_package } )
m.template( 'as3_model.erb', File.join(dest_dir, "#{as3_class.name}.as"), :collision => skip_or_force,
:assigns => { :as3_class => as3_class } )
end

end
end

protected

def banner
"Usage: #{$0} #{spec.name} actionscript.root.package [options]"
end

def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("--skip-helpers",
"Skip ActionScript helper classes that add simple remoting and Ruby/Rails style model behavior.") { |v| options[:skip_helpers] = v }
opt.on("--force-all",
"Force generation of all files. WARNING: This will overwrite any changes made to previously generated files.") { |v| options[:force_all] = v }
end

end

module RubyAMF::Quickly
class ActionScriptModel
attr_accessor :name, :properties, :package, :base_package, :ruby_class

def initialize( ar_class, root_package=nil )
self.name = ar_class.to_s.split(/::/).last
self.ruby_class = ar_class

# Determine this class' package
relative_package = ar_class.to_s.split(/::/)[0...-1].join('.').downcase!
self.package = [root_package, relative_package].compact.join('.')
self.base_package = [self.package, 'base'].compact.join('.')

# Build properties
self.properties = []
ar_class.columns.each do |col|
unless RubyAMF::Configuration::ClassMappings.ignore_fields.include? col.name
self.properties << ActionScriptProperty.new(col)
end
end

self.properties.sort! {|a, b| a.name <=> b.name }
end
end

class ActionScriptProperty
attr_accessor :name, :static_type, :accessor

def initialize( ar_column )
self.name = (RubyAMF::Configuration::ClassMappings.translate_case ? ar_column.name.dup.to_camel! : ar_column.name.dup)
self.static_type = TypeConverter.convert(ar_column)
self.accessor = (self.static_type == 'Boolean' && RubyAMF::Quickly::Config.prefix_booleans ? "is#{self.name.capitalize}" : self.name)
end

end

class TypeConverter
def self.convert( ar_col )
ruby_class, col_type = ar_col.klass, ar_col.type
if ruby_class == String or ruby_class < String
return 'String'
elsif ruby_class < Numeric
return 'Number'
elsif ruby_class == Object and ar_col.type == :boolean
return 'Boolean'
elsif ruby_class == Time or ruby_class == Date or ruby_class < Date or ruby_class < Time
return 'Date'
else
return 'Object'
end
end
end
end

Loading

0 comments on commit fc8432b

Please sign in to comment.