aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice/lib/action_web_service/container
diff options
context:
space:
mode:
authorLeon Breedt <bitserf@gmail.com>2005-02-25 23:39:39 +0000
committerLeon Breedt <bitserf@gmail.com>2005-02-25 23:39:39 +0000
commit6f5a7b200443baf209d2f33c428ed4a4059782f7 (patch)
tree9c3942fe27be69c102873d9fdaa13f66dc12853d /actionwebservice/lib/action_web_service/container
parent10faf204b712763f05a2b3155a4fd9c5338f1fb2 (diff)
downloadrails-6f5a7b200443baf209d2f33c428ed4a4059782f7.tar.gz
rails-6f5a7b200443baf209d2f33c428ed4a4059782f7.tar.bz2
rails-6f5a7b200443baf209d2f33c428ed4a4059782f7.zip
merged the changes for the upcoming 0.6.0:
seperate out protocol marshaling into a small 'ws' library in vendor, so that AWS itself only does integration with ActionPack, and so we can keep protocol specific code in AWS proper to a minimum. refactor unit tests to get 95% code coverage (for a baseline). be far more relaxed about the types given to us by the remote side, don't do any poor man's type checking, just try to cast and marshal to the correct types if possible, and if not, return what they gave us anyway. this should make interoperating with fuzzy XML-RPC clients easier. if exception reporting is turned on, do best-effort error responses, so that we can avoid "Internal protocol error" with no details if there is a bug in AWS itself. also perform extensive cleanups on AWS proper. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@800 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionwebservice/lib/action_web_service/container')
-rw-r--r--actionwebservice/lib/action_web_service/container/action_controller_container.rb92
-rw-r--r--actionwebservice/lib/action_web_service/container/delegated_container.rb87
-rw-r--r--actionwebservice/lib/action_web_service/container/direct_container.rb70
3 files changed, 249 insertions, 0 deletions
diff --git a/actionwebservice/lib/action_web_service/container/action_controller_container.rb b/actionwebservice/lib/action_web_service/container/action_controller_container.rb
new file mode 100644
index 0000000000..4dda93ec11
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/container/action_controller_container.rb
@@ -0,0 +1,92 @@
+module ActionWebService # :nodoc:
+ module Container # :nodoc:
+ module ActionController # :nodoc:
+ def self.append_features(base) # :nodoc:
+ base.class_eval do
+ class << self
+ alias_method :inherited_without_api, :inherited
+ alias_method :web_service_api_without_require, :web_service_api
+ end
+ end
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ # Creates a client for accessing remote web services, using the
+ # given +protocol+ to communicate with the +endpoint_uri+.
+ #
+ # ==== Example
+ #
+ # class MyController < ActionController::Base
+ # web_client_api :blogger, :xmlrpc, "http://blogger.com/myblog/api/RPC2", :handler_name => 'blogger'
+ # end
+ #
+ # In this example, a protected method named <tt>blogger</tt> will
+ # now exist on the controller, and calling it will return the
+ # XML-RPC client object for working with that remote service.
+ #
+ # +options+ is the set of protocol client specific options,
+ # see a protocol client class for details.
+ #
+ # If your API definition does not exist on the load path with the
+ # correct rules for it to be found using +name+, you can pass through
+ # the API definition class in +options+, using a key of <tt>:api</tt>
+ def web_client_api(name, protocol, endpoint_uri, options={})
+ unless method_defined?(name)
+ api_klass = options.delete(:api) || require_web_service_api(name)
+ class_eval do
+ define_method(name) do
+ create_web_service_client(api_klass, protocol, endpoint_uri, options)
+ end
+ protected name
+ end
+ end
+ end
+
+ def web_service_api(definition=nil) # :nodoc:
+ return web_service_api_without_require if definition.nil?
+ case definition
+ when String, Symbol
+ klass = require_web_service_api(definition)
+ else
+ klass = definition
+ end
+ web_service_api_without_require(klass)
+ end
+
+ def require_web_service_api(name) # :nodoc:
+ case name
+ when String, Symbol
+ file_name = name.to_s.underscore + "_api"
+ class_name = file_name.camelize
+ class_names = [class_name, class_name.sub(/Api$/, 'API')]
+ begin
+ require_dependency(file_name)
+ rescue LoadError => load_error
+ requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
+ raise LoadError, requiree == file_name ? "Missing API definition file in apis/#{file_name}.rb" : "Can't load file: #{requiree}"
+ end
+ klass = nil
+ class_names.each do |name|
+ klass = name.constantize rescue nil
+ break unless klass.nil?
+ end
+ unless klass
+ raise(NameError, "neither #{class_names[0]} or #{class_names[1]} found")
+ end
+ klass
+ else
+ raise(ArgumentError, "expected String or Symbol argument")
+ end
+ end
+
+ private
+ def inherited(child)
+ inherited_without_api(child)
+ child.web_service_api(child.controller_path)
+ rescue Exception => e
+ end
+ end
+ end
+ end
+end
diff --git a/actionwebservice/lib/action_web_service/container/delegated_container.rb b/actionwebservice/lib/action_web_service/container/delegated_container.rb
new file mode 100644
index 0000000000..674141aab6
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/container/delegated_container.rb
@@ -0,0 +1,87 @@
+module ActionWebService # :nodoc:
+ module Container # :nodoc:
+ module Delegated # :nodoc:
+ class ContainerError < ActionWebServiceError # :nodoc:
+ end
+
+ def self.append_features(base) # :nodoc:
+ super
+ base.extend(ClassMethods)
+ base.send(:include, ActionWebService::Container::Delegated::InstanceMethods)
+ end
+
+ module ClassMethods
+ # Declares a web service that will provides access to the API of the given
+ # +object+. +object+ must be an ActionWebService::Base derivative.
+ #
+ # Web 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 web service object example
+ #
+ # class ApiController < ApplicationController
+ # web_service_dispatching_mode :delegated
+ #
+ # web_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 web service object example
+ #
+ # class ApiController < ApplicationController
+ # web_service_dispatching_mode :delegated
+ #
+ # web_service(:person) { PersonService.new(@request.env) }
+ # end
+ def web_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("web_services", info)
+ call_web_service_definition_callbacks(self, name, info)
+ end
+
+ # Whether this service contains a service with the given +name+
+ def has_web_service?(name)
+ web_services.has_key?(name.to_sym)
+ end
+
+ def web_services # :nodoc:
+ read_inheritable_attribute("web_services") || {}
+ end
+
+ def add_web_service_definition_callback(&block) # :nodoc:
+ write_inheritable_array("web_service_definition_callbacks", [block])
+ end
+
+ private
+ def call_web_service_definition_callbacks(container_class, web_service_name, service_info)
+ (read_inheritable_attribute("web_service_definition_callbacks") || []).each do |block|
+ block.call(container_class, web_service_name, service_info)
+ end
+ end
+ end
+
+ module InstanceMethods # :nodoc:
+ def web_service_object(web_service_name)
+ info = self.class.web_services[web_service_name.to_sym]
+ unless info
+ raise(ContainerError, "no such web service '#{web_service_name}'")
+ end
+ service = info[:block]
+ service ? instance_eval(&service) : info[:object]
+ end
+ end
+ end
+ end
+end
diff --git a/actionwebservice/lib/action_web_service/container/direct_container.rb b/actionwebservice/lib/action_web_service/container/direct_container.rb
new file mode 100644
index 0000000000..b53c542fc8
--- /dev/null
+++ b/actionwebservice/lib/action_web_service/container/direct_container.rb
@@ -0,0 +1,70 @@
+module ActionWebService # :nodoc:
+ module Container # :nodoc:
+ module Direct # :nodoc:
+ class ContainerError < ActionWebServiceError # :nodoc:
+ end
+
+ def self.append_features(base) # :nodoc:
+ super
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ # Attaches ActionWebService API +definition+ to the calling class.
+ #
+ # Action Controllers can have a default associated API, removing the need
+ # to call this method if you follow the Action Web Service naming conventions.
+ #
+ # A controller with a class name of GoogleSearchController will
+ # implicitly load <tt>app/apis/google_search_api.rb</tt>, and expect the
+ # API definition class to be named <tt>GoogleSearchAPI</tt> or
+ # <tt>GoogleSearchApi</tt>.
+ #
+ # ==== Service class example
+ #
+ # class MyService < ActionWebService::Base
+ # web_service_api MyAPI
+ # end
+ #
+ # class MyAPI < ActionWebService::API::Base
+ # ...
+ # end
+ #
+ # ==== Controller class example
+ #
+ # class MyController < ActionController::Base
+ # web_service_api MyAPI
+ # end
+ #
+ # class MyAPI < ActionWebService::API::Base
+ # ...
+ # end
+ def web_service_api(definition=nil)
+ if definition.nil?
+ read_inheritable_attribute("web_service_api")
+ else
+ if definition.is_a?(Symbol)
+ raise(ContainerError, "symbols can only be used for #web_service_api inside of a controller")
+ end
+ unless definition.respond_to?(:ancestors) && definition.ancestors.include?(ActionWebService::API::Base)
+ raise(ContainerError, "#{definition.to_s} is not a valid API definition")
+ end
+ write_inheritable_attribute("web_service_api", definition)
+ call_web_service_api_callbacks(self, definition)
+ end
+ end
+
+ def add_web_service_api_callback(&block) # :nodoc:
+ write_inheritable_array("web_service_api_callbacks", [block])
+ end
+
+ private
+ def call_web_service_api_callbacks(container_class, definition)
+ (read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
+ block.call(container_class, definition)
+ end
+ end
+ end
+ end
+ end
+end