Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Get bootstrapping working for Rails 2 and Rails 3 and get request pro…

…cessing working for both through rack
  • Loading branch information...
commit e45701ffb4972060b4e7053d955418a3479bf2d1 1 parent 40c393a
@warhammerkid warhammerkid authored
View
23 lib/rubyamf.rb
@@ -1,7 +1,17 @@
require 'rocketamf'
require 'active_support/inflector'
+require 'rubyamf/version'
+require 'rubyamf/intermediate_object'
+require 'rubyamf/class_mapping'
+require 'rubyamf/serialization'
+require 'rubyamf/configuration'
+require 'rubyamf/request_parser'
+require 'rubyamf/request_processor'
+
module RubyAMF
+ MIME_TYPE = "application/x-amf".freeze
+
class << self
def configuration
@configuration ||= RubyAMF::Configuration.new
@@ -9,10 +19,10 @@ def configuration
def configure
yield configuration
- update_configs
+ bootstrap
end
- def update_configs
+ def bootstrap
configuration.propagate
configuration.preload_models.flatten.each {|m| m.to_s.constantize}
end
@@ -39,16 +49,11 @@ def const_missing const #:nodoc:
end
end
-require 'rubyamf/intermediate_object'
-require 'rubyamf/class_mapping'
-require 'rubyamf/serialization'
-require 'rubyamf/configuration'
-
if defined?(Rails)
if Rails::VERSION::MAJOR == 3
- puts "bootstrap rails 3"
+ require 'rubyamf/rails3/bootstrap'
elsif Rails::VERSION::MAJOR == 2 && Rails::VERSION::MINOR >= 3
- puts "bootstrap rails 2"
+ require 'rubyamf/rails2/bootstrap'
else
puts "unsupported rails version"
end
View
1  lib/rubyamf/configuration.rb
@@ -26,7 +26,6 @@ def class_mapper= klass
end
def propagate
- cm = RubyAMF::ClassMapper
[:translate_case, :auto_class_mapping, :use_array_collection].each do |prop|
if RubyAMF::ClassMapper.respond_to?("#{prop}=")
RubyAMF::ClassMapper.send("#{prop}=", self.send(prop))
View
20 lib/rubyamf/rails2/action_controller.rb
@@ -0,0 +1,20 @@
+require 'action_controller'
+
+Mime::Type.register RubyAMF::MIME_TYPE, :amf
+
+module ActionController
+ class Base
+ protected
+ attr_reader :amf_response
+
+ def render_with_amf(options = nil, extra_options ={}, &block)
+ if options && options.is_a?(Hash) && options.has_key?(:amf)
+ @performed_render = true
+ @amf_response = options[:amf]
+ else
+ render_without_amf(options, extra_options, &block)
+ end
+ end
+ alias_method_chain :render, :amf
+ end
+end
View
15 lib/rubyamf/rails2/bootstrap.rb
@@ -0,0 +1,15 @@
+require 'rubyamf/rails2/action_controller'
+require 'rubyamf/rails2/request_processor'
+
+module RubyAMF
+ class << self
+ alias_method :old_bootstrap, :bootstrap
+ def bootstrap
+ old_bootstrap
+
+ m = Rails.configuration.middleware
+ m.use RubyAMF::RequestParser, RubyAMF.configuration.gateway_path, Rails.logger
+ m.use RubyAMF::Rails2::RequestProcessor, Rails.logger
+ end
+ end
+end
View
43 lib/rubyamf/rails2/request_processor.rb
@@ -0,0 +1,43 @@
+module RubyAMF
+ module Rails2
+ class RequestProcessor < RubyAMF::RequestProcessor
+ def handle_method method, args, env
+ # Parse method and load service
+ path = method.split('.')
+ method_name = path.pop
+ controller_name = path.pop
+ controller = get_service controller_name, method_name
+
+ # Create request
+ new_env = env.dup
+ new_env['HTTP_ACCEPT'] = RubyAMF::MIME_TYPE # Force amf response
+ req = ActionController::Request.new(new_env)
+ req.parameters['controller'] = controller.controller_name
+ req.parameters['action'] = method_name
+
+ # Run it
+ con = controller.new
+ con.process(req, ActionController::Response.new)
+ return con.send(:amf_response)
+ end
+
+ def get_service controller_name, method_name
+ # Check controller and validate against hacking attempts
+ begin
+ controller_name += "Controller" unless controller_name =~ /^[A-Za-z:]+Controller$/
+ controller = controller_name.constantize
+ raise "not controller" unless controller.respond_to?(:controller_name) && controller.respond_to?(:action_methods)
+ rescue Exception => e
+ raise "Service #{controller_name} does not exist"
+ end
+
+ # Check action
+ unless controller.action_methods.include?(method_name)
+ raise "Service #{controller_name} does not respond to #{method_name}"
+ end
+
+ return controller
+ end
+ end
+ end
+end
View
15 lib/rubyamf/rails3/action_controller.rb
@@ -0,0 +1,15 @@
+require 'action_controller/railtie'
+
+Mime::Type.register RubyAMF::MIME_TYPE, :amf
+
+module ActionController
+ module Renderers
+ attr_reader :amf_response
+
+ add :amf do |amf, options|
+ @amf_response = amf
+ self.content_type ||= Mime::AMF
+ self.response_body = " "
+ end
+ end
+end
View
17 lib/rubyamf/rails3/bootstrap.rb
@@ -0,0 +1,17 @@
+require 'rubyamf/rails3/action_controller'
+require 'rubyamf/rails3/request_processor'
+
+module RubyAMF
+ class Railtie < Rails::Railtie
+ config.rubyamf = RubyAMF.configuration
+
+ initializer "rubyamf.configure" do
+ RubyAMF.bootstrap
+ end
+
+ initializer "rubyamf.middleware" do
+ config.app_middleware.use RubyAMF::RequestParser, config.rubyamf.gateway_path, Rails.logger
+ config.app_middleware.use RubyAMF::Rails3::RequestProcessor, Rails.logger
+ end
+ end
+end
View
43 lib/rubyamf/rails3/request_processor.rb
@@ -0,0 +1,43 @@
+require 'active_support/dependencies'
+
+module RubyAMF
+ module Rails3
+ class RequestProcessor < RubyAMF::RequestProcessor
+ def handle_method method, args, env
+ # Parse method and load service
+ path = method.split('.')
+ method_name = path.pop
+ controller_name = path.pop
+ controller = get_service controller_name, method_name
+
+ # Create rack request
+ new_env = env.dup
+ new_env['HTTP_ACCEPT'] = RubyAMF::MIME_TYPE # Force amf response
+ req = ActionDispatch::Request.new(new_env)
+
+ # Run it
+ con = controller.new
+ res = con.dispatch(method_name, req)
+ return con.amf_response
+ end
+
+ def get_service controller_name, method_name
+ # Check controller and validate against hacking attempts
+ begin
+ controller_name += "Controller" unless controller_name =~ /^[A-Za-z:]+Controller$/
+ controller = ActiveSupport::Dependencies.ref(controller_name).get
+ raise "not controller" unless controller.respond_to?(:controller_name) && controller.respond_to?(:action_methods)
+ rescue Exception => e
+ raise "Service #{controller_name} does not exist"
+ end
+
+ # Check action
+ unless controller.action_methods.include?(method_name)
+ raise "Service #{controller_name} does not respond to #{method_name}"
+ end
+
+ return controller
+ end
+ end
+ end
+end
View
43 lib/rubyamf/request_parser.rb
@@ -0,0 +1,43 @@
+require 'logger'
+
+module RubyAMF
+ class RequestParser
+ def initialize app, gateway_path, logger=nil
+ @app = app
+ @gateway_path = gateway_path
+ @logger = logger || Logger.new(STDERR)
+ end
+
+ # If the content type is AMF and the path matches the configured gateway path,
+ # it parses the request, creates a response object, and forwards the call
+ # to the next middleware. If the amf response is constructed, then it serializes
+ # the response and returns it as the response.
+ def call env
+ return @app.call(env) unless should_handle?(env)
+
+ # Wrap request and response
+ env['rack.input'].rewind
+ env['rubyamf.request'] = RocketAMF::Envelope.new.populate_from_stream(env['rack.input'].read)
+ env['rubyamf.response'] = RocketAMF::Envelope.new
+
+ # Pass up the chain to the request processor, or whatever is layered in between
+ result = @app.call(env)
+
+ # Calculate length and return response
+ if env['rubyamf.response'].constructed?
+ @logger.info "Sending back AMF"
+ response = env['rubyamf.response'].to_s
+ return [200, {"Content-Type" => RubyAMF::MIME_TYPE, 'Content-Length' => response.length.to_s}, [response]]
+ else
+ return result
+ end
+ end
+
+ # Check if we should handle it based on the environment
+ def should_handle? env
+ return false unless env['CONTENT_TYPE'] == RubyAMF::MIME_TYPE
+ return false unless @gateway_path == env['PATH_INFO']
+ true
+ end
+ end
+end
View
34 lib/rubyamf/request_processor.rb
@@ -0,0 +1,34 @@
+require 'logger'
+
+module RubyAMF
+ class RequestProcessor
+ def initialize app, logger=nil
+ @app = app
+ @logger = logger || Logger.new(STDERR)
+ end
+
+ # Processes the AMF request and forwards the method calls to the corresponding
+ # rails controllers. No middleware beyond the request processor will receive
+ # anything if the request is a handleable AMF request.
+ def call env
+ return @app.call(env) unless env['rubyamf.response']
+
+ # Handle each method call
+ req = env['rubyamf.request']
+ res = env['rubyamf.response']
+ res.each_method_call req do |method, args|
+ begin
+ handle_method method, args, env
+ rescue Exception => e
+ # Log and re-raise exception
+ @logger.error e.to_s+"\n"+e.backtrace.join("\n")
+ raise e
+ end
+ end
+ end
+
+ def handle_method method, args, env
+ raise "Cannot handle method: #{method}"
+ end
+ end
+end
View
4 rubyamf.gemspec
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
s.authors = ["Stephen Augenstein"]
s.email = ["perl.programmer@gmail.com"]
s.homepage = ""
- s.summary = %q{TODO: Write a gem summary}
- s.description = %q{TODO: Write a gem description}
+ s.summary = %q{AMF remoting for Ruby and Rails}
+ s.description = %q{RubyAMF is an open source flash remoting gateway for Ruby on Rails and other Rack-based web frameworks.}
s.rubyforge_project = "rubyamf"
View
37 spec/request_parser_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper.rb'
+
+describe RubyAMF::RequestParser do
+ before :each do
+ @mock_next = mock("Middleware")
+ @app = RubyAMF::RequestParser.new(@mock_next, "/amf", Logger.new(nil))
+ @env = {
+ 'PATH_INFO' => '/amf',
+ 'CONTENT_TYPE' => RubyAMF::MIME_TYPE,
+ 'rack.input' => StringIO.new(RocketAMF::Envelope.new.to_s)
+ }
+ end
+
+ it "should only handle requests with proper content type" do
+ @app.should_handle?(@env).should be_true
+ @env['CONTENT_TYPE'] = 'text/html'
+ @app.should_handle?(@env).should be_false
+ end
+
+ it "should only handle requests with proper gateway path" do
+ @app.should_handle?(@env).should be_true
+ @env['PATH_INFO'] = "/invalid"
+ @app.should_handle?(@env).should be_false
+ end
+
+ it "should pass through requests that aren't AMF" do
+ @mock_next.should_receive(:call).and_return("success")
+ @app.stub!(:should_handle?).and_return(false)
+ @app.call(@env).should == "success"
+ end
+
+ it "should serialize to AMF if the response is constructed" do
+ @mock_next.stub!(:call) {|env| env['rubyamf.response'].should_receive('constructed?').and_return(true)}
+ response = @app.call(@env)
+ response[1]["Content-Type"].should == RubyAMF::MIME_TYPE
+ end
+end
View
4 spec/spec_helper.rb
@@ -3,4 +3,6 @@
require 'spec'
require 'spec/autorun'
-require 'rubyamf'
+require 'rubyamf'
+
+RubyAMF::ClassMapper # Force it to be loaded

0 comments on commit e45701f

Please sign in to comment.
Something went wrong with that request. Please try again.