RailsとFlexでリモートオブジェクト通信して遊ぶ覚え書き

WebORBを使ってRemoteObjectでデータのやり取りをするのに、勝手に遊んでる所を今のうちにまとめときます。
インストールは当初の覚え書きに。
セットアップできたら、railsでは app/services にクラスを追加していくことになるのですが、サービスを書くのにひな形が欲しくって、ふつうのscaffoldでは作れないのでオリジナルのを書く。
railsにオリジナルのscaffoldを追加するには、ホームディレクトリに .rails ディレクトリ を作成して、その中に入れると良いみたいです。(Windowsな人がどうすればいいのか分かりません・・・すみません)
具体的には、

.rails
 - generators
  - scaffold_weborb
   - template

というディレクトリを掘ります。(scaffold_weborbという名前の例です)
このgenerators以下はrailsのscaffoldのコードが書かれているファイルのところと同じ構成です。
例えばうちのMacだと
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/rails-1.2.6/lib/rails_generator/generators/components
てなところにソースがあるみたいなので、この内容をまねして作ります。
scaffold_weborb直下に
scaffold_weborb_generator.rb

class ScaffoldingSandbox
  include ActionView::Helpers::ActiveRecordHelper

  attr_accessor :form_action, :singular_name, :suffix, :model_instance

  def sandbox_binding
    binding
  end
  
  def default_input_block
    Proc.new { |record, column| "        itm[:#{column.name}] = r[:#{column.name}]" }
  end
  
end

class ActionView::Helpers::InstanceTag
  def to_input_field_tag(field_type, options={})
    field_meth = "#{field_type}_field"
    "<%= #{field_meth} '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+options.inspect} %>"
  end

  def to_text_area_tag(options = {})
    "<%= text_area '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
  end

  def to_date_select_tag(options = {})
    "<%= date_select '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
  end

  def to_datetime_select_tag(options = {})
    "<%= datetime_select '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
  end
  
  def to_time_select_tag(options = {})
    "<%= time_select '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
  end
end

class ScaffoldWeborbGenerator < Rails::Generator::NamedBase
  attr_reader   :controller_name,
                :controller_class_path,
                :controller_file_path,
                :controller_class_nesting,
                :controller_class_nesting_depth,
                :controller_class_name,
                :controller_singular_name,
                :controller_plural_name
  alias_method  :controller_file_name,  :controller_singular_name
  alias_method  :controller_table_name, :controller_plural_name

  def initialize(runtime_args, runtime_options = {})
    super

    # Take controller name from the next argument.  Default to the pluralized model name.
    @controller_name = args.shift
    @controller_name ||= ActiveRecord::Base.pluralize_table_names ? @name.pluralize : @name

    base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
    @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)

    if @controller_class_nesting.empty?
      @controller_class_name = @controller_class_name_without_nesting
    else
      @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
    end
  end

  def manifest
    record do |m|
      # Check for class naming collisions.
      m.class_collisions controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}ControllerTest", "#{controller_class_name}Helper"

      # Controller, helper, views, and test directories.
      m.directory File.join('app/services')
      m.directory File.join('app/views', controller_class_path, controller_file_name)
      m.directory File.join('test/functional', controller_class_path)

      # Depend on model generator but skip if the model exists.
      m.dependency 'model', [singular_name], :collision => :skip, :skip_migration => true
      
      # Service
      m.complex_template 'service.rb',
                  File.join('app/services',
                            "#{controller_class_name}Service.rb"),
                   :insert => "service_scaffolding.rb",
                   :sandbox => lambda { create_sandbox },
                   :begin_mark => 'rec',
                   :end_mark => 'eorec',
                   :mark_id => singular_name

    end
  end

  protected
    # Override with your own usage banner.
    def banner
      "Usage: #{$0} scaffold ModelName [ControllerName] [action, ...]"
    end

    def scaffold_views
      %w(list)
    end

    def scaffold_actions
      scaffold_views + %w(index create update destroy)
    end
    
    def model_name 
      class_name.demodulize
    end

    def unscaffolded_actions
      args - scaffold_actions
    end

    def suffix
      "_#{singular_name}" if options[:suffix]
    end

    def create_sandbox
      sandbox = ScaffoldingSandbox.new
      sandbox.singular_name = singular_name
      begin
        sandbox.model_instance = model_instance
        sandbox.instance_variable_set("@#{singular_name}", sandbox.model_instance)
      rescue ActiveRecord::StatementInvalid => e
        logger.error "Before updating scaffolding from new DB schema, try creating a table for your model (#{class_name})"
        raise SystemExit
      end
      sandbox.suffix = suffix
      sandbox
    end
    
    def model_instance
      base = class_nesting.split('::').inject(Object) do |base, nested|
        break base.const_get(nested) if base.const_defined?(nested)
        base.const_set(nested, Module.new)
      end
      unless base.const_defined?(@class_name_without_nesting)
        base.const_set(@class_name_without_nesting, Class.new(ActiveRecord::Base))
      end
      class_name.constantize.new
    end
end

ってファイルを置いて
templatesの下に
service_scaffolding.rb

<%= all_input_tags(@model_instance, @singular_name, {}) %>


service.rb

class <%= controller_class_name %>Service

  def list<%= suffix %>(o)
    <%= model_name %>.find(
      :all ,
      :order => "id asc").collect{|r|
        itm = {}
        itm[:id] = r[:id]
<%= template_for_inclusion.gsub(/(\<\!\-\-)/,'#\1') %>
        itm }
  end

  def create<%= suffix %>(o)
    @<%= singular_name %> = <%= model_name %>.new(o)
    if @<%= singular_name %>.save
      @<%= singular_name %>
    else
      false
    end
  end

  def update(o)
    @<%= singular_name %> = <%= model_name %>.find(o['id'])
    if @<%= singular_name %>.update_attributes(o)
      @<%= singular_name %>
    else
      false
    end
  end

  def destroy<%= suffix %>(o)
    @<%= singular_name %> = <%= model_name %>.find(o['id'])
    if @<%= singular_name %>.destroy
      true
    else
      false
    end
  end

end

を置きます。
※オリジナルのscaffoldから適当に手を加えたので、いらないコードとか混じってるかもしれません。
以上をしておくと、

ruby script/generate scaffold_weborb モデル

ってコマンドでapp/servicesにファイルができます。
開いて必要な変更をしたらremoting-config.xmlファイルに

    <destination id="〜Service">
        <properties>
            <source>〜Service</source>
        </properties>
    </destination>

のような記述を追加するのを忘れずに(で、httpdをrestart)
あと、scaffoldの機能追加ではついでにテーブルの表示と更新なら出来ちゃうようなMXMLを自動生成しておくと、Flexにファイルを持っていってコンパイルするだけでテーブル管理のアプリが出来るので、オリジナルのscaffoldと近い機能が提供できます。
# なんか、もうだれか作ってたりしませんかね。見つからずに自作してるわけですが。
・・・scaffoldの話だけになっちゃった。