From ec03a601811587dd4265667aadd50c101c23b116 Mon Sep 17 00:00:00 2001 From: Leon Breedt Date: Fri, 18 Feb 2005 23:55:29 +0000 Subject: rename entire package to Action Web Service git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@672 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionwebservice/ChangeLog | 2 +- actionwebservice/HACKING | 4 +- actionwebservice/README | 54 +-- actionwebservice/Rakefile | 22 +- actionwebservice/TODO | 11 +- .../googlesearch/autoloading/google_search_api.rb | 8 +- .../delegated/google_search_service.rb | 10 +- .../googlesearch/direct/google_search_api.rb | 8 +- actionwebservice/examples/metaWeblog/README | 16 +- .../examples/metaWeblog/blog_controller.rb | 10 +- actionwebservice/lib/action_service.rb | 60 --- actionwebservice/lib/action_service/api.rb | 2 - .../lib/action_service/api/abstract.rb | 192 -------- .../lib/action_service/api/action_controller.rb | 92 ---- actionwebservice/lib/action_service/base.rb | 41 -- actionwebservice/lib/action_service/client.rb | 3 - actionwebservice/lib/action_service/client/base.rb | 35 -- actionwebservice/lib/action_service/client/soap.rb | 87 ---- .../lib/action_service/client/xmlrpc.rb | 76 ---- actionwebservice/lib/action_service/container.rb | 232 ---------- actionwebservice/lib/action_service/invocation.rb | 252 ----------- actionwebservice/lib/action_service/protocol.rb | 4 - .../lib/action_service/protocol/abstract.rb | 128 ------ .../lib/action_service/protocol/registry.rb | 55 --- .../lib/action_service/protocol/soap.rb | 484 --------------------- .../lib/action_service/protocol/xmlrpc.rb | 183 -------- actionwebservice/lib/action_service/router.rb | 2 - .../lib/action_service/router/action_controller.rb | 99 ----- actionwebservice/lib/action_service/router/wsdl.rb | 210 --------- actionwebservice/lib/action_service/struct.rb | 55 --- .../support/class_inheritable_options.rb | 26 -- .../lib/action_service/support/signature.rb | 100 ----- actionwebservice/lib/action_web_service.rb | 60 +++ actionwebservice/lib/action_web_service/api.rb | 2 + .../lib/action_web_service/api/abstract.rb | 192 ++++++++ .../action_web_service/api/action_controller.rb | 92 ++++ actionwebservice/lib/action_web_service/base.rb | 41 ++ actionwebservice/lib/action_web_service/client.rb | 3 + .../lib/action_web_service/client/base.rb | 35 ++ .../lib/action_web_service/client/soap.rb | 87 ++++ .../lib/action_web_service/client/xmlrpc.rb | 76 ++++ .../lib/action_web_service/container.rb | 232 ++++++++++ .../lib/action_web_service/invocation.rb | 252 +++++++++++ .../lib/action_web_service/protocol.rb | 4 + .../lib/action_web_service/protocol/abstract.rb | 128 ++++++ .../lib/action_web_service/protocol/registry.rb | 55 +++ .../lib/action_web_service/protocol/soap.rb | 484 +++++++++++++++++++++ .../lib/action_web_service/protocol/xmlrpc.rb | 183 ++++++++ actionwebservice/lib/action_web_service/router.rb | 2 + .../action_web_service/router/action_controller.rb | 99 +++++ .../lib/action_web_service/router/wsdl.rb | 210 +++++++++ actionwebservice/lib/action_web_service/struct.rb | 55 +++ .../support/class_inheritable_options.rb | 26 ++ .../lib/action_web_service/support/signature.rb | 100 +++++ actionwebservice/test/abstract_client.rb | 4 +- actionwebservice/test/abstract_soap.rb | 2 +- actionwebservice/test/abstract_unit.rb | 2 +- actionwebservice/test/api_test.rb | 2 +- actionwebservice/test/base_test.rb | 8 +- actionwebservice/test/client_soap_test.rb | 2 +- actionwebservice/test/client_xmlrpc_test.rb | 2 +- actionwebservice/test/invocation_test.rb | 6 +- actionwebservice/test/protocol_registry_test.rb | 8 +- actionwebservice/test/protocol_soap_test.rb | 14 +- actionwebservice/test/protocol_xmlrpc_test.rb | 14 +- .../test/router_action_controller_test.rb | 6 +- actionwebservice/test/router_wsdl_test.rb | 6 +- actionwebservice/test/struct_test.rb | 2 +- 68 files changed, 2527 insertions(+), 2532 deletions(-) delete mode 100644 actionwebservice/lib/action_service.rb delete mode 100644 actionwebservice/lib/action_service/api.rb delete mode 100644 actionwebservice/lib/action_service/api/abstract.rb delete mode 100644 actionwebservice/lib/action_service/api/action_controller.rb delete mode 100644 actionwebservice/lib/action_service/base.rb delete mode 100644 actionwebservice/lib/action_service/client.rb delete mode 100644 actionwebservice/lib/action_service/client/base.rb delete mode 100644 actionwebservice/lib/action_service/client/soap.rb delete mode 100644 actionwebservice/lib/action_service/client/xmlrpc.rb delete mode 100644 actionwebservice/lib/action_service/container.rb delete mode 100644 actionwebservice/lib/action_service/invocation.rb delete mode 100644 actionwebservice/lib/action_service/protocol.rb delete mode 100644 actionwebservice/lib/action_service/protocol/abstract.rb delete mode 100644 actionwebservice/lib/action_service/protocol/registry.rb delete mode 100644 actionwebservice/lib/action_service/protocol/soap.rb delete mode 100644 actionwebservice/lib/action_service/protocol/xmlrpc.rb delete mode 100644 actionwebservice/lib/action_service/router.rb delete mode 100644 actionwebservice/lib/action_service/router/action_controller.rb delete mode 100644 actionwebservice/lib/action_service/router/wsdl.rb delete mode 100644 actionwebservice/lib/action_service/struct.rb delete mode 100644 actionwebservice/lib/action_service/support/class_inheritable_options.rb delete mode 100644 actionwebservice/lib/action_service/support/signature.rb create mode 100644 actionwebservice/lib/action_web_service.rb create mode 100644 actionwebservice/lib/action_web_service/api.rb create mode 100644 actionwebservice/lib/action_web_service/api/abstract.rb create mode 100644 actionwebservice/lib/action_web_service/api/action_controller.rb create mode 100644 actionwebservice/lib/action_web_service/base.rb create mode 100644 actionwebservice/lib/action_web_service/client.rb create mode 100644 actionwebservice/lib/action_web_service/client/base.rb create mode 100644 actionwebservice/lib/action_web_service/client/soap.rb create mode 100644 actionwebservice/lib/action_web_service/client/xmlrpc.rb create mode 100644 actionwebservice/lib/action_web_service/container.rb create mode 100644 actionwebservice/lib/action_web_service/invocation.rb create mode 100644 actionwebservice/lib/action_web_service/protocol.rb create mode 100644 actionwebservice/lib/action_web_service/protocol/abstract.rb create mode 100644 actionwebservice/lib/action_web_service/protocol/registry.rb create mode 100644 actionwebservice/lib/action_web_service/protocol/soap.rb create mode 100644 actionwebservice/lib/action_web_service/protocol/xmlrpc.rb create mode 100644 actionwebservice/lib/action_web_service/router.rb create mode 100644 actionwebservice/lib/action_web_service/router/action_controller.rb create mode 100644 actionwebservice/lib/action_web_service/router/wsdl.rb create mode 100644 actionwebservice/lib/action_web_service/struct.rb create mode 100644 actionwebservice/lib/action_web_service/support/class_inheritable_options.rb create mode 100644 actionwebservice/lib/action_web_service/support/signature.rb diff --git a/actionwebservice/ChangeLog b/actionwebservice/ChangeLog index 010c4f638a..c9803bd2b7 100644 --- a/actionwebservice/ChangeLog +++ b/actionwebservice/ChangeLog @@ -31,7 +31,7 @@ UNRELEASED * lib/action_service/*, test/*: extensive refactoring: define API methods in a seperate class, and specify it wherever used with 'service_api'. this makes writing a client API for accessing defined API methods - with ActionService really easy. + with ActionWebService really easy. * lib/action_service/container.rb: fix a bug in default call handling for direct dispatching, and add ActionController filter support for direct dispatching. diff --git a/actionwebservice/HACKING b/actionwebservice/HACKING index 9c0cde6313..536aac7a24 100644 --- a/actionwebservice/HACKING +++ b/actionwebservice/HACKING @@ -34,10 +34,10 @@ the protocol implementations. == Adding support for a new protocol - 1. Add an ActionService::Protocol::YourProtocol module and any classes you need to + 1. Add an ActionWebService::Protocol::YourProtocol module and any classes you need to perform unmarshaling/marshaling of protocol requests. See the SOAP implementation for an example of a complex mapping, and also see - ActionService::Protocol::AbstractProtocol for the methods you need to implement. + ActionWebService::Protocol::AbstractProtocol for the methods you need to implement. 2. Add unit tests for your new protocol. Be sure to test using a Action Pack test request duplicating how the real requests will arrive and verify that mapping to and from Ruby diff --git a/actionwebservice/README b/actionwebservice/README index c011b9c537..c8a7da1c57 100644 --- a/actionwebservice/README +++ b/actionwebservice/README @@ -1,6 +1,6 @@ -= Action Service -- Serving APIs on rails += Action Web Service -- Serving APIs on rails -Action Service provides a way to publish interoperable web service APIs with +Action Web Service provides a way to publish interoperable web service APIs with Rails without spending a lot of time delving into protocol details. @@ -10,7 +10,7 @@ Rails without spending a lot of time delving into protocol details. * Dynamic WSDL generation for APIs * XML-RPC protocol support * Clients that use the same API definitions as the server for - easy interoperability with other Action Service based applications + easy interoperability with other Action Web Service based applications * Type signature hints to improve interoperability with static languages * Active Record model class support in signatures @@ -18,7 +18,7 @@ Rails without spending a lot of time delving into protocol details. == Defining your APIs You specify the methods you want to make available as API methods in an -ActionService::API::Base derivative, and then specify this API +ActionWebService::API::Base derivative, and then specify this API definition class wherever you want to use that API. The implementation of the methods is done seperately to the API @@ -27,14 +27,14 @@ specification. ==== Method name inflection -Action Service will camelcase the method names according to Rails Inflector +Action Web Service will camelcase the method names according to Rails Inflector rules for the API visible to public callers. What this means, for example is that the method names in generated WSDL will be camelcased, and callers will have to supply the camelcased name in their requests for the request to succeed. If you do not desire this behaviour, you can turn it off with the -ActionService::API::Base +inflect_names+ option. +ActionWebService::API::Base +inflect_names+ option. ==== Inflection examples @@ -45,14 +45,14 @@ ActionService::API::Base +inflect_names+ option. ==== Disabling inflection - class PersonAPI < ActionService::API::Base + class PersonAPI < ActionWebService::API::Base inflect_names false end ==== API definition example - class PersonAPI < ActionService::API::Base + class PersonAPI < ActionWebService::API::Base api_method :add, :expects => [:string, :string, :bool], :returns => [:int] api_method :remove, :expects => [:int], :returns => [:bool] end @@ -72,7 +72,7 @@ ActionService::API::Base +inflect_names+ option. == Publishing your APIs -Action Service uses Action Pack to process protocol requests. There are two +Action Web Service uses Action Pack to process protocol requests. There are two modes of dispatching protocol requests, _Direct_, and _Delegated_. @@ -102,7 +102,7 @@ overridden. end end - class PersonAPI < ActionService::API::Base + class PersonAPI < ActionWebService::API::Base ... end @@ -117,7 +117,7 @@ This mode can be turned on by setting the +web_service_dispatching_mode+ option in a controller. In this mode, the controller contains one or more web service objects (objects -that implement an ActionService::API::Base definition). These web service +that implement an ActionWebService::API::Base definition). These web service objects are each mapped onto one controller action only. ==== Delegated dispatching example @@ -128,7 +128,7 @@ objects are each mapped onto one controller action only. web_service :person, PersonService.new end - class PersonService < ActionService::Base + class PersonService < ActionWebService::Base web_service_api PersonAPI def add @@ -138,7 +138,7 @@ objects are each mapped onto one controller action only. end end - class PersonAPI < ActionService::API::Base + class PersonAPI < ActionWebService::API::Base ... end @@ -150,23 +150,23 @@ The /api/person action is generated when the +web_service+ method is called. This action must not be overridden. Other controller actions (actions that aren't the target of a +web_service+ call) -are ignored for ActionService purposes, and can do normal action tasks. +are ignored for ActionWebService purposes, and can do normal action tasks. == Using the client support -Action Service includes client classes that can use the same API +Action Web Service includes client classes that can use the same API definition as the server. The advantage of this approach is that your client will have the same support for Active Record and structured types as the server, and can just use them directly, and rely on the marshaling to Do The Right Thing. *Note*: The client support is intended for communication between Ruby on Rails -applications that both use Action Service. It may work with other servers, but +applications that both use Action Web Service. It may work with other servers, but that is not its intended use, and interoperability can't be guaranteed, especially not for .NET web services. -Web services protocol specifications are complex, and Action Service client +Web services protocol specifications are complex, and Action Web Service client support can only be guaranteed to work with a subset. @@ -180,31 +180,31 @@ support can only be guaranteed to work with a subset. web_client_api :google, :soap, 'http://url/to/blog/api/beta', :service_name => 'GoogleSearch' end -See ActionService::API::ActionController::ClassMethods for more details. +See ActionWebService::API::ActionController::ClassMethods for more details. ==== Manually created client example - class PersonAPI < ActionService::API::Base + class PersonAPI < ActionWebService::API::Base api_method :find_all, :returns => [[Person]] end - soap_client = ActionService::Client::Soap.new(PersonAPI, "http://...") + soap_client = ActionWebService::Client::Soap.new(PersonAPI, "http://...") persons = soap_client.find_all - class BloggerAPI < ActionService::API::Base + class BloggerAPI < ActionWebService::API::Base inflect_names false api_method :getRecentPosts, :returns => [[Blog::Post]] end - blog = ActionService::Client::XmlRpc.new(BloggerAPI, "http://.../xmlrpc", :handler_name => "blogger") + blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../xmlrpc", :handler_name => "blogger") posts = blog.getRecentPosts -See ActionService::Client::Soap and ActionService::Client::XmlRpc for more details. +See ActionWebService::Client::Soap and ActionWebService::Client::XmlRpc for more details. == Dependencies -Action Service requires that the Action Pack and Active Record are either +Action Web Service requires that the Action Pack and Active Record are either available to be required immediately or are accessible as GEMs. It also requires a version of Ruby that includes SOAP support in the standard @@ -214,20 +214,20 @@ is the version tested against. == Download -The latest Action Service version can be downloaded from +The latest Action Web Service version can be downloaded from http://rubyforge.org/projects/actionservice == Installation -You can install Action Service with the following command. +You can install Action Web Service with the following command. % [sudo] ruby setup.rb == License -Action Service is released under the MIT license. +Action Web Service is released under the MIT license. == Support diff --git a/actionwebservice/Rakefile b/actionwebservice/Rakefile index f4ed2bdb31..49c3409e60 100644 --- a/actionwebservice/Rakefile +++ b/actionwebservice/Rakefile @@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher' require 'fileutils' PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' -PKG_NAME = 'actionservice' +PKG_NAME = 'actionwebservice' PKG_VERSION = '0.4.0' + PKG_BUILD PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}" @@ -28,16 +28,16 @@ Rake::TestTask.new { |t| # Generate the RDoc documentation Rake::RDocTask.new { |rdoc| rdoc.rdoc_dir = 'doc' - rdoc.title = "Action Service -- Web services for Action Pack" + rdoc.title = "Action Web Service -- Web services for Action Pack" rdoc.options << '--line-numbers --inline-source --main README --accessor class_inheritable_option=RW' rdoc.rdoc_files.include('README') - rdoc.rdoc_files.include('lib/action_service.rb') - rdoc.rdoc_files.include('lib/action_service/*.rb') - rdoc.rdoc_files.include('lib/action_service/api/*.rb') - rdoc.rdoc_files.include('lib/action_service/client/*.rb') - rdoc.rdoc_files.include('lib/action_service/protocol/*.rb') - rdoc.rdoc_files.include('lib/action_service/router/*.rb') - rdoc.rdoc_files.include('lib/action_service/support/*.rb') + rdoc.rdoc_files.include('lib/action_web_service.rb') + rdoc.rdoc_files.include('lib/action_web_service/*.rb') + rdoc.rdoc_files.include('lib/action_web_service/api/*.rb') + rdoc.rdoc_files.include('lib/action_web_service/client/*.rb') + rdoc.rdoc_files.include('lib/action_web_service/protocol/*.rb') + rdoc.rdoc_files.include('lib/action_web_service/router/*.rb') + rdoc.rdoc_files.include('lib/action_web_service/support/*.rb') } @@ -61,7 +61,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' s.require_path = 'lib' - s.autorequire = 'action_service' + s.autorequire = 'action_web_service' s.files = [ "Rakefile", "setup.rb", "README", "TODO", "HACKING", "ChangeLog", "MIT-LICENSE" ] s.files = s.files + Dir.glob( "examples/**/*" ).delete_if { |item| item.include?( "\.svn" ) } @@ -93,7 +93,7 @@ def each_source_file(*args) prefix, includes, excludes, open_file = args prefix ||= File.dirname(__FILE__) open_file = true if open_file.nil? - includes ||= %w[lib\/action_service\.rb$ lib\/action_service\/.*\.rb$] + includes ||= %w[lib\/action_web_service\.rb$ lib\/action_web_service\/.*\.rb$] excludes ||= %w[] Find.find(prefix) do |file_name| next if file_name =~ /\.svn/ diff --git a/actionwebservice/TODO b/actionwebservice/TODO index 4ee7545de5..e753fbb1d7 100644 --- a/actionwebservice/TODO +++ b/actionwebservice/TODO @@ -8,7 +8,7 @@ - support XML-RPC's "handler." method namespacing. perhaps something like: - class BloggingServices < ActionService::LayeredService + class BloggingServices < ActionWebService::LayeredService def initialize(request) @request = controller.request end @@ -23,6 +23,13 @@ web_service :xmlrpc { BloggingServices.new(@request) } end + - supported namespaced custom types in WSDL in a way that interoperates + with .NET (.NET croaks on '::' currently) + + - simplification: collapse Router::ActionController, Router::Wsdl + and API::ActionController into Container::ActionController. + the seperation has gained us nothing. + = Low priority tasks - add better type mapping tests for XML-RPC - add tests for ActiveRecord support (with mock objects?) @@ -31,7 +38,7 @@ - Find an alternative way to map interesting types for SOAP (like ActiveRecord model classes) that doesn't require creation of a sanitized copy object with data copied from the real one. Ideally this would let us get rid of - ActionService::Struct altogether and provide a block that would yield the + ActionWebService::Struct altogether and provide a block that would yield the attributes and values. "Filters" ? Not sure how to integrate with SOAP though. - Don't have clean way to go from SOAP Class object to the xsd:NAME type diff --git a/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb b/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb index e7e33a1105..ed69fed7bc 100644 --- a/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb +++ b/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb @@ -1,9 +1,9 @@ -class DirectoryCategory < ActionService::Struct +class DirectoryCategory < ActionWebService::Struct member :fullViewableName, :string member :specialEncoding, :string end -class ResultElement < ActionService::Struct +class ResultElement < ActionWebService::Struct member :summary, :string member :URL, :string member :snippet, :string @@ -15,7 +15,7 @@ class ResultElement < ActionService::Struct member :directoryTitle, :string end -class GoogleSearchResult < ActionService::Struct +class GoogleSearchResult < ActionWebService::Struct member :documentFiltering, :bool member :searchComments, :string member :estimatedTotalResultsCount, :int @@ -29,7 +29,7 @@ class GoogleSearchResult < ActionService::Struct member :searchTime, :float end -class GoogleSearchAPI < ActionService::API::Base +class GoogleSearchAPI < ActionWebService::API::Base inflect_names false api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}] diff --git a/actionwebservice/examples/googlesearch/delegated/google_search_service.rb b/actionwebservice/examples/googlesearch/delegated/google_search_service.rb index da7f8f4529..ade354d89d 100644 --- a/actionwebservice/examples/googlesearch/delegated/google_search_service.rb +++ b/actionwebservice/examples/googlesearch/delegated/google_search_service.rb @@ -1,9 +1,9 @@ -class DirectoryCategory < ActionService::Struct +class DirectoryCategory < ActionWebService::Struct member :fullViewableName, :string member :specialEncoding, :string end -class ResultElement < ActionService::Struct +class ResultElement < ActionWebService::Struct member :summary, :string member :URL, :string member :snippet, :string @@ -15,7 +15,7 @@ class ResultElement < ActionService::Struct member :directoryTitle, :string end -class GoogleSearchResult < ActionService::Struct +class GoogleSearchResult < ActionWebService::Struct member :documentFiltering, :bool member :searchComments, :string member :estimatedTotalResultsCount, :int @@ -29,7 +29,7 @@ class GoogleSearchResult < ActionService::Struct member :searchTime, :float end -class GoogleSearchAPI < ActionService::API::Base +class GoogleSearchAPI < ActionWebService::API::Base inflect_names false api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}] @@ -49,7 +49,7 @@ class GoogleSearchAPI < ActionService::API::Base ] end -class GoogleSearchService < ActionService::Base +class GoogleSearchService < ActionWebService::Base web_service_api GoogleSearchAPI def doGetCachedPage(key, url) diff --git a/actionwebservice/examples/googlesearch/direct/google_search_api.rb b/actionwebservice/examples/googlesearch/direct/google_search_api.rb index e7e33a1105..ed69fed7bc 100644 --- a/actionwebservice/examples/googlesearch/direct/google_search_api.rb +++ b/actionwebservice/examples/googlesearch/direct/google_search_api.rb @@ -1,9 +1,9 @@ -class DirectoryCategory < ActionService::Struct +class DirectoryCategory < ActionWebService::Struct member :fullViewableName, :string member :specialEncoding, :string end -class ResultElement < ActionService::Struct +class ResultElement < ActionWebService::Struct member :summary, :string member :URL, :string member :snippet, :string @@ -15,7 +15,7 @@ class ResultElement < ActionService::Struct member :directoryTitle, :string end -class GoogleSearchResult < ActionService::Struct +class GoogleSearchResult < ActionWebService::Struct member :documentFiltering, :bool member :searchComments, :string member :estimatedTotalResultsCount, :int @@ -29,7 +29,7 @@ class GoogleSearchResult < ActionService::Struct member :searchTime, :float end -class GoogleSearchAPI < ActionService::API::Base +class GoogleSearchAPI < ActionWebService::API::Base inflect_names false api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}] diff --git a/actionwebservice/examples/metaWeblog/README b/actionwebservice/examples/metaWeblog/README index f8a56d7018..0550b06623 100644 --- a/actionwebservice/examples/metaWeblog/README +++ b/actionwebservice/examples/metaWeblog/README @@ -8,21 +8,9 @@ blogging application. = Running - 1. Ensure you have the 'actionservice' Gem installed. You can generate it using - this command: + 1. Copy blog_controller.rb to "app/controllers" in a Rails project. - $ rake package - - 2. Edit config/environment.rb, and add the following line after the rest of the - require_gem statements: - - require_gem 'actionservice' - - - 3. Copy blog_controller.rb to "app/controllers" in a Rails project. - - - 4. Fire up a desktop blogging application (such as BloGTK on Linux), + 2. Fire up a desktop blogging application (such as BloGTK on Linux), point it at http://localhost:3000/blog/api, and try creating or editing blog posts. diff --git a/actionwebservice/examples/metaWeblog/blog_controller.rb b/actionwebservice/examples/metaWeblog/blog_controller.rb index aff2e909ea..e575203311 100644 --- a/actionwebservice/examples/metaWeblog/blog_controller.rb +++ b/actionwebservice/examples/metaWeblog/blog_controller.rb @@ -4,18 +4,18 @@ # structures as defined by the metaWeblog/blogger # specifications. module Blog - class Enclosure < ActionService::Struct + class Enclosure < ActionWebService::Struct member :url, :string member :length, :int member :type, :string end - class Source < ActionService::Struct + class Source < ActionWebService::Struct member :url, :string member :name, :string end - class Post < ActionService::Struct + class Post < ActionWebService::Struct member :title, :string member :link, :string member :description, :string @@ -28,7 +28,7 @@ module Blog member :source, Source end - class Blog < ActionService::Struct + class Blog < ActionWebService::Struct member :url, :string member :blogid, :string member :blogName, :string @@ -36,7 +36,7 @@ module Blog end # skeleton metaWeblog API -class MetaWeblogAPI < ActionService::API::Base +class MetaWeblogAPI < ActionWebService::API::Base inflect_names false api_method :newPost, :returns => [:string], :expects => [ diff --git a/actionwebservice/lib/action_service.rb b/actionwebservice/lib/action_service.rb deleted file mode 100644 index 005e829e7b..0000000000 --- a/actionwebservice/lib/action_service.rb +++ /dev/null @@ -1,60 +0,0 @@ -#-- -# Copyright (C) 2005 Leon Breedt -# -# 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. -#++ - -begin - require 'active_support' - require 'action_controller' - require 'active_record' -rescue LoadError - require 'rubygems' - require_gem 'activesupport', '>= 0.9.0' - require_gem 'actionpack', '>= 1.4.0' - require_gem 'activerecord', '>= 1.6.0' -end - -$:.unshift(File.dirname(__FILE__)) - -require 'action_service/base' -require 'action_service/client' -require 'action_service/invocation' -require 'action_service/api' -require 'action_service/struct' -require 'action_service/container' -require 'action_service/protocol' -require 'action_service/router' - -ActionService::Base.class_eval do - include ActionService::API - include ActionService::Invocation -end - -ActionController::Base.class_eval do - include ActionService::Container - include ActionService::Protocol::Registry - include ActionService::Protocol::Soap - include ActionService::Protocol::XmlRpc - include ActionService::API - include ActionService::API::ActionController - include ActionService::Router::ActionController - include ActionService::Router::Wsdl -end diff --git a/actionwebservice/lib/action_service/api.rb b/actionwebservice/lib/action_service/api.rb deleted file mode 100644 index 61f36fff56..0000000000 --- a/actionwebservice/lib/action_service/api.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'action_service/api/abstract' -require 'action_service/api/action_controller' diff --git a/actionwebservice/lib/action_service/api/abstract.rb b/actionwebservice/lib/action_service/api/abstract.rb deleted file mode 100644 index aab37a285d..0000000000 --- a/actionwebservice/lib/action_service/api/abstract.rb +++ /dev/null @@ -1,192 +0,0 @@ -module ActionService # :nodoc: - module API # :nodoc: - class APIError < ActionService::ActionServiceError # :nodoc: - end - - def self.append_features(base) # :nodoc: - super - base.extend(ClassMethods) - end - - module ClassMethods - # Attaches ActionService 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 Service naming conventions. - # - # A controller with a class name of GoogleSearchController will - # implicitly load app/apis/google_search_api.rb, and expect the - # API definition class to be named GoogleSearchAPI or - # GoogleSearchApi. - # - # ==== Service class example - # - # class MyService < ActionService::Base - # web_service_api MyAPI - # end - # - # class MyAPI < ActionService::API::Base - # ... - # end - # - # ==== Controller class example - # - # class MyController < ActionController::Base - # web_service_api MyAPI - # end - # - # class MyAPI < ActionService::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(APIError, "symbols can only be used for #web_service_api inside of a controller") - end - unless definition.respond_to?(:ancestors) && definition.ancestors.include?(Base) - raise(APIError, "#{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 - - # A web service API class specifies the methods that will be available for - # invocation for an API. It also contains metadata such as the method type - # signature hints. - # - # It is not intended to be instantiated. - # - # It is attached to web service implementation classes like - # ActionService::Base and ActionController::Base derivatives by using - # ClassMethods#web_service_api. - class Base - # Whether to transform the public API method names into camel-cased names - class_inheritable_option :inflect_names, true - - # If present, the name of a method to call when the remote caller - # tried to call a nonexistent method. Semantically equivalent to - # +method_missing+. - class_inheritable_option :default_api_method - - # Disallow instantiation - private_class_method :new, :allocate - - class << self - include ActionService::Signature - - # API methods have a +name+, which must be the Ruby method name to use when - # performing the invocation on the web service object. - # - # The signatures for the method input parameters and return value can - # by specified in +options+. - # - # A signature is an array of one or more parameter specifiers. - # A parameter specifier can be one of the following: - # - # * A symbol or string of representing one of the Action Service base types. - # See ActionService::Signature for a canonical list of the base types. - # * The Class object of the parameter type - # * A single-element Array containing one of the two preceding items. This - # will cause Action Service to treat the parameter at that position - # as an array containing only values of the given type. - # * A Hash containing as key the name of the parameter, and as value - # one of the three preceding items - # - # If no method input parameter or method return value signatures are given, - # the method is assumed to take no parameters and/or return no values of - # interest, and any values that are received by the server will be - # discarded and ignored. - # - # Valid options: - # [:expects] Signature for the method input parameters - # [:returns] Signature for the method return value - # [:expects_and_returns] Signature for both input parameters and return value - def api_method(name, options={}) - validate_options([:expects, :returns, :expects_and_returns], options.keys) - if options[:expects_and_returns] - expects = options[:expects_and_returns] - returns = options[:expects_and_returns] - else - expects = options[:expects] - returns = options[:returns] - end - expects = canonical_signature(expects) if expects - returns = canonical_signature(returns) if returns - if expects - expects.each do |param| - klass = signature_parameter_class(param) - klass = klass[0] if klass.is_a?(Array) - if klass.ancestors.include?(ActiveRecord::Base) - raise(ActionServiceError, "ActiveRecord model classes not allowed in :expects") - end - end - end - name = name.to_sym - public_name = public_api_method_name(name) - info = { :expects => expects, :returns => returns } - write_inheritable_hash("api_methods", name => info) - write_inheritable_hash("api_public_method_names", public_name => name) - end - - # Whether the given method name is a service method on this API - def has_api_method?(name) - api_methods.has_key?(name) - end - - # Whether the given public method name has a corresponding service method - # on this API - def has_public_api_method?(public_name) - api_public_method_names.has_key?(public_name) - end - - # The corresponding public method name for the given service method name - def public_api_method_name(name) - if inflect_names - name.to_s.camelize - else - name.to_s - end - end - - # The corresponding service method name for the given public method name - def api_method_name(public_name) - api_public_method_names[public_name] - end - - # A Hash containing all service methods on this API, and their - # associated metadata. - def api_methods - read_inheritable_attribute("api_methods") || {} - end - - private - def api_public_method_names - read_inheritable_attribute("api_public_method_names") || {} - end - - def validate_options(valid_option_keys, supplied_option_keys) - unknown_option_keys = supplied_option_keys - valid_option_keys - unless unknown_option_keys.empty? - raise(ActionServiceError, "Unknown options: #{unknown_option_keys}") - end - end - - end - end - end -end diff --git a/actionwebservice/lib/action_service/api/action_controller.rb b/actionwebservice/lib/action_service/api/action_controller.rb deleted file mode 100644 index d603f3a570..0000000000 --- a/actionwebservice/lib/action_service/api/action_controller.rb +++ /dev/null @@ -1,92 +0,0 @@ -module ActionService # :nodoc: - module API # :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 blogger 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 :api - 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 - probe_protocol_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_service/base.rb b/actionwebservice/lib/action_service/base.rb deleted file mode 100644 index 05fd2afd34..0000000000 --- a/actionwebservice/lib/action_service/base.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'action_service/support/class_inheritable_options' -require 'action_service/support/signature' - -module ActionService # :nodoc: - class ActionServiceError < StandardError # :nodoc: - end - - # An Action Service object implements a specified API. - # - # Used by controllers operating in _Delegated_ dispatching mode. - # - # ==== Example - # - # class PersonService < ActionService::Base - # web_service_api PersonAPI - # - # def find_person(criteria) - # Person.find_all [...] - # end - # - # def delete_person(id) - # Person.find_by_id(id).destroy - # end - # end - # - # class PersonAPI < ActionService::API::Base - # api_method :find_person, :expects => [SearchCriteria], :returns => [[Person]] - # api_method :delete_person, :expects => [:int] - # end - # - # class SearchCriteria < ActionStruct::Base - # member :firstname, :string - # member :lastname, :string - # member :email, :string - # end - class Base - # Whether to report exceptions back to the caller in the protocol's exception - # format - class_inheritable_option :web_service_exception_reporting, true - end -end diff --git a/actionwebservice/lib/action_service/client.rb b/actionwebservice/lib/action_service/client.rb deleted file mode 100644 index ce91529f20..0000000000 --- a/actionwebservice/lib/action_service/client.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'action_service/client/base' -require 'action_service/client/soap' -require 'action_service/client/xmlrpc' diff --git a/actionwebservice/lib/action_service/client/base.rb b/actionwebservice/lib/action_service/client/base.rb deleted file mode 100644 index 955887a4d8..0000000000 --- a/actionwebservice/lib/action_service/client/base.rb +++ /dev/null @@ -1,35 +0,0 @@ -module ActionService # :nodoc: - module Client # :nodoc: - class ClientError < StandardError # :nodoc: - end - - class Base # :nodoc: - def initialize(api, endpoint_uri) - @api = api - @endpoint_uri = endpoint_uri - end - - def method_missing(name, *args) # :nodoc: - call_name = method_name(name) - return super(name, *args) if call_name.nil? - perform_invocation(call_name, args) - end - - protected - def perform_invocation(method_name, args) # :nodoc: - raise NotImplementedError, "use a protocol-specific client" - end - - private - def method_name(name) - if @api.has_api_method?(name.to_sym) - name.to_s - elsif @api.has_public_api_method?(name.to_s) - @api.api_method_name(name.to_s).to_s - else - nil - end - end - end - end -end diff --git a/actionwebservice/lib/action_service/client/soap.rb b/actionwebservice/lib/action_service/client/soap.rb deleted file mode 100644 index c617f36589..0000000000 --- a/actionwebservice/lib/action_service/client/soap.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'soap/rpc/driver' -require 'uri' - -module ActionService # :nodoc: - module Client # :nodoc: - - # Implements SOAP client support (using RPC encoding for the messages). - # - # ==== Example Usage - # - # class PersonAPI < ActionService::API::Base - # api_method :find_all, :returns => [[Person]] - # end - # - # soap_client = ActionService::Client::Soap.new(PersonAPI, "http://...") - # persons = soap_client.find_all - # - class Soap < Base - - # Creates a new web service client using the SOAP RPC protocol. - # - # +api+ must be an ActionService::API::Base derivative, and - # +endpoint_uri+ must point at the relevant URL to which protocol requests - # will be sent with HTTP POST. - # - # Valid options: - # [:service_name] If the remote server has used a custom +wsdl_service_name+ - # option, you must specify it here - def initialize(api, endpoint_uri, options={}) - super(api, endpoint_uri) - @service_name = options[:service_name] || 'ActionService' - @namespace = "urn:#{@service_name}" - @mapper = ActionService::Protocol::Soap::SoapMapper.new(@namespace) - @protocol = ActionService::Protocol::Soap::SoapProtocol.new(@mapper) - @soap_action_base = options[:soap_action_base] - @soap_action_base ||= URI.parse(endpoint_uri).path - @driver = create_soap_rpc_driver(api, endpoint_uri) - end - - protected - def perform_invocation(method_name, args) - @driver.send(method_name, *args) - end - - def soap_action(method_name) - "#{@soap_action_base}/#{method_name}" - end - - private - def create_soap_rpc_driver(api, endpoint_uri) - @mapper.map_api(api) - driver = SoapDriver.new(endpoint_uri, nil) - driver.mapping_registry = @mapper.registry - api.api_methods.each do |name, info| - public_name = api.public_api_method_name(name) - qname = XSD::QName.new(@namespace, public_name) - action = soap_action(public_name) - expects = info[:expects] - returns = info[:returns] - param_def = [] - i = 1 - if expects - expects.each do |klass| - param_name = klass.is_a?(Hash) ? klass.keys[0] : "param#{i}" - mapping = @mapper.lookup(klass) - param_def << ['in', param_name, mapping.registry_mapping] - i += 1 - end - end - if returns - mapping = @mapper.lookup(returns[0]) - param_def << ['retval', 'return', mapping.registry_mapping] - end - driver.add_method(qname, action, name.to_s, param_def) - end - driver - end - - class SoapDriver < SOAP::RPC::Driver # :nodoc: - def add_method(qname, soapaction, name, param_def) - @proxy.add_rpc_method(qname, soapaction, name, param_def) - add_rpc_method_interface(name, param_def) - end - end - end - end -end diff --git a/actionwebservice/lib/action_service/client/xmlrpc.rb b/actionwebservice/lib/action_service/client/xmlrpc.rb deleted file mode 100644 index d0d007f871..0000000000 --- a/actionwebservice/lib/action_service/client/xmlrpc.rb +++ /dev/null @@ -1,76 +0,0 @@ -require 'uri' -require 'xmlrpc/client' - -module ActionService # :nodoc: - module Client # :nodoc: - - # Implements XML-RPC client support - # - # ==== Example Usage - # - # class BloggerAPI < ActionService::API::Base - # inflect_names false - # api_method :getRecentPosts, :returns => [[Blog::Post]] - # end - # - # blog = ActionService::Client::XmlRpc.new(BloggerAPI, "http://.../RPC", :handler_name => "blogger") - # posts = blog.getRecentPosts - class XmlRpc < Base - - # Creates a new web service client using the XML-RPC protocol. - # - # +api+ must be an ActionService::API::Base derivative, and - # +endpoint_uri+ must point at the relevant URL to which protocol requests - # will be sent with HTTP POST. - # - # Valid options: - # [:handler_name] If the remote server defines its services inside special - # handler (the Blogger API uses a "blogger" handler name for example), - # provide it here, or your method calls will fail - def initialize(api, endpoint_uri, options={}) - @api = api - @handler_name = options[:handler_name] - @client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout]) - end - - protected - def perform_invocation(method_name, args) - args = transform_outgoing_method_params(method_name, args) - ok, return_value = @client.call2(public_name(method_name), *args) - return transform_return_value(method_name, return_value) if ok - raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}") - end - - def transform_outgoing_method_params(method_name, params) - info = @api.api_methods[method_name.to_sym] - signature = info[:expects] - signature_length = signature.nil?? 0 : signature.length - if signature_length != params.length - raise(ProtocolError, "API declares #{public_name(method_name)} to accept " + - "#{signature_length} parameters, but #{params.length} parameters " + - "were supplied") - end - if signature_length > 0 - signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature) - (1..signature.size).each do |i| - i -= 1 - params[i] = Protocol::XmlRpc::XmlRpcProtocol.ruby_to_xmlrpc(params[i], signature[i]) - end - end - params - end - - def transform_return_value(method_name, return_value) - info = @api.api_methods[method_name.to_sym] - return true unless signature = info[:returns] - signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature) - Protocol::XmlRpc::XmlRpcProtocol.xmlrpc_to_ruby(return_value, signature[0]) - end - - def public_name(method_name) - public_name = @api.public_api_method_name(method_name) - @handler_name ? "#{@handler_name}.#{public_name}" : public_name - end - end - end -end diff --git a/actionwebservice/lib/action_service/container.rb b/actionwebservice/lib/action_service/container.rb deleted file mode 100644 index 282e6ad928..0000000000 --- a/actionwebservice/lib/action_service/container.rb +++ /dev/null @@ -1,232 +0,0 @@ -module ActionService # :nodoc: - module Container # :nodoc: - class ContainerError < ActionService::ActionServiceError # :nodoc: - end - - def self.append_features(base) # :nodoc: - super - base.class_inheritable_option(:web_service_dispatching_mode, :direct) - base.class_inheritable_option(:web_service_exception_reporting, true) - base.extend(ClassMethods) - base.send(:include, ActionService::Container::InstanceMethods) - end - - module ClassMethods - # Declares a web service that will provides access to the API of the given - # +object+. +object+ must be an ActionService::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 - - private - def dispatch_web_service_request(protocol_request) - case web_service_dispatching_mode - when :direct - dispatch_direct_web_service_request(protocol_request) - when :delegated - dispatch_delegated_web_service_request(protocol_request) - else - raise(ContainerError, "unsupported dispatching mode :#{web_service_dispatching_mode}") - end - end - - def dispatch_direct_web_service_request(protocol_request) - public_method_name = protocol_request.public_method_name - api = self.class.web_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_web_service_request(protocol_request) - web_service_name = protocol_request.web_service_name - service = web_service_object(web_service_name) - api = service.class.web_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 /#{web_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 /#{web_service_name}##{public_method_name}") - end - result = perform_invoke.call - end - protocol_request.marshal(result) - end - end - end -end diff --git a/actionwebservice/lib/action_service/invocation.rb b/actionwebservice/lib/action_service/invocation.rb deleted file mode 100644 index f35ab76386..0000000000 --- a/actionwebservice/lib/action_service/invocation.rb +++ /dev/null @@ -1,252 +0,0 @@ -module ActionService # :nodoc: - module Invocation # :nodoc: - ConcreteInvocation = :concrete - VirtualInvocation = :virtual - UnpublishedConcreteInvocation = :unpublished_concrete - - class InvocationError < ActionService::ActionServiceError # :nodoc: - end - - def self.append_features(base) # :nodoc: - super - base.extend(ClassMethods) - base.send(:include, ActionService::Invocation::InstanceMethods) - end - - # Invocation interceptors provide a means to execute custom code before - # and after method invocations on ActionService::Base objects. - # - # When running in _Direct_ dispatching mode, ActionController filters - # should be used for this functionality instead. - # - # The semantics of invocation interceptors are the same as ActionController - # filters, and accept the same parameters and options. - # - # A _before_ interceptor can also cancel execution by returning +false+, - # or returning a [false, "cancel reason"] array if it wishes to supply - # a reason for canceling the request. - # - # === Example - # - # class CustomService < ActionService::Base - # before_invocation :intercept_add, :only => [:add] - # - # def add(a, b) - # a + b - # end - # - # private - # def intercept_add - # return [false, "permission denied"] # cancel it - # end - # end - # - # Options: - # [:except] A list of methods for which the interceptor will NOT be called - # [:only] A list of methods for which the interceptor WILL be called - module ClassMethods - # Appends the given +interceptors+ to be called - # _before_ method invocation. - def append_before_invocation(*interceptors, &block) - conditions = extract_conditions!(interceptors) - interceptors << block if block_given? - add_interception_conditions(interceptors, conditions) - append_interceptors_to_chain("before", interceptors) - end - - # Prepends the given +interceptors+ to be called - # _before_ method invocation. - def prepend_before_invocation(*interceptors, &block) - conditions = extract_conditions!(interceptors) - interceptors << block if block_given? - add_interception_conditions(interceptors, conditions) - prepend_interceptors_to_chain("before", interceptors) - end - - alias :before_invocation :append_before_invocation - - # Appends the given +interceptors+ to be called - # _after_ method invocation. - def append_after_invocation(*interceptors, &block) - conditions = extract_conditions!(interceptors) - interceptors << block if block_given? - add_interception_conditions(interceptors, conditions) - append_interceptors_to_chain("after", interceptors) - end - - # Prepends the given +interceptors+ to be called - # _after_ method invocation. - def prepend_after_invocation(*interceptors, &block) - conditions = extract_conditions!(interceptors) - interceptors << block if block_given? - add_interception_conditions(interceptors, conditions) - prepend_interceptors_to_chain("after", interceptors) - end - - alias :after_invocation :append_after_invocation - - def before_invocation_interceptors # :nodoc: - read_inheritable_attribute("before_invocation_interceptors") - end - - def after_invocation_interceptors # :nodoc: - read_inheritable_attribute("after_invocation_interceptors") - end - - def included_intercepted_methods # :nodoc: - read_inheritable_attribute("included_intercepted_methods") || {} - end - - def excluded_intercepted_methods # :nodoc: - read_inheritable_attribute("excluded_intercepted_methods") || {} - end - - private - def append_interceptors_to_chain(condition, interceptors) - write_inheritable_array("#{condition}_invocation_interceptors", interceptors) - end - - def prepend_interceptors_to_chain(condition, interceptors) - interceptors = interceptors + read_inheritable_attribute("#{condition}_invocation_interceptors") - write_inheritable_attribute("#{condition}_invocation_interceptors", interceptors) - end - - def extract_conditions!(interceptors) - return nil unless interceptors.last.is_a? Hash - interceptors.pop - end - - def add_interception_conditions(interceptors, conditions) - return unless conditions - included, excluded = conditions[:only], conditions[:except] - write_inheritable_hash("included_intercepted_methods", condition_hash(interceptors, included)) && return if included - write_inheritable_hash("excluded_intercepted_methods", condition_hash(interceptors, excluded)) if excluded - end - - def condition_hash(interceptors, *methods) - interceptors.inject({}) {|hash, interceptor| hash.merge(interceptor => methods.flatten.map {|method| method.to_s})} - end - end - - module InstanceMethods # :nodoc: - def self.append_features(base) - super - base.class_eval do - alias_method :perform_invocation_without_interception, :perform_invocation - alias_method :perform_invocation, :perform_invocation_with_interception - end - end - - def perform_invocation_with_interception(invocation, &block) - return if before_invocation(invocation.method_name, invocation.params, &block) == false - result = perform_invocation_without_interception(invocation) - after_invocation(invocation.method_name, invocation.params, result) - result - end - - def perform_invocation(invocation) - if invocation.concrete? - unless self.respond_to?(invocation.method_name) && \ - self.class.web_service_api.has_api_method?(invocation.method_name) - raise InvocationError, "no such web service method '#{invocation.method_name}' on service object" - end - end - params = invocation.params - if invocation.concrete? || invocation.unpublished_concrete? - self.send(invocation.method_name, *params) - else - if invocation.block - params = invocation.block_params + params - invocation.block.call(invocation.public_method_name, *params) - else - self.send(invocation.method_name, *params) - end - end - end - - def before_invocation(name, args, &block) - call_interceptors(self.class.before_invocation_interceptors, [name, args], &block) - end - - def after_invocation(name, args, result) - call_interceptors(self.class.after_invocation_interceptors, [name, args, result]) - end - - private - - def call_interceptors(interceptors, interceptor_args, &block) - if interceptors and not interceptors.empty? - interceptors.each do |interceptor| - next if method_exempted?(interceptor, interceptor_args[0].to_s) - result = case - when interceptor.is_a?(Symbol) - self.send(interceptor, *interceptor_args) - when interceptor_block?(interceptor) - interceptor.call(self, *interceptor_args) - when interceptor_class?(interceptor) - interceptor.intercept(self, *interceptor_args) - else - raise( - InvocationError, - "Interceptors need to be either a symbol, proc/method, or a class implementing a static intercept method" - ) - end - reason = nil - if result.is_a?(Array) - reason = result[1] if result[1] - result = result[0] - end - if result == false - block.call(reason) if block && reason - return false - end - end - end - end - - def interceptor_block?(interceptor) - interceptor.respond_to?("call") && (interceptor.arity == 3 || interceptor.arity == -1) - end - - def interceptor_class?(interceptor) - interceptor.respond_to?("intercept") - end - - def method_exempted?(interceptor, method_name) - case - when self.class.included_intercepted_methods[interceptor] - !self.class.included_intercepted_methods[interceptor].include?(method_name) - when self.class.excluded_intercepted_methods[interceptor] - self.class.excluded_intercepted_methods[interceptor].include?(method_name) - end - end - end - - class InvocationRequest # :nodoc: - attr_accessor :type - attr :public_method_name - attr_accessor :method_name - attr_accessor :params - attr_accessor :block - attr :block_params - - def initialize(type, public_method_name, method_name, params=nil) - @type = type - @public_method_name = public_method_name - @method_name = method_name - @params = params || [] - @block = nil - @block_params = [] - end - - def concrete? - @type == ConcreteInvocation ? true : false - end - - def unpublished_concrete? - @type == UnpublishedConcreteInvocation ? true : false - end - end - - end -end diff --git a/actionwebservice/lib/action_service/protocol.rb b/actionwebservice/lib/action_service/protocol.rb deleted file mode 100644 index 5e71b2bcfd..0000000000 --- a/actionwebservice/lib/action_service/protocol.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'action_service/protocol/abstract' -require 'action_service/protocol/registry' -require 'action_service/protocol/soap' -require 'action_service/protocol/xmlrpc' diff --git a/actionwebservice/lib/action_service/protocol/abstract.rb b/actionwebservice/lib/action_service/protocol/abstract.rb deleted file mode 100644 index bd02b6e829..0000000000 --- a/actionwebservice/lib/action_service/protocol/abstract.rb +++ /dev/null @@ -1,128 +0,0 @@ -module ActionService # :nodoc: - module Protocol # :nodoc: - CheckedMessage = :checked - UncheckedMessage = :unchecked - - class ProtocolError < ActionService::ActionServiceError # :nodoc: - end - - class AbstractProtocol # :nodoc: - attr :container_class - - def initialize(container_class) - @container_class = container_class - end - - def unmarshal_request(protocol_request) - raise NotImplementedError - end - - def marshal_response(protocol_request, return_value) - raise NotImplementedError - end - - def marshal_exception(exception) - raise NotImplementedError - end - - def self.create_protocol_request(container_class, action_pack_request) - nil - end - - def self.create_protocol_client(api, protocol_name, endpoint_uri, options) - nil - end - end - - class AbstractProtocolMessage # :nodoc: - attr_accessor :signature - attr_accessor :return_signature - attr_accessor :type - attr :options - - def initialize(options={}) - @signature = @return_signature = nil - @options = options - @type = @options[:type] || CheckedMessage - end - - def signature=(value) - return if value.nil? - @signature = [] - value.each do |klass| - if klass.is_a?(Hash) - @signature << klass.values.shift - else - @signature << klass - end - end - @signature - end - - def checked? - @type == CheckedMessage - end - - def check_parameter_types(values, signature) - return unless checked? && signature - unless signature.length == values.length - raise(ProtocolError, "Signature and parameter lengths mismatch") - end - (1..signature.length).each do |i| - check_compatibility(signature[i-1], values[i-1].class) - end - end - - def check_compatibility(expected_class, received_class) - return if \ - (expected_class == TrueClass or expected_class == FalseClass) and \ - (received_class == TrueClass or received_class == FalseClass) - unless received_class.ancestors.include?(expected_class) or \ - expected_class.ancestors.include?(received_class) - raise(ProtocolError, "value of type #{received_class.name} is not " + - "compatible with expected type #{expected_class.name}") - end - end - end - - class ProtocolRequest < AbstractProtocolMessage # :nodoc: - attr :protocol - attr :raw_body - - attr_accessor :web_service_name - attr_accessor :public_method_name - attr_accessor :content_type - - def initialize(protocol, raw_body, web_service_name, public_method_name, content_type, options={}) - super(options) - @protocol = protocol - @raw_body = raw_body - @web_service_name = web_service_name - @public_method_name = public_method_name - @content_type = content_type - end - - def unmarshal - @protocol.unmarshal_request(self) - end - - def marshal(return_value) - @protocol.marshal_response(self, return_value) - end - end - - class ProtocolResponse < AbstractProtocolMessage # :nodoc: - attr :protocol - attr :raw_body - - attr_accessor :content_type - - def initialize(protocol, raw_body, content_type, options={}) - super(options) - @protocol = protocol - @raw_body = raw_body - @content_type = content_type - end - end - end -end diff --git a/actionwebservice/lib/action_service/protocol/registry.rb b/actionwebservice/lib/action_service/protocol/registry.rb deleted file mode 100644 index e06361f916..0000000000 --- a/actionwebservice/lib/action_service/protocol/registry.rb +++ /dev/null @@ -1,55 +0,0 @@ -module ActionService # :nodoc: - module Protocol # :nodoc: - HeaderAndBody = :header_and_body - BodyOnly = :body_only - - module Registry # :nodoc: - def self.append_features(base) # :nodoc: - super - base.extend(ClassMethods) - base.send(:include, ActionService::Protocol::Registry::InstanceMethods) - end - - module ClassMethods # :nodoc: - def register_protocol(type, klass) # :nodoc: - case type - when HeaderAndBody - write_inheritable_array("header_and_body_protocols", [klass]) - when BodyOnly - write_inheritable_array("body_only_protocols", [klass]) - else - raise(ProtocolError, "unknown protocol type #{type}") - end - end - end - - module InstanceMethods # :nodoc: - private - def probe_request_protocol(action_pack_request) - (header_and_body_protocols + body_only_protocols).each do |protocol| - protocol_request = protocol.create_protocol_request(self.class, action_pack_request) - return protocol_request if protocol_request - end - raise(ProtocolError, "unsupported request message format") - end - - def probe_protocol_client(api, protocol_name, endpoint_uri, options) - (header_and_body_protocols + body_only_protocols).each do |protocol| - protocol_client = protocol.create_protocol_client(api, protocol_name, endpoint_uri, options) - return protocol_client if protocol_client - end - raise(ProtocolError, "unsupported client protocol :#{protocol_name}") - end - - def header_and_body_protocols - self.class.read_inheritable_attribute("header_and_body_protocols") || [] - end - - def body_only_protocols - self.class.read_inheritable_attribute("body_only_protocols") || [] - end - end - - end - end -end diff --git a/actionwebservice/lib/action_service/protocol/soap.rb b/actionwebservice/lib/action_service/protocol/soap.rb deleted file mode 100644 index 993e174e52..0000000000 --- a/actionwebservice/lib/action_service/protocol/soap.rb +++ /dev/null @@ -1,484 +0,0 @@ -require 'soap/processor' -require 'soap/mapping' -require 'soap/rpc/element' -require 'xsd/datatypes' -require 'xsd/ns' -require 'singleton' - -module ActionService # :nodoc: - module Protocol # :nodoc: - module Soap # :nodoc: - class ProtocolError < ActionService::ActionServiceError # :nodoc: - end - - def self.append_features(base) # :nodoc: - super - base.register_protocol(HeaderAndBody, SoapProtocol) - base.extend(ClassMethods) - base.wsdl_service_name('ActionService') - end - - module ClassMethods - # Specifies the WSDL service name to use when generating WSDL. Highly - # recommended that you set this value, or code generators may generate - # classes with very generic names. - # - # === Example - # class MyController < ActionController::Base - # wsdl_service_name 'MyService' - # end - def wsdl_service_name(name) - write_inheritable_attribute("soap_mapper", SoapMapper.new("urn:#{name}")) - end - - def soap_mapper # :nodoc: - read_inheritable_attribute("soap_mapper") - end - end - - class SoapProtocol < AbstractProtocol # :nodoc: - attr :mapper - - def initialize(mapper) - @mapper = mapper - end - - def self.create_protocol_request(container_class, action_pack_request) - soap_action = extract_soap_action(action_pack_request) - return nil unless soap_action - service_name = action_pack_request.parameters['action'] - public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1] - content_type = action_pack_request.env['HTTP_CONTENT_TYPE'] - content_type ||= 'text/xml' - protocol = SoapProtocol.new(container_class.soap_mapper) - ProtocolRequest.new(protocol, - action_pack_request.raw_post, - service_name.to_sym, - public_method_name, - content_type) - end - - def self.create_protocol_client(api, protocol_name, endpoint_uri, options) - return nil unless protocol_name.to_s.downcase.to_sym == :soap - ActionService::Client::Soap.new(api, endpoint_uri, options) - end - - def unmarshal_request(protocol_request) - unmarshal = lambda do - envelope = SOAP::Processor.unmarshal(protocol_request.raw_body) - request = envelope.body.request - values = request.collect{|k, v| request[k]} - soap_to_ruby_array(values) - end - signature = protocol_request.signature - if signature - map_signature_types(signature) - values = unmarshal.call - signature = signature.map{|x|mapper.lookup(x).ruby_klass} - protocol_request.check_parameter_types(values, signature) - values - else - if protocol_request.checked? - [] - else - unmarshal.call - end - end - end - - def marshal_response(protocol_request, return_value) - marshal = lambda do |signature| - mapping = mapper.lookup(signature[0]) - return_value = fixup_array_types(mapping, return_value) - signature = signature.map{|x|mapper.lookup(x).ruby_klass} - protocol_request.check_parameter_types([return_value], signature) - param_def = [['retval', 'return', mapping.registry_mapping]] - [param_def, ruby_to_soap(return_value)] - end - signature = protocol_request.return_signature - param_def = nil - if signature - param_def, return_value = marshal.call(signature) - else - if protocol_request.checked? - param_def, return_value = nil, nil - else - param_def, return_value = marshal.call([return_value.class]) - end - end - qname = XSD::QName.new(mapper.custom_namespace, - protocol_request.public_method_name) - response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def) - response.retval = return_value unless return_value.nil? - ProtocolResponse.new(self, create_response(response), 'text/xml') - end - - def marshal_exception(exc) - ProtocolResponse.new(self, create_exception_response(exc), 'text/xml') - end - - private - def self.extract_soap_action(request) - return nil unless request.method == :post - content_type = request.env['HTTP_CONTENT_TYPE'] || 'text/xml' - return nil unless content_type - soap_action = request.env['HTTP_SOAPACTION'] - return nil unless soap_action - soap_action.gsub!(/^"/, '') - soap_action.gsub!(/"$/, '') - soap_action.strip! - return nil if soap_action.empty? - soap_action - end - - def fixup_array_types(mapping, obj) - mapping.each_attribute do |name, type, attr_mapping| - if attr_mapping.custom_type? - attr_obj = obj.send(name) - new_obj = fixup_array_types(attr_mapping, attr_obj) - obj.send("#{name}=", new_obj) unless new_obj.equal?(attr_obj) - end - end - if mapping.is_a?(SoapArrayMapping) - obj = mapping.ruby_klass.new(obj) - # man, this is going to be slow for big arrays :( - (1..obj.size).each do |i| - i -= 1 - obj[i] = fixup_array_types(mapping.element_mapping, obj[i]) - end - else - if !mapping.generated_klass.nil? && mapping.generated_klass.respond_to?(:members) - # have to map the publically visible structure of the class - new_obj = mapping.generated_klass.new - mapping.generated_klass.members.each do |name, klass| - new_obj.send("#{name}=", obj.send(name)) - end - obj = new_obj - end - end - obj - end - - def map_signature_types(types) - types.collect{|type| mapper.map(type)} - end - - def create_response(body) - header = SOAP::SOAPHeader.new - body = SOAP::SOAPBody.new(body) - envelope = SOAP::SOAPEnvelope.new(header, body) - SOAP::Processor.marshal(envelope) - end - - def create_exception_response(exc) - detail = SOAP::Mapping::SOAPException.new(exc) - body = SOAP::SOAPFault.new( - SOAP::SOAPString.new('Server'), - SOAP::SOAPString.new(exc.to_s), - SOAP::SOAPString.new(self.class.name), - SOAP::Mapping.obj2soap(detail)) - create_response(body) - end - - def ruby_to_soap(obj) - SOAP::Mapping.obj2soap(obj, mapper.registry) - end - - def soap_to_ruby(obj) - SOAP::Mapping.soap2obj(obj, mapper.registry) - end - - def soap_to_ruby_array(array) - array.map{|x| soap_to_ruby(x)} - end - end - - class SoapMapper # :nodoc: - attr :registry - attr :custom_namespace - attr :custom_types - - def initialize(custom_namespace) - @custom_namespace = custom_namespace - @registry = SOAP::Mapping::Registry.new - @klass2map = {} - @custom_types = {} - @ar2klass = {} - end - - def lookup(klass) - lookup_klass = klass.is_a?(Array) ? klass[0] : klass - generated_klass = nil - unless lookup_klass.respond_to?(:ancestors) - raise(ProtocolError, "expected parameter type definition to be a Class") - end - if lookup_klass.ancestors.include?(ActiveRecord::Base) - generated_klass = @ar2klass.has_key?(klass) ? @ar2klass[klass] : nil - klass = generated_klass if generated_klass - end - return @klass2map[klass] if @klass2map.has_key?(klass) - - custom_type = false - - ruby_klass = select_class(lookup_klass) - generated_klass = @ar2klass[lookup_klass] if @ar2klass.has_key?(lookup_klass) - type_name = ruby_klass.name - - # Array signatures generate a double-mapping and require generation - # of an Array subclass to represent the mapping in the SOAP - # registry - array_klass = nil - if klass.is_a?(Array) - array_klass = Class.new(Array) do - module_eval <<-END - def self.name - "#{type_name}Array" - end - END - end - end - - mapping = @registry.find_mapped_soap_class(ruby_klass) rescue nil - unless mapping - # Custom structured type, generate a mapping - info = { :type => XSD::QName.new(@custom_namespace, type_name) } - @registry.add(ruby_klass, - SOAP::SOAPStruct, - SOAP::Mapping::Registry::TypedStructFactory, - info) - mapping = ensure_mapped(ruby_klass) - custom_type = true - end - - array_mapping = nil - if array_klass - # Typed array always requires a custom type. The info of the array - # is the info of its element type (in mapping[2]), falling back - # to SOAP base types. - info = mapping[2] - info ||= {} - info[:type] ||= soap_base_type_qname(mapping[0]) - @registry.add(array_klass, - SOAP::SOAPArray, - SOAP::Mapping::Registry::TypedArrayFactory, - info) - array_mapping = ensure_mapped(array_klass) - end - - if array_mapping - @klass2map[ruby_klass] = SoapMapping.new(self, - type_name, - ruby_klass, - generated_klass, - mapping[0], - mapping, - custom_type) - @klass2map[klass] = SoapArrayMapping.new(self, - type_name, - array_klass, - array_mapping[0], - array_mapping, - @klass2map[ruby_klass]) - @custom_types[klass] = @klass2map[klass] - @custom_types[ruby_klass] = @klass2map[ruby_klass] if custom_type - else - @klass2map[klass] = SoapMapping.new(self, - type_name, - ruby_klass, - generated_klass, - mapping[0], - mapping, - custom_type) - @custom_types[klass] = @klass2map[klass] if custom_type - end - - @klass2map[klass] - end - alias :map :lookup - - def map_container_services(container, &block) - dispatching_mode = container.web_service_dispatching_mode - web_services = nil - case dispatching_mode - when :direct - api = container.class.web_service_api - if container.respond_to?(:controller_class_name) - web_service_name = container.controller_class_name.sub(/Controller$/, '').underscore - else - web_service_name = container.class.name.demodulize.underscore - end - web_services = { web_service_name => api } - when :delegated - web_services = {} - container.class.web_services.each do |web_service_name, web_service_info| - begin - object = container.web_service_object(web_service_name) - rescue Exception => e - raise(ProtocolError, "failed to retrieve web service object for web service '#{web_service_name}': #{e.message}") - end - web_services[web_service_name] = object.class.web_service_api - end - end - web_services.each do |web_service_name, api| - if api.nil? - raise(ProtocolError, "no web service API set while in :#{dispatching_mode} mode") - end - map_api(api) do |api_methods| - yield web_service_name, api, api_methods if block_given? - end - end - end - - def map_api(api, &block) - lookup_proc = lambda do |klass| - mapping = lookup(klass) - custom_mapping = nil - if mapping.respond_to?(:element_mapping) - custom_mapping = mapping.element_mapping - else - custom_mapping = mapping - end - if custom_mapping && custom_mapping.custom_type? - # What gives? This is required so that structure types - # referenced only by structures (and not signatures) still - # have a custom type mapping in the registry (needed for WSDL - # generation). - custom_mapping.each_attribute{} - end - mapping - end - api_methods = block.nil?? nil : {} - api.api_methods.each do |method_name, method_info| - expects = method_info[:expects] - expects_signature = nil - if expects - expects_signature = block ? [] : nil - expects.each do |klass| - lookup_klass = nil - if klass.is_a?(Hash) - lookup_klass = lookup_proc.call(klass.values[0]) - expects_signature << {klass.keys[0]=>lookup_klass} if block - else - lookup_klass = lookup_proc.call(klass) - expects_signature << lookup_klass if block - end - end - end - returns = method_info[:returns] - returns_signature = returns ? returns.map{|klass| lookup_proc.call(klass)} : nil - if block - api_methods[method_name] = { - :expects => expects_signature, - :returns => returns_signature - } - end - end - yield api_methods if block - end - - private - def select_class(klass) - return Integer if klass == Fixnum - if klass.ancestors.include?(ActiveRecord::Base) - new_klass = Class.new(ActionService::Struct) - new_klass.class_eval <<-EOS - def self.name - "#{klass.name}" - end - EOS - klass.columns.each do |column| - next if column.klass.nil? - new_klass.send(:member, column.name.to_sym, column.klass) - end - @ar2klass[klass] = new_klass - return new_klass - end - klass - end - - def ensure_mapped(klass) - mapping = @registry.find_mapped_soap_class(klass) rescue nil - raise(ProtocolError, "failed to register #{klass.name}") unless mapping - mapping - end - - def soap_base_type_qname(base_type) - xsd_type = base_type.ancestors.find{|c| c.const_defined? 'Type'} - xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type - end - end - - class SoapMapping # :nodoc: - attr :ruby_klass - attr :generated_klass - attr :soap_klass - attr :registry_mapping - - def initialize(mapper, type_name, ruby_klass, generated_klass, soap_klass, registry_mapping, - custom_type=false) - @mapper = mapper - @type_name = type_name - @ruby_klass = ruby_klass - @generated_klass = generated_klass - @soap_klass = soap_klass - @registry_mapping = registry_mapping - @custom_type = custom_type - end - - def type_name - @type_name - end - - def custom_type? - @custom_type - end - - def qualified_type_name - name = type_name - if custom_type? - "typens:#{name}" - else - xsd_type_for(@soap_klass) - end - end - - def each_attribute(&block) - if @ruby_klass.respond_to?(:members) - @ruby_klass.members.each do |name, klass| - name = name.to_s - mapping = @mapper.lookup(klass) - yield name, mapping.qualified_type_name, mapping - end - end - end - - def is_xsd_type?(klass) - klass.ancestors.include?(XSD::NSDBase) - end - - def xsd_type_for(klass) - ns = XSD::NS.new - ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag) - xsd_klass = klass.ancestors.find{|c| c.const_defined?('Type')} - return ns.name(XSD::AnyTypeName) unless xsd_klass - ns.name(xsd_klass.const_get('Type')) - end - end - - class SoapArrayMapping < SoapMapping # :nodoc: - attr :element_mapping - - def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping, element_mapping) - super(mapper, type_name, ruby_klass, nil, soap_klass, registry_mapping, true) - @element_mapping = element_mapping - end - - def type_name - super + "Array" - end - - def each_attribute(&block); end - end - end - end -end diff --git a/actionwebservice/lib/action_service/protocol/xmlrpc.rb b/actionwebservice/lib/action_service/protocol/xmlrpc.rb deleted file mode 100644 index 32b8e00327..0000000000 --- a/actionwebservice/lib/action_service/protocol/xmlrpc.rb +++ /dev/null @@ -1,183 +0,0 @@ -require 'xmlrpc/parser' -require 'xmlrpc/create' -require 'xmlrpc/config' -require 'xmlrpc/utils' -require 'singleton' - -module XMLRPC # :nodoc: - class XmlRpcHelper # :nodoc: - include Singleton - include ParserWriterChooseMixin - - def parse_method_call(message) - parser().parseMethodCall(message) - end - - def create_method_response(successful, return_value) - create().methodResponse(successful, return_value) - end - end -end - -module ActionService # :nodoc: - module Protocol # :nodoc: - module XmlRpc # :nodoc: - def self.append_features(base) # :nodoc: - super - base.register_protocol(BodyOnly, XmlRpcProtocol) - end - - class XmlRpcProtocol < AbstractProtocol # :nodoc: - def self.create_protocol_request(container_class, action_pack_request) - helper = XMLRPC::XmlRpcHelper.instance - service_name = action_pack_request.parameters['action'] - methodname, params = helper.parse_method_call(action_pack_request.raw_post) - methodname.gsub!(/^[^\.]+\./, '') unless methodname =~ /^system\./ # XXX - protocol = XmlRpcProtocol.new(container_class) - content_type = action_pack_request.env['HTTP_CONTENT_TYPE'] - content_type ||= 'text/xml' - request = ProtocolRequest.new(protocol, - action_pack_request.raw_post, - service_name.to_sym, - methodname, - content_type, - :xmlrpc_values => params) - request - rescue - nil - end - - def self.create_protocol_client(api, protocol_name, endpoint_uri, options) - return nil unless protocol_name.to_s.downcase.to_sym == :xmlrpc - ActionService::Client::XmlRpc.new(api, endpoint_uri, options) - end - - def initialize(container_class) - super(container_class) - container_class.write_inheritable_hash('default_system_methods', XmlRpcProtocol => method(:xmlrpc_default_system_handler)) - end - - def unmarshal_request(protocol_request) - values = protocol_request.options[:xmlrpc_values] - signature = protocol_request.signature - if signature - values = self.class.transform_incoming_method_params(self.class.transform_array_types(signature), values) - protocol_request.check_parameter_types(values, check_array_types(signature)) - values - else - protocol_request.checked? ? [] : values - end - end - - def marshal_response(protocol_request, return_value) - helper = XMLRPC::XmlRpcHelper.instance - signature = protocol_request.return_signature - if signature - protocol_request.check_parameter_types([return_value], check_array_types(signature)) - return_value = self.class.transform_return_value(self.class.transform_array_types(signature), return_value) - raw_response = helper.create_method_response(true, return_value) - else - # XML-RPC doesn't have the concept of a void method, nor does it - # support a nil return value, so return true if we would have returned - # nil - if protocol_request.checked? - raw_response = helper.create_method_response(true, true) - else - return_value = true if return_value.nil? - raw_response = helper.create_method_response(true, return_value) - end - end - ProtocolResponse.new(self, raw_response, 'text/xml') - end - - def marshal_exception(exception) - helper = XMLRPC::XmlRpcHelper.instance - exception = XMLRPC::FaultException.new(1, exception.message) - raw_response = helper.create_method_response(false, exception) - ProtocolResponse.new(self, raw_response, 'text/xml') - end - - class << self - def transform_incoming_method_params(signature, params) - (1..signature.size).each do |i| - i -= 1 - params[i] = xmlrpc_to_ruby(params[i], signature[i]) - end - params - end - - def transform_return_value(signature, return_value) - ruby_to_xmlrpc(return_value, signature[0]) - end - - def ruby_to_xmlrpc(param, param_class) - if param_class.is_a?(XmlRpcArray) - param.map{|p| ruby_to_xmlrpc(p, param_class.klass)} - elsif param_class.ancestors.include?(ActiveRecord::Base) - param.instance_variable_get('@attributes') - elsif param_class.ancestors.include?(ActionService::Struct) - struct = {} - param_class.members.each do |name, klass| - value = param.send(name) - next if value.nil? - struct[name.to_s] = value - end - struct - else - param - end - end - - def xmlrpc_to_ruby(param, param_class) - if param_class.is_a?(XmlRpcArray) - param.map{|p| xmlrpc_to_ruby(p, param_class.klass)} - elsif param_class.ancestors.include?(ActiveRecord::Base) - raise(ProtocolError, "incoming ActiveRecord::Base types are not allowed") - elsif param_class.ancestors.include?(ActionService::Struct) - unless param.is_a?(Hash) - raise(ProtocolError, "expected parameter to be a Hash") - end - new_param = param_class.new - param_class.members.each do |name, klass| - new_param.send('%s=' % name.to_s, param[name.to_s]) - end - new_param - else - param - end - end - - def transform_array_types(signature) - signature.map{|x| x.is_a?(Array) ? XmlRpcArray.new(x[0]) : x} - end - end - - private - def xmlrpc_default_system_handler(name, service_class, *args) - case name - when 'system.listMethods' - methods = [] - api = service_class.web_service_api - api.api_methods.each do |name, info| - methods << api.public_api_method_name(name) - end - methods.sort - else - throw :try_default - end - end - - def check_array_types(signature) - signature.map{|x| x.is_a?(Array) ? Array : x} - end - - class XmlRpcArray - attr :klass - def initialize(klass) - @klass = klass - end - end - end - end - end -end diff --git a/actionwebservice/lib/action_service/router.rb b/actionwebservice/lib/action_service/router.rb deleted file mode 100644 index 16f0ae4463..0000000000 --- a/actionwebservice/lib/action_service/router.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'action_service/router/action_controller' -require 'action_service/router/wsdl' diff --git a/actionwebservice/lib/action_service/router/action_controller.rb b/actionwebservice/lib/action_service/router/action_controller.rb deleted file mode 100644 index ca9c94e35c..0000000000 --- a/actionwebservice/lib/action_service/router/action_controller.rb +++ /dev/null @@ -1,99 +0,0 @@ -module ActionService # :nodoc: - module Router # :nodoc: - module ActionController # :nodoc: - def self.append_features(base) # :nodoc: - base.add_web_service_api_callback do |container_class, api| - if container_class.web_service_dispatching_mode == :direct - container_class.class_eval <<-EOS - def api - process_action_service_request - end - EOS - end - end - base.add_web_service_definition_callback do |klass, name, info| - if klass.web_service_dispatching_mode == :delegated - klass.class_eval <<-EOS - def #{name} - process_action_service_request - end - EOS - end - end - base.send(:include, ActionService::Router::ActionController::InstanceMethods) - end - - module InstanceMethods # :nodoc: - private - def process_action_service_request - protocol_request = nil - begin - begin - protocol_request = probe_request_protocol(self.request) - rescue Exception => e - unless logger.nil? - logger.error "Invalid request: #{e.message}" - logger.error self.request.raw_post - end - raise - end - if protocol_request - log_request(protocol_request) - protocol_response = dispatch_web_service_request(protocol_request) - log_response(protocol_response) - response_options = { - :type => protocol_response.content_type, - :disposition => 'inline' - } - send_data(protocol_response.raw_body, response_options) - else - logger.fatal "Invalid Action Service service or method requested" unless logger.nil? - render_text 'Internal protocol error', "500 Invalid service/method" - end - rescue Exception => e - log_error e unless logger.nil? - exc_response = nil - case web_service_dispatching_mode - when :direct - if self.class.web_service_exception_reporting - exc_response = protocol_request.protocol.marshal_exception(e) - end - when :delegated - web_service = web_service_object(protocol_request.service_name) rescue nil - if web_service && web_service.class.web_service_exception_reporting - exc_response = protocol_request.protocol.marshal_exception(e) rescue nil - end - end - if exc_response - response_options = { - :type => exc_response.content_type, - :disposition => 'inline' - } - log_response exc_response - send_data(exc_response.raw_body, response_options) - else - render_text 'Internal protocol error', "500 #{e.message}" - end - end - end - - def log_request(protocol_request) - unless logger.nil? - web_service_name = protocol_request.web_service_name - method_name = protocol_request.public_method_name - logger.info "\nProcessing Action Service Request: #{web_service_name}##{method_name}" - logger.info "Raw Request Body:" - logger.info protocol_request.raw_body - end - end - - def log_response(protocol_response) - unless logger.nil? - logger.info "\nRaw Response Body:" - logger.info protocol_response.raw_body - end - end - end - end - end -end diff --git a/actionwebservice/lib/action_service/router/wsdl.rb b/actionwebservice/lib/action_service/router/wsdl.rb deleted file mode 100644 index c2f29da0b0..0000000000 --- a/actionwebservice/lib/action_service/router/wsdl.rb +++ /dev/null @@ -1,210 +0,0 @@ -module ActionService # :nodoc: - module Router # :nodoc: - module Wsdl # :nodoc: - def self.append_features(base) # :nodoc: - base.class_eval do - class << self - alias_method :inherited_without_wsdl, :inherited - end - end - base.extend(ClassMethods) - end - - module ClassMethods - def inherited(child) - inherited_without_wsdl(child) - child.send(:include, ActionService::Router::Wsdl::InstanceMethods) - end - end - - module InstanceMethods # :nodoc: - XsdNs = 'http://www.w3.org/2001/XMLSchema' - WsdlNs = 'http://schemas.xmlsoap.org/wsdl/' - SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/' - SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/' - SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http' - - def wsdl - case @request.method - when :get - begin - host_name = @request.env['HTTP_HOST']||@request.env['SERVER_NAME'] - uri = "http://#{host_name}/#{controller_name}/" - soap_action_base = "/#{controller_name}" - xml = to_wsdl(self, uri, soap_action_base) - send_data(xml, :type => 'text/xml', :disposition => 'inline') - rescue Exception => e - log_error e unless logger.nil? - render_text('', "500 #{e.message}") - end - when :post - render_text('', "500 POST not supported") - end - end - - private - def to_wsdl(container, uri, soap_action_base) - wsdl = "" - - web_service_dispatching_mode = container.web_service_dispatching_mode - mapper = container.class.soap_mapper - namespace = mapper.custom_namespace - wsdl_service_name = namespace.split(/:/)[1] - - services = {} - mapper.map_container_services(container) do |name, api, api_methods| - services[name] = [api, api_methods] - end - custom_types = mapper.custom_types - - - xm = Builder::XmlMarkup.new(:target => wsdl, :indent => 2) - xm.instruct! - - xm.definitions('name' => wsdl_service_name, - 'targetNamespace' => namespace, - 'xmlns:typens' => namespace, - 'xmlns:xsd' => XsdNs, - 'xmlns:soap' => SoapNs, - 'xmlns:soapenc' => SoapEncodingNs, - 'xmlns:wsdl' => WsdlNs, - 'xmlns' => WsdlNs) do - - # Custom type XSD generation - if custom_types.size > 0 - xm.types do - xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do - custom_types.each do |klass, mapping| - case - when mapping.is_a?(ActionService::Protocol::Soap::SoapArrayMapping) - xm.xsd(:complexType, 'name' => mapping.type_name) do - xm.xsd(:complexContent) do - xm.xsd(:restriction, 'base' => 'soapenc:Array') do - xm.xsd(:attribute, 'ref' => 'soapenc:arrayType', - 'wsdl:arrayType' => mapping.element_mapping.qualified_type_name + '[]') - end - end - end - when mapping.is_a?(ActionService::Protocol::Soap::SoapMapping) - xm.xsd(:complexType, 'name' => mapping.type_name) do - xm.xsd(:all) do - mapping.each_attribute do |name, type_name| - xm.xsd(:element, 'name' => name, 'type' => type_name) - end - end - end - else - raise(WsdlError, "unsupported mapping type #{mapping.class.name}") - end - end - end - end - end - - services.each do |service_name, service_values| - service_api, api_methods = service_values - # Parameter list message definitions - api_methods.each do |method_name, method_signature| - gen = lambda do |msg_name, direction| - xm.message('name' => msg_name) do - sym = nil - if direction == :out - if method_signature[:returns] - xm.part('name' => 'return', 'type' => method_signature[:returns][0].qualified_type_name) - end - else - mapping_list = method_signature[:expects] - i = 1 - mapping_list.each do |mapping| - if mapping.is_a?(Hash) - param_name = mapping.keys.shift - mapping = mapping.values.shift - else - param_name = "param#{i}" - end - xm.part('name' => param_name, 'type' => mapping.qualified_type_name) - i += 1 - end if mapping_list - end - end - end - public_name = service_api.public_api_method_name(method_name) - gen.call(public_name, :in) - gen.call("#{public_name}Response", :out) - end - - # Declare the port - port_name = port_name_for(wsdl_service_name, service_name) - xm.portType('name' => port_name) do - api_methods.each do |method_name, method_signature| - public_name = service_api.public_api_method_name(method_name) - xm.operation('name' => public_name) do - xm.input('message' => "typens:#{public_name}") - xm.output('message' => "typens:#{public_name}Response") - end - end - end - - # Bind the port to SOAP - binding_name = binding_name_for(wsdl_service_name, service_name) - xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do - xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport) - api_methods.each do |method_name, method_signature| - public_name = service_api.public_api_method_name(method_name) - xm.operation('name' => public_name) do - case web_service_dispatching_mode - when :direct - soap_action = soap_action_base + "/api/" + public_name - when :delegated - soap_action = soap_action_base \ - + "/" + service_name.to_s \ - + "/" + public_name - end - xm.soap(:operation, 'soapAction' => soap_action) - xm.input do - xm.soap(:body, - 'use' => 'encoded', - 'namespace' => namespace, - 'encodingStyle' => SoapEncodingNs) - end - xm.output do - xm.soap(:body, - 'use' => 'encoded', - 'namespace' => namespace, - 'encodingStyle' => SoapEncodingNs) - end - end - end - end - end - - # Define the service - xm.service('name' => "#{wsdl_service_name}Service") do - services.each do |service_name, service_values| - port_name = port_name_for(wsdl_service_name, service_name) - binding_name = binding_name_for(wsdl_service_name, service_name) - case web_service_dispatching_mode - when :direct - binding_target = 'api' - when :delegated - binding_target = service_name.to_s - end - xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do - xm.soap(:address, 'location' => "#{uri}#{binding_target}") - end - end - end - end - end - - def port_name_for(wsdl_service_name, service_name) - "#{wsdl_service_name}#{service_name.to_s.camelize}Port" - end - - def binding_name_for(wsdl_service_name, service_name) - "#{wsdl_service_name}#{service_name.to_s.camelize}Binding" - end - end - end - end -end diff --git a/actionwebservice/lib/action_service/struct.rb b/actionwebservice/lib/action_service/struct.rb deleted file mode 100644 index 142127b052..0000000000 --- a/actionwebservice/lib/action_service/struct.rb +++ /dev/null @@ -1,55 +0,0 @@ -module ActionService - # To send structured types across the wire, derive from ActionService::Struct, - # and use +member+ to declare structure members. - # - # ActionService::Struct should be used in method signatures when you want to accept or return - # structured types that have no Active Record model class representations, or you don't - # want to expose your entire Active Record model to remote callers. - # - # === Example - # - # class Person < ActionService::Struct - # member :id, :int - # member :firstnames, [:string] - # member :lastname, :string - # member :email, :string - # end - # person = Person.new(:id => 5, :firstname => 'john', :lastname => 'doe') - # - # Active Record model classes are already implicitly supported for method - # return signatures. A structure containing its columns as members will be - # automatically generated if its present in a signature. - class Struct - - # If a Hash is given as argument to an ActionService::Struct constructor, - # it can contain initial values for the structure member. - def initialize(values={}) - if values.is_a?(Hash) - values.map{|k,v| send('%s=' % k.to_s, v)} - end - end - - # The member with the given name - def [](name) - send(name.to_s) - end - - class << self - include ActionService::Signature - - # Creates a structure member with the specified +name+ and +type+. Generates - # accessor methods for reading and writing the member value. - def member(name, type) - write_inheritable_hash("struct_members", name => signature_parameter_class(type)) - class_eval <<-END - def #{name}; @#{name}; end - def #{name}=(value); @#{name} = value; end - END - end - - def members # :nodoc: - read_inheritable_attribute("struct_members") || {} - end - end - end -end diff --git a/actionwebservice/lib/action_service/support/class_inheritable_options.rb b/actionwebservice/lib/action_service/support/class_inheritable_options.rb deleted file mode 100644 index 4d1c2ed471..0000000000 --- a/actionwebservice/lib/action_service/support/class_inheritable_options.rb +++ /dev/null @@ -1,26 +0,0 @@ -class Class # :nodoc: - def class_inheritable_option(sym, default_value=nil) - write_inheritable_attribute sym, default_value - class_eval <<-EOS - def self.#{sym}(value=nil) - if !value.nil? - write_inheritable_attribute(:#{sym}, value) - else - read_inheritable_attribute(:#{sym}) - end - end - - def self.#{sym}=(value) - write_inheritable_attribute(:#{sym}, value) - end - - def #{sym} - self.class.#{sym} - end - - def #{sym}=(value) - self.class.#{sym} = value - end - EOS - end -end diff --git a/actionwebservice/lib/action_service/support/signature.rb b/actionwebservice/lib/action_service/support/signature.rb deleted file mode 100644 index 946118c523..0000000000 --- a/actionwebservice/lib/action_service/support/signature.rb +++ /dev/null @@ -1,100 +0,0 @@ -module ActionService # :nodoc: - # Action Service parameter specifiers may contain symbols or strings - # instead of Class objects, for a limited set of base types. - # - # This provides an unambiguous way to specify that a given parameter - # contains an integer or boolean value, for example. - # - # The allowed set of symbol/string aliases: - # - # [:int] any integer value - # [:float] any floating point value - # [:string] any string value - # [:bool] any boolean value - # [:time] any value containing both date and time - # [:date] any value containing only a date - module Signature - class SignatureError < StandardError # :nodoc: - end - - private - def canonical_signature(params) - return nil if params.nil? - params.map do |param| - klass = signature_parameter_class(param) - if param.is_a?(Hash) - param[param.keys[0]] = klass - param - else - klass - end - end - end - - def signature_parameter_class(param) - param = param.is_a?(Hash) ? param.values[0] : param - is_array = param.is_a?(Array) - param = is_array ? param[0] : param - param = param.is_a?(String) ? param.to_sym : param - param = param.is_a?(Symbol) ? signature_ruby_class(param) : param - is_array ? [param] : param - end - - - def canonical_signature_base_type(base_type) - base_type = base_type.to_sym - case base_type - when :int, :integer, :fixnum, :bignum - :int - when :string, :base64 - :string - when :bool, :boolean - :bool - when :float, :double - :float - when :time, :datetime, :timestamp - :time - when :date - :date - else - raise(SignatureError, ":#{base_type} is not an ActionService base type") - end - end - - def signature_ruby_class(base_type) - case canonical_signature_base_type(base_type) - when :int - Integer - when :string - String - when :bool - TrueClass - when :float - Float - when :time - Time - when :date - Date - end - end - - def signature_base_type(ruby_class) - case ruby_class - when Bignum, Integer, Fixnum - :int - when String - :string - when TrueClass, FalseClass - :bool - when Float, Numeric, Precision - :float - when Time, DateTime - :time - when Date - :date - else - raise(SignatureError, "#{ruby_class.name} is not an ActionService base type") - end - end - end -end diff --git a/actionwebservice/lib/action_web_service.rb b/actionwebservice/lib/action_web_service.rb new file mode 100644 index 0000000000..a55afc2244 --- /dev/null +++ b/actionwebservice/lib/action_web_service.rb @@ -0,0 +1,60 @@ +#-- +# Copyright (C) 2005 Leon Breedt +# +# 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. +#++ + +begin + require 'active_support' + require 'action_controller' + require 'active_record' +rescue LoadError + require 'rubygems' + require_gem 'activesupport', '>= 0.9.0' + require_gem 'actionpack', '>= 1.4.0' + require_gem 'activerecord', '>= 1.6.0' +end + +$:.unshift(File.dirname(__FILE__)) + +require 'action_web_service/base' +require 'action_web_service/client' +require 'action_web_service/invocation' +require 'action_web_service/api' +require 'action_web_service/struct' +require 'action_web_service/container' +require 'action_web_service/protocol' +require 'action_web_service/router' + +ActionWebService::Base.class_eval do + include ActionWebService::API + include ActionWebService::Invocation +end + +ActionController::Base.class_eval do + include ActionWebService::Container + include ActionWebService::Protocol::Registry + include ActionWebService::Protocol::Soap + include ActionWebService::Protocol::XmlRpc + include ActionWebService::API + include ActionWebService::API::ActionController + include ActionWebService::Router::ActionController + include ActionWebService::Router::Wsdl +end diff --git a/actionwebservice/lib/action_web_service/api.rb b/actionwebservice/lib/action_web_service/api.rb new file mode 100644 index 0000000000..0c71de5654 --- /dev/null +++ b/actionwebservice/lib/action_web_service/api.rb @@ -0,0 +1,2 @@ +require 'action_web_service/api/abstract' +require 'action_web_service/api/action_controller' diff --git a/actionwebservice/lib/action_web_service/api/abstract.rb b/actionwebservice/lib/action_web_service/api/abstract.rb new file mode 100644 index 0000000000..0ce68d10f7 --- /dev/null +++ b/actionwebservice/lib/action_web_service/api/abstract.rb @@ -0,0 +1,192 @@ +module ActionWebService # :nodoc: + module API # :nodoc: + class APIError < ActionWebService::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 app/apis/google_search_api.rb, and expect the + # API definition class to be named GoogleSearchAPI or + # GoogleSearchApi. + # + # ==== 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(APIError, "symbols can only be used for #web_service_api inside of a controller") + end + unless definition.respond_to?(:ancestors) && definition.ancestors.include?(Base) + raise(APIError, "#{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 + + # A web service API class specifies the methods that will be available for + # invocation for an API. It also contains metadata such as the method type + # signature hints. + # + # It is not intended to be instantiated. + # + # It is attached to web service implementation classes like + # ActionWebService::Base and ActionController::Base derivatives by using + # ClassMethods#web_service_api. + class Base + # Whether to transform the public API method names into camel-cased names + class_inheritable_option :inflect_names, true + + # If present, the name of a method to call when the remote caller + # tried to call a nonexistent method. Semantically equivalent to + # +method_missing+. + class_inheritable_option :default_api_method + + # Disallow instantiation + private_class_method :new, :allocate + + class << self + include ActionWebService::Signature + + # API methods have a +name+, which must be the Ruby method name to use when + # performing the invocation on the web service object. + # + # The signatures for the method input parameters and return value can + # by specified in +options+. + # + # A signature is an array of one or more parameter specifiers. + # A parameter specifier can be one of the following: + # + # * A symbol or string of representing one of the Action Web Service base types. + # See ActionWebService::Signature for a canonical list of the base types. + # * The Class object of the parameter type + # * A single-element Array containing one of the two preceding items. This + # will cause Action Web Service to treat the parameter at that position + # as an array containing only values of the given type. + # * A Hash containing as key the name of the parameter, and as value + # one of the three preceding items + # + # If no method input parameter or method return value signatures are given, + # the method is assumed to take no parameters and/or return no values of + # interest, and any values that are received by the server will be + # discarded and ignored. + # + # Valid options: + # [:expects] Signature for the method input parameters + # [:returns] Signature for the method return value + # [:expects_and_returns] Signature for both input parameters and return value + def api_method(name, options={}) + validate_options([:expects, :returns, :expects_and_returns], options.keys) + if options[:expects_and_returns] + expects = options[:expects_and_returns] + returns = options[:expects_and_returns] + else + expects = options[:expects] + returns = options[:returns] + end + expects = canonical_signature(expects) if expects + returns = canonical_signature(returns) if returns + if expects + expects.each do |param| + klass = signature_parameter_class(param) + klass = klass[0] if klass.is_a?(Array) + if klass.ancestors.include?(ActiveRecord::Base) + raise(ActionWebServiceError, "ActiveRecord model classes not allowed in :expects") + end + end + end + name = name.to_sym + public_name = public_api_method_name(name) + info = { :expects => expects, :returns => returns } + write_inheritable_hash("api_methods", name => info) + write_inheritable_hash("api_public_method_names", public_name => name) + end + + # Whether the given method name is a service method on this API + def has_api_method?(name) + api_methods.has_key?(name) + end + + # Whether the given public method name has a corresponding service method + # on this API + def has_public_api_method?(public_name) + api_public_method_names.has_key?(public_name) + end + + # The corresponding public method name for the given service method name + def public_api_method_name(name) + if inflect_names + name.to_s.camelize + else + name.to_s + end + end + + # The corresponding service method name for the given public method name + def api_method_name(public_name) + api_public_method_names[public_name] + end + + # A Hash containing all service methods on this API, and their + # associated metadata. + def api_methods + read_inheritable_attribute("api_methods") || {} + end + + private + def api_public_method_names + read_inheritable_attribute("api_public_method_names") || {} + end + + def validate_options(valid_option_keys, supplied_option_keys) + unknown_option_keys = supplied_option_keys - valid_option_keys + unless unknown_option_keys.empty? + raise(ActionWebServiceError, "Unknown options: #{unknown_option_keys}") + end + end + + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/api/action_controller.rb b/actionwebservice/lib/action_web_service/api/action_controller.rb new file mode 100644 index 0000000000..604cbfe704 --- /dev/null +++ b/actionwebservice/lib/action_web_service/api/action_controller.rb @@ -0,0 +1,92 @@ +module ActionWebService # :nodoc: + module API # :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 blogger 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 :api + 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 + probe_protocol_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/base.rb b/actionwebservice/lib/action_web_service/base.rb new file mode 100644 index 0000000000..42a514716a --- /dev/null +++ b/actionwebservice/lib/action_web_service/base.rb @@ -0,0 +1,41 @@ +require 'action_web_service/support/class_inheritable_options' +require 'action_web_service/support/signature' + +module ActionWebService # :nodoc: + class ActionWebServiceError < StandardError # :nodoc: + end + + # An Action Web Service object implements a specified API. + # + # Used by controllers operating in _Delegated_ dispatching mode. + # + # ==== Example + # + # class PersonService < ActionWebService::Base + # web_service_api PersonAPI + # + # def find_person(criteria) + # Person.find_all [...] + # end + # + # def delete_person(id) + # Person.find_by_id(id).destroy + # end + # end + # + # class PersonAPI < ActionWebService::API::Base + # api_method :find_person, :expects => [SearchCriteria], :returns => [[Person]] + # api_method :delete_person, :expects => [:int] + # end + # + # class SearchCriteria < ActionStruct::Base + # member :firstname, :string + # member :lastname, :string + # member :email, :string + # end + class Base + # Whether to report exceptions back to the caller in the protocol's exception + # format + class_inheritable_option :web_service_exception_reporting, true + end +end diff --git a/actionwebservice/lib/action_web_service/client.rb b/actionwebservice/lib/action_web_service/client.rb new file mode 100644 index 0000000000..77f934882c --- /dev/null +++ b/actionwebservice/lib/action_web_service/client.rb @@ -0,0 +1,3 @@ +require 'action_web_service/client/base' +require 'action_web_service/client/soap' +require 'action_web_service/client/xmlrpc' diff --git a/actionwebservice/lib/action_web_service/client/base.rb b/actionwebservice/lib/action_web_service/client/base.rb new file mode 100644 index 0000000000..d01cffcd56 --- /dev/null +++ b/actionwebservice/lib/action_web_service/client/base.rb @@ -0,0 +1,35 @@ +module ActionWebService # :nodoc: + module Client # :nodoc: + class ClientError < StandardError # :nodoc: + end + + class Base # :nodoc: + def initialize(api, endpoint_uri) + @api = api + @endpoint_uri = endpoint_uri + end + + def method_missing(name, *args) # :nodoc: + call_name = method_name(name) + return super(name, *args) if call_name.nil? + perform_invocation(call_name, args) + end + + protected + def perform_invocation(method_name, args) # :nodoc: + raise NotImplementedError, "use a protocol-specific client" + end + + private + def method_name(name) + if @api.has_api_method?(name.to_sym) + name.to_s + elsif @api.has_public_api_method?(name.to_s) + @api.api_method_name(name.to_s).to_s + else + nil + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/client/soap.rb b/actionwebservice/lib/action_web_service/client/soap.rb new file mode 100644 index 0000000000..3557f88594 --- /dev/null +++ b/actionwebservice/lib/action_web_service/client/soap.rb @@ -0,0 +1,87 @@ +require 'soap/rpc/driver' +require 'uri' + +module ActionWebService # :nodoc: + module Client # :nodoc: + + # Implements SOAP client support (using RPC encoding for the messages). + # + # ==== Example Usage + # + # class PersonAPI < ActionWebService::API::Base + # api_method :find_all, :returns => [[Person]] + # end + # + # soap_client = ActionWebService::Client::Soap.new(PersonAPI, "http://...") + # persons = soap_client.find_all + # + class Soap < Base + + # Creates a new web service client using the SOAP RPC protocol. + # + # +api+ must be an ActionWebService::API::Base derivative, and + # +endpoint_uri+ must point at the relevant URL to which protocol requests + # will be sent with HTTP POST. + # + # Valid options: + # [:service_name] If the remote server has used a custom +wsdl_service_name+ + # option, you must specify it here + def initialize(api, endpoint_uri, options={}) + super(api, endpoint_uri) + @service_name = options[:service_name] || 'ActionWebService' + @namespace = "urn:#{@service_name}" + @mapper = ActionWebService::Protocol::Soap::SoapMapper.new(@namespace) + @protocol = ActionWebService::Protocol::Soap::SoapProtocol.new(@mapper) + @soap_action_base = options[:soap_action_base] + @soap_action_base ||= URI.parse(endpoint_uri).path + @driver = create_soap_rpc_driver(api, endpoint_uri) + end + + protected + def perform_invocation(method_name, args) + @driver.send(method_name, *args) + end + + def soap_action(method_name) + "#{@soap_action_base}/#{method_name}" + end + + private + def create_soap_rpc_driver(api, endpoint_uri) + @mapper.map_api(api) + driver = SoapDriver.new(endpoint_uri, nil) + driver.mapping_registry = @mapper.registry + api.api_methods.each do |name, info| + public_name = api.public_api_method_name(name) + qname = XSD::QName.new(@namespace, public_name) + action = soap_action(public_name) + expects = info[:expects] + returns = info[:returns] + param_def = [] + i = 1 + if expects + expects.each do |klass| + param_name = klass.is_a?(Hash) ? klass.keys[0] : "param#{i}" + mapping = @mapper.lookup(klass) + param_def << ['in', param_name, mapping.registry_mapping] + i += 1 + end + end + if returns + mapping = @mapper.lookup(returns[0]) + param_def << ['retval', 'return', mapping.registry_mapping] + end + driver.add_method(qname, action, name.to_s, param_def) + end + driver + end + + class SoapDriver < SOAP::RPC::Driver # :nodoc: + def add_method(qname, soapaction, name, param_def) + @proxy.add_rpc_method(qname, soapaction, name, param_def) + add_rpc_method_interface(name, param_def) + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/client/xmlrpc.rb b/actionwebservice/lib/action_web_service/client/xmlrpc.rb new file mode 100644 index 0000000000..df51230b81 --- /dev/null +++ b/actionwebservice/lib/action_web_service/client/xmlrpc.rb @@ -0,0 +1,76 @@ +require 'uri' +require 'xmlrpc/client' + +module ActionWebService # :nodoc: + module Client # :nodoc: + + # Implements XML-RPC client support + # + # ==== Example Usage + # + # class BloggerAPI < ActionWebService::API::Base + # inflect_names false + # api_method :getRecentPosts, :returns => [[Blog::Post]] + # end + # + # blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../RPC", :handler_name => "blogger") + # posts = blog.getRecentPosts + class XmlRpc < Base + + # Creates a new web service client using the XML-RPC protocol. + # + # +api+ must be an ActionWebService::API::Base derivative, and + # +endpoint_uri+ must point at the relevant URL to which protocol requests + # will be sent with HTTP POST. + # + # Valid options: + # [:handler_name] If the remote server defines its services inside special + # handler (the Blogger API uses a "blogger" handler name for example), + # provide it here, or your method calls will fail + def initialize(api, endpoint_uri, options={}) + @api = api + @handler_name = options[:handler_name] + @client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout]) + end + + protected + def perform_invocation(method_name, args) + args = transform_outgoing_method_params(method_name, args) + ok, return_value = @client.call2(public_name(method_name), *args) + return transform_return_value(method_name, return_value) if ok + raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}") + end + + def transform_outgoing_method_params(method_name, params) + info = @api.api_methods[method_name.to_sym] + signature = info[:expects] + signature_length = signature.nil?? 0 : signature.length + if signature_length != params.length + raise(ProtocolError, "API declares #{public_name(method_name)} to accept " + + "#{signature_length} parameters, but #{params.length} parameters " + + "were supplied") + end + if signature_length > 0 + signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature) + (1..signature.size).each do |i| + i -= 1 + params[i] = Protocol::XmlRpc::XmlRpcProtocol.ruby_to_xmlrpc(params[i], signature[i]) + end + end + params + end + + def transform_return_value(method_name, return_value) + info = @api.api_methods[method_name.to_sym] + return true unless signature = info[:returns] + signature = Protocol::XmlRpc::XmlRpcProtocol.transform_array_types(signature) + Protocol::XmlRpc::XmlRpcProtocol.xmlrpc_to_ruby(return_value, signature[0]) + end + + def public_name(method_name) + public_name = @api.public_api_method_name(method_name) + @handler_name ? "#{@handler_name}.#{public_name}" : public_name + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/container.rb b/actionwebservice/lib/action_web_service/container.rb new file mode 100644 index 0000000000..6fa14def56 --- /dev/null +++ b/actionwebservice/lib/action_web_service/container.rb @@ -0,0 +1,232 @@ +module ActionWebService # :nodoc: + module Container # :nodoc: + class ContainerError < ActionWebService::ActionWebServiceError # :nodoc: + end + + def self.append_features(base) # :nodoc: + super + base.class_inheritable_option(:web_service_dispatching_mode, :direct) + base.class_inheritable_option(:web_service_exception_reporting, true) + base.extend(ClassMethods) + base.send(:include, ActionWebService::Container::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 + + private + def dispatch_web_service_request(protocol_request) + case web_service_dispatching_mode + when :direct + dispatch_direct_web_service_request(protocol_request) + when :delegated + dispatch_delegated_web_service_request(protocol_request) + else + raise(ContainerError, "unsupported dispatching mode :#{web_service_dispatching_mode}") + end + end + + def dispatch_direct_web_service_request(protocol_request) + public_method_name = protocol_request.public_method_name + api = self.class.web_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_web_service_request(protocol_request) + web_service_name = protocol_request.web_service_name + service = web_service_object(web_service_name) + api = service.class.web_service_api + public_method_name = protocol_request.public_method_name + method_name = api.api_method_name(public_method_name) + + invocation = ActionWebService::Invocation::InvocationRequest.new( + ActionWebService::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 = ActionWebService::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 /#{web_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 = ActionWebService::Invocation::UnpublishedConcreteInvocation + else + raise(ContainerError, "no such method /#{web_service_name}##{public_method_name}") + end + result = perform_invoke.call + end + protocol_request.marshal(result) + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/invocation.rb b/actionwebservice/lib/action_web_service/invocation.rb new file mode 100644 index 0000000000..64d3e8d524 --- /dev/null +++ b/actionwebservice/lib/action_web_service/invocation.rb @@ -0,0 +1,252 @@ +module ActionWebService # :nodoc: + module Invocation # :nodoc: + ConcreteInvocation = :concrete + VirtualInvocation = :virtual + UnpublishedConcreteInvocation = :unpublished_concrete + + class InvocationError < ActionWebService::ActionWebServiceError # :nodoc: + end + + def self.append_features(base) # :nodoc: + super + base.extend(ClassMethods) + base.send(:include, ActionWebService::Invocation::InstanceMethods) + end + + # Invocation interceptors provide a means to execute custom code before + # and after method invocations on ActionWebService::Base objects. + # + # When running in _Direct_ dispatching mode, ActionController filters + # should be used for this functionality instead. + # + # The semantics of invocation interceptors are the same as ActionController + # filters, and accept the same parameters and options. + # + # A _before_ interceptor can also cancel execution by returning +false+, + # or returning a [false, "cancel reason"] array if it wishes to supply + # a reason for canceling the request. + # + # === Example + # + # class CustomService < ActionWebService::Base + # before_invocation :intercept_add, :only => [:add] + # + # def add(a, b) + # a + b + # end + # + # private + # def intercept_add + # return [false, "permission denied"] # cancel it + # end + # end + # + # Options: + # [:except] A list of methods for which the interceptor will NOT be called + # [:only] A list of methods for which the interceptor WILL be called + module ClassMethods + # Appends the given +interceptors+ to be called + # _before_ method invocation. + def append_before_invocation(*interceptors, &block) + conditions = extract_conditions!(interceptors) + interceptors << block if block_given? + add_interception_conditions(interceptors, conditions) + append_interceptors_to_chain("before", interceptors) + end + + # Prepends the given +interceptors+ to be called + # _before_ method invocation. + def prepend_before_invocation(*interceptors, &block) + conditions = extract_conditions!(interceptors) + interceptors << block if block_given? + add_interception_conditions(interceptors, conditions) + prepend_interceptors_to_chain("before", interceptors) + end + + alias :before_invocation :append_before_invocation + + # Appends the given +interceptors+ to be called + # _after_ method invocation. + def append_after_invocation(*interceptors, &block) + conditions = extract_conditions!(interceptors) + interceptors << block if block_given? + add_interception_conditions(interceptors, conditions) + append_interceptors_to_chain("after", interceptors) + end + + # Prepends the given +interceptors+ to be called + # _after_ method invocation. + def prepend_after_invocation(*interceptors, &block) + conditions = extract_conditions!(interceptors) + interceptors << block if block_given? + add_interception_conditions(interceptors, conditions) + prepend_interceptors_to_chain("after", interceptors) + end + + alias :after_invocation :append_after_invocation + + def before_invocation_interceptors # :nodoc: + read_inheritable_attribute("before_invocation_interceptors") + end + + def after_invocation_interceptors # :nodoc: + read_inheritable_attribute("after_invocation_interceptors") + end + + def included_intercepted_methods # :nodoc: + read_inheritable_attribute("included_intercepted_methods") || {} + end + + def excluded_intercepted_methods # :nodoc: + read_inheritable_attribute("excluded_intercepted_methods") || {} + end + + private + def append_interceptors_to_chain(condition, interceptors) + write_inheritable_array("#{condition}_invocation_interceptors", interceptors) + end + + def prepend_interceptors_to_chain(condition, interceptors) + interceptors = interceptors + read_inheritable_attribute("#{condition}_invocation_interceptors") + write_inheritable_attribute("#{condition}_invocation_interceptors", interceptors) + end + + def extract_conditions!(interceptors) + return nil unless interceptors.last.is_a? Hash + interceptors.pop + end + + def add_interception_conditions(interceptors, conditions) + return unless conditions + included, excluded = conditions[:only], conditions[:except] + write_inheritable_hash("included_intercepted_methods", condition_hash(interceptors, included)) && return if included + write_inheritable_hash("excluded_intercepted_methods", condition_hash(interceptors, excluded)) if excluded + end + + def condition_hash(interceptors, *methods) + interceptors.inject({}) {|hash, interceptor| hash.merge(interceptor => methods.flatten.map {|method| method.to_s})} + end + end + + module InstanceMethods # :nodoc: + def self.append_features(base) + super + base.class_eval do + alias_method :perform_invocation_without_interception, :perform_invocation + alias_method :perform_invocation, :perform_invocation_with_interception + end + end + + def perform_invocation_with_interception(invocation, &block) + return if before_invocation(invocation.method_name, invocation.params, &block) == false + result = perform_invocation_without_interception(invocation) + after_invocation(invocation.method_name, invocation.params, result) + result + end + + def perform_invocation(invocation) + if invocation.concrete? + unless self.respond_to?(invocation.method_name) && \ + self.class.web_service_api.has_api_method?(invocation.method_name) + raise InvocationError, "no such web service method '#{invocation.method_name}' on service object" + end + end + params = invocation.params + if invocation.concrete? || invocation.unpublished_concrete? + self.send(invocation.method_name, *params) + else + if invocation.block + params = invocation.block_params + params + invocation.block.call(invocation.public_method_name, *params) + else + self.send(invocation.method_name, *params) + end + end + end + + def before_invocation(name, args, &block) + call_interceptors(self.class.before_invocation_interceptors, [name, args], &block) + end + + def after_invocation(name, args, result) + call_interceptors(self.class.after_invocation_interceptors, [name, args, result]) + end + + private + + def call_interceptors(interceptors, interceptor_args, &block) + if interceptors and not interceptors.empty? + interceptors.each do |interceptor| + next if method_exempted?(interceptor, interceptor_args[0].to_s) + result = case + when interceptor.is_a?(Symbol) + self.send(interceptor, *interceptor_args) + when interceptor_block?(interceptor) + interceptor.call(self, *interceptor_args) + when interceptor_class?(interceptor) + interceptor.intercept(self, *interceptor_args) + else + raise( + InvocationError, + "Interceptors need to be either a symbol, proc/method, or a class implementing a static intercept method" + ) + end + reason = nil + if result.is_a?(Array) + reason = result[1] if result[1] + result = result[0] + end + if result == false + block.call(reason) if block && reason + return false + end + end + end + end + + def interceptor_block?(interceptor) + interceptor.respond_to?("call") && (interceptor.arity == 3 || interceptor.arity == -1) + end + + def interceptor_class?(interceptor) + interceptor.respond_to?("intercept") + end + + def method_exempted?(interceptor, method_name) + case + when self.class.included_intercepted_methods[interceptor] + !self.class.included_intercepted_methods[interceptor].include?(method_name) + when self.class.excluded_intercepted_methods[interceptor] + self.class.excluded_intercepted_methods[interceptor].include?(method_name) + end + end + end + + class InvocationRequest # :nodoc: + attr_accessor :type + attr :public_method_name + attr_accessor :method_name + attr_accessor :params + attr_accessor :block + attr :block_params + + def initialize(type, public_method_name, method_name, params=nil) + @type = type + @public_method_name = public_method_name + @method_name = method_name + @params = params || [] + @block = nil + @block_params = [] + end + + def concrete? + @type == ConcreteInvocation ? true : false + end + + def unpublished_concrete? + @type == UnpublishedConcreteInvocation ? true : false + end + end + + end +end diff --git a/actionwebservice/lib/action_web_service/protocol.rb b/actionwebservice/lib/action_web_service/protocol.rb new file mode 100644 index 0000000000..733787136a --- /dev/null +++ b/actionwebservice/lib/action_web_service/protocol.rb @@ -0,0 +1,4 @@ +require 'action_web_service/protocol/abstract' +require 'action_web_service/protocol/registry' +require 'action_web_service/protocol/soap' +require 'action_web_service/protocol/xmlrpc' diff --git a/actionwebservice/lib/action_web_service/protocol/abstract.rb b/actionwebservice/lib/action_web_service/protocol/abstract.rb new file mode 100644 index 0000000000..9199dfe33f --- /dev/null +++ b/actionwebservice/lib/action_web_service/protocol/abstract.rb @@ -0,0 +1,128 @@ +module ActionWebService # :nodoc: + module Protocol # :nodoc: + CheckedMessage = :checked + UncheckedMessage = :unchecked + + class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc: + end + + class AbstractProtocol # :nodoc: + attr :container_class + + def initialize(container_class) + @container_class = container_class + end + + def unmarshal_request(protocol_request) + raise NotImplementedError + end + + def marshal_response(protocol_request, return_value) + raise NotImplementedError + end + + def marshal_exception(exception) + raise NotImplementedError + end + + def self.create_protocol_request(container_class, action_pack_request) + nil + end + + def self.create_protocol_client(api, protocol_name, endpoint_uri, options) + nil + end + end + + class AbstractProtocolMessage # :nodoc: + attr_accessor :signature + attr_accessor :return_signature + attr_accessor :type + attr :options + + def initialize(options={}) + @signature = @return_signature = nil + @options = options + @type = @options[:type] || CheckedMessage + end + + def signature=(value) + return if value.nil? + @signature = [] + value.each do |klass| + if klass.is_a?(Hash) + @signature << klass.values.shift + else + @signature << klass + end + end + @signature + end + + def checked? + @type == CheckedMessage + end + + def check_parameter_types(values, signature) + return unless checked? && signature + unless signature.length == values.length + raise(ProtocolError, "Signature and parameter lengths mismatch") + end + (1..signature.length).each do |i| + check_compatibility(signature[i-1], values[i-1].class) + end + end + + def check_compatibility(expected_class, received_class) + return if \ + (expected_class == TrueClass or expected_class == FalseClass) and \ + (received_class == TrueClass or received_class == FalseClass) + unless received_class.ancestors.include?(expected_class) or \ + expected_class.ancestors.include?(received_class) + raise(ProtocolError, "value of type #{received_class.name} is not " + + "compatible with expected type #{expected_class.name}") + end + end + end + + class ProtocolRequest < AbstractProtocolMessage # :nodoc: + attr :protocol + attr :raw_body + + attr_accessor :web_service_name + attr_accessor :public_method_name + attr_accessor :content_type + + def initialize(protocol, raw_body, web_service_name, public_method_name, content_type, options={}) + super(options) + @protocol = protocol + @raw_body = raw_body + @web_service_name = web_service_name + @public_method_name = public_method_name + @content_type = content_type + end + + def unmarshal + @protocol.unmarshal_request(self) + end + + def marshal(return_value) + @protocol.marshal_response(self, return_value) + end + end + + class ProtocolResponse < AbstractProtocolMessage # :nodoc: + attr :protocol + attr :raw_body + + attr_accessor :content_type + + def initialize(protocol, raw_body, content_type, options={}) + super(options) + @protocol = protocol + @raw_body = raw_body + @content_type = content_type + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/protocol/registry.rb b/actionwebservice/lib/action_web_service/protocol/registry.rb new file mode 100644 index 0000000000..0173673556 --- /dev/null +++ b/actionwebservice/lib/action_web_service/protocol/registry.rb @@ -0,0 +1,55 @@ +module ActionWebService # :nodoc: + module Protocol # :nodoc: + HeaderAndBody = :header_and_body + BodyOnly = :body_only + + module Registry # :nodoc: + def self.append_features(base) # :nodoc: + super + base.extend(ClassMethods) + base.send(:include, ActionWebService::Protocol::Registry::InstanceMethods) + end + + module ClassMethods # :nodoc: + def register_protocol(type, klass) # :nodoc: + case type + when HeaderAndBody + write_inheritable_array("header_and_body_protocols", [klass]) + when BodyOnly + write_inheritable_array("body_only_protocols", [klass]) + else + raise(ProtocolError, "unknown protocol type #{type}") + end + end + end + + module InstanceMethods # :nodoc: + private + def probe_request_protocol(action_pack_request) + (header_and_body_protocols + body_only_protocols).each do |protocol| + protocol_request = protocol.create_protocol_request(self.class, action_pack_request) + return protocol_request if protocol_request + end + raise(ProtocolError, "unsupported request message format") + end + + def probe_protocol_client(api, protocol_name, endpoint_uri, options) + (header_and_body_protocols + body_only_protocols).each do |protocol| + protocol_client = protocol.create_protocol_client(api, protocol_name, endpoint_uri, options) + return protocol_client if protocol_client + end + raise(ProtocolError, "unsupported client protocol :#{protocol_name}") + end + + def header_and_body_protocols + self.class.read_inheritable_attribute("header_and_body_protocols") || [] + end + + def body_only_protocols + self.class.read_inheritable_attribute("body_only_protocols") || [] + end + end + + end + end +end diff --git a/actionwebservice/lib/action_web_service/protocol/soap.rb b/actionwebservice/lib/action_web_service/protocol/soap.rb new file mode 100644 index 0000000000..3c527fea93 --- /dev/null +++ b/actionwebservice/lib/action_web_service/protocol/soap.rb @@ -0,0 +1,484 @@ +require 'soap/processor' +require 'soap/mapping' +require 'soap/rpc/element' +require 'xsd/datatypes' +require 'xsd/ns' +require 'singleton' + +module ActionWebService # :nodoc: + module Protocol # :nodoc: + module Soap # :nodoc: + class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc: + end + + def self.append_features(base) # :nodoc: + super + base.register_protocol(HeaderAndBody, SoapProtocol) + base.extend(ClassMethods) + base.wsdl_service_name('ActionWebService') + end + + module ClassMethods + # Specifies the WSDL service name to use when generating WSDL. Highly + # recommended that you set this value, or code generators may generate + # classes with very generic names. + # + # === Example + # class MyController < ActionController::Base + # wsdl_service_name 'MyService' + # end + def wsdl_service_name(name) + write_inheritable_attribute("soap_mapper", SoapMapper.new("urn:#{name}")) + end + + def soap_mapper # :nodoc: + read_inheritable_attribute("soap_mapper") + end + end + + class SoapProtocol < AbstractProtocol # :nodoc: + attr :mapper + + def initialize(mapper) + @mapper = mapper + end + + def self.create_protocol_request(container_class, action_pack_request) + soap_action = extract_soap_action(action_pack_request) + return nil unless soap_action + service_name = action_pack_request.parameters['action'] + public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1] + content_type = action_pack_request.env['HTTP_CONTENT_TYPE'] + content_type ||= 'text/xml' + protocol = SoapProtocol.new(container_class.soap_mapper) + ProtocolRequest.new(protocol, + action_pack_request.raw_post, + service_name.to_sym, + public_method_name, + content_type) + end + + def self.create_protocol_client(api, protocol_name, endpoint_uri, options) + return nil unless protocol_name.to_s.downcase.to_sym == :soap + ActionWebService::Client::Soap.new(api, endpoint_uri, options) + end + + def unmarshal_request(protocol_request) + unmarshal = lambda do + envelope = SOAP::Processor.unmarshal(protocol_request.raw_body) + request = envelope.body.request + values = request.collect{|k, v| request[k]} + soap_to_ruby_array(values) + end + signature = protocol_request.signature + if signature + map_signature_types(signature) + values = unmarshal.call + signature = signature.map{|x|mapper.lookup(x).ruby_klass} + protocol_request.check_parameter_types(values, signature) + values + else + if protocol_request.checked? + [] + else + unmarshal.call + end + end + end + + def marshal_response(protocol_request, return_value) + marshal = lambda do |signature| + mapping = mapper.lookup(signature[0]) + return_value = fixup_array_types(mapping, return_value) + signature = signature.map{|x|mapper.lookup(x).ruby_klass} + protocol_request.check_parameter_types([return_value], signature) + param_def = [['retval', 'return', mapping.registry_mapping]] + [param_def, ruby_to_soap(return_value)] + end + signature = protocol_request.return_signature + param_def = nil + if signature + param_def, return_value = marshal.call(signature) + else + if protocol_request.checked? + param_def, return_value = nil, nil + else + param_def, return_value = marshal.call([return_value.class]) + end + end + qname = XSD::QName.new(mapper.custom_namespace, + protocol_request.public_method_name) + response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def) + response.retval = return_value unless return_value.nil? + ProtocolResponse.new(self, create_response(response), 'text/xml') + end + + def marshal_exception(exc) + ProtocolResponse.new(self, create_exception_response(exc), 'text/xml') + end + + private + def self.extract_soap_action(request) + return nil unless request.method == :post + content_type = request.env['HTTP_CONTENT_TYPE'] || 'text/xml' + return nil unless content_type + soap_action = request.env['HTTP_SOAPACTION'] + return nil unless soap_action + soap_action.gsub!(/^"/, '') + soap_action.gsub!(/"$/, '') + soap_action.strip! + return nil if soap_action.empty? + soap_action + end + + def fixup_array_types(mapping, obj) + mapping.each_attribute do |name, type, attr_mapping| + if attr_mapping.custom_type? + attr_obj = obj.send(name) + new_obj = fixup_array_types(attr_mapping, attr_obj) + obj.send("#{name}=", new_obj) unless new_obj.equal?(attr_obj) + end + end + if mapping.is_a?(SoapArrayMapping) + obj = mapping.ruby_klass.new(obj) + # man, this is going to be slow for big arrays :( + (1..obj.size).each do |i| + i -= 1 + obj[i] = fixup_array_types(mapping.element_mapping, obj[i]) + end + else + if !mapping.generated_klass.nil? && mapping.generated_klass.respond_to?(:members) + # have to map the publically visible structure of the class + new_obj = mapping.generated_klass.new + mapping.generated_klass.members.each do |name, klass| + new_obj.send("#{name}=", obj.send(name)) + end + obj = new_obj + end + end + obj + end + + def map_signature_types(types) + types.collect{|type| mapper.map(type)} + end + + def create_response(body) + header = SOAP::SOAPHeader.new + body = SOAP::SOAPBody.new(body) + envelope = SOAP::SOAPEnvelope.new(header, body) + SOAP::Processor.marshal(envelope) + end + + def create_exception_response(exc) + detail = SOAP::Mapping::SOAPException.new(exc) + body = SOAP::SOAPFault.new( + SOAP::SOAPString.new('Server'), + SOAP::SOAPString.new(exc.to_s), + SOAP::SOAPString.new(self.class.name), + SOAP::Mapping.obj2soap(detail)) + create_response(body) + end + + def ruby_to_soap(obj) + SOAP::Mapping.obj2soap(obj, mapper.registry) + end + + def soap_to_ruby(obj) + SOAP::Mapping.soap2obj(obj, mapper.registry) + end + + def soap_to_ruby_array(array) + array.map{|x| soap_to_ruby(x)} + end + end + + class SoapMapper # :nodoc: + attr :registry + attr :custom_namespace + attr :custom_types + + def initialize(custom_namespace) + @custom_namespace = custom_namespace + @registry = SOAP::Mapping::Registry.new + @klass2map = {} + @custom_types = {} + @ar2klass = {} + end + + def lookup(klass) + lookup_klass = klass.is_a?(Array) ? klass[0] : klass + generated_klass = nil + unless lookup_klass.respond_to?(:ancestors) + raise(ProtocolError, "expected parameter type definition to be a Class") + end + if lookup_klass.ancestors.include?(ActiveRecord::Base) + generated_klass = @ar2klass.has_key?(klass) ? @ar2klass[klass] : nil + klass = generated_klass if generated_klass + end + return @klass2map[klass] if @klass2map.has_key?(klass) + + custom_type = false + + ruby_klass = select_class(lookup_klass) + generated_klass = @ar2klass[lookup_klass] if @ar2klass.has_key?(lookup_klass) + type_name = ruby_klass.name + + # Array signatures generate a double-mapping and require generation + # of an Array subclass to represent the mapping in the SOAP + # registry + array_klass = nil + if klass.is_a?(Array) + array_klass = Class.new(Array) do + module_eval <<-END + def self.name + "#{type_name}Array" + end + END + end + end + + mapping = @registry.find_mapped_soap_class(ruby_klass) rescue nil + unless mapping + # Custom structured type, generate a mapping + info = { :type => XSD::QName.new(@custom_namespace, type_name) } + @registry.add(ruby_klass, + SOAP::SOAPStruct, + SOAP::Mapping::Registry::TypedStructFactory, + info) + mapping = ensure_mapped(ruby_klass) + custom_type = true + end + + array_mapping = nil + if array_klass + # Typed array always requires a custom type. The info of the array + # is the info of its element type (in mapping[2]), falling back + # to SOAP base types. + info = mapping[2] + info ||= {} + info[:type] ||= soap_base_type_qname(mapping[0]) + @registry.add(array_klass, + SOAP::SOAPArray, + SOAP::Mapping::Registry::TypedArrayFactory, + info) + array_mapping = ensure_mapped(array_klass) + end + + if array_mapping + @klass2map[ruby_klass] = SoapMapping.new(self, + type_name, + ruby_klass, + generated_klass, + mapping[0], + mapping, + custom_type) + @klass2map[klass] = SoapArrayMapping.new(self, + type_name, + array_klass, + array_mapping[0], + array_mapping, + @klass2map[ruby_klass]) + @custom_types[klass] = @klass2map[klass] + @custom_types[ruby_klass] = @klass2map[ruby_klass] if custom_type + else + @klass2map[klass] = SoapMapping.new(self, + type_name, + ruby_klass, + generated_klass, + mapping[0], + mapping, + custom_type) + @custom_types[klass] = @klass2map[klass] if custom_type + end + + @klass2map[klass] + end + alias :map :lookup + + def map_container_services(container, &block) + dispatching_mode = container.web_service_dispatching_mode + web_services = nil + case dispatching_mode + when :direct + api = container.class.web_service_api + if container.respond_to?(:controller_class_name) + web_service_name = container.controller_class_name.sub(/Controller$/, '').underscore + else + web_service_name = container.class.name.demodulize.underscore + end + web_services = { web_service_name => api } + when :delegated + web_services = {} + container.class.web_services.each do |web_service_name, web_service_info| + begin + object = container.web_service_object(web_service_name) + rescue Exception => e + raise(ProtocolError, "failed to retrieve web service object for web service '#{web_service_name}': #{e.message}") + end + web_services[web_service_name] = object.class.web_service_api + end + end + web_services.each do |web_service_name, api| + if api.nil? + raise(ProtocolError, "no web service API set while in :#{dispatching_mode} mode") + end + map_api(api) do |api_methods| + yield web_service_name, api, api_methods if block_given? + end + end + end + + def map_api(api, &block) + lookup_proc = lambda do |klass| + mapping = lookup(klass) + custom_mapping = nil + if mapping.respond_to?(:element_mapping) + custom_mapping = mapping.element_mapping + else + custom_mapping = mapping + end + if custom_mapping && custom_mapping.custom_type? + # What gives? This is required so that structure types + # referenced only by structures (and not signatures) still + # have a custom type mapping in the registry (needed for WSDL + # generation). + custom_mapping.each_attribute{} + end + mapping + end + api_methods = block.nil?? nil : {} + api.api_methods.each do |method_name, method_info| + expects = method_info[:expects] + expects_signature = nil + if expects + expects_signature = block ? [] : nil + expects.each do |klass| + lookup_klass = nil + if klass.is_a?(Hash) + lookup_klass = lookup_proc.call(klass.values[0]) + expects_signature << {klass.keys[0]=>lookup_klass} if block + else + lookup_klass = lookup_proc.call(klass) + expects_signature << lookup_klass if block + end + end + end + returns = method_info[:returns] + returns_signature = returns ? returns.map{|klass| lookup_proc.call(klass)} : nil + if block + api_methods[method_name] = { + :expects => expects_signature, + :returns => returns_signature + } + end + end + yield api_methods if block + end + + private + def select_class(klass) + return Integer if klass == Fixnum + if klass.ancestors.include?(ActiveRecord::Base) + new_klass = Class.new(ActionWebService::Struct) + new_klass.class_eval <<-EOS + def self.name + "#{klass.name}" + end + EOS + klass.columns.each do |column| + next if column.klass.nil? + new_klass.send(:member, column.name.to_sym, column.klass) + end + @ar2klass[klass] = new_klass + return new_klass + end + klass + end + + def ensure_mapped(klass) + mapping = @registry.find_mapped_soap_class(klass) rescue nil + raise(ProtocolError, "failed to register #{klass.name}") unless mapping + mapping + end + + def soap_base_type_qname(base_type) + xsd_type = base_type.ancestors.find{|c| c.const_defined? 'Type'} + xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type + end + end + + class SoapMapping # :nodoc: + attr :ruby_klass + attr :generated_klass + attr :soap_klass + attr :registry_mapping + + def initialize(mapper, type_name, ruby_klass, generated_klass, soap_klass, registry_mapping, + custom_type=false) + @mapper = mapper + @type_name = type_name + @ruby_klass = ruby_klass + @generated_klass = generated_klass + @soap_klass = soap_klass + @registry_mapping = registry_mapping + @custom_type = custom_type + end + + def type_name + @type_name + end + + def custom_type? + @custom_type + end + + def qualified_type_name + name = type_name + if custom_type? + "typens:#{name}" + else + xsd_type_for(@soap_klass) + end + end + + def each_attribute(&block) + if @ruby_klass.respond_to?(:members) + @ruby_klass.members.each do |name, klass| + name = name.to_s + mapping = @mapper.lookup(klass) + yield name, mapping.qualified_type_name, mapping + end + end + end + + def is_xsd_type?(klass) + klass.ancestors.include?(XSD::NSDBase) + end + + def xsd_type_for(klass) + ns = XSD::NS.new + ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag) + xsd_klass = klass.ancestors.find{|c| c.const_defined?('Type')} + return ns.name(XSD::AnyTypeName) unless xsd_klass + ns.name(xsd_klass.const_get('Type')) + end + end + + class SoapArrayMapping < SoapMapping # :nodoc: + attr :element_mapping + + def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping, element_mapping) + super(mapper, type_name, ruby_klass, nil, soap_klass, registry_mapping, true) + @element_mapping = element_mapping + end + + def type_name + super + "Array" + end + + def each_attribute(&block); end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/protocol/xmlrpc.rb b/actionwebservice/lib/action_web_service/protocol/xmlrpc.rb new file mode 100644 index 0000000000..414bcfdbf7 --- /dev/null +++ b/actionwebservice/lib/action_web_service/protocol/xmlrpc.rb @@ -0,0 +1,183 @@ +require 'xmlrpc/parser' +require 'xmlrpc/create' +require 'xmlrpc/config' +require 'xmlrpc/utils' +require 'singleton' + +module XMLRPC # :nodoc: + class XmlRpcHelper # :nodoc: + include Singleton + include ParserWriterChooseMixin + + def parse_method_call(message) + parser().parseMethodCall(message) + end + + def create_method_response(successful, return_value) + create().methodResponse(successful, return_value) + end + end +end + +module ActionWebService # :nodoc: + module Protocol # :nodoc: + module XmlRpc # :nodoc: + def self.append_features(base) # :nodoc: + super + base.register_protocol(BodyOnly, XmlRpcProtocol) + end + + class XmlRpcProtocol < AbstractProtocol # :nodoc: + def self.create_protocol_request(container_class, action_pack_request) + helper = XMLRPC::XmlRpcHelper.instance + service_name = action_pack_request.parameters['action'] + methodname, params = helper.parse_method_call(action_pack_request.raw_post) + methodname.gsub!(/^[^\.]+\./, '') unless methodname =~ /^system\./ # XXX + protocol = XmlRpcProtocol.new(container_class) + content_type = action_pack_request.env['HTTP_CONTENT_TYPE'] + content_type ||= 'text/xml' + request = ProtocolRequest.new(protocol, + action_pack_request.raw_post, + service_name.to_sym, + methodname, + content_type, + :xmlrpc_values => params) + request + rescue + nil + end + + def self.create_protocol_client(api, protocol_name, endpoint_uri, options) + return nil unless protocol_name.to_s.downcase.to_sym == :xmlrpc + ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options) + end + + def initialize(container_class) + super(container_class) + container_class.write_inheritable_hash('default_system_methods', XmlRpcProtocol => method(:xmlrpc_default_system_handler)) + end + + def unmarshal_request(protocol_request) + values = protocol_request.options[:xmlrpc_values] + signature = protocol_request.signature + if signature + values = self.class.transform_incoming_method_params(self.class.transform_array_types(signature), values) + protocol_request.check_parameter_types(values, check_array_types(signature)) + values + else + protocol_request.checked? ? [] : values + end + end + + def marshal_response(protocol_request, return_value) + helper = XMLRPC::XmlRpcHelper.instance + signature = protocol_request.return_signature + if signature + protocol_request.check_parameter_types([return_value], check_array_types(signature)) + return_value = self.class.transform_return_value(self.class.transform_array_types(signature), return_value) + raw_response = helper.create_method_response(true, return_value) + else + # XML-RPC doesn't have the concept of a void method, nor does it + # support a nil return value, so return true if we would have returned + # nil + if protocol_request.checked? + raw_response = helper.create_method_response(true, true) + else + return_value = true if return_value.nil? + raw_response = helper.create_method_response(true, return_value) + end + end + ProtocolResponse.new(self, raw_response, 'text/xml') + end + + def marshal_exception(exception) + helper = XMLRPC::XmlRpcHelper.instance + exception = XMLRPC::FaultException.new(1, exception.message) + raw_response = helper.create_method_response(false, exception) + ProtocolResponse.new(self, raw_response, 'text/xml') + end + + class << self + def transform_incoming_method_params(signature, params) + (1..signature.size).each do |i| + i -= 1 + params[i] = xmlrpc_to_ruby(params[i], signature[i]) + end + params + end + + def transform_return_value(signature, return_value) + ruby_to_xmlrpc(return_value, signature[0]) + end + + def ruby_to_xmlrpc(param, param_class) + if param_class.is_a?(XmlRpcArray) + param.map{|p| ruby_to_xmlrpc(p, param_class.klass)} + elsif param_class.ancestors.include?(ActiveRecord::Base) + param.instance_variable_get('@attributes') + elsif param_class.ancestors.include?(ActionWebService::Struct) + struct = {} + param_class.members.each do |name, klass| + value = param.send(name) + next if value.nil? + struct[name.to_s] = value + end + struct + else + param + end + end + + def xmlrpc_to_ruby(param, param_class) + if param_class.is_a?(XmlRpcArray) + param.map{|p| xmlrpc_to_ruby(p, param_class.klass)} + elsif param_class.ancestors.include?(ActiveRecord::Base) + raise(ProtocolError, "incoming ActiveRecord::Base types are not allowed") + elsif param_class.ancestors.include?(ActionWebService::Struct) + unless param.is_a?(Hash) + raise(ProtocolError, "expected parameter to be a Hash") + end + new_param = param_class.new + param_class.members.each do |name, klass| + new_param.send('%s=' % name.to_s, param[name.to_s]) + end + new_param + else + param + end + end + + def transform_array_types(signature) + signature.map{|x| x.is_a?(Array) ? XmlRpcArray.new(x[0]) : x} + end + end + + private + def xmlrpc_default_system_handler(name, service_class, *args) + case name + when 'system.listMethods' + methods = [] + api = service_class.web_service_api + api.api_methods.each do |name, info| + methods << api.public_api_method_name(name) + end + methods.sort + else + throw :try_default + end + end + + def check_array_types(signature) + signature.map{|x| x.is_a?(Array) ? Array : x} + end + + class XmlRpcArray + attr :klass + def initialize(klass) + @klass = klass + end + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/router.rb b/actionwebservice/lib/action_web_service/router.rb new file mode 100644 index 0000000000..4bfb0bc8b7 --- /dev/null +++ b/actionwebservice/lib/action_web_service/router.rb @@ -0,0 +1,2 @@ +require 'action_web_service/router/action_controller' +require 'action_web_service/router/wsdl' diff --git a/actionwebservice/lib/action_web_service/router/action_controller.rb b/actionwebservice/lib/action_web_service/router/action_controller.rb new file mode 100644 index 0000000000..591fe4e232 --- /dev/null +++ b/actionwebservice/lib/action_web_service/router/action_controller.rb @@ -0,0 +1,99 @@ +module ActionWebService # :nodoc: + module Router # :nodoc: + module ActionController # :nodoc: + def self.append_features(base) # :nodoc: + base.add_web_service_api_callback do |container_class, api| + if container_class.web_service_dispatching_mode == :direct + container_class.class_eval <<-EOS + def api + process_action_service_request + end + EOS + end + end + base.add_web_service_definition_callback do |klass, name, info| + if klass.web_service_dispatching_mode == :delegated + klass.class_eval <<-EOS + def #{name} + process_action_service_request + end + EOS + end + end + base.send(:include, ActionWebService::Router::ActionController::InstanceMethods) + end + + module InstanceMethods # :nodoc: + private + def process_action_service_request + protocol_request = nil + begin + begin + protocol_request = probe_request_protocol(self.request) + rescue Exception => e + unless logger.nil? + logger.error "Invalid request: #{e.message}" + logger.error self.request.raw_post + end + raise + end + if protocol_request + log_request(protocol_request) + protocol_response = dispatch_web_service_request(protocol_request) + log_response(protocol_response) + response_options = { + :type => protocol_response.content_type, + :disposition => 'inline' + } + send_data(protocol_response.raw_body, response_options) + else + logger.fatal "Invalid Action Web Service service or method requested" unless logger.nil? + render_text 'Internal protocol error', "500 Invalid service/method" + end + rescue Exception => e + log_error e unless logger.nil? + exc_response = nil + case web_service_dispatching_mode + when :direct + if self.class.web_service_exception_reporting + exc_response = protocol_request.protocol.marshal_exception(e) + end + when :delegated + web_service = web_service_object(protocol_request.service_name) rescue nil + if web_service && web_service.class.web_service_exception_reporting + exc_response = protocol_request.protocol.marshal_exception(e) rescue nil + end + end + if exc_response + response_options = { + :type => exc_response.content_type, + :disposition => 'inline' + } + log_response exc_response + send_data(exc_response.raw_body, response_options) + else + render_text 'Internal protocol error', "500 #{e.message}" + end + end + end + + def log_request(protocol_request) + unless logger.nil? + web_service_name = protocol_request.web_service_name + method_name = protocol_request.public_method_name + logger.info "\nProcessing Action Web Service Request: #{web_service_name}##{method_name}" + logger.info "Raw Request Body:" + logger.info protocol_request.raw_body + end + end + + def log_response(protocol_response) + unless logger.nil? + logger.info "\nRaw Response Body:" + logger.info protocol_response.raw_body + end + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/router/wsdl.rb b/actionwebservice/lib/action_web_service/router/wsdl.rb new file mode 100644 index 0000000000..6963334818 --- /dev/null +++ b/actionwebservice/lib/action_web_service/router/wsdl.rb @@ -0,0 +1,210 @@ +module ActionWebService # :nodoc: + module Router # :nodoc: + module Wsdl # :nodoc: + def self.append_features(base) # :nodoc: + base.class_eval do + class << self + alias_method :inherited_without_wsdl, :inherited + end + end + base.extend(ClassMethods) + end + + module ClassMethods + def inherited(child) + inherited_without_wsdl(child) + child.send(:include, ActionWebService::Router::Wsdl::InstanceMethods) + end + end + + module InstanceMethods # :nodoc: + XsdNs = 'http://www.w3.org/2001/XMLSchema' + WsdlNs = 'http://schemas.xmlsoap.org/wsdl/' + SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/' + SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/' + SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http' + + def wsdl + case @request.method + when :get + begin + host_name = @request.env['HTTP_HOST']||@request.env['SERVER_NAME'] + uri = "http://#{host_name}/#{controller_name}/" + soap_action_base = "/#{controller_name}" + xml = to_wsdl(self, uri, soap_action_base) + send_data(xml, :type => 'text/xml', :disposition => 'inline') + rescue Exception => e + log_error e unless logger.nil? + render_text('', "500 #{e.message}") + end + when :post + render_text('', "500 POST not supported") + end + end + + private + def to_wsdl(container, uri, soap_action_base) + wsdl = "" + + web_service_dispatching_mode = container.web_service_dispatching_mode + mapper = container.class.soap_mapper + namespace = mapper.custom_namespace + wsdl_service_name = namespace.split(/:/)[1] + + services = {} + mapper.map_container_services(container) do |name, api, api_methods| + services[name] = [api, api_methods] + end + custom_types = mapper.custom_types + + + xm = Builder::XmlMarkup.new(:target => wsdl, :indent => 2) + xm.instruct! + + xm.definitions('name' => wsdl_service_name, + 'targetNamespace' => namespace, + 'xmlns:typens' => namespace, + 'xmlns:xsd' => XsdNs, + 'xmlns:soap' => SoapNs, + 'xmlns:soapenc' => SoapEncodingNs, + 'xmlns:wsdl' => WsdlNs, + 'xmlns' => WsdlNs) do + + # Custom type XSD generation + if custom_types.size > 0 + xm.types do + xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do + custom_types.each do |klass, mapping| + case + when mapping.is_a?(ActionWebService::Protocol::Soap::SoapArrayMapping) + xm.xsd(:complexType, 'name' => mapping.type_name) do + xm.xsd(:complexContent) do + xm.xsd(:restriction, 'base' => 'soapenc:Array') do + xm.xsd(:attribute, 'ref' => 'soapenc:arrayType', + 'wsdl:arrayType' => mapping.element_mapping.qualified_type_name + '[]') + end + end + end + when mapping.is_a?(ActionWebService::Protocol::Soap::SoapMapping) + xm.xsd(:complexType, 'name' => mapping.type_name) do + xm.xsd(:all) do + mapping.each_attribute do |name, type_name| + xm.xsd(:element, 'name' => name, 'type' => type_name) + end + end + end + else + raise(WsdlError, "unsupported mapping type #{mapping.class.name}") + end + end + end + end + end + + services.each do |service_name, service_values| + service_api, api_methods = service_values + # Parameter list message definitions + api_methods.each do |method_name, method_signature| + gen = lambda do |msg_name, direction| + xm.message('name' => msg_name) do + sym = nil + if direction == :out + if method_signature[:returns] + xm.part('name' => 'return', 'type' => method_signature[:returns][0].qualified_type_name) + end + else + mapping_list = method_signature[:expects] + i = 1 + mapping_list.each do |mapping| + if mapping.is_a?(Hash) + param_name = mapping.keys.shift + mapping = mapping.values.shift + else + param_name = "param#{i}" + end + xm.part('name' => param_name, 'type' => mapping.qualified_type_name) + i += 1 + end if mapping_list + end + end + end + public_name = service_api.public_api_method_name(method_name) + gen.call(public_name, :in) + gen.call("#{public_name}Response", :out) + end + + # Declare the port + port_name = port_name_for(wsdl_service_name, service_name) + xm.portType('name' => port_name) do + api_methods.each do |method_name, method_signature| + public_name = service_api.public_api_method_name(method_name) + xm.operation('name' => public_name) do + xm.input('message' => "typens:#{public_name}") + xm.output('message' => "typens:#{public_name}Response") + end + end + end + + # Bind the port to SOAP + binding_name = binding_name_for(wsdl_service_name, service_name) + xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do + xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport) + api_methods.each do |method_name, method_signature| + public_name = service_api.public_api_method_name(method_name) + xm.operation('name' => public_name) do + case web_service_dispatching_mode + when :direct + soap_action = soap_action_base + "/api/" + public_name + when :delegated + soap_action = soap_action_base \ + + "/" + service_name.to_s \ + + "/" + public_name + end + xm.soap(:operation, 'soapAction' => soap_action) + xm.input do + xm.soap(:body, + 'use' => 'encoded', + 'namespace' => namespace, + 'encodingStyle' => SoapEncodingNs) + end + xm.output do + xm.soap(:body, + 'use' => 'encoded', + 'namespace' => namespace, + 'encodingStyle' => SoapEncodingNs) + end + end + end + end + end + + # Define the service + xm.service('name' => "#{wsdl_service_name}Service") do + services.each do |service_name, service_values| + port_name = port_name_for(wsdl_service_name, service_name) + binding_name = binding_name_for(wsdl_service_name, service_name) + case web_service_dispatching_mode + when :direct + binding_target = 'api' + when :delegated + binding_target = service_name.to_s + end + xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do + xm.soap(:address, 'location' => "#{uri}#{binding_target}") + end + end + end + end + end + + def port_name_for(wsdl_service_name, service_name) + "#{wsdl_service_name}#{service_name.to_s.camelize}Port" + end + + def binding_name_for(wsdl_service_name, service_name) + "#{wsdl_service_name}#{service_name.to_s.camelize}Binding" + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/struct.rb b/actionwebservice/lib/action_web_service/struct.rb new file mode 100644 index 0000000000..5420f4cf49 --- /dev/null +++ b/actionwebservice/lib/action_web_service/struct.rb @@ -0,0 +1,55 @@ +module ActionWebService + # To send structured types across the wire, derive from ActionWebService::Struct, + # and use +member+ to declare structure members. + # + # ActionWebService::Struct should be used in method signatures when you want to accept or return + # structured types that have no Active Record model class representations, or you don't + # want to expose your entire Active Record model to remote callers. + # + # === Example + # + # class Person < ActionWebService::Struct + # member :id, :int + # member :firstnames, [:string] + # member :lastname, :string + # member :email, :string + # end + # person = Person.new(:id => 5, :firstname => 'john', :lastname => 'doe') + # + # Active Record model classes are already implicitly supported for method + # return signatures. A structure containing its columns as members will be + # automatically generated if its present in a signature. + class Struct + + # If a Hash is given as argument to an ActionWebService::Struct constructor, + # it can contain initial values for the structure member. + def initialize(values={}) + if values.is_a?(Hash) + values.map{|k,v| send('%s=' % k.to_s, v)} + end + end + + # The member with the given name + def [](name) + send(name.to_s) + end + + class << self + include ActionWebService::Signature + + # Creates a structure member with the specified +name+ and +type+. Generates + # accessor methods for reading and writing the member value. + def member(name, type) + write_inheritable_hash("struct_members", name => signature_parameter_class(type)) + class_eval <<-END + def #{name}; @#{name}; end + def #{name}=(value); @#{name} = value; end + END + end + + def members # :nodoc: + read_inheritable_attribute("struct_members") || {} + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/support/class_inheritable_options.rb b/actionwebservice/lib/action_web_service/support/class_inheritable_options.rb new file mode 100644 index 0000000000..4d1c2ed471 --- /dev/null +++ b/actionwebservice/lib/action_web_service/support/class_inheritable_options.rb @@ -0,0 +1,26 @@ +class Class # :nodoc: + def class_inheritable_option(sym, default_value=nil) + write_inheritable_attribute sym, default_value + class_eval <<-EOS + def self.#{sym}(value=nil) + if !value.nil? + write_inheritable_attribute(:#{sym}, value) + else + read_inheritable_attribute(:#{sym}) + end + end + + def self.#{sym}=(value) + write_inheritable_attribute(:#{sym}, value) + end + + def #{sym} + self.class.#{sym} + end + + def #{sym}=(value) + self.class.#{sym} = value + end + EOS + end +end diff --git a/actionwebservice/lib/action_web_service/support/signature.rb b/actionwebservice/lib/action_web_service/support/signature.rb new file mode 100644 index 0000000000..00c62a2232 --- /dev/null +++ b/actionwebservice/lib/action_web_service/support/signature.rb @@ -0,0 +1,100 @@ +module ActionWebService # :nodoc: + # Action Web Service parameter specifiers may contain symbols or strings + # instead of Class objects, for a limited set of base types. + # + # This provides an unambiguous way to specify that a given parameter + # contains an integer or boolean value, for example. + # + # The allowed set of symbol/string aliases: + # + # [:int] any integer value + # [:float] any floating point value + # [:string] any string value + # [:bool] any boolean value + # [:time] any value containing both date and time + # [:date] any value containing only a date + module Signature + class SignatureError < StandardError # :nodoc: + end + + private + def canonical_signature(params) + return nil if params.nil? + params.map do |param| + klass = signature_parameter_class(param) + if param.is_a?(Hash) + param[param.keys[0]] = klass + param + else + klass + end + end + end + + def signature_parameter_class(param) + param = param.is_a?(Hash) ? param.values[0] : param + is_array = param.is_a?(Array) + param = is_array ? param[0] : param + param = param.is_a?(String) ? param.to_sym : param + param = param.is_a?(Symbol) ? signature_ruby_class(param) : param + is_array ? [param] : param + end + + + def canonical_signature_base_type(base_type) + base_type = base_type.to_sym + case base_type + when :int, :integer, :fixnum, :bignum + :int + when :string, :base64 + :string + when :bool, :boolean + :bool + when :float, :double + :float + when :time, :datetime, :timestamp + :time + when :date + :date + else + raise(SignatureError, ":#{base_type} is not an ActionWebService base type") + end + end + + def signature_ruby_class(base_type) + case canonical_signature_base_type(base_type) + when :int + Integer + when :string + String + when :bool + TrueClass + when :float + Float + when :time + Time + when :date + Date + end + end + + def signature_base_type(ruby_class) + case ruby_class + when Bignum, Integer, Fixnum + :int + when String + :string + when TrueClass, FalseClass + :bool + when Float, Numeric, Precision + :float + when Time, DateTime + :time + when Date + :date + else + raise(SignatureError, "#{ruby_class.name} is not an ActionWebService base type") + end + end + end +end diff --git a/actionwebservice/test/abstract_client.rb b/actionwebservice/test/abstract_client.rb index 70d2d21124..0197b87d63 100644 --- a/actionwebservice/test/abstract_client.rb +++ b/actionwebservice/test/abstract_client.rb @@ -4,7 +4,7 @@ require 'webrick/log' require 'singleton' module ClientTest - class Person < ActionService::Struct + class Person < ActionWebService::Struct member :firstnames, [:string] member :lastname, :string @@ -13,7 +13,7 @@ module ClientTest end end - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :void api_method :normal, :expects => [:int, :int], :returns => [:int] api_method :array_return, :returns => [[Person]] diff --git a/actionwebservice/test/abstract_soap.rb b/actionwebservice/test/abstract_soap.rb index 7454be9bdf..351a4f8479 100644 --- a/actionwebservice/test/abstract_soap.rb +++ b/actionwebservice/test/abstract_soap.rb @@ -22,7 +22,7 @@ class AbstractSoapTest < Test::Unit::TestCase param_def << ["in", "param#{i}", mapping.registry_mapping] i += 1 end - qname = XSD::QName.new('urn:ActionService', public_method_name) + qname = XSD::QName.new('urn:ActionWebService', public_method_name) request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def) soap_args = [] i = 1 diff --git a/actionwebservice/test/abstract_unit.rb b/actionwebservice/test/abstract_unit.rb index 54ca73b35c..e33c5c9bbe 100644 --- a/actionwebservice/test/abstract_unit.rb +++ b/actionwebservice/test/abstract_unit.rb @@ -1,7 +1,7 @@ $:.unshift(File.dirname(__FILE__) + '/../lib') require 'test/unit' -require 'action_service' +require 'action_web_service' require 'action_controller' require 'action_controller/test_process' diff --git a/actionwebservice/test/api_test.rb b/actionwebservice/test/api_test.rb index 2ef5cc7bda..b61a4b57c5 100644 --- a/actionwebservice/test/api_test.rb +++ b/actionwebservice/test/api_test.rb @@ -1,7 +1,7 @@ require File.dirname(__FILE__) + '/abstract_unit' module APITest - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :void api_method :expects_and_returns, :expects_and_returns => [:string] api_method :expects, :expects => [:int, :bool] diff --git a/actionwebservice/test/base_test.rb b/actionwebservice/test/base_test.rb index a9fbdd1a8b..55a112a089 100644 --- a/actionwebservice/test/base_test.rb +++ b/actionwebservice/test/base_test.rb @@ -1,19 +1,19 @@ require File.dirname(__FILE__) + '/abstract_unit' module BaseTest - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :add, :expects => [:int, :int], :returns => [:int] api_method :void end - class PristineAPI < ActionService::API::Base + class PristineAPI < ActionWebService::API::Base inflect_names false api_method :add api_method :under_score end - class Service < ActionService::Base + class Service < ActionWebService::Base web_service_api API def add(a, b) @@ -23,7 +23,7 @@ module BaseTest end end - class PristineService < ActionService::Base + class PristineService < ActionWebService::Base web_service_api PristineAPI def add diff --git a/actionwebservice/test/client_soap_test.rb b/actionwebservice/test/client_soap_test.rb index 80fae90543..82ccef6f1b 100644 --- a/actionwebservice/test/client_soap_test.rb +++ b/actionwebservice/test/client_soap_test.rb @@ -48,7 +48,7 @@ class TC_ClientSoap < Test::Unit::TestCase def setup @server = SoapServer.instance @container = @server.container - @client = ActionService::Client::Soap.new(API, "http://localhost:#{@server.server_port}/client/api") + @client = ActionWebService::Client::Soap.new(API, "http://localhost:#{@server.server_port}/client/api") end def test_void diff --git a/actionwebservice/test/client_xmlrpc_test.rb b/actionwebservice/test/client_xmlrpc_test.rb index 35768adf32..f9be9bfe16 100644 --- a/actionwebservice/test/client_xmlrpc_test.rb +++ b/actionwebservice/test/client_xmlrpc_test.rb @@ -47,7 +47,7 @@ class TC_ClientXmlRpc < Test::Unit::TestCase def setup @server = XmlRpcServer.instance @container = @server.container - @client = ActionService::Client::XmlRpc.new(API, "http://localhost:#{@server.server_port}/client/api") + @client = ActionWebService::Client::XmlRpc.new(API, "http://localhost:#{@server.server_port}/client/api") end def test_void diff --git a/actionwebservice/test/invocation_test.rb b/actionwebservice/test/invocation_test.rb index e4c82a35f8..8ca80ec8f2 100644 --- a/actionwebservice/test/invocation_test.rb +++ b/actionwebservice/test/invocation_test.rb @@ -1,7 +1,7 @@ require File.dirname(__FILE__) + '/abstract_unit' module InvocationTest - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :add, :expects => [:int, :int], :returns => [:int] api_method :transmogrify, :expects_and_returns => [:string] api_method :fail_with_reason @@ -12,7 +12,7 @@ module InvocationTest api_method :only_two end - class Service < ActionService::Base + class Service < ActionWebService::Base web_service_api API before_invocation :intercept_before, :except => [:no_before] @@ -81,7 +81,7 @@ module InvocationTest end class TC_Invocation < Test::Unit::TestCase - include ActionService::Invocation + include ActionWebService::Invocation def setup @service = InvocationTest::Service.new diff --git a/actionwebservice/test/protocol_registry_test.rb b/actionwebservice/test/protocol_registry_test.rb index 8e2b9659a6..261f6f400e 100644 --- a/actionwebservice/test/protocol_registry_test.rb +++ b/actionwebservice/test/protocol_registry_test.rb @@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/abstract_unit' module Foo - include ActionService::Protocol + include ActionWebService::Protocol def self.append_features(base) super @@ -15,14 +15,14 @@ module Foo class FooFullProtocol < AbstractProtocol def self.create_protocol_request(klass, request) protocol = FooFullProtocol.new klass - ActionService::Protocol::ProtocolRequest.new(protocol, '', '', '', '') + ActionWebService::Protocol::ProtocolRequest.new(protocol, '', '', '', '') end end class FooMinimalProtocol < AbstractProtocol def self.create_protocol_request(klass, request) protocol = FooMinimalProtocol.new klass - ActionService::Protocol::ProtocolRequest.new(protocol, '', '', '', '') + ActionWebService::Protocol::ProtocolRequest.new(protocol, '', '', '', '') end end @@ -31,7 +31,7 @@ module Foo end class ProtocolRegistry - include ActionService::Protocol::Registry + include ActionWebService::Protocol::Registry include Foo def all_protocols diff --git a/actionwebservice/test/protocol_soap_test.rb b/actionwebservice/test/protocol_soap_test.rb index 164d06bbd6..1130dff3a7 100644 --- a/actionwebservice/test/protocol_soap_test.rb +++ b/actionwebservice/test/protocol_soap_test.rb @@ -1,7 +1,7 @@ require File.dirname(__FILE__) + '/abstract_soap' module ProtocolSoapTest - class Person < ActionService::Struct + class Person < ActionWebService::Struct member :id, Integer member :names, [String] member :lastname, String @@ -12,7 +12,7 @@ module ProtocolSoapTest end end - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :argument_passing, :expects => [{:int=>:int}, {:string=>:string}, {:array=>[:int]}], :returns => [:bool] api_method :array_returner, :returns => [[:int]] api_method :nil_returner @@ -22,7 +22,7 @@ module ProtocolSoapTest default_api_method :default end - class Service < ActionService::Base + class Service < ActionWebService::Base web_service_api API attr :int @@ -73,10 +73,10 @@ module ProtocolSoapTest end class AbstractContainer - include ActionService::API - include ActionService::Container - include ActionService::Protocol::Registry - include ActionService::Protocol::Soap + include ActionWebService::API + include ActionWebService::Container + include ActionWebService::Protocol::Registry + include ActionWebService::Protocol::Soap wsdl_service_name 'Test' diff --git a/actionwebservice/test/protocol_xmlrpc_test.rb b/actionwebservice/test/protocol_xmlrpc_test.rb index a8a6efc07e..7dc5cf0252 100644 --- a/actionwebservice/test/protocol_xmlrpc_test.rb +++ b/actionwebservice/test/protocol_xmlrpc_test.rb @@ -18,13 +18,13 @@ module XMLRPC end module ProtocolXmlRpcTest - class Person < ActionService::Struct + class Person < ActionWebService::Struct member :firstname, String member :lastname, String member :active, TrueClass end - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :add, :expects => [Integer, Integer], :returns => [Integer] api_method :hash_returner, :returns => [Hash] api_method :array_returner, :returns => [[Integer]] @@ -34,7 +34,7 @@ module ProtocolXmlRpcTest default_api_method :default end - class Service < ActionService::Base + class Service < ActionWebService::Base web_service_api API attr :result @@ -80,10 +80,10 @@ module ProtocolXmlRpcTest $service = Service.new class Container - include ActionService::Container - include ActionService::Protocol::Registry - include ActionService::Protocol::Soap - include ActionService::Protocol::XmlRpc + include ActionWebService::Container + include ActionWebService::Protocol::Registry + include ActionWebService::Protocol::Soap + include ActionWebService::Protocol::XmlRpc def protocol_request(request) probe_request_protocol(request) diff --git a/actionwebservice/test/router_action_controller_test.rb b/actionwebservice/test/router_action_controller_test.rb index e26d67c194..4f6d40e738 100644 --- a/actionwebservice/test/router_action_controller_test.rb +++ b/actionwebservice/test/router_action_controller_test.rb @@ -2,11 +2,11 @@ require File.dirname(__FILE__) + '/abstract_soap' require 'wsdl/parser' module RouterActionControllerTest - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :add, :expects => [:int, :int], :returns => [:int] end - class Service < ActionService::Base + class Service < ActionWebService::Base web_service_api API attr :added @@ -22,7 +22,7 @@ module RouterActionControllerTest web_service(:test_service) { @service ||= Service.new; @service } end - class DirectAPI < ActionService::API::Base + class DirectAPI < ActionWebService::API::Base api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] api_method :before_filtered api_method :after_filtered, :returns => [:int] diff --git a/actionwebservice/test/router_wsdl_test.rb b/actionwebservice/test/router_wsdl_test.rb index 6812d25579..a441e73f37 100644 --- a/actionwebservice/test/router_wsdl_test.rb +++ b/actionwebservice/test/router_wsdl_test.rb @@ -2,20 +2,20 @@ require File.dirname(__FILE__) + '/abstract_unit' require 'wsdl/parser' module RouterWsdlTest - class Person < ActionService::Struct + class Person < ActionWebService::Struct member :id, Integer member :names, [String] member :lastname, String member :deleted, TrueClass end - class API < ActionService::API::Base + class API < ActionWebService::API::Base api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] api_method :find_people, :returns => [[Person]] api_method :nil_returner end - class Service < ActionService::Base + class Service < ActionWebService::Base web_service_api API def add(a, b) diff --git a/actionwebservice/test/struct_test.rb b/actionwebservice/test/struct_test.rb index b883c6d991..e6f1603c73 100644 --- a/actionwebservice/test/struct_test.rb +++ b/actionwebservice/test/struct_test.rb @@ -1,7 +1,7 @@ require File.dirname(__FILE__) + '/abstract_unit' module StructTest - class Struct < ActionService::Struct + class Struct < ActionWebService::Struct member :id, Integer member :name, String member :items, [String] -- cgit v1.2.3