aboutsummaryrefslogtreecommitdiffstats
path: root/actionservice/lib/action_service/container.rb
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-02-18 10:35:25 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-02-18 10:35:25 +0000
commite7a29380292902eae4799b2658507b3cfffb9cec (patch)
tree99a7cd3c7d720ef73f998c2756be1fef77ff0ee1 /actionservice/lib/action_service/container.rb
parente39bf105941133d3d6699c52c18dbd3b9aa0bf5c (diff)
downloadrails-e7a29380292902eae4799b2658507b3cfffb9cec.tar.gz
rails-e7a29380292902eae4799b2658507b3cfffb9cec.tar.bz2
rails-e7a29380292902eae4799b2658507b3cfffb9cec.zip
Added Action Service to the repository
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@658 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionservice/lib/action_service/container.rb')
-rw-r--r--actionservice/lib/action_service/container.rb232
1 files changed, 232 insertions, 0 deletions
diff --git a/actionservice/lib/action_service/container.rb b/actionservice/lib/action_service/container.rb
new file mode 100644
index 0000000000..b2317fc941
--- /dev/null
+++ b/actionservice/lib/action_service/container.rb
@@ -0,0 +1,232 @@
+module ActionService # :nodoc:
+ module Container # :nodoc:
+ class ContainerError < ActionService::ActionServiceError # :nodoc:
+ end
+
+ def self.append_features(base) # :nodoc:
+ super
+ base.class_inheritable_option(:service_dispatching_mode, :direct)
+ base.class_inheritable_option(:service_exception_reporting, true)
+ base.extend(ClassMethods)
+ base.send(:include, ActionService::Container::InstanceMethods)
+ end
+
+ module ClassMethods
+ # Declares a service that will provides access to the API of the given
+ # service +object+. +object+ must be an ActionService::Base derivative.
+ #
+ # Service object creation can either be _immediate_, where the object
+ # instance is given at class definition time, or _deferred_, where
+ # object instantiation is delayed until request time.
+ #
+ # ==== Immediate service object example
+ #
+ # class ApiController < ApplicationController
+ # service_dispatching_mode :delegated
+ #
+ # service :person, PersonService.new
+ # end
+ #
+ # For deferred instantiation, a block should be given instead of an
+ # object instance. This block will be executed in controller instance
+ # context, so it can rely on controller instance variables being present.
+ #
+ # ==== Deferred service object example
+ #
+ # class ApiController < ApplicationController
+ # service_dispatching_mode :delegated
+ #
+ # service(:person) { PersonService.new(@request.env) }
+ # end
+ def service(name, object=nil, &block)
+ if (object && block_given?) || (object.nil? && block.nil?)
+ raise(ContainerError, "either service, or a block must be given")
+ end
+ name = name.to_sym
+ if block_given?
+ info = { name => { :block => block } }
+ else
+ info = { name => { :object => object } }
+ end
+ write_inheritable_hash("action_services", info)
+ call_service_definition_callbacks(self, name, info)
+ end
+
+ # Whether this service contains a service with the given +name+
+ def has_service?(name)
+ services.has_key?(name.to_sym)
+ end
+
+ def services # :nodoc:
+ read_inheritable_attribute("action_services") || {}
+ end
+
+ def add_service_definition_callback(&block) # :nodoc:
+ write_inheritable_array("service_definition_callbacks", [block])
+ end
+
+ private
+ def call_service_definition_callbacks(container_class, service_name, service_info)
+ (read_inheritable_attribute("service_definition_callbacks") || []).each do |block|
+ block.call(container_class, service_name, service_info)
+ end
+ end
+ end
+
+ module InstanceMethods # :nodoc:
+ def service_object(service_name)
+ info = self.class.services[service_name.to_sym]
+ unless info
+ raise(ContainerError, "no such service '#{service_name}'")
+ end
+ service = info[:block]
+ service ? instance_eval(&service) : info[:object]
+ end
+
+ private
+ def dispatch_service_request(protocol_request)
+ case service_dispatching_mode
+ when :direct
+ dispatch_direct_service_request(protocol_request)
+ when :delegated
+ dispatch_delegated_service_request(protocol_request)
+ else
+ raise(ContainerError, "unsupported dispatching mode '#{service_dispatching_mode}'")
+ end
+ end
+
+ def dispatch_direct_service_request(protocol_request)
+ public_method_name = protocol_request.public_method_name
+ api = self.class.service_api
+ method_name = api.api_method_name(public_method_name)
+ block = nil
+ expects = nil
+ if method_name
+ signature = api.api_methods[method_name]
+ expects = signature[:expects]
+ protocol_request.type = Protocol::CheckedMessage
+ protocol_request.signature = expects
+ protocol_request.return_signature = signature[:returns]
+ else
+ protocol_request.type = Protocol::UncheckedMessage
+ system_methods = self.class.read_inheritable_attribute('default_system_methods') || {}
+ protocol = protocol_request.protocol
+ block = system_methods[protocol.class]
+ unless block
+ method_name = api.default_api_method
+ unless method_name && respond_to?(method_name)
+ raise(ContainerError, "no such method ##{public_method_name}")
+ end
+ end
+ end
+
+ @method_params = protocol_request.unmarshal
+ @params ||= {}
+ if expects
+ (1..@method_params.size).each do |i|
+ i -= 1
+ if expects[i].is_a?(Hash)
+ @params[expects[i].keys.shift.to_s] = @method_params[i]
+ else
+ @params["param#{i}"] = @method_params[i]
+ end
+ end
+ end
+
+ if respond_to?(:before_action)
+ @params['action'] = method_name.to_s
+ return protocol_request.marshal(nil) if before_action == false
+ end
+
+ perform_invoke = lambda do
+ if block
+ block.call(public_method_name, self.class, *@method_params)
+ else
+ send(method_name)
+ end
+ end
+ try_default = true
+ result = nil
+ catch(:try_default) do
+ result = perform_invoke.call
+ try_default = false
+ end
+ if try_default
+ method_name = api.default_api_method
+ if method_name
+ protocol_request.type = Protocol::UncheckedMessage
+ else
+ raise(ContainerError, "no such method ##{public_method_name}")
+ end
+ result = perform_invoke.call
+ end
+ after_action if respond_to?(:after_action)
+ protocol_request.marshal(result)
+ end
+
+ def dispatch_delegated_service_request(protocol_request)
+ service_name = protocol_request.service_name
+ service = service_object(service_name)
+ api = service.class.service_api
+ public_method_name = protocol_request.public_method_name
+ method_name = api.api_method_name(public_method_name)
+
+ invocation = ActionService::Invocation::InvocationRequest.new(
+ ActionService::Invocation::ConcreteInvocation,
+ public_method_name,
+ method_name)
+
+ if method_name
+ protocol_request.type = Protocol::CheckedMessage
+ signature = api.api_methods[method_name]
+ protocol_request.signature = signature[:expects]
+ protocol_request.return_signature = signature[:returns]
+ invocation.params = protocol_request.unmarshal
+ else
+ protocol_request.type = Protocol::UncheckedMessage
+ invocation.type = ActionService::Invocation::VirtualInvocation
+ system_methods = self.class.read_inheritable_attribute('default_system_methods') || {}
+ protocol = protocol_request.protocol
+ block = system_methods[protocol.class]
+ if block
+ invocation.block = block
+ invocation.block_params << service.class
+ else
+ method_name = api.default_api_method
+ if method_name && service.respond_to?(method_name)
+ invocation.params = protocol_request.unmarshal
+ invocation.method_name = method_name.to_sym
+ else
+ raise(ContainerError, "no such method /#{service_name}##{public_method_name}")
+ end
+ end
+ end
+
+ canceled_reason = nil
+ canceled_block = lambda{|r| canceled_reason = r}
+ perform_invoke = lambda do
+ service.perform_invocation(invocation, &canceled_block)
+ end
+ try_default = true
+ result = nil
+ catch(:try_default) do
+ result = perform_invoke.call
+ try_default = false
+ end
+ if try_default
+ method_name = api.default_api_method
+ if method_name
+ protocol_request.type = Protocol::UncheckedMessage
+ invocation.params = protocol_request.unmarshal
+ invocation.method_name = method_name.to_sym
+ invocation.type = ActionService::Invocation::UnpublishedConcreteInvocation
+ else
+ raise(ContainerError, "no such method /#{service_name}##{public_method_name}")
+ end
+ result = perform_invoke.call
+ end
+ protocol_request.marshal(result)
+ end
+ end
+ end
+end