mirror of
https://github.com/TracksApp/tracks.git
synced 2025-09-22 05:50:47 +02:00
Remove the vendored gems
Bundler allows us to specify the exact versions of gems which eliminates the need for vendoring gems (IMO)
This commit is contained in:
parent
5daff93349
commit
2ff4d7961d
683 changed files with 0 additions and 84008 deletions
|
@ -1,96 +0,0 @@
|
|||
--- !ruby/object:Gem::Specification
|
||||
name: datanoise-actionwebservice
|
||||
version: !ruby/object:Gem::Version
|
||||
hash: 7
|
||||
prerelease:
|
||||
segments:
|
||||
- 2
|
||||
- 3
|
||||
- 2
|
||||
version: 2.3.2
|
||||
platform: ruby
|
||||
authors:
|
||||
- Leon Breedt, Kent Sibilev
|
||||
autorequire:
|
||||
bindir: bin
|
||||
cert_chain: []
|
||||
|
||||
date: 2009-03-29 00:00:00 Z
|
||||
dependencies:
|
||||
- !ruby/object:Gem::Dependency
|
||||
name: actionpack
|
||||
prerelease: false
|
||||
requirement: &id001 !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - "="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 7
|
||||
segments:
|
||||
- 2
|
||||
- 3
|
||||
- 12
|
||||
version: 2.3.12
|
||||
type: :runtime
|
||||
version_requirements: *id001
|
||||
- !ruby/object:Gem::Dependency
|
||||
name: activerecord
|
||||
prerelease: false
|
||||
requirement: &id002 !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - "="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 7
|
||||
segments:
|
||||
- 2
|
||||
- 3
|
||||
- 12
|
||||
version: 2.3.12
|
||||
type: :runtime
|
||||
version_requirements: *id002
|
||||
description: Adds WSDL/SOAP and XML-RPC web service support to Action Pack
|
||||
email: bitserf@gmail.com, ksibilev@yahoo.com
|
||||
executables: []
|
||||
|
||||
extensions: []
|
||||
|
||||
extra_rdoc_files: []
|
||||
|
||||
files: []
|
||||
|
||||
homepage: http://www.rubyonrails.org
|
||||
licenses: []
|
||||
|
||||
post_install_message:
|
||||
rdoc_options: []
|
||||
|
||||
require_paths:
|
||||
- lib
|
||||
required_ruby_version: !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 3
|
||||
segments:
|
||||
- 0
|
||||
version: "0"
|
||||
required_rubygems_version: !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 3
|
||||
segments:
|
||||
- 0
|
||||
version: "0"
|
||||
requirements:
|
||||
- none
|
||||
rubyforge_project: aws
|
||||
rubygems_version: 1.7.2
|
||||
signing_key:
|
||||
specification_version: 2
|
||||
summary: Web service support for Action Pack.
|
||||
test_files: []
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
*2.1.0*
|
||||
|
||||
* Porting to Rails 2.1.0 [Kent Sibilev]
|
||||
|
||||
* Documentation for ActionWebService::API::Base. Closes #7275. [zackchandler]
|
||||
|
||||
* Allow action_web_service to handle various HTTP methods including GET. Closes #7011. [zackchandler]
|
||||
|
||||
* Ensure that DispatcherError is being thrown when a malformed request is received. [Kent Sibilev]
|
||||
|
||||
* Added support for decimal types. Closes #6676. [Kent Sibilev]
|
||||
|
||||
* Removed deprecated end_form_tag helper. [Kent Sibilev]
|
||||
|
||||
* Removed deprecated @request and @response usages. [Kent Sibilev]
|
||||
|
||||
* Removed deprecated end_form_tag helper. [Kent Sibilev]
|
||||
|
||||
* Removed deprecated @request and @response usages. [Kent Sibilev]
|
||||
|
||||
*1.2.6* (November 24th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.6
|
||||
|
||||
* Depend on Active Record 1.15.6
|
||||
|
||||
|
||||
*1.2.5* (October 12th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.5
|
||||
|
||||
* Depend on Active Record 1.15.5
|
||||
|
||||
|
||||
*1.2.4* (October 4th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.4
|
||||
|
||||
* Depend on Active Record 1.15.4
|
||||
|
||||
|
||||
*1.2.3* (March 12th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.3
|
||||
|
||||
|
||||
*1.2.2* (Feburary 4th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.2
|
||||
|
||||
|
||||
*1.2.1* (January 16th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.1
|
||||
|
||||
|
||||
*1.2.0* (January 16th, 2007)
|
||||
|
||||
* Removed invocation of deprecated before_action and around_action filter methods. Corresponding before_invocation and after_invocation methods should be used instead. #6275 [Kent Sibilev]
|
||||
|
||||
* Provide access to the underlying SOAP driver. #6212 [bmilekic, Kent Sibilev]
|
||||
|
||||
* ActionWebService WSDL generation ignores HTTP_X_FORWARDED_HOST [Paul Butcher <paul@paulbutcher.com>]
|
||||
|
||||
* Tighten rescue clauses. #5985 [james@grayproductions.net]
|
||||
|
||||
* Fixed XMLRPC multicall when one of the called methods returns a struct object. [Kent Sibilev]
|
||||
|
||||
* Fix invoke_layered since api_method didn't declare :expects. Closes #4720. [Kevin Ballard <kevin@sb.org>, Kent Sibilev]
|
||||
|
||||
|
||||
*1.1.6* (August 10th, 2006)
|
||||
|
||||
* Rely on Action Pack 1.12.5
|
||||
|
||||
|
||||
*1.1.5* (August 8th, 2006)
|
||||
|
||||
* Rely on Action Pack 1.12.4 and Active Record 1.14.4
|
||||
|
||||
|
||||
*1.1.4* (June 29th, 2006)
|
||||
|
||||
* Rely on Action Pack 1.12.3
|
||||
|
||||
|
||||
*1.1.3* (June 27th, 2006)
|
||||
|
||||
* Rely on Action Pack 1.12.2 and Active Record 1.14.3
|
||||
|
||||
|
||||
*1.1.2* (April 9th, 2006)
|
||||
|
||||
* Rely on Active Record 1.14.2
|
||||
|
||||
|
||||
*1.1.1* (April 6th, 2006)
|
||||
|
||||
* Do not convert driver options to strings (#4499)
|
||||
|
||||
|
||||
*1.1.0* (March 27th, 2006)
|
||||
|
||||
* Make ActiveWebService::Struct type reloadable
|
||||
|
||||
* Fix scaffolding action when one of the members of a structural type has date or time type
|
||||
|
||||
* Remove extra index hash when generating scaffold html for parameters of structural type #4374 [joe@mjg2.com]
|
||||
|
||||
* Fix Scaffold Fails with Struct as a Parameter #4363 [joe@mjg2.com]
|
||||
|
||||
* Fix soap type registration of multidimensional arrays (#4232)
|
||||
|
||||
* Fix that marshaler couldn't handle ActiveRecord models defined in a different namespace (#2392).
|
||||
|
||||
* Fix that marshaler couldn't handle structs with members of ActiveRecord type (#1889).
|
||||
|
||||
* Fix that marshaler couldn't handle nil values for inner structs (#3576).
|
||||
|
||||
* Fix that changes to ActiveWebService::API::Base required restarting of the server (#2390).
|
||||
|
||||
* Fix scaffolding for signatures with :date, :time and :base64 types (#3321, #2769, #2078).
|
||||
|
||||
* Fix for incorrect casting of TrueClass/FalseClass instances (#2633, #3421).
|
||||
|
||||
* Fix for incompatibility problems with SOAP4R 1.5.5 (#2553) [Kent Sibilev]
|
||||
|
||||
|
||||
*1.0.0* (December 13th, 2005)
|
||||
|
||||
* Become part of Rails 1.0
|
||||
|
||||
*0.9.4* (December 7th, 2005)
|
||||
|
||||
* Update from LGPL to MIT license as per Minero Aoki's permission. [Marcel Molina Jr.]
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
* Fix that XML-RPC date/time values did not have well-defined behaviour (#2516, #2534). This fix has one caveat, in that we can't support pre-1970 dates from XML-RPC clients.
|
||||
|
||||
*0.9.3* (November 7th, 2005)
|
||||
|
||||
* Upgraded to Action Pack 1.11.0 and Active Record 1.13.0
|
||||
|
||||
|
||||
*0.9.2* (October 26th, 2005)
|
||||
|
||||
* Upgraded to Action Pack 1.10.2 and Active Record 1.12.2
|
||||
|
||||
|
||||
*0.9.1* (October 19th, 2005)
|
||||
|
||||
* Upgraded to Action Pack 1.10.1 and Active Record 1.12.1
|
||||
|
||||
|
||||
*0.9.0* (October 16th, 2005)
|
||||
|
||||
* Fix invalid XML request generation bug in test_invoke [Ken Barker]
|
||||
|
||||
* Add XML-RPC 'system.multicall' support #1941 [jbonnar]
|
||||
|
||||
* Fix duplicate XSD entries for custom types shared across delegated/layered services #1729 [Tyler Kovacs]
|
||||
|
||||
* Allow multiple invocations in the same test method #1720 [dkhawk]
|
||||
|
||||
* Added ActionWebService::API::Base.soap_client and ActionWebService::API::Base.xmlrpc_client helper methods to create the internal clients for an API, useful for testing from ./script/console
|
||||
|
||||
* ActionWebService now always returns UTF-8 responses.
|
||||
|
||||
|
||||
*0.8.1* (11 July, 2005)
|
||||
|
||||
* Fix scaffolding for Action Pack controller changes
|
||||
|
||||
|
||||
*0.8.0* (6 July, 2005)
|
||||
|
||||
* Fix WSDL generation by aliasing #inherited instead of trying to overwrite it, or the WSDL action may end up not being defined in the controller
|
||||
|
||||
* Add ActionController::Base.wsdl_namespace option, to allow overriding of the namespace used in generated WSDL and SOAP messages. This is equivalent to the [WebService(Namespace = "Value")] attribute in .NET.
|
||||
|
||||
* Add workaround for Ruby 1.8.3's SOAP4R changing the return value of SOAP::Mapping::Registry#find_mapped_soap_class #1414 [Shugo Maeda]
|
||||
|
||||
* Fix moduled controller URLs in WSDL, and add unit test to verify the generated URL #1428
|
||||
|
||||
* Fix scaffolding template paths, it was broken on Win32
|
||||
|
||||
* Fix that functional testing of :layered controllers failed when using the SOAP protocol
|
||||
|
||||
* Allow invocation filters in :direct controllers as well, as they have access to more information regarding the web service request than ActionPack filters
|
||||
|
||||
* Add support for a :base64 signature type #1272 [Shugo Maeda]
|
||||
|
||||
* Fix that boolean fields were not rendered correctly in scaffolding
|
||||
|
||||
* Fix that scaffolding was not working for :delegated dispatching
|
||||
|
||||
* Add support for structured types as input parameters to scaffolding, this should let one test the blogging APIs using scaffolding as well
|
||||
|
||||
* Fix that generated WSDL was not using relative_url_root for base URI #1210 [Shugo Maeda]
|
||||
|
||||
* Use UTF-8 encoding by default for SOAP responses, but if an encoding is supplied by caller, use that for the response #1211 [Shugo Maeda, NAKAMURA Hiroshi]
|
||||
|
||||
* If the WSDL was retrieved over HTTPS, use HTTPS URLs in the WSDL too
|
||||
|
||||
* Fix that casting change in 0.7.0 would convert nil values to the default value for the type instead of leaving it as nil
|
||||
|
||||
|
||||
*0.7.1* (20th April, 2005)
|
||||
|
||||
* Depend on Active Record 1.10.1 and Action Pack 1.8.1
|
||||
|
||||
|
||||
*0.7.0* (19th April, 2005)
|
||||
|
||||
* When casting structured types, don't try to send obj.name= unless obj responds to it, causes casting to be less likely to fail for XML-RPC
|
||||
|
||||
* Add scaffolding via ActionController::Base.web_service_scaffold for quick testing using a web browser
|
||||
|
||||
* ActionWebService::API::Base#api_methods now returns a hash containing ActionWebService::API::Method objects instead of hashes. However, ActionWebService::API::Method defines a #[]() backwards compatibility method so any existing code utilizing this will still work.
|
||||
|
||||
* The :layered dispatching mode can now be used with SOAP as well, allowing you to support SOAP and XML-RPC clients for APIs like the metaWeblog API
|
||||
|
||||
* Remove ActiveRecordSoapMarshallable workaround, see #912 for details
|
||||
|
||||
* Generalize casting code to be used by both SOAP and XML-RPC (previously, it was only XML-RPC)
|
||||
|
||||
* Ensure return value is properly cast as well, fixes XML-RPC interoperability with Ecto and possibly other clients
|
||||
|
||||
* Include backtraces in 500 error responses for failed request parsing, and remove "rescue nil" statements obscuring real errors for XML-RPC
|
||||
|
||||
* Perform casting of struct members even if the structure is already of the correct type, so that the type we specify for the struct member is always the type of the value seen by the API implementation
|
||||
|
||||
|
||||
*0.6.2* (27th March, 2005)
|
||||
|
||||
* Allow method declarations for direct dispatching to declare parameters as well. We treat an arity of < 0 or > 0 as an indication that we should send through parameters. Closes #939.
|
||||
|
||||
|
||||
*0.6.1* (22th March, 2005)
|
||||
|
||||
* Fix that method response QNames mismatched with that declared in the WSDL, makes SOAP::WSDLDriverFactory work against AWS again
|
||||
|
||||
* Fix that @request.env was being modified, instead, dup the value gotten from env
|
||||
|
||||
* Fix XML-RPC example to use :layered mode, so it works again
|
||||
|
||||
* Support casting '0' or 0 into false, and '1' or 1 into true, when expecting a boolean value
|
||||
|
||||
* Fix that SOAP fault response fault code values were not QName's #804
|
||||
|
||||
|
||||
*0.6.0* (7th March, 2005)
|
||||
|
||||
* Add action_controller/test_invoke, used for integrating AWS with the Rails testing infrastructure
|
||||
|
||||
* Allow passing through options to the SOAP RPC driver for the SOAP client
|
||||
|
||||
* Make the SOAP WS marshaler use #columns to decide which fields to marshal as well, avoids providing attributes brought in by associations
|
||||
|
||||
* Add <tt>ActionWebService::API::Base.allow_active_record_expects</tt> option, with a default of false. Setting this to true will allow specifying ActiveRecord::Base model classes in <tt>:expects</tt>. API writers should take care to validate the received ActiveRecord model objects when turning it on, and/or have an authentication mechanism in place to reduce the security risk.
|
||||
|
||||
* Improve error message reporting. Bugs in either AWS or the web service itself will send back a protocol-specific error report message if possible, otherwise, provide as much detail as possible.
|
||||
|
||||
* Removed type checking of received parameters, and perform casting for XML-RPC if possible, but fallback to the received parameters if casting fails, closes #677
|
||||
|
||||
* Refactored SOAP and XML-RPC marshaling and encoding into a small library devoted exclusively to protocol specifics, also cleaned up the SOAP marshaling approach, so that array and custom type marshaling should be a bit faster.
|
||||
|
||||
* Add namespaced XML-RPC method name support, closes #678
|
||||
|
||||
* Replace '::' with '..' in fully qualified type names for marshaling and WSDL. This improves interoperability with .NET, and closes #676.
|
||||
|
||||
|
||||
*0.5.0* (24th February, 2005)
|
||||
|
||||
* lib/action_service/dispatcher*: replace "router" fragments with
|
||||
one file for Action Controllers, moves dispatching work out of
|
||||
the container
|
||||
* lib/*,test/*,examples/*: rename project to
|
||||
ActionWebService. prefix all generic "service" type names with web_.
|
||||
update all using code as well as the RDoc.
|
||||
* lib/action_service/router/wsdl.rb: ensure that #wsdl is
|
||||
defined in the final container class, or the new ActionPack
|
||||
filtering will exclude it
|
||||
* lib/action_service/struct.rb,test/struct_test.rb: create a
|
||||
default #initialize on inherit that accepts a Hash containing
|
||||
the default member values
|
||||
* lib/action_service/api/action_controller.rb: add support and
|
||||
tests for #client_api in controller
|
||||
* test/router_wsdl_test.rb: add tests to ensure declared
|
||||
service names don't contain ':', as ':' causes interoperability
|
||||
issues
|
||||
* lib/*, test/*: rename "interface" concept to "api", and change all
|
||||
related uses to reflect this change. update all uses of Inflector
|
||||
to call the method on String instead.
|
||||
* test/api_test.rb: add test to ensure API definition not
|
||||
instantiatable
|
||||
* lib/action_service/invocation.rb: change @invocation_params to
|
||||
@method_params
|
||||
* lib/*: update RDoc
|
||||
* lib/action_service/struct.rb: update to support base types
|
||||
* lib/action_service/support/signature.rb: support the notion of
|
||||
"base types" in signatures, with well-known unambiguous names such as :int,
|
||||
:bool, etc, which map to the correct Ruby class. accept the same names
|
||||
used by ActiveRecord as well as longer versions of each, as aliases.
|
||||
* examples/*: update for seperate API definition updates
|
||||
* 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 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.
|
||||
* test/router_action_controller_test.rb: add tests to ensure
|
||||
ActionController filters are actually called.
|
||||
* test/protocol_soap_test.rb: add more tests for direct dispatching.
|
||||
|
||||
0.3.0
|
||||
|
||||
* First public release
|
|
@ -1,21 +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.
|
||||
|
381
vendor/gems/datanoise-actionwebservice-2.3.2/README
vendored
381
vendor/gems/datanoise-actionwebservice-2.3.2/README
vendored
|
@ -1,381 +0,0 @@
|
|||
= Action Web Service -- Serving APIs on rails
|
||||
|
||||
Action Web Service provides a way to publish interoperable web service APIs with
|
||||
Rails without spending a lot of time delving into protocol details.
|
||||
|
||||
|
||||
== Features
|
||||
|
||||
* SOAP RPC protocol support
|
||||
* 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 Web Service based applications
|
||||
* Type signature hints to improve interoperability with static languages
|
||||
* Active Record model class support in signatures
|
||||
|
||||
|
||||
== Defining your APIs
|
||||
|
||||
You specify the methods you want to make available as API methods in an
|
||||
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 separately from the API
|
||||
specification.
|
||||
|
||||
|
||||
==== Method name inflection
|
||||
|
||||
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
|
||||
ActionWebService::API::Base +inflect_names+ option.
|
||||
|
||||
|
||||
==== Inflection examples
|
||||
|
||||
:add => Add
|
||||
:find_all => FindAll
|
||||
|
||||
|
||||
==== Disabling inflection
|
||||
|
||||
class PersonAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
end
|
||||
|
||||
|
||||
==== API definition example
|
||||
|
||||
class PersonAPI < ActionWebService::API::Base
|
||||
api_method :add, :expects => [:string, :string, :bool], :returns => [:int]
|
||||
api_method :remove, :expects => [:int], :returns => [:bool]
|
||||
end
|
||||
|
||||
==== API usage example
|
||||
|
||||
class PersonController < ActionController::Base
|
||||
web_service_api PersonAPI
|
||||
|
||||
def add
|
||||
end
|
||||
|
||||
def remove
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
== Publishing your APIs
|
||||
|
||||
Action Web Service uses Action Pack to process protocol requests. There are two
|
||||
modes of dispatching protocol requests, _Direct_, and _Delegated_.
|
||||
|
||||
|
||||
=== Direct dispatching
|
||||
|
||||
This is the default mode. In this mode, public controller instance methods
|
||||
implement the API methods, and parameters are passed through to the methods in
|
||||
accordance with the API specification.
|
||||
|
||||
The return value of the method is sent back as the return value to the
|
||||
caller.
|
||||
|
||||
In this mode, a special <tt>api</tt> action is generated in the target
|
||||
controller to unwrap the protocol request, forward it on to the relevant method
|
||||
and send back the wrapped return value. <em>This action must not be
|
||||
overridden.</em>
|
||||
|
||||
==== Direct dispatching example
|
||||
|
||||
class PersonController < ApplicationController
|
||||
web_service_api PersonAPI
|
||||
|
||||
def add
|
||||
end
|
||||
|
||||
def remove
|
||||
end
|
||||
end
|
||||
|
||||
class PersonAPI < ActionWebService::API::Base
|
||||
...
|
||||
end
|
||||
|
||||
|
||||
For this example, protocol requests for +Add+ and +Remove+ methods sent to
|
||||
<tt>/person/api</tt> will be routed to the controller methods +add+ and +remove+.
|
||||
|
||||
|
||||
=== Delegated dispatching
|
||||
|
||||
This mode can be turned on by setting the +web_service_dispatching_mode+ option
|
||||
in a controller to <tt>:delegated</tt>.
|
||||
|
||||
In this mode, the controller contains one or more web service objects (objects
|
||||
that implement an ActionWebService::API::Base definition). These web service
|
||||
objects are each mapped onto one controller action only.
|
||||
|
||||
==== Delegated dispatching example
|
||||
|
||||
class ApiController < ApplicationController
|
||||
web_service_dispatching_mode :delegated
|
||||
|
||||
web_service :person, PersonService.new
|
||||
end
|
||||
|
||||
class PersonService < ActionWebService::Base
|
||||
web_service_api PersonAPI
|
||||
|
||||
def add
|
||||
end
|
||||
|
||||
def remove
|
||||
end
|
||||
end
|
||||
|
||||
class PersonAPI < ActionWebService::API::Base
|
||||
...
|
||||
end
|
||||
|
||||
|
||||
For this example, all protocol requests for +PersonService+ are
|
||||
sent to the <tt>/api/person</tt> action.
|
||||
|
||||
The <tt>/api/person</tt> action is generated when the +web_service+
|
||||
method is called. <em>This action must not be overridden.</em>
|
||||
|
||||
Other controller actions (actions that aren't the target of a +web_service+ call)
|
||||
are ignored for ActionWebService purposes, and can do normal action tasks.
|
||||
|
||||
|
||||
=== Layered dispatching
|
||||
|
||||
This mode can be turned on by setting the +web_service_dispatching_mode+ option
|
||||
in a controller to <tt>:layered</tt>.
|
||||
|
||||
This mode is similar to _delegated_ mode, in that multiple web service objects
|
||||
can be attached to one controller, however, all protocol requests are sent to a
|
||||
single endpoint.
|
||||
|
||||
Use this mode when you want to share code between XML-RPC and SOAP clients,
|
||||
for APIs where the XML-RPC method names have prefixes. An example of such
|
||||
a method name would be <tt>blogger.newPost</tt>.
|
||||
|
||||
|
||||
==== Layered dispatching example
|
||||
|
||||
|
||||
class ApiController < ApplicationController
|
||||
web_service_dispatching_mode :layered
|
||||
|
||||
web_service :mt, MovableTypeService.new
|
||||
web_service :blogger, BloggerService.new
|
||||
web_service :metaWeblog, MetaWeblogService.new
|
||||
end
|
||||
|
||||
class MovableTypeService < ActionWebService::Base
|
||||
...
|
||||
end
|
||||
|
||||
class BloggerService < ActionWebService::Base
|
||||
...
|
||||
end
|
||||
|
||||
class MetaWeblogService < ActionWebService::API::Base
|
||||
...
|
||||
end
|
||||
|
||||
|
||||
For this example, an XML-RPC call for a method with a name like
|
||||
<tt>mt.getCategories</tt> will be sent to the <tt>getCategories</tt>
|
||||
method on the <tt>:mt</tt> service.
|
||||
|
||||
|
||||
== Customizing WSDL generation
|
||||
|
||||
You can customize the names used for the SOAP bindings in the generated
|
||||
WSDL by using the wsdl_service_name option in a controller:
|
||||
|
||||
class WsController < ApplicationController
|
||||
wsdl_service_name 'MyApp'
|
||||
end
|
||||
|
||||
You can also customize the namespace used in the generated WSDL for
|
||||
custom types and message definition types:
|
||||
|
||||
class WsController < ApplicationController
|
||||
wsdl_namespace 'http://my.company.com/app/wsapi'
|
||||
end
|
||||
|
||||
The default namespace used is 'urn:ActionWebService', if you don't supply
|
||||
one.
|
||||
|
||||
|
||||
== ActionWebService and UTF-8
|
||||
|
||||
If you're going to be sending back strings containing non-ASCII UTF-8
|
||||
characters using the <tt>:string</tt> data type, you need to make sure that
|
||||
Ruby is using UTF-8 as the default encoding for its strings.
|
||||
|
||||
The default in Ruby is to use US-ASCII encoding for strings, which causes a string
|
||||
validation check in the Ruby SOAP library to fail and your string to be sent
|
||||
back as a Base-64 value, which may confuse clients that expected strings
|
||||
because of the WSDL.
|
||||
|
||||
Two ways of setting the default string encoding are:
|
||||
|
||||
* Start Ruby using the <tt>-Ku</tt> command-line option to the Ruby executable
|
||||
* Set the <tt>$KCODE</tt> flag in <tt>config/environment.rb</tt> to the
|
||||
string <tt>'UTF8'</tt>
|
||||
|
||||
|
||||
== Testing your APIs
|
||||
|
||||
|
||||
=== Functional testing
|
||||
|
||||
You can perform testing of your APIs by creating a functional test for the
|
||||
controller dispatching the API, and calling #invoke in the test case to
|
||||
perform the invocation.
|
||||
|
||||
Example:
|
||||
|
||||
class PersonApiControllerTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = PersonController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_add
|
||||
result = invoke :remove, 1
|
||||
assert_equal true, result
|
||||
end
|
||||
end
|
||||
|
||||
This example invokes the API method <tt>test</tt>, defined on
|
||||
the PersonController, and returns the result.
|
||||
|
||||
If you're not using SOAP (or you're having serialisation difficulties),
|
||||
you can test XMLRPC like this:
|
||||
|
||||
class PersonApiControllerTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = PersonController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
|
||||
@protocol = :xmlrpc # can also be :soap, the default
|
||||
end
|
||||
|
||||
def test_add
|
||||
result = invoke :remove, 1 # no change here
|
||||
assert_equal true, result
|
||||
end
|
||||
end
|
||||
|
||||
=== Scaffolding
|
||||
|
||||
You can also test your APIs with a web browser by attaching scaffolding
|
||||
to the controller.
|
||||
|
||||
Example:
|
||||
|
||||
class PersonController
|
||||
web_service_scaffold :invocation
|
||||
end
|
||||
|
||||
This creates an action named <tt>invocation</tt> on the PersonController.
|
||||
|
||||
Navigating to this action lets you select the method to invoke, supply the parameters,
|
||||
and view the result of the invocation.
|
||||
|
||||
|
||||
== Using the client support
|
||||
|
||||
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 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 Web Service client
|
||||
support can only be guaranteed to work with a subset.
|
||||
|
||||
|
||||
==== Factory created client example
|
||||
|
||||
class BlogManagerController < ApplicationController
|
||||
web_client_api :blogger, :xmlrpc, 'http://url/to/blog/api/RPC2', :handler_name => 'blogger'
|
||||
end
|
||||
|
||||
class SearchingController < ApplicationController
|
||||
web_client_api :google, :soap, 'http://url/to/blog/api/beta', :service_name => 'GoogleSearch'
|
||||
end
|
||||
|
||||
See ActionWebService::API::ActionController::ClassMethods for more details.
|
||||
|
||||
==== Manually created client example
|
||||
|
||||
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 BloggerAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
api_method :getRecentPosts, :returns => [[Blog::Post]]
|
||||
end
|
||||
|
||||
blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../xmlrpc", :handler_name => "blogger")
|
||||
posts = blog.getRecentPosts
|
||||
|
||||
|
||||
See ActionWebService::Client::Soap and ActionWebService::Client::XmlRpc for more details.
|
||||
|
||||
== Dependencies
|
||||
|
||||
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
|
||||
library. At least version 1.8.2 final (2004-12-25) of Ruby is recommended; this
|
||||
is the version tested against.
|
||||
|
||||
|
||||
== Download
|
||||
|
||||
The latest Action Web Service version can be downloaded from
|
||||
http://rubyforge.org/projects/actionservice
|
||||
|
||||
|
||||
== Installation
|
||||
|
||||
You can install Action Web Service with the following command.
|
||||
|
||||
% [sudo] ruby setup.rb
|
||||
|
||||
|
||||
== License
|
||||
|
||||
Action Web Service is released under the MIT license.
|
||||
|
||||
|
||||
== Support
|
||||
|
||||
The Ruby on Rails mailing list
|
||||
|
||||
Or, to contact the author, send mail to bitserf@gmail.com
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
require 'fileutils'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'action_web_service', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'actionwebservice'
|
||||
PKG_VERSION = ActionWebService::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
|
||||
RUBY_FORGE_PROJECT = "aws"
|
||||
RUBY_FORGE_USER = "webster132"
|
||||
|
||||
desc "Default Task"
|
||||
task :default => [ :test ]
|
||||
|
||||
|
||||
# Run the unit tests
|
||||
Rake::TestTask.new { |t|
|
||||
t.libs << "test"
|
||||
t.test_files = Dir['test/*_test.rb']
|
||||
t.verbose = true
|
||||
}
|
||||
|
||||
SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
|
||||
|
||||
desc 'Build the MySQL test database'
|
||||
task :build_database do
|
||||
%x( mysqladmin -uroot create actionwebservice_unittest )
|
||||
%x( mysql -uroot actionwebservice_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
|
||||
end
|
||||
|
||||
|
||||
# Generate the RDoc documentation
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Action Web Service -- Web services for Action Pack"
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.options << '--charset' << 'utf-8'
|
||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('CHANGELOG')
|
||||
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/container/*.rb')
|
||||
rdoc.rdoc_files.include('lib/action_web_service/dispatcher/*.rb')
|
||||
rdoc.rdoc_files.include('lib/action_web_service/protocol/*.rb')
|
||||
rdoc.rdoc_files.include('lib/action_web_service/support/*.rb')
|
||||
}
|
||||
|
||||
|
||||
# Create compressed packages
|
||||
spec = Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.name = PKG_NAME
|
||||
s.summary = "Web service support for Action Pack."
|
||||
s.description = %q{Adds WSDL/SOAP and XML-RPC web service support to Action Pack}
|
||||
s.version = PKG_VERSION
|
||||
|
||||
s.author = "Leon Breedt, Kent Sibilev"
|
||||
s.email = "bitserf@gmail.com, ksibilev@yahoo.com"
|
||||
s.rubyforge_project = "aws"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 2.3.12' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 2.3.12' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'actionwebservice'
|
||||
|
||||
s.files = [ "Rakefile", "setup.rb", "README", "TODO", "CHANGELOG", "MIT-LICENSE" ]
|
||||
s.files = s.files + Dir.glob( "examples/**/*" ).delete_if { |item| item.match( /\.(svn|git)/ ) }
|
||||
s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.match( /\.(svn|git)/ ) }
|
||||
s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.match( /\.(svn|git)/ ) }
|
||||
s.files = s.files + Dir.glob( "generators/**/*" ).delete_if { |item| item.match( /\.(svn|git)/ ) }
|
||||
end
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
end
|
||||
|
||||
|
||||
# Publish beta gem
|
||||
desc "Publish the API documentation"
|
||||
task :pgem => [:package] do
|
||||
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
||||
`ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
|
||||
end
|
||||
|
||||
# Publish documentation
|
||||
desc "Publish the API documentation"
|
||||
task :pdoc => [:rdoc] do
|
||||
Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/aws", "doc").upload
|
||||
end
|
||||
|
||||
|
||||
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_web_service\.rb$ lib\/action_web_service\/.*\.rb$]
|
||||
excludes ||= %w[lib\/action_web_service\/vendor]
|
||||
Find.find(prefix) do |file_name|
|
||||
next if file_name =~ /\.svn/
|
||||
file_name.gsub!(/^\.\//, '')
|
||||
continue = false
|
||||
includes.each do |inc|
|
||||
if file_name.match(/#{inc}/)
|
||||
continue = true
|
||||
break
|
||||
end
|
||||
end
|
||||
next unless continue
|
||||
excludes.each do |exc|
|
||||
if file_name.match(/#{exc}/)
|
||||
continue = false
|
||||
break
|
||||
end
|
||||
end
|
||||
next unless continue
|
||||
if open_file
|
||||
File.open(file_name) do |f|
|
||||
yield file_name, f
|
||||
end
|
||||
else
|
||||
yield file_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "Count lines of the AWS source code"
|
||||
task :lines do
|
||||
total_lines = total_loc = 0
|
||||
puts "Per File:"
|
||||
each_source_file do |file_name, f|
|
||||
file_lines = file_loc = 0
|
||||
while line = f.gets
|
||||
file_lines += 1
|
||||
next if line =~ /^\s*$/
|
||||
next if line =~ /^\s*#/
|
||||
file_loc += 1
|
||||
end
|
||||
puts " #{file_name}: Lines #{file_lines}, LOC #{file_loc}"
|
||||
total_lines += file_lines
|
||||
total_loc += file_loc
|
||||
end
|
||||
puts "Total:"
|
||||
puts " Lines #{total_lines}, LOC #{total_loc}"
|
||||
end
|
||||
|
||||
desc "Publish the release files to RubyForge."
|
||||
task :release => [ :package ] do
|
||||
require 'rubyforge'
|
||||
|
||||
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
||||
|
||||
rubyforge = RubyForge.new
|
||||
rubyforge.login
|
||||
rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
= Post-1.0
|
||||
- Document/Literal SOAP support
|
||||
- URL-based dispatching, URL identifies method
|
||||
|
||||
- Add :rest dispatching mode, a.l.a. Backpack API. Clean up dispatching
|
||||
in general. Support vanilla XML-format as a "Rails" protocol?
|
||||
XML::Simple deserialization into params?
|
||||
|
||||
web_service_dispatching_mode :rest
|
||||
|
||||
def method1(params)
|
||||
end
|
||||
|
||||
def method2(params)
|
||||
end
|
||||
|
||||
|
||||
/ws/method1
|
||||
<xml>
|
||||
/ws/method2
|
||||
<yaml>
|
||||
|
||||
- Allow locking down a controller to only accept messages for a particular
|
||||
protocol. This will allow us to generate fully conformant error messages
|
||||
in cases where we currently fudge it if we don't know the protocol.
|
||||
|
||||
- Allow AWS user to participate in typecasting, so they can centralize
|
||||
workarounds for buggy input in one place
|
||||
|
||||
= Refactoring
|
||||
- Don't have clean way to go from SOAP Class object to the xsd:NAME type
|
||||
string -- NaHi possibly looking at remedying this situation
|
|
@ -1,143 +0,0 @@
|
|||
= Google Service example
|
||||
|
||||
This example shows how one would implement an API like Google
|
||||
Search that uses lots of structured types.
|
||||
|
||||
There are examples for "Direct" and "Delegated" dispatching
|
||||
modes.
|
||||
|
||||
There is also an example for API definition file autoloading.
|
||||
|
||||
|
||||
= Running the examples
|
||||
|
||||
1. Add the files to an Action Web Service enabled Rails project.
|
||||
|
||||
"Direct" example:
|
||||
|
||||
* Copy direct/search_controller.rb to "app/controllers"
|
||||
in a Rails project.
|
||||
* Copy direct/google_search_api.rb to "app/apis"
|
||||
in a Rails project
|
||||
|
||||
"Delegated" example:
|
||||
|
||||
* Copy delegated/search_controller.rb to "app/controllers"
|
||||
in a Rails project.
|
||||
* Copy delegated/google_search_service.rb to "lib"
|
||||
in a Rails project.
|
||||
|
||||
"Autoloading" example:
|
||||
|
||||
* Copy autoloading/google_search_api.rb to "app/apis" (create the directory
|
||||
if it doesn't exist) in a Rails project.
|
||||
|
||||
* Copy autoloading/google_search_controller.rb "app/controllers"
|
||||
in a Rails project.
|
||||
|
||||
|
||||
2. Go to the WSDL url in a browser, and check that it looks correct.
|
||||
|
||||
"Direct" and "Delegated" examples:
|
||||
http://url_to_project/search/wsdl
|
||||
|
||||
"Autoloading" example:
|
||||
http://url_to_project/google_search/wsdl
|
||||
|
||||
You can compare it to Google's hand-coded WSDL at http://api.google.com/GoogleSearch.wsdl
|
||||
and see how close (or not) the generated version is.
|
||||
|
||||
Note that I used GoogleSearch as the canonical "best practice"
|
||||
interoperable example when implementing WSDL/SOAP support, which might
|
||||
explain extreme similarities :)
|
||||
|
||||
|
||||
3. Test that it works with .NET (Mono in this example):
|
||||
|
||||
$ wget WSDL_URL
|
||||
$ mv wsdl GoogleSearch.wsdl
|
||||
$ wsdl -out:GoogleSearch.cs GoogleSearch.wsdl
|
||||
|
||||
Add these lines to the GoogleSearchService class body (be mindful of the
|
||||
wrapping):
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GoogleSearchResult result;
|
||||
GoogleSearchService service;
|
||||
|
||||
service = new GoogleSearchService();
|
||||
result = service.doGoogleSearch("myApiKey", "my query", 10, 30, true, "restrict", false, "lr", "ie", "oe");
|
||||
System.Console.WriteLine("documentFiltering: {0}", result.documentFiltering);
|
||||
System.Console.WriteLine("searchComments: {0}", result.searchComments);
|
||||
System.Console.WriteLine("estimatedTotalResultsCount: {0}", result.estimatedTotalResultsCount);
|
||||
System.Console.WriteLine("estimateIsExact: {0}", result.estimateIsExact);
|
||||
System.Console.WriteLine("resultElements:");
|
||||
foreach (ResultElement element in result.resultElements) {
|
||||
System.Console.WriteLine("\tsummary: {0}", element.summary);
|
||||
System.Console.WriteLine("\tURL: {0}", element.URL);
|
||||
System.Console.WriteLine("\tsnippet: {0}", element.snippet);
|
||||
System.Console.WriteLine("\ttitle: {0}", element.title);
|
||||
System.Console.WriteLine("\tcachedSize: {0}", element.cachedSize);
|
||||
System.Console.WriteLine("\trelatedInformationPresent: {0}", element.relatedInformationPresent);
|
||||
System.Console.WriteLine("\thostName: {0}", element.hostName);
|
||||
System.Console.WriteLine("\tdirectoryCategory: {0}", element.directoryCategory.fullViewableName);
|
||||
System.Console.WriteLine("\tdirectoryTitle: {0}", element.directoryTitle);
|
||||
}
|
||||
System.Console.WriteLine("searchQuery: {0}", result.searchQuery);
|
||||
System.Console.WriteLine("startIndex: {0}", result.startIndex);
|
||||
System.Console.WriteLine("endIndex: {0}", result.endIndex);
|
||||
System.Console.WriteLine("searchTips: {0}", result.searchTips);
|
||||
System.Console.WriteLine("directoryCategories:");
|
||||
foreach (DirectoryCategory cat in result.directoryCategories) {
|
||||
System.Console.WriteLine("\t{0} ({1})", cat.fullViewableName, cat.specialEncoding);
|
||||
}
|
||||
System.Console.WriteLine("searchTime: {0}", result.searchTime);
|
||||
}
|
||||
|
||||
Now compile and run:
|
||||
|
||||
$ mcs -reference:System.Web.Services GoogleSearch.cs
|
||||
$ mono GoogleSearch.exe
|
||||
|
||||
|
||||
If you had the application running (on the same host you got
|
||||
the WSDL from), you should see something like this:
|
||||
|
||||
|
||||
documentFiltering: True
|
||||
searchComments:
|
||||
estimatedTotalResultsCount: 322000
|
||||
estimateIsExact: False
|
||||
resultElements:
|
||||
summary: ONlamp.com: Rolling with Ruby on Rails
|
||||
URL: http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html
|
||||
snippet: Curt Hibbs shows off Ruby on Rails by building a simple ...
|
||||
title: Teh Railz0r
|
||||
cachedSize: Almost no lines of code!
|
||||
relatedInformationPresent: True
|
||||
hostName: rubyonrails.com
|
||||
directoryCategory: Web Development
|
||||
directoryTitle:
|
||||
searchQuery: http://www.google.com/search?q=ruby+on+rails
|
||||
startIndex: 10
|
||||
endIndex: 40
|
||||
searchTips: "on" is a very common word and was not included in your search [details]
|
||||
directoryCategories:
|
||||
Web Development (UTF-8)
|
||||
Programming (US-ASCII)
|
||||
searchTime: 1E-06
|
||||
|
||||
|
||||
Also, if an API method throws an exception, it will be sent back to the
|
||||
caller in the protocol's exception format, so they should get an exception
|
||||
thrown on their side with a meaningful error message.
|
||||
|
||||
If you don't like this behaviour, you can do:
|
||||
|
||||
class MyController < ActionController::Base
|
||||
web_service_exception_reporting false
|
||||
end
|
||||
|
||||
4. Crack open a beer. Publishing APIs for working with the same model as
|
||||
your Rails web app should be easy from now on :)
|
|
@ -1,50 +0,0 @@
|
|||
class DirectoryCategory < ActionWebService::Struct
|
||||
member :fullViewableName, :string
|
||||
member :specialEncoding, :string
|
||||
end
|
||||
|
||||
class ResultElement < ActionWebService::Struct
|
||||
member :summary, :string
|
||||
member :URL, :string
|
||||
member :snippet, :string
|
||||
member :title, :string
|
||||
member :cachedSize, :string
|
||||
member :relatedInformationPresent, :bool
|
||||
member :hostName, :string
|
||||
member :directoryCategory, DirectoryCategory
|
||||
member :directoryTitle, :string
|
||||
end
|
||||
|
||||
class GoogleSearchResult < ActionWebService::Struct
|
||||
member :documentFiltering, :bool
|
||||
member :searchComments, :string
|
||||
member :estimatedTotalResultsCount, :int
|
||||
member :estimateIsExact, :bool
|
||||
member :resultElements, [ResultElement]
|
||||
member :searchQuery, :string
|
||||
member :startIndex, :int
|
||||
member :endIndex, :int
|
||||
member :searchTips, :string
|
||||
member :directoryCategories, [DirectoryCategory]
|
||||
member :searchTime, :float
|
||||
end
|
||||
|
||||
class GoogleSearchAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
|
||||
api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
|
||||
api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
|
||||
|
||||
api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
|
||||
{:key=>:string},
|
||||
{:q=>:string},
|
||||
{:start=>:int},
|
||||
{:maxResults=>:int},
|
||||
{:filter=>:bool},
|
||||
{:restrict=>:string},
|
||||
{:safeSearch=>:bool},
|
||||
{:lr=>:string},
|
||||
{:ie=>:string},
|
||||
{:oe=>:string}
|
||||
]
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
class GoogleSearchController < ApplicationController
|
||||
wsdl_service_name 'GoogleSearch'
|
||||
|
||||
def doGetCachedPage
|
||||
"<html><body>i am a cached page. my key was %s, url was %s</body></html>" % [@params['key'], @params['url']]
|
||||
end
|
||||
|
||||
def doSpellingSuggestion
|
||||
"%s: Did you mean '%s'?" % [@params['key'], @params['phrase']]
|
||||
end
|
||||
|
||||
def doGoogleSearch
|
||||
resultElement = ResultElement.new
|
||||
resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
|
||||
resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
|
||||
resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
|
||||
"almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
|
||||
resultElement.title = "Teh Railz0r"
|
||||
resultElement.cachedSize = "Almost no lines of code!"
|
||||
resultElement.relatedInformationPresent = true
|
||||
resultElement.hostName = "rubyonrails.com"
|
||||
resultElement.directoryCategory = category("Web Development", "UTF-8")
|
||||
|
||||
result = GoogleSearchResult.new
|
||||
result.documentFiltering = @params['filter']
|
||||
result.searchComments = ""
|
||||
result.estimatedTotalResultsCount = 322000
|
||||
result.estimateIsExact = false
|
||||
result.resultElements = [resultElement]
|
||||
result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
|
||||
result.startIndex = @params['start']
|
||||
result.endIndex = @params['start'] + @params['maxResults']
|
||||
result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
|
||||
result.searchTime = 0.000001
|
||||
|
||||
# For Mono, we have to clone objects if they're referenced by more than one place, otherwise
|
||||
# the Ruby SOAP collapses them into one instance and uses references all over the
|
||||
# place, confusing Mono.
|
||||
#
|
||||
# This has recently been fixed:
|
||||
# http://bugzilla.ximian.com/show_bug.cgi?id=72265
|
||||
result.directoryCategories = [
|
||||
category("Web Development", "UTF-8"),
|
||||
category("Programming", "US-ASCII"),
|
||||
]
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
def category(name, encoding)
|
||||
cat = DirectoryCategory.new
|
||||
cat.fullViewableName = name.dup
|
||||
cat.specialEncoding = encoding.dup
|
||||
cat
|
||||
end
|
||||
end
|
|
@ -1,108 +0,0 @@
|
|||
class DirectoryCategory < ActionWebService::Struct
|
||||
member :fullViewableName, :string
|
||||
member :specialEncoding, :string
|
||||
end
|
||||
|
||||
class ResultElement < ActionWebService::Struct
|
||||
member :summary, :string
|
||||
member :URL, :string
|
||||
member :snippet, :string
|
||||
member :title, :string
|
||||
member :cachedSize, :string
|
||||
member :relatedInformationPresent, :bool
|
||||
member :hostName, :string
|
||||
member :directoryCategory, DirectoryCategory
|
||||
member :directoryTitle, :string
|
||||
end
|
||||
|
||||
class GoogleSearchResult < ActionWebService::Struct
|
||||
member :documentFiltering, :bool
|
||||
member :searchComments, :string
|
||||
member :estimatedTotalResultsCount, :int
|
||||
member :estimateIsExact, :bool
|
||||
member :resultElements, [ResultElement]
|
||||
member :searchQuery, :string
|
||||
member :startIndex, :int
|
||||
member :endIndex, :int
|
||||
member :searchTips, :string
|
||||
member :directoryCategories, [DirectoryCategory]
|
||||
member :searchTime, :float
|
||||
end
|
||||
|
||||
class GoogleSearchAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
|
||||
api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
|
||||
api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
|
||||
|
||||
api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
|
||||
{:key=>:string},
|
||||
{:q=>:string},
|
||||
{:start=>:int},
|
||||
{:maxResults=>:int},
|
||||
{:filter=>:bool},
|
||||
{:restrict=>:string},
|
||||
{:safeSearch=>:bool},
|
||||
{:lr=>:string},
|
||||
{:ie=>:string},
|
||||
{:oe=>:string}
|
||||
]
|
||||
end
|
||||
|
||||
class GoogleSearchService < ActionWebService::Base
|
||||
web_service_api GoogleSearchAPI
|
||||
|
||||
def doGetCachedPage(key, url)
|
||||
"<html><body>i am a cached page</body></html>"
|
||||
end
|
||||
|
||||
def doSpellingSuggestion(key, phrase)
|
||||
"Did you mean 'teh'?"
|
||||
end
|
||||
|
||||
def doGoogleSearch(key, q, start, maxResults, filter, restrict, safeSearch, lr, ie, oe)
|
||||
resultElement = ResultElement.new
|
||||
resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
|
||||
resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
|
||||
resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
|
||||
"almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
|
||||
resultElement.title = "Teh Railz0r"
|
||||
resultElement.cachedSize = "Almost no lines of code!"
|
||||
resultElement.relatedInformationPresent = true
|
||||
resultElement.hostName = "rubyonrails.com"
|
||||
resultElement.directoryCategory = category("Web Development", "UTF-8")
|
||||
|
||||
result = GoogleSearchResult.new
|
||||
result.documentFiltering = filter
|
||||
result.searchComments = ""
|
||||
result.estimatedTotalResultsCount = 322000
|
||||
result.estimateIsExact = false
|
||||
result.resultElements = [resultElement]
|
||||
result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
|
||||
result.startIndex = start
|
||||
result.endIndex = start + maxResults
|
||||
result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
|
||||
result.searchTime = 0.000001
|
||||
|
||||
# For Mono, we have to clone objects if they're referenced by more than one place, otherwise
|
||||
# the Ruby SOAP collapses them into one instance and uses references all over the
|
||||
# place, confusing Mono.
|
||||
#
|
||||
# This has recently been fixed:
|
||||
# http://bugzilla.ximian.com/show_bug.cgi?id=72265
|
||||
result.directoryCategories = [
|
||||
category("Web Development", "UTF-8"),
|
||||
category("Programming", "US-ASCII"),
|
||||
]
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
def category(name, encoding)
|
||||
cat = DirectoryCategory.new
|
||||
cat.fullViewableName = name.dup
|
||||
cat.specialEncoding = encoding.dup
|
||||
cat
|
||||
end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
require 'google_search_service'
|
||||
|
||||
class SearchController < ApplicationController
|
||||
wsdl_service_name 'GoogleSearch'
|
||||
web_service_dispatching_mode :delegated
|
||||
web_service :beta3, GoogleSearchService.new
|
||||
end
|
|
@ -1,50 +0,0 @@
|
|||
class DirectoryCategory < ActionWebService::Struct
|
||||
member :fullViewableName, :string
|
||||
member :specialEncoding, :string
|
||||
end
|
||||
|
||||
class ResultElement < ActionWebService::Struct
|
||||
member :summary, :string
|
||||
member :URL, :string
|
||||
member :snippet, :string
|
||||
member :title, :string
|
||||
member :cachedSize, :string
|
||||
member :relatedInformationPresent, :bool
|
||||
member :hostName, :string
|
||||
member :directoryCategory, DirectoryCategory
|
||||
member :directoryTitle, :string
|
||||
end
|
||||
|
||||
class GoogleSearchResult < ActionWebService::Struct
|
||||
member :documentFiltering, :bool
|
||||
member :searchComments, :string
|
||||
member :estimatedTotalResultsCount, :int
|
||||
member :estimateIsExact, :bool
|
||||
member :resultElements, [ResultElement]
|
||||
member :searchQuery, :string
|
||||
member :startIndex, :int
|
||||
member :endIndex, :int
|
||||
member :searchTips, :string
|
||||
member :directoryCategories, [DirectoryCategory]
|
||||
member :searchTime, :float
|
||||
end
|
||||
|
||||
class GoogleSearchAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
|
||||
api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
|
||||
api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
|
||||
|
||||
api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
|
||||
{:key=>:string},
|
||||
{:q=>:string},
|
||||
{:start=>:int},
|
||||
{:maxResults=>:int},
|
||||
{:filter=>:bool},
|
||||
{:restrict=>:string},
|
||||
{:safeSearch=>:bool},
|
||||
{:lr=>:string},
|
||||
{:ie=>:string},
|
||||
{:oe=>:string}
|
||||
]
|
||||
end
|
|
@ -1,58 +0,0 @@
|
|||
class SearchController < ApplicationController
|
||||
web_service_api :google_search
|
||||
wsdl_service_name 'GoogleSearch'
|
||||
|
||||
def doGetCachedPage
|
||||
"<html><body>i am a cached page. my key was %s, url was %s</body></html>" % [@params['key'], @params['url']]
|
||||
end
|
||||
|
||||
def doSpellingSuggestion
|
||||
"%s: Did you mean '%s'?" % [@params['key'], @params['phrase']]
|
||||
end
|
||||
|
||||
def doGoogleSearch
|
||||
resultElement = ResultElement.new
|
||||
resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
|
||||
resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
|
||||
resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
|
||||
"almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
|
||||
resultElement.title = "Teh Railz0r"
|
||||
resultElement.cachedSize = "Almost no lines of code!"
|
||||
resultElement.relatedInformationPresent = true
|
||||
resultElement.hostName = "rubyonrails.com"
|
||||
resultElement.directoryCategory = category("Web Development", "UTF-8")
|
||||
|
||||
result = GoogleSearchResult.new
|
||||
result.documentFiltering = @params['filter']
|
||||
result.searchComments = ""
|
||||
result.estimatedTotalResultsCount = 322000
|
||||
result.estimateIsExact = false
|
||||
result.resultElements = [resultElement]
|
||||
result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
|
||||
result.startIndex = @params['start']
|
||||
result.endIndex = @params['start'] + @params['maxResults']
|
||||
result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
|
||||
result.searchTime = 0.000001
|
||||
|
||||
# For Mono, we have to clone objects if they're referenced by more than one place, otherwise
|
||||
# the Ruby SOAP collapses them into one instance and uses references all over the
|
||||
# place, confusing Mono.
|
||||
#
|
||||
# This has recently been fixed:
|
||||
# http://bugzilla.ximian.com/show_bug.cgi?id=72265
|
||||
result.directoryCategories = [
|
||||
category("Web Development", "UTF-8"),
|
||||
category("Programming", "US-ASCII"),
|
||||
]
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
def category(name, encoding)
|
||||
cat = DirectoryCategory.new
|
||||
cat.fullViewableName = name.dup
|
||||
cat.specialEncoding = encoding.dup
|
||||
cat
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
= metaWeblog example
|
||||
|
||||
This example shows how one might begin to go about adding metaWeblog
|
||||
(http://www.xmlrpc.com/metaWeblogApi) API support to a Rails-based
|
||||
blogging application.
|
||||
|
||||
The example APIs are more verbose than you may want to make them, for documentation
|
||||
reasons.
|
||||
|
||||
= Running
|
||||
|
||||
1. Copy the "apis" directory and its files into "app" in a Rails project.
|
||||
|
||||
2. Copy the "controllers" directory and its files into "app" in a Rails project
|
||||
|
||||
3. Fire up a desktop blogging application (such as w.bloggar, MarsEdit, or BloGTK),
|
||||
point it at http://localhost:3000/xmlrpc/api, and try creating or editing blog posts.
|
|
@ -1,60 +0,0 @@
|
|||
#
|
||||
# see the blogger API spec at http://www.blogger.com/developers/api/1_docs/
|
||||
# note that the method signatures are subtly different to metaWeblog, they
|
||||
# are not identical. take care to ensure you handle the different semantics
|
||||
# properly if you want to support blogger API too, to get maximum compatibility.
|
||||
#
|
||||
|
||||
module Blog
|
||||
class Blog < ActionWebService::Struct
|
||||
member :url, :string
|
||||
member :blogid, :string
|
||||
member :blogName, :string
|
||||
end
|
||||
|
||||
class User < ActionWebService::Struct
|
||||
member :nickname, :string
|
||||
member :userid, :string
|
||||
member :url, :string
|
||||
member :email, :string
|
||||
member :lastname, :string
|
||||
member :firstname, :string
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# blogger
|
||||
#
|
||||
class BloggerAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
|
||||
api_method :newPost, :returns => [:string], :expects => [
|
||||
{:appkey=>:string},
|
||||
{:blogid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
{:content=>:string},
|
||||
{:publish=>:bool}
|
||||
]
|
||||
|
||||
api_method :editPost, :returns => [:bool], :expects => [
|
||||
{:appkey=>:string},
|
||||
{:postid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
{:content=>:string},
|
||||
{:publish=>:bool}
|
||||
]
|
||||
|
||||
api_method :getUsersBlogs, :returns => [[Blog::Blog]], :expects => [
|
||||
{:appkey=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string}
|
||||
]
|
||||
|
||||
api_method :getUserInfo, :returns => [Blog::User], :expects => [
|
||||
{:appkey=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string}
|
||||
]
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
require 'blogger_api'
|
||||
|
||||
class BloggerService < ActionWebService::Base
|
||||
web_service_api BloggerAPI
|
||||
|
||||
def initialize
|
||||
@postid = 0
|
||||
end
|
||||
|
||||
def newPost(key, id, user, pw, content, publish)
|
||||
$stderr.puts "id=#{id} user=#{user} pw=#{pw}, content=#{content.inspect} [#{publish}]"
|
||||
(@postid += 1).to_s
|
||||
end
|
||||
|
||||
def editPost(key, post_id, user, pw, content, publish)
|
||||
$stderr.puts "id=#{post_id} user=#{user} pw=#{pw} content=#{content.inspect} [#{publish}]"
|
||||
true
|
||||
end
|
||||
|
||||
def getUsersBlogs(key, user, pw)
|
||||
$stderr.puts "getting blogs for #{user}"
|
||||
blog = Blog::Blog.new(
|
||||
:url =>'http://blog',
|
||||
:blogid => 'myblog',
|
||||
:blogName => 'My Blog'
|
||||
)
|
||||
[blog]
|
||||
end
|
||||
|
||||
def getUserInfo(key, user, pw)
|
||||
$stderr.puts "getting user info for #{user}"
|
||||
Blog::User.new(:nickname => 'user', :email => 'user@test.com')
|
||||
end
|
||||
end
|
|
@ -1,67 +0,0 @@
|
|||
#
|
||||
# here lie structures, cousins of those on http://www.xmlrpc.com/metaWeblog
|
||||
# but they don't necessarily the real world reflect
|
||||
# so if you do, find that your client complains:
|
||||
# please tell, of problems you suffered through
|
||||
#
|
||||
|
||||
module Blog
|
||||
class Post < ActionWebService::Struct
|
||||
member :title, :string
|
||||
member :link, :string
|
||||
member :description, :string
|
||||
member :author, :string
|
||||
member :category, :string
|
||||
member :comments, :string
|
||||
member :guid, :string
|
||||
member :pubDate, :string
|
||||
end
|
||||
|
||||
class Category < ActionWebService::Struct
|
||||
member :description, :string
|
||||
member :htmlUrl, :string
|
||||
member :rssUrl, :string
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# metaWeblog
|
||||
#
|
||||
class MetaWeblogAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
|
||||
api_method :newPost, :returns => [:string], :expects => [
|
||||
{:blogid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
{:struct=>Blog::Post},
|
||||
{:publish=>:bool}
|
||||
]
|
||||
|
||||
api_method :editPost, :returns => [:bool], :expects => [
|
||||
{:postid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
{:struct=>Blog::Post},
|
||||
{:publish=>:bool},
|
||||
]
|
||||
|
||||
api_method :getPost, :returns => [Blog::Post], :expects => [
|
||||
{:postid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
]
|
||||
|
||||
api_method :getCategories, :returns => [[Blog::Category]], :expects => [
|
||||
{:blogid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
]
|
||||
|
||||
api_method :getRecentPosts, :returns => [[Blog::Post]], :expects => [
|
||||
{:blogid=>:string},
|
||||
{:username=>:string},
|
||||
{:password=>:string},
|
||||
{:numberOfPosts=>:int},
|
||||
]
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
require 'meta_weblog_api'
|
||||
|
||||
class MetaWeblogService < ActionWebService::Base
|
||||
web_service_api MetaWeblogAPI
|
||||
|
||||
def initialize
|
||||
@postid = 0
|
||||
end
|
||||
|
||||
def newPost(id, user, pw, struct, publish)
|
||||
$stderr.puts "id=#{id} user=#{user} pw=#{pw}, struct=#{struct.inspect} [#{publish}]"
|
||||
(@postid += 1).to_s
|
||||
end
|
||||
|
||||
def editPost(post_id, user, pw, struct, publish)
|
||||
$stderr.puts "id=#{post_id} user=#{user} pw=#{pw} struct=#{struct.inspect} [#{publish}]"
|
||||
true
|
||||
end
|
||||
|
||||
def getPost(post_id, user, pw)
|
||||
$stderr.puts "get post #{post_id}"
|
||||
Blog::Post.new(:title => 'hello world', :description => 'first post!')
|
||||
end
|
||||
|
||||
def getCategories(id, user, pw)
|
||||
$stderr.puts "categories for #{user}"
|
||||
cat = Blog::Category.new(
|
||||
:description => 'Tech',
|
||||
:htmlUrl => 'http://blog/tech',
|
||||
:rssUrl => 'http://blog/tech.rss')
|
||||
[cat]
|
||||
end
|
||||
|
||||
def getRecentPosts(id, user, pw, num)
|
||||
$stderr.puts "recent #{num} posts for #{user} on blog #{id}"
|
||||
post1 = Blog::Post.new(
|
||||
:title => 'first post!',
|
||||
:link => 'http://blog.xeraph.org/testOne.html',
|
||||
:description => 'this is the first post'
|
||||
)
|
||||
post2 = Blog::Post.new(
|
||||
:title => 'second post!',
|
||||
:link => 'http://blog.xeraph.org/testTwo.html',
|
||||
:description => 'this is the second post'
|
||||
)
|
||||
[post1, post2]
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
#
|
||||
# example controller implementing both blogger and metaWeblog APIs
|
||||
# in a way that should be compatible with clients supporting both/either.
|
||||
#
|
||||
# test by pointing your client at http://URL/xmlrpc/api
|
||||
#
|
||||
|
||||
require 'meta_weblog_service'
|
||||
require 'blogger_service'
|
||||
|
||||
class XmlrpcController < ApplicationController
|
||||
web_service_dispatching_mode :layered
|
||||
|
||||
web_service :metaWeblog, MetaWeblogService.new
|
||||
web_service :blogger, BloggerService.new
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
Description:
|
||||
The web service generator creates the controller and API definition for
|
||||
a web service.
|
||||
|
||||
The generator takes a web service name and a list of API methods as arguments.
|
||||
The web service name may be given in CamelCase or under_score and should
|
||||
contain no extra suffixes. To create a web service within a
|
||||
module, specify the web service name as 'module/webservice'.
|
||||
|
||||
The generator creates a controller class in app/controllers, an API definition
|
||||
in app/apis, and a functional test suite in test/functional.
|
||||
|
||||
Example:
|
||||
./script/generate web_service User add edit list remove
|
||||
|
||||
User web service.
|
||||
Controller: app/controllers/user_controller.rb
|
||||
API: app/apis/user_api.rb
|
||||
Test: test/functional/user_api_test.rb
|
||||
|
||||
Modules Example:
|
||||
./script/generate web_service 'api/registration' register renew
|
||||
|
||||
Registration web service.
|
||||
Controller: app/controllers/api/registration_controller.rb
|
||||
API: app/apis/api/registration_api.rb
|
||||
Test: test/functional/api/registration_api_test.rb
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
class <%= class_name %>Api < ActionWebService::API::Base
|
||||
<% for method_name in args -%>
|
||||
api_method :<%= method_name %>
|
||||
<% end -%>
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
class <%= class_name %>Controller < ApplicationController
|
||||
wsdl_service_name '<%= class_name %>'
|
||||
<% for method_name in args -%>
|
||||
|
||||
def <%= method_name %>
|
||||
end
|
||||
<% end -%>
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
|
||||
require '<%= file_path %>_controller'
|
||||
|
||||
class <%= class_name %>Controller; def rescue_action(e) raise e end; end
|
||||
|
||||
class <%= class_name %>ControllerApiTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = <%= class_name %>Controller.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
<% for method_name in args -%>
|
||||
|
||||
def test_<%= method_name %>
|
||||
result = invoke :<%= method_name %>
|
||||
assert_equal nil, result
|
||||
end
|
||||
<% end -%>
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
class WebServiceGenerator < Rails::Generator::NamedBase
|
||||
def manifest
|
||||
record do |m|
|
||||
# Check for class naming collisions.
|
||||
m.class_collisions class_path, "#{class_name}Api", "#{class_name}Controller", "#{class_name}ApiTest"
|
||||
|
||||
# API and test directories.
|
||||
m.directory File.join('app/services', class_path)
|
||||
m.directory File.join('app/controllers', class_path)
|
||||
m.directory File.join('test/functional', class_path)
|
||||
|
||||
# API definition, controller, and functional test.
|
||||
m.template 'api_definition.rb',
|
||||
File.join('app/services',
|
||||
class_path,
|
||||
"#{file_name}_api.rb")
|
||||
|
||||
m.template 'controller.rb',
|
||||
File.join('app/controllers',
|
||||
class_path,
|
||||
"#{file_name}_controller.rb")
|
||||
|
||||
m.template 'functional_test.rb',
|
||||
File.join('test/functional',
|
||||
class_path,
|
||||
"#{file_name}_api_test.rb")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,66 +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'
|
||||
gem 'activesupport', '>= 2.3.0'
|
||||
gem 'actionpack', '>= 2.3.0'
|
||||
gem 'activerecord', '>= 2.3.0'
|
||||
end
|
||||
|
||||
$:.unshift(File.dirname(__FILE__) + "/action_web_service/vendor/")
|
||||
|
||||
require 'action_web_service/support/class_inheritable_options'
|
||||
require 'action_web_service/support/signature_types'
|
||||
require 'action_web_service/base'
|
||||
require 'action_web_service/client'
|
||||
require 'action_web_service/invocation'
|
||||
require 'action_web_service/api'
|
||||
require 'action_web_service/casting'
|
||||
require 'action_web_service/struct'
|
||||
require 'action_web_service/container'
|
||||
require 'action_web_service/protocol'
|
||||
require 'action_web_service/dispatcher'
|
||||
require 'action_web_service/scaffolding'
|
||||
|
||||
ActionWebService::Base.class_eval do
|
||||
include ActionWebService::Container::Direct
|
||||
include ActionWebService::Invocation
|
||||
end
|
||||
|
||||
ActionController::Base.class_eval do
|
||||
include ActionWebService::Protocol::Discovery
|
||||
include ActionWebService::Protocol::Soap
|
||||
include ActionWebService::Protocol::XmlRpc
|
||||
include ActionWebService::Container::Direct
|
||||
include ActionWebService::Container::Delegated
|
||||
include ActionWebService::Container::ActionController
|
||||
include ActionWebService::Invocation
|
||||
include ActionWebService::Dispatcher
|
||||
include ActionWebService::Dispatcher::ActionController
|
||||
include ActionWebService::Scaffolding
|
||||
end
|
|
@ -1,297 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module API # :nodoc:
|
||||
# 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
|
||||
# <tt>container.web_service_api</tt>, where <tt>container</tt> is an
|
||||
# ActionController::Base or a ActionWebService::Base.
|
||||
#
|
||||
# See ActionWebService::Container::Direct::ClassMethods for an example
|
||||
# of use.
|
||||
class Base
|
||||
# Whether to transform the public API method names into camel-cased names
|
||||
class_inheritable_option :inflect_names, true
|
||||
|
||||
# By default only HTTP POST requests are processed
|
||||
class_inheritable_option :allowed_http_methods, [ :post ]
|
||||
|
||||
# Whether to allow ActiveRecord::Base models in <tt>:expects</tt>.
|
||||
# The default is +false+; you should be aware of the security implications
|
||||
# of allowing this, and ensure that you don't allow remote callers to
|
||||
# easily overwrite data they should not have access to.
|
||||
class_inheritable_option :allow_active_record_expects, false
|
||||
|
||||
# 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::SignatureTypes
|
||||
|
||||
# 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 representing one of the Action Web Service base types.
|
||||
# See ActionWebService::SignatureTypes 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:
|
||||
# [<tt>:expects</tt>] Signature for the method input parameters
|
||||
# [<tt>:returns</tt>] Signature for the method return value
|
||||
# [<tt>:expects_and_returns</tt>] Signature for both input parameters and return value
|
||||
def api_method(name, options={})
|
||||
unless options.is_a?(Hash)
|
||||
raise(ActionWebServiceError, "Expected a Hash for options")
|
||||
end
|
||||
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)
|
||||
returns = canonical_signature(returns)
|
||||
if expects
|
||||
expects.each do |type|
|
||||
type = type.element_type if type.is_a?(ArrayType)
|
||||
if type.type_class.ancestors.include?(ActiveRecord::Base) && !allow_active_record_expects
|
||||
raise(ActionWebServiceError, "ActiveRecord model classes not allowed in :expects")
|
||||
end
|
||||
end
|
||||
end
|
||||
name = name.to_sym
|
||||
public_name = public_api_method_name(name)
|
||||
method = Method.new(name, public_name, expects, returns)
|
||||
write_inheritable_hash("api_methods", name => method)
|
||||
write_inheritable_hash("api_public_method_names", public_name => name)
|
||||
end
|
||||
|
||||
# Whether the given method name is a service method on this API
|
||||
#
|
||||
# class ProjectsApi < ActionWebService::API::Base
|
||||
# api_method :getCount, :returns => [:int]
|
||||
# end
|
||||
#
|
||||
# ProjectsApi.has_api_method?('GetCount') #=> false
|
||||
# ProjectsApi.has_api_method?(:getCount) #=> true
|
||||
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
|
||||
#
|
||||
# class ProjectsApi < ActionWebService::API::Base
|
||||
# api_method :getCount, :returns => [:int]
|
||||
# end
|
||||
#
|
||||
# ProjectsApi.has_api_method?(:getCount) #=> false
|
||||
# ProjectsApi.has_api_method?('GetCount') #=> true
|
||||
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
|
||||
#
|
||||
# ProjectsApi.public_api_method_name('GetCount') #=> "GetCount"
|
||||
# ProjectsApi.public_api_method_name(:getCount) #=> "GetCount"
|
||||
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
|
||||
#
|
||||
# class ProjectsApi < ActionWebService::API::Base
|
||||
# api_method :getCount, :returns => [:int]
|
||||
# end
|
||||
#
|
||||
# ProjectsApi.api_method_name('GetCount') #=> :getCount
|
||||
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.
|
||||
#
|
||||
# class ProjectsApi < ActionWebService::API::Base
|
||||
# api_method :getCount, :returns => [:int]
|
||||
# api_method :getCompletedCount, :returns => [:int]
|
||||
# end
|
||||
#
|
||||
# ProjectsApi.api_methods #=>
|
||||
# {:getCount=>#<ActionWebService::API::Method:0x24379d8 ...>,
|
||||
# :getCompletedCount=>#<ActionWebService::API::Method:0x2437794 ...>}
|
||||
# ProjectsApi.api_methods[:getCount].public_name #=> "GetCount"
|
||||
def api_methods
|
||||
read_inheritable_attribute("api_methods") || {}
|
||||
end
|
||||
|
||||
# The Method instance for the given public API method name, if any
|
||||
#
|
||||
# class ProjectsApi < ActionWebService::API::Base
|
||||
# api_method :getCount, :returns => [:int]
|
||||
# api_method :getCompletedCount, :returns => [:int]
|
||||
# end
|
||||
#
|
||||
# ProjectsApi.public_api_method_instance('GetCount') #=> <#<ActionWebService::API::Method:0x24379d8 ...>
|
||||
# ProjectsApi.public_api_method_instance(:getCount) #=> nil
|
||||
def public_api_method_instance(public_method_name)
|
||||
api_method_instance(api_method_name(public_method_name))
|
||||
end
|
||||
|
||||
# The Method instance for the given API method name, if any
|
||||
#
|
||||
# class ProjectsApi < ActionWebService::API::Base
|
||||
# api_method :getCount, :returns => [:int]
|
||||
# api_method :getCompletedCount, :returns => [:int]
|
||||
# end
|
||||
#
|
||||
# ProjectsApi.api_method_instance(:getCount) #=> <ActionWebService::API::Method:0x24379d8 ...>
|
||||
# ProjectsApi.api_method_instance('GetCount') #=> <ActionWebService::API::Method:0x24379d8 ...>
|
||||
def api_method_instance(method_name)
|
||||
api_methods[method_name]
|
||||
end
|
||||
|
||||
# The Method instance for the default API method, if any
|
||||
def default_api_method_instance
|
||||
return nil unless name = default_api_method
|
||||
instance = read_inheritable_attribute("default_api_method_instance")
|
||||
if instance && instance.name == name
|
||||
return instance
|
||||
end
|
||||
instance = Method.new(name, public_api_method_name(name), nil, nil)
|
||||
write_inheritable_attribute("default_api_method_instance", instance)
|
||||
instance
|
||||
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
|
||||
|
||||
# Represents an API method and its associated metadata, and provides functionality
|
||||
# to assist in commonly performed API method tasks.
|
||||
class Method
|
||||
attr :name
|
||||
attr :public_name
|
||||
attr :expects
|
||||
attr :returns
|
||||
|
||||
def initialize(name, public_name, expects, returns)
|
||||
@name = name
|
||||
@public_name = public_name
|
||||
@expects = expects
|
||||
@returns = returns
|
||||
@caster = ActionWebService::Casting::BaseCaster.new(self)
|
||||
end
|
||||
|
||||
# The list of parameter names for this method
|
||||
def param_names
|
||||
return [] unless @expects
|
||||
@expects.map{ |type| type.name }
|
||||
end
|
||||
|
||||
# Casts a set of Ruby values into the expected Ruby values
|
||||
def cast_expects(params)
|
||||
@caster.cast_expects(params)
|
||||
end
|
||||
|
||||
# Cast a Ruby return value into the expected Ruby value
|
||||
def cast_returns(return_value)
|
||||
@caster.cast_returns(return_value)
|
||||
end
|
||||
|
||||
# Returns the index of the first expected parameter
|
||||
# with the given name
|
||||
def expects_index_of(param_name)
|
||||
return -1 if @expects.nil?
|
||||
(0..(@expects.length-1)).each do |i|
|
||||
return i if @expects[i].name.to_s == param_name.to_s
|
||||
end
|
||||
-1
|
||||
end
|
||||
|
||||
# Returns a hash keyed by parameter name for the given
|
||||
# parameter list
|
||||
def expects_to_hash(params)
|
||||
return {} if @expects.nil?
|
||||
h = {}
|
||||
@expects.zip(params){ |type, param| h[type.name] = param }
|
||||
h
|
||||
end
|
||||
|
||||
# Backwards compatibility with previous API
|
||||
def [](sig_type)
|
||||
case sig_type
|
||||
when :expects
|
||||
@expects.map{|x| compat_signature_entry(x)}
|
||||
when :returns
|
||||
@returns.map{|x| compat_signature_entry(x)}
|
||||
end
|
||||
end
|
||||
|
||||
# String representation of this method
|
||||
def to_s
|
||||
fqn = ""
|
||||
fqn << (@returns ? (@returns[0].human_name(false) + " ") : "void ")
|
||||
fqn << "#{@public_name}("
|
||||
fqn << @expects.map{ |p| p.human_name }.join(", ") if @expects
|
||||
fqn << ")"
|
||||
fqn
|
||||
end
|
||||
|
||||
private
|
||||
def compat_signature_entry(entry)
|
||||
if entry.array?
|
||||
[compat_signature_entry(entry.element_type)]
|
||||
else
|
||||
if entry.spec.is_a?(Hash)
|
||||
{entry.spec.keys.first => entry.type_class}
|
||||
else
|
||||
entry.type_class
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,38 +0,0 @@
|
|||
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 < ActionWebService::Struct
|
||||
# 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
|
|
@ -1,149 +0,0 @@
|
|||
require 'time'
|
||||
require 'date'
|
||||
require 'xmlrpc/datetime'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module Casting # :nodoc:
|
||||
class CastingError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
# Performs casting of arbitrary values into the correct types for the signature
|
||||
class BaseCaster # :nodoc:
|
||||
def initialize(api_method)
|
||||
@api_method = api_method
|
||||
end
|
||||
|
||||
# Coerces the parameters in +params+ (an Enumerable) into the types
|
||||
# this method expects
|
||||
def cast_expects(params)
|
||||
self.class.cast_expects(@api_method, params)
|
||||
end
|
||||
|
||||
# Coerces the given +return_value+ into the type returned by this
|
||||
# method
|
||||
def cast_returns(return_value)
|
||||
self.class.cast_returns(@api_method, return_value)
|
||||
end
|
||||
|
||||
class << self
|
||||
include ActionWebService::SignatureTypes
|
||||
|
||||
def cast_expects(api_method, params) # :nodoc:
|
||||
return [] if api_method.expects.nil?
|
||||
api_method.expects.zip(params).map{ |type, param| cast(param, type) }
|
||||
end
|
||||
|
||||
def cast_returns(api_method, return_value) # :nodoc:
|
||||
return nil if api_method.returns.nil?
|
||||
cast(return_value, api_method.returns[0])
|
||||
end
|
||||
|
||||
def cast(value, signature_type) # :nodoc:
|
||||
return value if signature_type.nil? # signature.length != params.length
|
||||
return nil if value.nil?
|
||||
# XMLRPC protocol doesn't support nil values. It uses false instead.
|
||||
# It should never happen for SOAP.
|
||||
if signature_type.structured? && value.equal?(false)
|
||||
return nil
|
||||
end
|
||||
unless signature_type.array? || signature_type.structured?
|
||||
return value if canonical_type(value.class) == signature_type.type
|
||||
end
|
||||
if signature_type.array?
|
||||
unless value.respond_to?(:entries) && !value.is_a?(String)
|
||||
raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
|
||||
end
|
||||
value.entries.map do |entry|
|
||||
cast(entry, signature_type.element_type)
|
||||
end
|
||||
elsif signature_type.structured?
|
||||
cast_to_structured_type(value, signature_type)
|
||||
elsif !signature_type.custom?
|
||||
cast_base_type(value, signature_type)
|
||||
end
|
||||
end
|
||||
|
||||
def cast_base_type(value, signature_type) # :nodoc:
|
||||
# This is a work-around for the fact that XML-RPC special-cases DateTime values into its own DateTime type
|
||||
# in order to support iso8601 dates. This doesn't work too well for us, so we'll convert it into a Time,
|
||||
# with the caveat that we won't be able to handle pre-1970 dates that are sent to us.
|
||||
#
|
||||
# See http://dev.rubyonrails.com/ticket/2516
|
||||
value = value.to_time if value.is_a?(XMLRPC::DateTime)
|
||||
|
||||
case signature_type.type
|
||||
when :int
|
||||
Integer(value)
|
||||
when :string
|
||||
value.to_s
|
||||
when :base64
|
||||
if value.is_a?(ActionWebService::Base64)
|
||||
value
|
||||
else
|
||||
ActionWebService::Base64.new(value.to_s)
|
||||
end
|
||||
when :bool
|
||||
return false if value.nil?
|
||||
return value if value == true || value == false
|
||||
case value.to_s.downcase
|
||||
when '1', 'true', 'y', 'yes'
|
||||
true
|
||||
when '0', 'false', 'n', 'no'
|
||||
false
|
||||
else
|
||||
raise CastingError, "Don't know how to cast #{value.class} into Boolean"
|
||||
end
|
||||
when :float
|
||||
Float(value)
|
||||
when :decimal
|
||||
BigDecimal(value.to_s)
|
||||
when :time
|
||||
value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6]) if value.kind_of?(Hash)
|
||||
if value.kind_of?(Time)
|
||||
value
|
||||
elsif value.kind_of?(DateTime)
|
||||
value.to_time
|
||||
else
|
||||
Time.parse(value.to_s)
|
||||
end
|
||||
when :date
|
||||
value = "%s/%s/%s" % value.values_at(*%w[2 3 1]) if value.kind_of?(Hash)
|
||||
value.kind_of?(Date) ? value : Date.parse(value.to_s)
|
||||
when :datetime
|
||||
value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6]) if value.kind_of?(Hash)
|
||||
value.kind_of?(DateTime) ? value : DateTime.parse(value.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def cast_to_structured_type(value, signature_type) # :nodoc:
|
||||
obj = nil
|
||||
# if the canonical classes are the same or if the given value is of
|
||||
# a type that is derived from the signature_type do not attempt to
|
||||
# "cast" the value into the signature_type as it's already good to go
|
||||
obj = (
|
||||
canonical_type(value.class) == canonical_type(signature_type.type) or
|
||||
derived_from?(signature_type.type, value.class)
|
||||
) ? value : signature_type.type_class.new
|
||||
if value.respond_to?(:each_pair)
|
||||
klass = signature_type.type_class
|
||||
value.each_pair do |name, val|
|
||||
type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
|
||||
val = cast(val, type) if type
|
||||
# See http://dev.rubyonrails.com/ticket/3567
|
||||
val = val.to_time if val.is_a?(XMLRPC::DateTime)
|
||||
obj.__send__("#{name}=", val) if obj.respond_to?(name)
|
||||
end
|
||||
elsif value.respond_to?(:attributes)
|
||||
signature_type.each_member do |name, type|
|
||||
val = value.__send__(name)
|
||||
obj.__send__("#{name}=", cast(val, type)) if obj.respond_to?(name)
|
||||
end
|
||||
else
|
||||
raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
|
||||
end
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
require 'action_web_service/client/base'
|
||||
require 'action_web_service/client/soap_client'
|
||||
require 'action_web_service/client/xmlrpc_client'
|
|
@ -1,28 +0,0 @@
|
|||
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?
|
||||
self.perform_invocation(call_name, args)
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,113 +0,0 @@
|
|||
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
|
||||
# provides access to the underlying soap driver
|
||||
attr_reader :driver
|
||||
|
||||
# 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:
|
||||
# [<tt>:namespace</tt>] If the remote server has used a custom namespace to
|
||||
# declare its custom types, you can specify it here. This would
|
||||
# be the namespace declared with a [WebService(Namespace = "http://namespace")] attribute
|
||||
# in .NET, for example.
|
||||
# [<tt>:driver_options</tt>] If you want to supply any custom SOAP RPC driver
|
||||
# options, you can provide them as a Hash here
|
||||
#
|
||||
# The <tt>:driver_options</tt> option can be used to configure the backend SOAP
|
||||
# RPC driver. An example of configuring the SOAP backend to do
|
||||
# client-certificate authenticated SSL connections to the server:
|
||||
#
|
||||
# opts = {}
|
||||
# opts['protocol.http.ssl_config.verify_mode'] = 'OpenSSL::SSL::VERIFY_PEER'
|
||||
# opts['protocol.http.ssl_config.client_cert'] = client_cert_file_path
|
||||
# opts['protocol.http.ssl_config.client_key'] = client_key_file_path
|
||||
# opts['protocol.http.ssl_config.ca_file'] = ca_cert_file_path
|
||||
# client = ActionWebService::Client::Soap.new(api, 'https://some/service', :driver_options => opts)
|
||||
def initialize(api, endpoint_uri, options={})
|
||||
super(api, endpoint_uri)
|
||||
@namespace = options[:namespace] || 'urn:ActionWebService'
|
||||
@driver_options = options[:driver_options] || {}
|
||||
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.new @namespace
|
||||
@soap_action_base = options[:soap_action_base]
|
||||
@soap_action_base ||= URI.parse(endpoint_uri).path
|
||||
@driver = create_soap_rpc_driver(api, endpoint_uri)
|
||||
@driver_options.each do |name, value|
|
||||
@driver.options[name.to_s] = value
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def perform_invocation(method_name, args)
|
||||
method = @api.api_methods[method_name.to_sym]
|
||||
args = method.cast_expects(args.dup) rescue args
|
||||
return_value = @driver.send(method_name, *args)
|
||||
method.cast_returns(return_value.dup) rescue return_value
|
||||
end
|
||||
|
||||
def soap_action(method_name)
|
||||
"#{@soap_action_base}/#{method_name}"
|
||||
end
|
||||
|
||||
private
|
||||
def create_soap_rpc_driver(api, endpoint_uri)
|
||||
@protocol.register_api(api)
|
||||
driver = SoapDriver.new(endpoint_uri, nil)
|
||||
driver.mapping_registry = @protocol.marshaler.registry
|
||||
api.api_methods.each do |name, method|
|
||||
qname = XSD::QName.new(@namespace, method.public_name)
|
||||
action = soap_action(method.public_name)
|
||||
expects = method.expects
|
||||
returns = method.returns
|
||||
param_def = []
|
||||
if expects
|
||||
expects.each do |type|
|
||||
type_binding = @protocol.marshaler.lookup_type(type)
|
||||
if SOAP::Version >= "1.5.5"
|
||||
param_def << ['in', type.name.to_s, [type_binding.type.type_class.to_s]]
|
||||
else
|
||||
param_def << ['in', type.name, type_binding.mapping]
|
||||
end
|
||||
end
|
||||
end
|
||||
if returns
|
||||
type_binding = @protocol.marshaler.lookup_type(returns[0])
|
||||
if SOAP::Version >= "1.5.5"
|
||||
param_def << ['retval', 'return', [type_binding.type.type_class.to_s]]
|
||||
else
|
||||
param_def << ['retval', 'return', type_binding.mapping]
|
||||
end
|
||||
end
|
||||
driver.add_method(qname, action, method.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
|
|
@ -1,58 +0,0 @@
|
|||
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:
|
||||
# [<tt>:handler_name</tt>] If the remote server defines its services inside special
|
||||
# handler (the Blogger API uses a <tt>"blogger"</tt> 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]
|
||||
@protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
|
||||
@client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
|
||||
end
|
||||
|
||||
protected
|
||||
def perform_invocation(method_name, args)
|
||||
method = @api.api_methods[method_name.to_sym]
|
||||
if method.expects && method.expects.length != args.length
|
||||
raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
|
||||
end
|
||||
args = method.cast_expects(args.dup) rescue args
|
||||
if method.expects
|
||||
method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
|
||||
end
|
||||
ok, return_value = @client.call2(public_name(method_name), *args)
|
||||
return (method.cast_returns(return_value.dup) rescue return_value) if ok
|
||||
raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
|
||||
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
|
|
@ -1,3 +0,0 @@
|
|||
require 'action_web_service/container/direct_container'
|
||||
require 'action_web_service/container/delegated_container'
|
||||
require 'action_web_service/container/action_controller_container'
|
|
@ -1,93 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Container # :nodoc:
|
||||
module ActionController # :nodoc:
|
||||
def self.included(base) # :nodoc:
|
||||
class << base
|
||||
include ClassMethods
|
||||
alias_method_chain :inherited, :api
|
||||
alias_method_chain :web_service_api, :require
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Creates a client for accessing remote web services, using the
|
||||
# given +protocol+ to communicate with the +endpoint_uri+.
|
||||
#
|
||||
# ==== Example
|
||||
#
|
||||
# class MyController < ActionController::Base
|
||||
# web_client_api :blogger, :xmlrpc, "http://blogger.com/myblog/api/RPC2", :handler_name => 'blogger'
|
||||
# end
|
||||
#
|
||||
# In this example, a protected method named <tt>blogger</tt> will
|
||||
# now exist on the controller, and calling it will return the
|
||||
# XML-RPC client object for working with that remote service.
|
||||
#
|
||||
# +options+ is the set of protocol client specific options (see
|
||||
# a protocol client class for details).
|
||||
#
|
||||
# If your API definition does not exist on the load path with the
|
||||
# correct rules for it to be found using +name+, you can pass in
|
||||
# the API definition class via +options+, using a key of <tt>:api</tt>
|
||||
def web_client_api(name, protocol, endpoint_uri, options={})
|
||||
unless method_defined?(name)
|
||||
api_klass = options.delete(:api) || require_web_service_api(name)
|
||||
class_eval do
|
||||
define_method(name) do
|
||||
create_web_service_client(api_klass, protocol, endpoint_uri, options)
|
||||
end
|
||||
protected name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def web_service_api_with_require(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]
|
||||
msg = requiree == file_name ? "Missing API definition file in apis/#{file_name}.rb" : "Can't load file: #{requiree}"
|
||||
raise LoadError.new(msg).copy_blame!(load_error)
|
||||
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_with_api(child)
|
||||
inherited_without_api(child)
|
||||
begin child.web_service_api(child.controller_path)
|
||||
rescue MissingSourceFile => e
|
||||
raise unless e.is_missing?("apis/#{child.controller_path}_api")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,86 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Container # :nodoc:
|
||||
module Delegated # :nodoc:
|
||||
class ContainerError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Container::Delegated::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Declares a web service that will provide 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 ? self.instance_eval(&service) : info[:object]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,69 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Container # :nodoc:
|
||||
module Direct # :nodoc:
|
||||
class ContainerError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Attaches ActionWebService API +definition+ to the calling class.
|
||||
#
|
||||
# Action Controllers can have a default associated API, removing the need
|
||||
# to call this method if you follow the Action Web Service naming conventions.
|
||||
#
|
||||
# A controller with a class name of GoogleSearchController will
|
||||
# implicitly load <tt>app/apis/google_search_api.rb</tt>, and expect the
|
||||
# API definition class to be named <tt>GoogleSearchAPI</tt> or
|
||||
# <tt>GoogleSearchApi</tt>.
|
||||
#
|
||||
# ==== Service class example
|
||||
#
|
||||
# class MyService < ActionWebService::Base
|
||||
# web_service_api MyAPI
|
||||
# end
|
||||
#
|
||||
# class MyAPI < ActionWebService::API::Base
|
||||
# ...
|
||||
# end
|
||||
#
|
||||
# ==== Controller class example
|
||||
#
|
||||
# class MyController < ActionController::Base
|
||||
# web_service_api MyAPI
|
||||
# end
|
||||
#
|
||||
# class MyAPI < ActionWebService::API::Base
|
||||
# ...
|
||||
# end
|
||||
def web_service_api(definition=nil)
|
||||
if definition.nil?
|
||||
read_inheritable_attribute("web_service_api")
|
||||
else
|
||||
if definition.is_a?(Symbol)
|
||||
raise(ContainerError, "symbols can only be used for #web_service_api inside of a controller")
|
||||
end
|
||||
unless definition.respond_to?(:ancestors) && definition.ancestors.include?(ActionWebService::API::Base)
|
||||
raise(ContainerError, "#{definition.to_s} is not a valid API definition")
|
||||
end
|
||||
write_inheritable_attribute("web_service_api", definition)
|
||||
call_web_service_api_callbacks(self, definition)
|
||||
end
|
||||
end
|
||||
|
||||
def add_web_service_api_callback(&block) # :nodoc:
|
||||
write_inheritable_array("web_service_api_callbacks", [block])
|
||||
end
|
||||
|
||||
private
|
||||
def call_web_service_api_callbacks(container_class, definition)
|
||||
(read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
|
||||
block.call(container_class, definition)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
require 'action_web_service/dispatcher/abstract'
|
||||
require 'action_web_service/dispatcher/action_controller_dispatcher'
|
|
@ -1,207 +0,0 @@
|
|||
require 'benchmark'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module Dispatcher # :nodoc:
|
||||
class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc:
|
||||
def initialize(*args)
|
||||
super
|
||||
set_backtrace(caller)
|
||||
end
|
||||
end
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.class_inheritable_option(:web_service_dispatching_mode, :direct)
|
||||
base.class_inheritable_option(:web_service_exception_reporting, true)
|
||||
base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
private
|
||||
def invoke_web_service_request(protocol_request)
|
||||
invocation = web_service_invocation(protocol_request)
|
||||
if invocation.is_a?(Array) && protocol_request.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
|
||||
xmlrpc_multicall_invoke(invocation)
|
||||
else
|
||||
web_service_invoke(invocation)
|
||||
end
|
||||
end
|
||||
|
||||
def web_service_direct_invoke(invocation)
|
||||
@method_params = invocation.method_ordered_params
|
||||
arity = method(invocation.api_method.name).arity rescue 0
|
||||
if arity < 0 || arity > 0
|
||||
params = @method_params
|
||||
else
|
||||
params = []
|
||||
end
|
||||
web_service_filtered_invoke(invocation, params)
|
||||
end
|
||||
|
||||
def web_service_delegated_invoke(invocation)
|
||||
web_service_filtered_invoke(invocation, invocation.method_ordered_params)
|
||||
end
|
||||
|
||||
def web_service_filtered_invoke(invocation, params)
|
||||
cancellation_reason = nil
|
||||
return_value = invocation.service.perform_invocation(invocation.api_method.name, params) do |x|
|
||||
cancellation_reason = x
|
||||
end
|
||||
if cancellation_reason
|
||||
raise(DispatcherError, "request canceled: #{cancellation_reason}")
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def web_service_invoke(invocation)
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
return_value = web_service_direct_invoke(invocation)
|
||||
when :delegated, :layered
|
||||
return_value = web_service_delegated_invoke(invocation)
|
||||
end
|
||||
web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
|
||||
end
|
||||
|
||||
def xmlrpc_multicall_invoke(invocations)
|
||||
responses = []
|
||||
invocations.each do |invocation|
|
||||
if invocation.is_a?(Hash)
|
||||
responses << [invocation, nil]
|
||||
next
|
||||
end
|
||||
begin
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
return_value = web_service_direct_invoke(invocation)
|
||||
when :delegated, :layered
|
||||
return_value = web_service_delegated_invoke(invocation)
|
||||
end
|
||||
api_method = invocation.api_method
|
||||
if invocation.api.has_api_method?(api_method.name)
|
||||
response_type = (api_method.returns ? api_method.returns[0] : nil)
|
||||
return_value = api_method.cast_returns(return_value)
|
||||
else
|
||||
response_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
|
||||
end
|
||||
responses << [return_value, response_type]
|
||||
rescue Exception => e
|
||||
responses << [{ 'faultCode' => 3, 'faultString' => e.message }, nil]
|
||||
end
|
||||
end
|
||||
invocation = invocations[0]
|
||||
invocation.protocol.encode_multicall_response(responses, invocation.protocol_options)
|
||||
end
|
||||
|
||||
def web_service_invocation(request, level = 0)
|
||||
public_method_name = request.method_name
|
||||
invocation = Invocation.new
|
||||
invocation.protocol = request.protocol
|
||||
invocation.protocol_options = request.protocol_options
|
||||
invocation.service_name = request.service_name
|
||||
if web_service_dispatching_mode == :layered
|
||||
case invocation.protocol
|
||||
when Protocol::Soap::SoapProtocol
|
||||
soap_action = request.protocol_options[:soap_action]
|
||||
if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
|
||||
invocation.service_name = $1
|
||||
end
|
||||
when Protocol::XmlRpc::XmlRpcProtocol
|
||||
if request.method_name =~ /^([^\.]+)\.(.*)$/
|
||||
public_method_name = $2
|
||||
invocation.service_name = $1
|
||||
end
|
||||
end
|
||||
end
|
||||
if invocation.protocol.is_a? Protocol::XmlRpc::XmlRpcProtocol
|
||||
if public_method_name == 'multicall' && invocation.service_name == 'system'
|
||||
if level > 0
|
||||
raise(DispatcherError, "Recursive system.multicall invocations not allowed")
|
||||
end
|
||||
multicall = request.method_params.dup
|
||||
unless multicall.is_a?(Array) && multicall[0].is_a?(Array)
|
||||
raise(DispatcherError, "Malformed multicall (expected array of Hash elements)")
|
||||
end
|
||||
multicall = multicall[0]
|
||||
return multicall.map do |item|
|
||||
raise(DispatcherError, "Multicall elements must be Hash") unless item.is_a?(Hash)
|
||||
raise(DispatcherError, "Multicall elements must contain a 'methodName' key") unless item.has_key?('methodName')
|
||||
method_name = item['methodName']
|
||||
params = item.has_key?('params') ? item['params'] : []
|
||||
multicall_request = request.dup
|
||||
multicall_request.method_name = method_name
|
||||
multicall_request.method_params = params
|
||||
begin
|
||||
web_service_invocation(multicall_request, level + 1)
|
||||
rescue Exception => e
|
||||
{'faultCode' => 4, 'faultMessage' => e.message}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
invocation.api = self.class.web_service_api
|
||||
invocation.service = self
|
||||
when :delegated, :layered
|
||||
invocation.service = web_service_object(invocation.service_name)
|
||||
invocation.api = invocation.service.class.web_service_api
|
||||
end
|
||||
if invocation.api.nil?
|
||||
raise(DispatcherError, "no API attached to #{invocation.service.class}")
|
||||
end
|
||||
invocation.protocol.register_api(invocation.api)
|
||||
request.api = invocation.api
|
||||
if invocation.api.has_public_api_method?(public_method_name)
|
||||
invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
|
||||
else
|
||||
if invocation.api.default_api_method.nil?
|
||||
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
|
||||
else
|
||||
invocation.api_method = invocation.api.default_api_method_instance
|
||||
end
|
||||
end
|
||||
if invocation.service.nil?
|
||||
raise(DispatcherError, "no service available for service name #{invocation.service_name}")
|
||||
end
|
||||
unless invocation.service.respond_to?(invocation.api_method.name)
|
||||
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
|
||||
end
|
||||
request.api_method = invocation.api_method
|
||||
begin
|
||||
invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
|
||||
rescue
|
||||
logger.warn "Casting of method parameters failed" unless logger.nil?
|
||||
invocation.method_ordered_params = request.method_params
|
||||
end
|
||||
request.method_params = invocation.method_ordered_params
|
||||
invocation.method_named_params = {}
|
||||
invocation.api_method.param_names.inject(0) do |m, n|
|
||||
invocation.method_named_params[n] = invocation.method_ordered_params[m]
|
||||
m + 1
|
||||
end
|
||||
invocation
|
||||
end
|
||||
|
||||
def web_service_create_response(protocol, protocol_options, api, api_method, return_value)
|
||||
if api.has_api_method?(api_method.name)
|
||||
return_type = api_method.returns ? api_method.returns[0] : nil
|
||||
return_value = api_method.cast_returns(return_value)
|
||||
else
|
||||
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
|
||||
end
|
||||
protocol.encode_response(api_method.public_name + 'Response', return_value, return_type, protocol_options)
|
||||
end
|
||||
|
||||
class Invocation # :nodoc:
|
||||
attr_accessor :protocol
|
||||
attr_accessor :protocol_options
|
||||
attr_accessor :service_name
|
||||
attr_accessor :api
|
||||
attr_accessor :api_method
|
||||
attr_accessor :method_ordered_params
|
||||
attr_accessor :method_named_params
|
||||
attr_accessor :service
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,379 +0,0 @@
|
|||
require 'benchmark'
|
||||
require 'builder/xmlmarkup'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module Dispatcher # :nodoc:
|
||||
module ActionController # :nodoc:
|
||||
def self.included(base) # :nodoc:
|
||||
class << base
|
||||
include ClassMethods
|
||||
alias_method_chain :inherited, :action_controller
|
||||
end
|
||||
base.class_eval do
|
||||
alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
|
||||
end
|
||||
base.add_web_service_api_callback do |klass, api|
|
||||
if klass.web_service_dispatching_mode == :direct
|
||||
klass.class_eval 'def api; dispatch_web_service_request; end'
|
||||
end
|
||||
end
|
||||
base.add_web_service_definition_callback do |klass, name, info|
|
||||
if klass.web_service_dispatching_mode == :delegated
|
||||
klass.class_eval "def #{name}; dispatch_web_service_request; end"
|
||||
elsif klass.web_service_dispatching_mode == :layered
|
||||
klass.class_eval 'def api; dispatch_web_service_request; end'
|
||||
end
|
||||
end
|
||||
base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
def inherited_with_action_controller(child)
|
||||
inherited_without_action_controller(child)
|
||||
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
private
|
||||
def dispatch_web_service_request
|
||||
method = request.method.to_s.upcase
|
||||
allowed_methods = self.class.web_service_api ? (self.class.web_service_api.allowed_http_methods || []) : [ :post ]
|
||||
allowed_methods = allowed_methods.map{|m| m.to_s.upcase }
|
||||
if !allowed_methods.include?(method)
|
||||
render :text => "#{method} not supported", :status=>500
|
||||
return
|
||||
end
|
||||
exception = nil
|
||||
begin
|
||||
ws_request = discover_web_service_request(request)
|
||||
rescue Exception => e
|
||||
exception = e
|
||||
end
|
||||
if ws_request
|
||||
ws_response = nil
|
||||
exception = nil
|
||||
bm = Benchmark.measure do
|
||||
begin
|
||||
ws_response = invoke_web_service_request(ws_request)
|
||||
rescue Exception => e
|
||||
exception = e
|
||||
end
|
||||
end
|
||||
log_request(ws_request, request.raw_post)
|
||||
if exception
|
||||
log_error(exception) unless logger.nil?
|
||||
send_web_service_error_response(ws_request, exception)
|
||||
else
|
||||
send_web_service_response(ws_response, bm.real)
|
||||
end
|
||||
else
|
||||
exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
|
||||
log_error(exception) unless logger.nil?
|
||||
send_web_service_error_response(ws_request, exception)
|
||||
end
|
||||
rescue Exception => e
|
||||
log_error(e) unless logger.nil?
|
||||
send_web_service_error_response(ws_request, e)
|
||||
end
|
||||
|
||||
def send_web_service_response(ws_response, elapsed=nil)
|
||||
log_response(ws_response, elapsed)
|
||||
options = { :type => ws_response.content_type, :disposition => 'inline' }
|
||||
send_data(ws_response.body, options)
|
||||
end
|
||||
|
||||
def send_web_service_error_response(ws_request, exception)
|
||||
if ws_request
|
||||
unless self.class.web_service_exception_reporting
|
||||
exception = DispatcherError.new("Internal server error (exception raised)")
|
||||
end
|
||||
api_method = ws_request.api_method
|
||||
public_method_name = api_method ? api_method.public_name : ws_request.method_name
|
||||
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
|
||||
ws_response = ws_request.protocol.encode_response(public_method_name + 'Response', exception, return_type, ws_request.protocol_options)
|
||||
send_web_service_response(ws_response)
|
||||
else
|
||||
if self.class.web_service_exception_reporting
|
||||
message = exception.message
|
||||
backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
|
||||
else
|
||||
message = "Exception raised"
|
||||
backtrace = ""
|
||||
end
|
||||
render :status => 500, :text => "Internal protocol error: #{message}#{backtrace}"
|
||||
end
|
||||
end
|
||||
|
||||
def web_service_direct_invoke(invocation)
|
||||
invocation.method_named_params.each do |name, value|
|
||||
params[name] = value
|
||||
end
|
||||
web_service_direct_invoke_without_controller(invocation)
|
||||
end
|
||||
|
||||
def log_request(ws_request, body)
|
||||
unless logger.nil?
|
||||
name = ws_request.method_name
|
||||
api_method = ws_request.api_method
|
||||
params = ws_request.method_params
|
||||
if api_method && api_method.expects
|
||||
params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
|
||||
else
|
||||
params = params.map{ |param| param.inspect }
|
||||
end
|
||||
service = ws_request.service_name
|
||||
logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
|
||||
logger.debug(indent(body))
|
||||
end
|
||||
end
|
||||
|
||||
def log_response(ws_response, elapsed=nil)
|
||||
unless logger.nil?
|
||||
elapsed = (elapsed ? " (%f):" % elapsed : ":")
|
||||
logger.debug("\nWeb Service Response" + elapsed + " => #{ws_response.return_value.inspect}")
|
||||
logger.debug(indent(ws_response.body))
|
||||
end
|
||||
end
|
||||
|
||||
def indent(body)
|
||||
body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
module WsdlAction # :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
|
||||
options = { :type => 'text/xml', :disposition => 'inline' }
|
||||
send_data(to_wsdl, options)
|
||||
rescue Exception => e
|
||||
log_error(e) unless logger.nil?
|
||||
end
|
||||
when :post
|
||||
render :status => 500, :text => 'POST not supported'
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def base_uri
|
||||
host = request.host_with_port
|
||||
relative_url_root = ::ActionController::Base.relative_url_root
|
||||
scheme = request.ssl? ? 'https' : 'http'
|
||||
'%s://%s%s/%s/' % [scheme, host, relative_url_root, self.class.controller_path]
|
||||
end
|
||||
|
||||
def to_wsdl
|
||||
xml = ''
|
||||
dispatching_mode = web_service_dispatching_mode
|
||||
global_service_name = wsdl_service_name
|
||||
namespace = wsdl_namespace || 'urn:ActionWebService'
|
||||
soap_action_base = "/#{controller_name}"
|
||||
|
||||
marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
|
||||
apis = {}
|
||||
case dispatching_mode
|
||||
when :direct
|
||||
api = self.class.web_service_api
|
||||
web_service_name = controller_class_name.sub(/Controller$/, '').underscore
|
||||
apis[web_service_name] = [api, register_api(api, marshaler)]
|
||||
when :delegated, :layered
|
||||
self.class.web_services.each do |web_service_name, info|
|
||||
service = web_service_object(web_service_name)
|
||||
api = service.class.web_service_api
|
||||
apis[web_service_name] = [api, register_api(api, marshaler)]
|
||||
end
|
||||
end
|
||||
custom_types = []
|
||||
apis.values.each do |api, bindings|
|
||||
bindings.each do |b|
|
||||
custom_types << b unless custom_types.include?(b)
|
||||
end
|
||||
end
|
||||
|
||||
xm = Builder::XmlMarkup.new(:target => xml, :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
|
||||
# Generate XSD
|
||||
if custom_types.size > 0
|
||||
xm.types do
|
||||
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
||||
custom_types.each do |binding|
|
||||
case
|
||||
when binding.type.array?
|
||||
xm.xsd(:complexType, 'name' => binding.type_name) do
|
||||
xm.xsd(:complexContent) do
|
||||
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
||||
xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
|
||||
'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
|
||||
end
|
||||
end
|
||||
end
|
||||
when binding.type.structured?
|
||||
xm.xsd(:complexType, 'name' => binding.type_name) do
|
||||
xm.xsd(:all) do
|
||||
binding.type.each_member do |name, type|
|
||||
b = marshaler.register_type(type)
|
||||
xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# APIs
|
||||
apis.each do |api_name, values|
|
||||
api = values[0]
|
||||
api.api_methods.each do |name, method|
|
||||
gen = lambda do |msg_name, direction|
|
||||
xm.message('name' => message_name_for(api_name, msg_name)) do
|
||||
sym = nil
|
||||
if direction == :out
|
||||
returns = method.returns
|
||||
if returns
|
||||
binding = marshaler.register_type(returns[0])
|
||||
xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
|
||||
end
|
||||
else
|
||||
expects = method.expects
|
||||
expects.each do |type|
|
||||
binding = marshaler.register_type(type)
|
||||
xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
|
||||
end if expects
|
||||
end
|
||||
end
|
||||
end
|
||||
public_name = method.public_name
|
||||
gen.call(public_name, :in)
|
||||
gen.call("#{public_name}Response", :out)
|
||||
end
|
||||
|
||||
# Port
|
||||
port_name = port_name_for(global_service_name, api_name)
|
||||
xm.portType('name' => port_name) do
|
||||
api.api_methods.each do |name, method|
|
||||
xm.operation('name' => method.public_name) do
|
||||
xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
|
||||
xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Bind it
|
||||
binding_name = binding_name_for(global_service_name, api_name)
|
||||
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
||||
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
||||
api.api_methods.each do |name, method|
|
||||
xm.operation('name' => method.public_name) do
|
||||
case web_service_dispatching_mode
|
||||
when :direct
|
||||
soap_action = soap_action_base + "/api/" + method.public_name
|
||||
when :delegated, :layered
|
||||
soap_action = soap_action_base \
|
||||
+ "/" + api_name.to_s \
|
||||
+ "/" + method.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 it
|
||||
xm.service('name' => "#{global_service_name}Service") do
|
||||
apis.each do |api_name, values|
|
||||
port_name = port_name_for(global_service_name, api_name)
|
||||
binding_name = binding_name_for(global_service_name, api_name)
|
||||
case web_service_dispatching_mode
|
||||
when :direct, :layered
|
||||
binding_target = 'api'
|
||||
when :delegated
|
||||
binding_target = api_name.to_s
|
||||
end
|
||||
xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
|
||||
xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def port_name_for(global_service, service)
|
||||
"#{global_service}#{service.to_s.camelize}Port"
|
||||
end
|
||||
|
||||
def binding_name_for(global_service, service)
|
||||
"#{global_service}#{service.to_s.camelize}Binding"
|
||||
end
|
||||
|
||||
def message_name_for(api_name, message_name)
|
||||
mode = web_service_dispatching_mode
|
||||
if mode == :layered || mode == :delegated
|
||||
api_name.to_s + '-' + message_name
|
||||
else
|
||||
message_name
|
||||
end
|
||||
end
|
||||
|
||||
def register_api(api, marshaler)
|
||||
bindings = {}
|
||||
traverse_custom_types(api, marshaler, bindings) do |binding|
|
||||
bindings[binding] = nil unless bindings.has_key?(binding)
|
||||
element_binding = binding.element_binding
|
||||
bindings[element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
|
||||
end
|
||||
bindings.keys
|
||||
end
|
||||
|
||||
def traverse_custom_types(api, marshaler, bindings, &block)
|
||||
api.api_methods.each do |name, method|
|
||||
expects, returns = method.expects, method.returns
|
||||
expects.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if expects
|
||||
returns.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if returns
|
||||
end
|
||||
end
|
||||
|
||||
def traverse_type(marshaler, type, bindings, &block)
|
||||
binding = marshaler.register_type(type)
|
||||
return if bindings.has_key?(binding)
|
||||
bindings[binding] = nil
|
||||
yield binding
|
||||
if type.array?
|
||||
yield marshaler.register_type(type.element_type)
|
||||
type = type.element_type
|
||||
end
|
||||
type.each_member{ |name, type| traverse_type(marshaler, type, bindings, &block) } if type.structured?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,202 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Invocation # :nodoc:
|
||||
class InvocationError < ActionWebService::ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
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 <tt>[false, "cancel reason"]</tt> 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:
|
||||
# [<tt>:except</tt>] A list of methods for which the interceptor will NOT be called
|
||||
# [<tt>:only</tt>] 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.included(base)
|
||||
base.class_eval do
|
||||
alias_method_chain :perform_invocation, :interception
|
||||
end
|
||||
end
|
||||
|
||||
def perform_invocation_with_interception(method_name, params, &block)
|
||||
return if before_invocation(method_name, params, &block) == false
|
||||
return_value = perform_invocation_without_interception(method_name, params)
|
||||
after_invocation(method_name, params, return_value)
|
||||
return_value
|
||||
end
|
||||
|
||||
def perform_invocation(method_name, params)
|
||||
send(method_name, *params)
|
||||
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
|
||||
end
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
require 'action_web_service/protocol/abstract'
|
||||
require 'action_web_service/protocol/discovery'
|
||||
require 'action_web_service/protocol/soap_protocol'
|
||||
require 'action_web_service/protocol/xmlrpc_protocol'
|
|
@ -1,112 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Protocol # :nodoc:
|
||||
class ProtocolError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
class AbstractProtocol # :nodoc:
|
||||
def setup(controller)
|
||||
end
|
||||
|
||||
def decode_action_pack_request(action_pack_request)
|
||||
end
|
||||
|
||||
def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
|
||||
klass = options[:request_class] || SimpleActionPackRequest
|
||||
request = klass.new({})
|
||||
request.request_parameters['action'] = service_name.to_s
|
||||
request.env['RAW_POST_DATA'] = raw_body
|
||||
request.env['REQUEST_METHOD'] = 'POST'
|
||||
request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
||||
request
|
||||
end
|
||||
|
||||
def decode_request(raw_request, service_name, protocol_options={})
|
||||
end
|
||||
|
||||
def encode_request(method_name, params, param_types)
|
||||
end
|
||||
|
||||
def decode_response(raw_response)
|
||||
end
|
||||
|
||||
def encode_response(method_name, return_value, return_type, protocol_options={})
|
||||
end
|
||||
|
||||
def protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
end
|
||||
|
||||
def register_api(api)
|
||||
end
|
||||
end
|
||||
|
||||
class Request # :nodoc:
|
||||
attr :protocol
|
||||
attr_accessor :method_name
|
||||
attr_accessor :method_params
|
||||
attr :service_name
|
||||
attr_accessor :api
|
||||
attr_accessor :api_method
|
||||
attr :protocol_options
|
||||
|
||||
def initialize(protocol, method_name, method_params, service_name, api=nil, api_method=nil, protocol_options=nil)
|
||||
@protocol = protocol
|
||||
@method_name = method_name
|
||||
@method_params = method_params
|
||||
@service_name = service_name
|
||||
@api = api
|
||||
@api_method = api_method
|
||||
@protocol_options = protocol_options || {}
|
||||
end
|
||||
end
|
||||
|
||||
class Response # :nodoc:
|
||||
attr :body
|
||||
attr :content_type
|
||||
attr :return_value
|
||||
|
||||
def initialize(body, content_type, return_value)
|
||||
@body = body
|
||||
@content_type = content_type
|
||||
@return_value = return_value
|
||||
end
|
||||
end
|
||||
|
||||
class SimpleActionPackRequest < ActionController::Request # :nodoc:
|
||||
def initialize(env = {})
|
||||
@env = env
|
||||
@qparams = {}
|
||||
@rparams = {}
|
||||
@cookies = {}
|
||||
reset_session
|
||||
end
|
||||
|
||||
def query_parameters
|
||||
@qparams
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@rparams
|
||||
end
|
||||
|
||||
def env
|
||||
@env
|
||||
end
|
||||
|
||||
def host
|
||||
''
|
||||
end
|
||||
|
||||
def cookies
|
||||
@cookies
|
||||
end
|
||||
|
||||
def session
|
||||
@session
|
||||
end
|
||||
|
||||
def reset_session
|
||||
@session = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
module Protocol # :nodoc:
|
||||
module Discovery # :nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionWebService::Protocol::Discovery::InstanceMethods)
|
||||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
def register_protocol(klass)
|
||||
write_inheritable_array("web_service_protocols", [klass])
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
private
|
||||
def discover_web_service_request(action_pack_request)
|
||||
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
|
||||
protocol = protocol.create(self)
|
||||
request = protocol.decode_action_pack_request(action_pack_request)
|
||||
return request unless request.nil?
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def create_web_service_client(api, protocol_name, endpoint_uri, options)
|
||||
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
|
||||
protocol = protocol.create(self)
|
||||
client = protocol.protocol_client(api, protocol_name, endpoint_uri, options)
|
||||
return client unless client.nil?
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,176 +0,0 @@
|
|||
require 'action_web_service/protocol/soap_protocol/marshaler'
|
||||
require 'soap/streamHandler'
|
||||
require 'action_web_service/client/soap_client'
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module API # :nodoc:
|
||||
class Base # :nodoc:
|
||||
def self.soap_client(endpoint_uri, options={})
|
||||
ActionWebService::Client::Soap.new self, endpoint_uri, options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Protocol # :nodoc:
|
||||
module Soap # :nodoc:
|
||||
def self.included(base)
|
||||
base.register_protocol(SoapProtocol)
|
||||
base.class_inheritable_option(:wsdl_service_name)
|
||||
base.class_inheritable_option(:wsdl_namespace)
|
||||
end
|
||||
|
||||
class SoapProtocol < AbstractProtocol # :nodoc:
|
||||
AWSEncoding = 'UTF-8'
|
||||
XSDEncoding = 'UTF8'
|
||||
|
||||
attr :marshaler
|
||||
|
||||
def initialize(namespace=nil)
|
||||
namespace ||= 'urn:ActionWebService'
|
||||
@marshaler = SoapMarshaler.new namespace
|
||||
end
|
||||
|
||||
def self.create(controller)
|
||||
SoapProtocol.new(controller.wsdl_namespace)
|
||||
end
|
||||
|
||||
def decode_action_pack_request(action_pack_request)
|
||||
return nil unless soap_action = has_valid_soap_action?(action_pack_request)
|
||||
service_name = action_pack_request.parameters['action']
|
||||
input_encoding = parse_charset(action_pack_request.env['HTTP_CONTENT_TYPE'])
|
||||
protocol_options = {
|
||||
:soap_action => soap_action,
|
||||
:charset => input_encoding
|
||||
}
|
||||
decode_request(action_pack_request.raw_post, service_name, protocol_options)
|
||||
end
|
||||
|
||||
def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
|
||||
request = super
|
||||
request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name]
|
||||
request
|
||||
end
|
||||
|
||||
def decode_request(raw_request, service_name, protocol_options={})
|
||||
envelope = SOAP::Processor.unmarshal(raw_request, :charset => protocol_options[:charset])
|
||||
unless envelope
|
||||
raise ProtocolError, "Failed to parse SOAP request message"
|
||||
end
|
||||
request = envelope.body.request
|
||||
method_name = request.elename.name
|
||||
params = request.collect{ |k, v| marshaler.soap_to_ruby(request[k]) }
|
||||
Request.new(self, method_name, params, service_name, nil, nil, protocol_options)
|
||||
end
|
||||
|
||||
def encode_request(method_name, params, param_types)
|
||||
param_types.each{ |type| marshaler.register_type(type) } if param_types
|
||||
qname = XSD::QName.new(marshaler.namespace, method_name)
|
||||
param_def = []
|
||||
if param_types
|
||||
params = param_types.zip(params).map do |type, param|
|
||||
param_def << ['in', type.name, marshaler.lookup_type(type).mapping]
|
||||
[type.name, marshaler.ruby_to_soap(param)]
|
||||
end
|
||||
else
|
||||
params = []
|
||||
end
|
||||
request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
|
||||
request.set_param(params)
|
||||
envelope = create_soap_envelope(request)
|
||||
SOAP::Processor.marshal(envelope)
|
||||
end
|
||||
|
||||
def decode_response(raw_response)
|
||||
envelope = SOAP::Processor.unmarshal(raw_response)
|
||||
unless envelope
|
||||
raise ProtocolError, "Failed to parse SOAP request message"
|
||||
end
|
||||
method_name = envelope.body.request.elename.name
|
||||
return_value = envelope.body.response
|
||||
return_value = marshaler.soap_to_ruby(return_value) unless return_value.nil?
|
||||
[method_name, return_value]
|
||||
end
|
||||
|
||||
def encode_response(method_name, return_value, return_type, protocol_options={})
|
||||
if return_type
|
||||
return_binding = marshaler.register_type(return_type)
|
||||
marshaler.annotate_arrays(return_binding, return_value)
|
||||
end
|
||||
qname = XSD::QName.new(marshaler.namespace, method_name)
|
||||
if return_value.nil?
|
||||
response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
|
||||
else
|
||||
if return_value.is_a?(Exception)
|
||||
detail = SOAP::Mapping::SOAPException.new(return_value)
|
||||
response = SOAP::SOAPFault.new(
|
||||
SOAP::SOAPQName.new('%s:%s' % [SOAP::SOAPNamespaceTag, 'Server']),
|
||||
SOAP::SOAPString.new(return_value.to_s),
|
||||
SOAP::SOAPString.new(self.class.name),
|
||||
marshaler.ruby_to_soap(detail))
|
||||
else
|
||||
if return_type
|
||||
param_def = [['retval', 'return', marshaler.lookup_type(return_type).mapping]]
|
||||
response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
|
||||
response.retval = marshaler.ruby_to_soap(return_value)
|
||||
else
|
||||
response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
envelope = create_soap_envelope(response)
|
||||
|
||||
# FIXME: This is not thread-safe, but StringFactory_ in SOAP4R only
|
||||
# reads target encoding from the XSD::Charset.encoding variable.
|
||||
# This is required to ensure $KCODE strings are converted
|
||||
# correctly to UTF-8 for any values of $KCODE.
|
||||
previous_encoding = XSD::Charset.encoding
|
||||
XSD::Charset.encoding = XSDEncoding
|
||||
response_body = SOAP::Processor.marshal(envelope, :charset => AWSEncoding)
|
||||
XSD::Charset.encoding = previous_encoding
|
||||
|
||||
Response.new(response_body, "text/xml; charset=#{AWSEncoding}", return_value)
|
||||
end
|
||||
|
||||
def protocol_client(api, protocol_name, endpoint_uri, options={})
|
||||
return nil unless protocol_name == :soap
|
||||
ActionWebService::Client::Soap.new(api, endpoint_uri, options)
|
||||
end
|
||||
|
||||
def register_api(api)
|
||||
api.api_methods.each do |name, method|
|
||||
method.expects.each{ |type| marshaler.register_type(type) } if method.expects
|
||||
method.returns.each{ |type| marshaler.register_type(type) } if method.returns
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def has_valid_soap_action?(request)
|
||||
return nil unless request.method == :post
|
||||
soap_action = request.env['HTTP_SOAPACTION']
|
||||
return nil unless soap_action
|
||||
soap_action = soap_action.dup
|
||||
soap_action.gsub!(/^"/, '')
|
||||
soap_action.gsub!(/"$/, '')
|
||||
soap_action.strip!
|
||||
return nil if soap_action.empty?
|
||||
soap_action
|
||||
end
|
||||
|
||||
def create_soap_envelope(body)
|
||||
header = SOAP::SOAPHeader.new
|
||||
body = SOAP::SOAPBody.new(body)
|
||||
SOAP::SOAPEnvelope.new(header, body)
|
||||
end
|
||||
|
||||
def parse_charset(content_type)
|
||||
return AWSEncoding if content_type.nil?
|
||||
if /^text\/xml(?:\s*;\s*charset=([^"]+|"[^"]+"))$/i =~ content_type
|
||||
$1
|
||||
else
|
||||
AWSEncoding
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,242 +0,0 @@
|
|||
require 'soap/mapping'
|
||||
|
||||
# hack to improve the .Net interoperability
|
||||
class SOAP::Mapping::Object
|
||||
def each_pair
|
||||
self.__xmlele.each { |n, v| yield n.name, v.to_s }
|
||||
end
|
||||
end
|
||||
|
||||
module ActionWebService
|
||||
module Protocol
|
||||
module Soap
|
||||
# Workaround for SOAP4R return values changing
|
||||
class Registry < SOAP::Mapping::Registry
|
||||
if SOAP::Version >= "1.5.4"
|
||||
def find_mapped_soap_class(obj_class)
|
||||
return @map.instance_eval { @obj2soap[obj_class][0] }
|
||||
end
|
||||
|
||||
def find_mapped_obj_class(soap_class)
|
||||
return @map.instance_eval { @soap2obj[soap_class][0] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SoapMarshaler
|
||||
attr :namespace
|
||||
attr :registry
|
||||
|
||||
def initialize(namespace=nil)
|
||||
@namespace = namespace || 'urn:ActionWebService'
|
||||
@registry = Registry.new
|
||||
@type2binding = {}
|
||||
register_static_factories
|
||||
end
|
||||
|
||||
def soap_to_ruby(obj)
|
||||
SOAP::Mapping.soap2obj(obj, @registry)
|
||||
end
|
||||
|
||||
def ruby_to_soap(obj)
|
||||
soap = SOAP::Mapping.obj2soap(obj, @registry)
|
||||
soap.elename = XSD::QName.new if SOAP::Version >= "1.5.5" && soap.elename == XSD::QName::EMPTY
|
||||
soap
|
||||
end
|
||||
|
||||
def register_type(type)
|
||||
return @type2binding[type] if @type2binding.has_key?(type)
|
||||
|
||||
if type.array?
|
||||
array_mapping = @registry.find_mapped_soap_class(Array)
|
||||
qname = XSD::QName.new(@namespace, soap_type_name(type.element_type.type_class.name) + 'Array')
|
||||
element_type_binding = register_type(type.element_type)
|
||||
@type2binding[type] = SoapBinding.new(self, qname, type, array_mapping, element_type_binding)
|
||||
elsif (mapping = @registry.find_mapped_soap_class(type.type_class) rescue nil)
|
||||
qname = mapping[2] ? mapping[2][:type] : nil
|
||||
qname ||= soap_base_type_name(mapping[0])
|
||||
@type2binding[type] = SoapBinding.new(self, qname, type, mapping)
|
||||
else
|
||||
qname = XSD::QName.new(@namespace, soap_type_name(type.type_class.name))
|
||||
@registry.add(type.type_class,
|
||||
SOAP::SOAPStruct,
|
||||
typed_struct_factory(type.type_class),
|
||||
{ :type => qname })
|
||||
mapping = @registry.find_mapped_soap_class(type.type_class)
|
||||
@type2binding[type] = SoapBinding.new(self, qname, type, mapping)
|
||||
end
|
||||
|
||||
if type.structured?
|
||||
type.each_member do |m_name, m_type|
|
||||
register_type(m_type)
|
||||
end
|
||||
end
|
||||
|
||||
@type2binding[type]
|
||||
end
|
||||
alias :lookup_type :register_type
|
||||
|
||||
def annotate_arrays(binding, value)
|
||||
if value.nil?
|
||||
return
|
||||
elsif binding.type.array?
|
||||
mark_typed_array(value, binding.element_binding.qname)
|
||||
if binding.element_binding.type.custom?
|
||||
value.each do |element|
|
||||
annotate_arrays(binding.element_binding, element)
|
||||
end
|
||||
end
|
||||
elsif binding.type.structured?
|
||||
binding.type.each_member do |name, type|
|
||||
member_binding = register_type(type)
|
||||
member_value = value.respond_to?('[]') ? value[name] : value.send(name)
|
||||
annotate_arrays(member_binding, member_value) if type.custom?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def typed_struct_factory(type_class)
|
||||
if Object.const_defined?('ActiveRecord')
|
||||
if type_class.ancestors.include?(ActiveRecord::Base)
|
||||
qname = XSD::QName.new(@namespace, soap_type_name(type_class.name))
|
||||
type_class.instance_variable_set('@qname', qname)
|
||||
return SoapActiveRecordStructFactory.new
|
||||
end
|
||||
end
|
||||
SOAP::Mapping::Registry::TypedStructFactory
|
||||
end
|
||||
|
||||
def mark_typed_array(array, qname)
|
||||
(class << array; self; end).class_eval do
|
||||
define_method(:arytype) do
|
||||
qname
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def soap_base_type_name(type)
|
||||
xsd_type = type.ancestors.find{ |c| c.const_defined? 'Type' }
|
||||
xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
|
||||
end
|
||||
|
||||
def soap_type_name(type_name)
|
||||
type_name.gsub(/::/, '..')
|
||||
end
|
||||
|
||||
def register_static_factories
|
||||
@registry.add(ActionWebService::Base64, SOAP::SOAPBase64, SoapBase64Factory.new, nil)
|
||||
mapping = @registry.find_mapped_soap_class(ActionWebService::Base64)
|
||||
@type2binding[ActionWebService::Base64] =
|
||||
SoapBinding.new(self, SOAP::SOAPBase64::Type, ActionWebService::Base64, mapping)
|
||||
@registry.add(Array, SOAP::SOAPArray, SoapTypedArrayFactory.new, nil)
|
||||
@registry.add(::BigDecimal, SOAP::SOAPDouble, SOAP::Mapping::Registry::BasetypeFactory, {:derived_class => true})
|
||||
end
|
||||
end
|
||||
|
||||
class SoapBinding
|
||||
attr :qname
|
||||
attr :type
|
||||
attr :mapping
|
||||
attr :element_binding
|
||||
|
||||
def initialize(marshaler, qname, type, mapping, element_binding=nil)
|
||||
@marshaler = marshaler
|
||||
@qname = qname
|
||||
@type = type
|
||||
@mapping = mapping
|
||||
@element_binding = element_binding
|
||||
end
|
||||
|
||||
def type_name
|
||||
@type.custom? ? @qname.name : nil
|
||||
end
|
||||
|
||||
def qualified_type_name(ns=nil)
|
||||
if @type.custom?
|
||||
"#{ns ? ns : @qname.namespace}:#{@qname.name}"
|
||||
else
|
||||
ns = XSD::NS.new
|
||||
ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
|
||||
ns.assign(SOAP::EncodingNamespace, "soapenc")
|
||||
xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
|
||||
return ns.name(XSD::AnyTypeName) unless xsd_klass
|
||||
ns.name(xsd_klass.const_get('Type'))
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
@qname == other.qname
|
||||
end
|
||||
alias :== :eql?
|
||||
|
||||
def hash
|
||||
@qname.hash
|
||||
end
|
||||
end
|
||||
|
||||
class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
|
||||
def obj2soap(soap_class, obj, info, map)
|
||||
unless obj.is_a?(ActiveRecord::Base)
|
||||
return nil
|
||||
end
|
||||
soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
|
||||
obj.class.columns.each do |column|
|
||||
key = column.name.to_s
|
||||
value = obj.send(key)
|
||||
soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
|
||||
end
|
||||
soap_obj
|
||||
end
|
||||
|
||||
def soap2obj(obj_class, node, info, map)
|
||||
unless node.type == obj_class.instance_variable_get('@qname')
|
||||
return false
|
||||
end
|
||||
obj = obj_class.new
|
||||
node.each do |key, value|
|
||||
obj[key] = value.data
|
||||
end
|
||||
obj.instance_variable_set('@new_record', false)
|
||||
return true, obj
|
||||
end
|
||||
end
|
||||
|
||||
class SoapTypedArrayFactory < SOAP::Mapping::Factory
|
||||
def obj2soap(soap_class, obj, info, map)
|
||||
unless obj.respond_to?(:arytype)
|
||||
return nil
|
||||
end
|
||||
soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
|
||||
mark_marshalled_obj(obj, soap_obj)
|
||||
obj.each do |item|
|
||||
child = SOAP::Mapping._obj2soap(item, map)
|
||||
soap_obj.add(child)
|
||||
end
|
||||
soap_obj
|
||||
end
|
||||
|
||||
def soap2obj(obj_class, node, info, map)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
class SoapBase64Factory < SOAP::Mapping::Factory
|
||||
def obj2soap(soap_class, obj, info, map)
|
||||
unless obj.is_a?(ActionWebService::Base64)
|
||||
return nil
|
||||
end
|
||||
return soap_class.new(obj)
|
||||
end
|
||||
|
||||
def soap2obj(obj_class, node, info, map)
|
||||
unless node.type == SOAP::SOAPBase64::Type
|
||||
return false
|
||||
end
|
||||
return true, obj_class.new(node.string)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,122 +0,0 @@
|
|||
require 'xmlrpc/marshal'
|
||||
require 'action_web_service/client/xmlrpc_client'
|
||||
|
||||
module XMLRPC # :nodoc:
|
||||
class FaultException # :nodoc:
|
||||
alias :message :faultString
|
||||
end
|
||||
|
||||
class Create
|
||||
def wrong_type(value)
|
||||
if BigDecimal === value
|
||||
[true, value.to_f]
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActionWebService # :nodoc:
|
||||
module API # :nodoc:
|
||||
class Base # :nodoc:
|
||||
def self.xmlrpc_client(endpoint_uri, options={})
|
||||
ActionWebService::Client::XmlRpc.new self, endpoint_uri, options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Protocol # :nodoc:
|
||||
module XmlRpc # :nodoc:
|
||||
def self.included(base)
|
||||
base.register_protocol(XmlRpcProtocol)
|
||||
end
|
||||
|
||||
class XmlRpcProtocol < AbstractProtocol # :nodoc:
|
||||
def self.create(controller)
|
||||
XmlRpcProtocol.new
|
||||
end
|
||||
|
||||
def decode_action_pack_request(action_pack_request)
|
||||
service_name = action_pack_request.parameters['action']
|
||||
decode_request(action_pack_request.raw_post, service_name)
|
||||
end
|
||||
|
||||
def decode_request(raw_request, service_name)
|
||||
method_name, params = XMLRPC::Marshal.load_call(raw_request)
|
||||
Request.new(self, method_name, params, service_name)
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
|
||||
def encode_request(method_name, params, param_types)
|
||||
if param_types
|
||||
params = params.dup
|
||||
param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
|
||||
end
|
||||
XMLRPC::Marshal.dump_call(method_name, *params)
|
||||
end
|
||||
|
||||
def decode_response(raw_response)
|
||||
[nil, XMLRPC::Marshal.load_response(raw_response)]
|
||||
end
|
||||
|
||||
def encode_response(method_name, return_value, return_type, protocol_options={})
|
||||
if return_value && return_type
|
||||
return_value = value_to_xmlrpc_wire_format(return_value, return_type)
|
||||
end
|
||||
return_value = false if return_value.nil?
|
||||
raw_response = XMLRPC::Marshal.dump_response(return_value)
|
||||
Response.new(raw_response, 'text/xml', return_value)
|
||||
end
|
||||
|
||||
def encode_multicall_response(responses, protocol_options={})
|
||||
result = responses.map do |return_value, return_type|
|
||||
if return_value && return_type
|
||||
return_value = value_to_xmlrpc_wire_format(return_value, return_type)
|
||||
return_value = [return_value] unless return_value.nil?
|
||||
end
|
||||
return_value = false if return_value.nil?
|
||||
return_value
|
||||
end
|
||||
raw_response = XMLRPC::Marshal.dump_response(result)
|
||||
Response.new(raw_response, 'text/xml', result)
|
||||
end
|
||||
|
||||
def protocol_client(api, protocol_name, endpoint_uri, options={})
|
||||
return nil unless protocol_name == :xmlrpc
|
||||
ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
|
||||
end
|
||||
|
||||
def value_to_xmlrpc_wire_format(value, value_type)
|
||||
if value_type.array?
|
||||
value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
|
||||
else
|
||||
if value.is_a?(ActionWebService::Struct)
|
||||
struct = {}
|
||||
value.class.members.each do |name, type|
|
||||
member_value = value[name]
|
||||
next if member_value.nil?
|
||||
struct[name.to_s] = value_to_xmlrpc_wire_format(member_value, type)
|
||||
end
|
||||
struct
|
||||
elsif value.is_a?(ActiveRecord::Base)
|
||||
struct = {}
|
||||
value.attributes.each do |key, member_value|
|
||||
next if member_value.nil?
|
||||
struct[key.to_s] = member_value
|
||||
end
|
||||
struct
|
||||
elsif value.is_a?(ActionWebService::Base64)
|
||||
XMLRPC::Base64.new(value)
|
||||
elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
|
||||
XMLRPC::FaultException.new(2, value.message)
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,281 +0,0 @@
|
|||
require 'benchmark'
|
||||
require 'pathname'
|
||||
|
||||
module ActionWebService
|
||||
module Scaffolding # :nodoc:
|
||||
class ScaffoldingError < ActionWebServiceError # :nodoc:
|
||||
end
|
||||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Web service invocation scaffolding provides a way to quickly invoke web service methods in a controller. The
|
||||
# generated scaffold actions have default views to let you enter the method parameters and view the
|
||||
# results.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# class ApiController < ActionController
|
||||
# web_service_scaffold :invoke
|
||||
# end
|
||||
#
|
||||
# This example generates an +invoke+ action in the +ApiController+ that you can navigate to from
|
||||
# your browser, select the API method, enter its parameters, and perform the invocation.
|
||||
#
|
||||
# If you want to customize the default views, create the following views in "app/views":
|
||||
#
|
||||
# * <tt>action_name/methods.html.erb</tt>
|
||||
# * <tt>action_name/parameters.html.erb</tt>
|
||||
# * <tt>action_name/result.html.erb</tt>
|
||||
# * <tt>action_name/layout.html.erb</tt>
|
||||
#
|
||||
# Where <tt>action_name</tt> is the name of the action you gave to ClassMethods#web_service_scaffold.
|
||||
#
|
||||
# You can use the default views in <tt>RAILS_DIR/lib/action_web_service/templates/scaffolds</tt> as
|
||||
# a guide.
|
||||
module ClassMethods
|
||||
# Generates web service invocation scaffolding for the current controller. The given action name
|
||||
# can then be used as the entry point for invoking API methods from a web browser.
|
||||
def web_service_scaffold(action_name)
|
||||
add_template_helper(Helpers)
|
||||
module_eval <<-"end_eval", __FILE__, __LINE__ + 1
|
||||
def #{action_name}
|
||||
if request.method == :get
|
||||
setup_invocation_assigns
|
||||
render_invocation_scaffold 'methods'
|
||||
end
|
||||
end
|
||||
|
||||
def #{action_name}_method_params
|
||||
if request.method == :get
|
||||
setup_invocation_assigns
|
||||
render_invocation_scaffold 'parameters'
|
||||
end
|
||||
end
|
||||
|
||||
def #{action_name}_submit
|
||||
if request.method == :post
|
||||
setup_invocation_assigns
|
||||
protocol_name = params['protocol'] ? params['protocol'].to_sym : :soap
|
||||
case protocol_name
|
||||
when :soap
|
||||
@protocol = Protocol::Soap::SoapProtocol.create(self)
|
||||
when :xmlrpc
|
||||
@protocol = Protocol::XmlRpc::XmlRpcProtocol.create(self)
|
||||
end
|
||||
bm = Benchmark.measure do
|
||||
@protocol.register_api(@scaffold_service.api)
|
||||
post_params = params['method_params'] ? params['method_params'].dup : nil
|
||||
params = []
|
||||
@scaffold_method.expects.each_with_index do |spec, i|
|
||||
params << post_params[i.to_s]
|
||||
end if @scaffold_method.expects
|
||||
params = @scaffold_method.cast_expects(params)
|
||||
method_name = public_method_name(@scaffold_service.name, @scaffold_method.public_name)
|
||||
@method_request_xml = @protocol.encode_request(method_name, params, @scaffold_method.expects)
|
||||
new_request = @protocol.encode_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
|
||||
prepare_request(new_request, @scaffold_service.name, @scaffold_method.public_name)
|
||||
self.request = new_request
|
||||
if @scaffold_container.dispatching_mode != :direct
|
||||
request.parameters['action'] = @scaffold_service.name
|
||||
end
|
||||
dispatch_web_service_request
|
||||
@method_response_xml = response.body
|
||||
method_name, obj = @protocol.decode_response(@method_response_xml)
|
||||
return if handle_invocation_exception(obj)
|
||||
@method_return_value = @scaffold_method.cast_returns(obj)
|
||||
end
|
||||
@method_elapsed = bm.real
|
||||
reset_invocation_response
|
||||
render_invocation_scaffold 'result'
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def setup_invocation_assigns
|
||||
@scaffold_class = self.class
|
||||
@scaffold_action_name = "#{action_name}"
|
||||
@scaffold_container = WebServiceModel::Container.new(self)
|
||||
if params['service'] && params['method']
|
||||
@scaffold_service = @scaffold_container.services.find{ |x| x.name == params['service'] }
|
||||
@scaffold_method = @scaffold_service.api_methods[params['method']]
|
||||
end
|
||||
end
|
||||
|
||||
def render_invocation_scaffold(action)
|
||||
customized_template = "\#{self.class.controller_path}/#{action_name}/\#{action}"
|
||||
default_template = scaffold_path(action)
|
||||
begin
|
||||
content = @template.render(:file => customized_template)
|
||||
rescue ActionView::MissingTemplate
|
||||
content = @template.render(:file => default_template)
|
||||
end
|
||||
@template.instance_variable_set("@content_for_layout", content)
|
||||
if self.active_layout.nil?
|
||||
render :file => scaffold_path("layout")
|
||||
else
|
||||
render :file => self.active_layout, :use_full_path => true
|
||||
end
|
||||
end
|
||||
|
||||
def scaffold_path(template_name)
|
||||
File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".html.erb"
|
||||
end
|
||||
|
||||
def reset_invocation_response
|
||||
erase_render_results
|
||||
response.instance_variable_set :@header, Rack::Utils::HeaderHash.new(::ActionController::Response::DEFAULT_HEADERS.merge("cookie" => []))
|
||||
end
|
||||
|
||||
def public_method_name(service_name, method_name)
|
||||
if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
|
||||
service_name + '.' + method_name
|
||||
else
|
||||
method_name
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_request(new_request, service_name, method_name)
|
||||
new_request.parameters.update(request.parameters)
|
||||
request.env.each{ |k, v| new_request.env[k] = v unless new_request.env.has_key?(k) }
|
||||
if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
|
||||
new_request.env['HTTP_SOAPACTION'] = "/\#{controller_name()}/\#{service_name}/\#{method_name}"
|
||||
end
|
||||
end
|
||||
|
||||
def handle_invocation_exception(obj)
|
||||
exception = nil
|
||||
if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
|
||||
exception = obj.detail.cause
|
||||
elsif obj.is_a?(XMLRPC::FaultException)
|
||||
exception = obj
|
||||
end
|
||||
return unless exception
|
||||
reset_invocation_response
|
||||
rescue_action(exception)
|
||||
true
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
end
|
||||
|
||||
module Helpers # :nodoc:
|
||||
def method_parameter_input_fields(method, type, field_name_base, idx, was_structured=false)
|
||||
if type.array?
|
||||
return content_tag('em', "Typed array input fields not supported yet (#{type.name})")
|
||||
end
|
||||
if type.structured?
|
||||
return content_tag('em', "Nested structural types not supported yet (#{type.name})") if was_structured
|
||||
parameters = ""
|
||||
type.each_member do |member_name, member_type|
|
||||
label = method_parameter_label(member_name, member_type)
|
||||
nested_content = method_parameter_input_fields(
|
||||
method,
|
||||
member_type,
|
||||
"#{field_name_base}[#{idx}][#{member_name}]",
|
||||
idx,
|
||||
true)
|
||||
if member_type.custom?
|
||||
parameters << content_tag('li', label)
|
||||
parameters << content_tag('ul', nested_content)
|
||||
else
|
||||
parameters << content_tag('li', label + ' ' + nested_content)
|
||||
end
|
||||
end
|
||||
content_tag('ul', parameters)
|
||||
else
|
||||
# If the data source was structured previously we already have the index set
|
||||
field_name_base = "#{field_name_base}[#{idx}]" unless was_structured
|
||||
|
||||
case type.type
|
||||
when :int
|
||||
text_field_tag "#{field_name_base}"
|
||||
when :string
|
||||
text_field_tag "#{field_name_base}"
|
||||
when :base64
|
||||
text_area_tag "#{field_name_base}", nil, :size => "40x5"
|
||||
when :bool
|
||||
radio_button_tag("#{field_name_base}", "true") + " True" +
|
||||
radio_button_tag("#{field_name_base}", "false") + "False"
|
||||
when :float
|
||||
text_field_tag "#{field_name_base}"
|
||||
when :time, :datetime
|
||||
time = Time.now
|
||||
i = 0
|
||||
%w|year month day hour minute second|.map do |name|
|
||||
i += 1
|
||||
send("select_#{name}", time, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
|
||||
end.join
|
||||
when :date
|
||||
date = Date.today
|
||||
i = 0
|
||||
%w|year month day|.map do |name|
|
||||
i += 1
|
||||
send("select_#{name}", date, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
|
||||
end.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def method_parameter_label(name, type)
|
||||
name.to_s.capitalize + ' (' + type.human_name(false) + ')'
|
||||
end
|
||||
|
||||
def service_method_list(service)
|
||||
action = @scaffold_action_name + '_method_params'
|
||||
methods = service.api_methods_full.map do |desc, name|
|
||||
content_tag("li", link_to(desc, :action => action, :service => service.name, :method => name))
|
||||
end
|
||||
content_tag("ul", methods.join("\n"))
|
||||
end
|
||||
end
|
||||
|
||||
module WebServiceModel # :nodoc:
|
||||
class Container # :nodoc:
|
||||
attr :services
|
||||
attr :dispatching_mode
|
||||
|
||||
def initialize(real_container)
|
||||
@real_container = real_container
|
||||
@dispatching_mode = @real_container.class.web_service_dispatching_mode
|
||||
@services = []
|
||||
if @dispatching_mode == :direct
|
||||
@services << Service.new(@real_container.controller_name, @real_container)
|
||||
else
|
||||
@real_container.class.web_services.each do |name, obj|
|
||||
@services << Service.new(name, @real_container.instance_eval{ web_service_object(name) })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Service # :nodoc:
|
||||
attr :name
|
||||
attr :object
|
||||
attr :api
|
||||
attr :api_methods
|
||||
attr :api_methods_full
|
||||
|
||||
def initialize(name, real_service)
|
||||
@name = name.to_s
|
||||
@object = real_service
|
||||
@api = @object.class.web_service_api
|
||||
if @api.nil?
|
||||
raise ScaffoldingError, "No web service API attached to #{object.class}"
|
||||
end
|
||||
@api_methods = {}
|
||||
@api_methods_full = []
|
||||
@api.api_methods.each do |name, method|
|
||||
@api_methods[method.public_name.to_s] = method
|
||||
@api_methods_full << [method.to_s, method.public_name.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
self.name.camelize
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,64 +0,0 @@
|
|||
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 in method
|
||||
# signatures.
|
||||
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
|
||||
|
||||
# Iterates through each member
|
||||
def each_pair(&block)
|
||||
self.class.members.each do |name, type|
|
||||
yield name, self.__send__(name)
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
# Creates a structure member with the specified +name+ and +type+. Generates
|
||||
# accessor methods for reading and writing the member value.
|
||||
def member(name, type)
|
||||
name = name.to_sym
|
||||
type = ActionWebService::SignatureTypes.canonical_signature_entry({ name => type }, 0)
|
||||
write_inheritable_hash("struct_members", name => 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
|
||||
|
||||
def member_type(name) # :nodoc:
|
||||
members[name.to_sym]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -1,226 +0,0 @@
|
|||
module ActionWebService # :nodoc:
|
||||
# Action Web Service supports the following base types in a signature:
|
||||
#
|
||||
# [<tt>:int</tt>] Represents an integer value, will be cast to an integer using <tt>Integer(value)</tt>
|
||||
# [<tt>:string</tt>] Represents a string value, will be cast to an string using the <tt>to_s</tt> method on an object
|
||||
# [<tt>:base64</tt>] Represents a Base 64 value, will contain the binary bytes of a Base 64 value sent by the caller
|
||||
# [<tt>:bool</tt>] Represents a boolean value, whatever is passed will be cast to boolean (<tt>true</tt>, '1', 'true', 'y', 'yes' are taken to represent true; <tt>false</tt>, '0', 'false', 'n', 'no' and <tt>nil</tt> represent false)
|
||||
# [<tt>:float</tt>] Represents a floating point value, will be cast to a float using <tt>Float(value)</tt>
|
||||
# [<tt>:time</tt>] Represents a timestamp, will be cast to a <tt>Time</tt> object
|
||||
# [<tt>:datetime</tt>] Represents a timestamp, will be cast to a <tt>DateTime</tt> object
|
||||
# [<tt>:date</tt>] Represents a date, will be cast to a <tt>Date</tt> object
|
||||
#
|
||||
# For structured types, you'll need to pass in the Class objects of
|
||||
# ActionWebService::Struct and ActiveRecord::Base derivatives.
|
||||
module SignatureTypes
|
||||
def canonical_signature(signature) # :nodoc:
|
||||
return nil if signature.nil?
|
||||
unless signature.is_a?(Array)
|
||||
raise(ActionWebServiceError, "Expected signature to be an Array")
|
||||
end
|
||||
i = -1
|
||||
signature.map{ |spec| canonical_signature_entry(spec, i += 1) }
|
||||
end
|
||||
|
||||
def canonical_signature_entry(spec, i) # :nodoc:
|
||||
orig_spec = spec
|
||||
name = "param#{i}"
|
||||
if spec.is_a?(Hash)
|
||||
name, spec = spec.keys.first, spec.values.first
|
||||
end
|
||||
type = spec
|
||||
if spec.is_a?(Array)
|
||||
ArrayType.new(orig_spec, canonical_signature_entry(spec[0], 0), name)
|
||||
else
|
||||
type = canonical_type(type)
|
||||
if type.is_a?(Symbol)
|
||||
BaseType.new(orig_spec, type, name)
|
||||
else
|
||||
StructuredType.new(orig_spec, type, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def canonical_type(type) # :nodoc:
|
||||
type_name = symbol_name(type) || class_to_type_name(type)
|
||||
type = type_name || type
|
||||
return canonical_type_name(type) if type.is_a?(Symbol)
|
||||
type
|
||||
end
|
||||
|
||||
def canonical_type_name(name) # :nodoc:
|
||||
name = name.to_sym
|
||||
case name
|
||||
when :int, :integer, :fixnum, :bignum
|
||||
:int
|
||||
when :string, :text
|
||||
:string
|
||||
when :base64, :binary
|
||||
:base64
|
||||
when :bool, :boolean
|
||||
:bool
|
||||
when :float, :double
|
||||
:float
|
||||
when :decimal
|
||||
:decimal
|
||||
when :time, :timestamp
|
||||
:time
|
||||
when :datetime
|
||||
:datetime
|
||||
when :date
|
||||
:date
|
||||
else
|
||||
raise(TypeError, "#{name} is not a valid base type")
|
||||
end
|
||||
end
|
||||
|
||||
def canonical_type_class(type) # :nodoc:
|
||||
type = canonical_type(type)
|
||||
type.is_a?(Symbol) ? type_name_to_class(type) : type
|
||||
end
|
||||
|
||||
def symbol_name(name) # :nodoc:
|
||||
return name.to_sym if name.is_a?(Symbol) || name.is_a?(String)
|
||||
nil
|
||||
end
|
||||
|
||||
def class_to_type_name(klass) # :nodoc:
|
||||
klass = klass.class unless klass.is_a?(Class)
|
||||
if derived_from?(Integer, klass) || derived_from?(Fixnum, klass) || derived_from?(Bignum, klass)
|
||||
:int
|
||||
elsif klass == String
|
||||
:string
|
||||
elsif klass == Base64
|
||||
:base64
|
||||
elsif klass == TrueClass || klass == FalseClass
|
||||
:bool
|
||||
elsif derived_from?(Float, klass) || derived_from?(Precision, klass) || derived_from?(Numeric, klass)
|
||||
:float
|
||||
elsif klass == Time
|
||||
:time
|
||||
elsif klass == DateTime
|
||||
:datetime
|
||||
elsif klass == Date
|
||||
:date
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def type_name_to_class(name) # :nodoc:
|
||||
case canonical_type_name(name)
|
||||
when :int
|
||||
Integer
|
||||
when :string
|
||||
String
|
||||
when :base64
|
||||
Base64
|
||||
when :bool
|
||||
TrueClass
|
||||
when :float
|
||||
Float
|
||||
when :decimal
|
||||
BigDecimal
|
||||
when :time
|
||||
Time
|
||||
when :date
|
||||
Date
|
||||
when :datetime
|
||||
DateTime
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def derived_from?(ancestor, child) # :nodoc:
|
||||
child.ancestors.include?(ancestor)
|
||||
end
|
||||
|
||||
module_function :type_name_to_class
|
||||
module_function :class_to_type_name
|
||||
module_function :symbol_name
|
||||
module_function :canonical_type_class
|
||||
module_function :canonical_type_name
|
||||
module_function :canonical_type
|
||||
module_function :canonical_signature_entry
|
||||
module_function :canonical_signature
|
||||
module_function :derived_from?
|
||||
end
|
||||
|
||||
class BaseType # :nodoc:
|
||||
include SignatureTypes
|
||||
|
||||
attr :spec
|
||||
attr :type
|
||||
attr :type_class
|
||||
attr :name
|
||||
|
||||
def initialize(spec, type, name)
|
||||
@spec = spec
|
||||
@type = canonical_type(type)
|
||||
@type_class = canonical_type_class(@type)
|
||||
@name = name
|
||||
end
|
||||
|
||||
def custom?
|
||||
false
|
||||
end
|
||||
|
||||
def array?
|
||||
false
|
||||
end
|
||||
|
||||
def structured?
|
||||
false
|
||||
end
|
||||
|
||||
def human_name(show_name=true)
|
||||
type_type = array? ? element_type.type.to_s : self.type.to_s
|
||||
str = array? ? (type_type + '[]') : type_type
|
||||
show_name ? (str + " " + name.to_s) : str
|
||||
end
|
||||
end
|
||||
|
||||
class ArrayType < BaseType # :nodoc:
|
||||
attr :element_type
|
||||
|
||||
def initialize(spec, element_type, name)
|
||||
super(spec, Array, name)
|
||||
@element_type = element_type
|
||||
end
|
||||
|
||||
def custom?
|
||||
true
|
||||
end
|
||||
|
||||
def array?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class StructuredType < BaseType # :nodoc:
|
||||
def each_member
|
||||
if @type_class.respond_to?(:members)
|
||||
@type_class.members.each do |name, type|
|
||||
yield name, type
|
||||
end
|
||||
elsif @type_class.respond_to?(:columns)
|
||||
i = -1
|
||||
@type_class.columns.each do |column|
|
||||
yield column.name, canonical_signature_entry(column.type, i += 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def custom?
|
||||
true
|
||||
end
|
||||
|
||||
def structured?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class Base64 < String # :nodoc:
|
||||
end
|
||||
end
|
|
@ -1,65 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title><%= @scaffold_class.wsdl_service_name %> Web Service</title>
|
||||
<style>
|
||||
body { background-color: #fff; color: #333; }
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
a { color: #000; }
|
||||
a:visited { color: #666; }
|
||||
a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
.fieldWithErrors {
|
||||
padding: 2px;
|
||||
background-color: red;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#errorExplanation {
|
||||
width: 400px;
|
||||
border: 2px solid red;
|
||||
padding: 7px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#errorExplanation h2 {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
padding: 5px 5px 5px 15px;
|
||||
font-size: 12px;
|
||||
margin: -7px;
|
||||
background-color: #c00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#errorExplanation p {
|
||||
color: #333;
|
||||
margin-bottom: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#errorExplanation ul li {
|
||||
font-size: 12px;
|
||||
list-style: square;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<%= @content_for_layout %>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +0,0 @@
|
|||
<% @scaffold_container.services.each do |service| %>
|
||||
|
||||
<h4>API Methods for <%= service %></h4>
|
||||
<%= service_method_list(service) %>
|
||||
|
||||
<% end %>
|
|
@ -1,29 +0,0 @@
|
|||
<h4>Method Invocation Details for <em><%= @scaffold_service %>#<%= @scaffold_method.public_name %></em></h4>
|
||||
|
||||
<% form_tag(:action => @scaffold_action_name + '_submit') do -%>
|
||||
<%= hidden_field_tag "service", @scaffold_service.name %>
|
||||
<%= hidden_field_tag "method", @scaffold_method.public_name %>
|
||||
|
||||
<p>
|
||||
<label for="protocol">Protocol:</label><br />
|
||||
<%= select_tag 'protocol', options_for_select([['SOAP', 'soap'], ['XML-RPC', 'xmlrpc']], params['protocol']) %>
|
||||
</p>
|
||||
|
||||
<% if @scaffold_method.expects %>
|
||||
|
||||
<strong>Method Parameters:</strong><br />
|
||||
<% @scaffold_method.expects.each_with_index do |type, i| %>
|
||||
<p>
|
||||
<label for="method_params[<%= i %>]"><%= method_parameter_label(type.name, type) %> </label><br />
|
||||
<%= method_parameter_input_fields(@scaffold_method, type, "method_params", i) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
|
||||
<%= submit_tag "Invoke" %>
|
||||
<% end -%>
|
||||
|
||||
<p>
|
||||
<%= link_to "Back", :action => @scaffold_action_name %>
|
||||
</p>
|
|
@ -1,30 +0,0 @@
|
|||
<h4>Method Invocation Result for <em><%= @scaffold_service %>#<%= @scaffold_method.public_name %></em></h4>
|
||||
|
||||
<p>
|
||||
Invocation took <tt><%= '%f' % @method_elapsed %></tt> seconds
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Return Value:</strong><br />
|
||||
<pre>
|
||||
<%= h @method_return_value.inspect %>
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Request XML:</strong><br />
|
||||
<pre>
|
||||
<%= h @method_request_xml %>
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Response XML:</strong><br />
|
||||
<pre>
|
||||
<%= h @method_response_xml %>
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= link_to "Back", :action => @scaffold_action_name + '_method_params', :method => @scaffold_method.public_name, :service => @scaffold_service.name %>
|
||||
</p>
|
|
@ -1,110 +0,0 @@
|
|||
require 'test/unit'
|
||||
|
||||
module Test # :nodoc:
|
||||
module Unit # :nodoc:
|
||||
class TestCase # :nodoc:
|
||||
private
|
||||
# invoke the specified API method
|
||||
def invoke_direct(method_name, *args)
|
||||
prepare_request('api', 'api', method_name, *args)
|
||||
@controller.process(@request, @response)
|
||||
decode_rpc_response
|
||||
end
|
||||
alias_method :invoke, :invoke_direct
|
||||
|
||||
# invoke the specified API method on the specified service
|
||||
def invoke_delegated(service_name, method_name, *args)
|
||||
prepare_request(service_name.to_s, service_name, method_name, *args)
|
||||
@controller.process(@request, @response)
|
||||
decode_rpc_response
|
||||
end
|
||||
|
||||
# invoke the specified layered API method on the correct service
|
||||
def invoke_layered(service_name, method_name, *args)
|
||||
prepare_request('api', service_name, method_name, *args)
|
||||
@controller.process(@request, @response)
|
||||
decode_rpc_response
|
||||
end
|
||||
|
||||
# ---------------------- internal ---------------------------
|
||||
|
||||
def prepare_request(action, service_name, api_method_name, *args)
|
||||
@request.recycle!
|
||||
@request.request_parameters['action'] = action
|
||||
@request.env['REQUEST_METHOD'] = 'POST'
|
||||
@request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
||||
@request.env['RAW_POST_DATA'] = encode_rpc_call(service_name, api_method_name, *args)
|
||||
case protocol
|
||||
when ActionWebService::Protocol::Soap::SoapProtocol
|
||||
soap_action = "/#{@controller.controller_name}/#{service_name}/#{public_method_name(service_name, api_method_name)}"
|
||||
@request.env['HTTP_SOAPACTION'] = soap_action
|
||||
when ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
|
||||
@request.env.delete('HTTP_SOAPACTION')
|
||||
end
|
||||
end
|
||||
|
||||
def encode_rpc_call(service_name, api_method_name, *args)
|
||||
case @controller.web_service_dispatching_mode
|
||||
when :direct
|
||||
api = @controller.class.web_service_api
|
||||
when :delegated, :layered
|
||||
api = @controller.web_service_object(service_name.to_sym).class.web_service_api
|
||||
end
|
||||
protocol.register_api(api)
|
||||
method = api.api_methods[api_method_name.to_sym]
|
||||
raise ArgumentError, "wrong number of arguments for rpc call (#{args.length} for #{method.expects.length})" if method && method.expects && args.length != method.expects.length
|
||||
protocol.encode_request(public_method_name(service_name, api_method_name), args.dup, method.expects)
|
||||
end
|
||||
|
||||
def decode_rpc_response
|
||||
public_method_name, return_value = protocol.decode_response(@response.body)
|
||||
exception = is_exception?(return_value)
|
||||
raise exception if exception
|
||||
return_value
|
||||
end
|
||||
|
||||
def public_method_name(service_name, api_method_name)
|
||||
public_name = service_api(service_name).public_api_method_name(api_method_name)
|
||||
if @controller.web_service_dispatching_mode == :layered && protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
|
||||
'%s.%s' % [service_name.to_s, public_name]
|
||||
else
|
||||
public_name
|
||||
end
|
||||
end
|
||||
|
||||
def service_api(service_name)
|
||||
case @controller.web_service_dispatching_mode
|
||||
when :direct
|
||||
@controller.class.web_service_api
|
||||
when :delegated, :layered
|
||||
@controller.web_service_object(service_name.to_sym).class.web_service_api
|
||||
end
|
||||
end
|
||||
|
||||
def protocol
|
||||
if @protocol.nil?
|
||||
@protocol ||= ActionWebService::Protocol::Soap::SoapProtocol.create(@controller)
|
||||
else
|
||||
case @protocol
|
||||
when :xmlrpc
|
||||
@protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.create(@controller)
|
||||
when :soap
|
||||
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.create(@controller)
|
||||
else
|
||||
@protocol
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
case protocol
|
||||
when :soap, ActionWebService::Protocol::Soap::SoapProtocol
|
||||
(obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \
|
||||
obj.detail.cause.is_a?(Exception)) ? obj.detail.cause : nil
|
||||
when :xmlrpc, ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
|
||||
obj.is_a?(XMLRPC::FaultException) ? obj : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module ActionWebService
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 11
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
|
@ -1 +0,0 @@
|
|||
require 'action_web_service'
|
1379
vendor/gems/datanoise-actionwebservice-2.3.2/setup.rb
vendored
1379
vendor/gems/datanoise-actionwebservice-2.3.2/setup.rb
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,183 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
require 'webrick'
|
||||
require 'webrick/log'
|
||||
require 'singleton'
|
||||
|
||||
module ClientTest
|
||||
class Person < ActionWebService::Struct
|
||||
member :firstnames, [:string]
|
||||
member :lastname, :string
|
||||
|
||||
def ==(other)
|
||||
firstnames == other.firstnames && lastname == other.lastname
|
||||
end
|
||||
end
|
||||
|
||||
class Inner < ActionWebService::Struct
|
||||
member :name, :string
|
||||
end
|
||||
|
||||
class Outer < ActionWebService::Struct
|
||||
member :name, :string
|
||||
member :inner, Inner
|
||||
end
|
||||
|
||||
class User < ActiveRecord::Base
|
||||
end
|
||||
|
||||
module Accounting
|
||||
class User < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
class WithModel < ActionWebService::Struct
|
||||
member :user, User
|
||||
member :users, [User]
|
||||
end
|
||||
|
||||
class WithMultiDimArray < ActionWebService::Struct
|
||||
member :pref, [[:string]]
|
||||
end
|
||||
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :void
|
||||
api_method :normal, :expects => [:int, :int], :returns => [:int]
|
||||
api_method :array_return, :returns => [[Person]]
|
||||
api_method :struct_pass, :expects => [[Person]], :returns => [:bool]
|
||||
api_method :nil_struct_return, :returns => [Person]
|
||||
api_method :inner_nil, :returns => [Outer]
|
||||
api_method :client_container, :returns => [:int]
|
||||
api_method :named_parameters, :expects => [{:key=>:string}, {:id=>:int}]
|
||||
api_method :thrower
|
||||
api_method :user_return, :returns => [User]
|
||||
api_method :with_model_return, :returns => [WithModel]
|
||||
api_method :scoped_model_return, :returns => [Accounting::User]
|
||||
api_method :multi_dim_return, :returns => [WithMultiDimArray]
|
||||
end
|
||||
|
||||
class NullLogOut
|
||||
def <<(*args); end
|
||||
end
|
||||
|
||||
class Container < ActionController::Base
|
||||
web_service_api API
|
||||
|
||||
attr_accessor :value_void
|
||||
attr_accessor :value_normal
|
||||
attr_accessor :value_array_return
|
||||
attr_accessor :value_struct_pass
|
||||
attr_accessor :value_named_parameters
|
||||
|
||||
def initialize
|
||||
@value_void = nil
|
||||
@value_normal = nil
|
||||
@value_array_return = nil
|
||||
@value_struct_pass = nil
|
||||
@value_named_parameters = nil
|
||||
end
|
||||
|
||||
def void
|
||||
@value_void = @method_params
|
||||
end
|
||||
|
||||
def normal
|
||||
@value_normal = @method_params
|
||||
5
|
||||
end
|
||||
|
||||
def array_return
|
||||
person = Person.new
|
||||
person.firstnames = ["one", "two"]
|
||||
person.lastname = "last"
|
||||
@value_array_return = [person]
|
||||
end
|
||||
|
||||
def struct_pass
|
||||
@value_struct_pass = @method_params
|
||||
true
|
||||
end
|
||||
|
||||
def nil_struct_return
|
||||
nil
|
||||
end
|
||||
|
||||
def inner_nil
|
||||
Outer.new :name => 'outer', :inner => nil
|
||||
end
|
||||
|
||||
def client_container
|
||||
50
|
||||
end
|
||||
|
||||
def named_parameters
|
||||
@value_named_parameters = @method_params
|
||||
end
|
||||
|
||||
def thrower
|
||||
raise "Hi"
|
||||
end
|
||||
|
||||
def user_return
|
||||
User.find(1)
|
||||
end
|
||||
|
||||
def with_model_return
|
||||
WithModel.new :user => User.find(1), :users => User.find(:all)
|
||||
end
|
||||
|
||||
def scoped_model_return
|
||||
Accounting::User.find(1)
|
||||
end
|
||||
|
||||
def multi_dim_return
|
||||
WithMultiDimArray.new :pref => [%w{pref1 value1}, %w{pref2 value2}]
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractClientLet < WEBrick::HTTPServlet::AbstractServlet
|
||||
def initialize(controller)
|
||||
@controller = controller
|
||||
end
|
||||
|
||||
def get_instance(*args)
|
||||
self
|
||||
end
|
||||
|
||||
def require_path_info?
|
||||
false
|
||||
end
|
||||
|
||||
def do_GET(req, res)
|
||||
raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed."
|
||||
end
|
||||
|
||||
def do_POST(req, res)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractServer
|
||||
include ClientTest
|
||||
include Singleton
|
||||
attr :container
|
||||
def initialize
|
||||
@container = Container.new
|
||||
@clientlet = create_clientlet(@container)
|
||||
log = WEBrick::BasicLog.new(NullLogOut.new)
|
||||
@server = WEBrick::HTTPServer.new(:Port => server_port, :Logger => log, :AccessLog => [])
|
||||
@server.mount('/', @clientlet)
|
||||
@thr = Thread.new { @server.start }
|
||||
until @server.status == :Running; end
|
||||
at_exit { @server.stop; @thr.join }
|
||||
end
|
||||
|
||||
protected
|
||||
def create_clientlet
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def server_port
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,548 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
require 'stringio'
|
||||
|
||||
class ActionController::Base; def rescue_action(e) raise e end; end
|
||||
|
||||
module DispatcherTest
|
||||
Utf8String = "One World Caf\303\251"
|
||||
WsdlNamespace = 'http://rubyonrails.com/some/namespace'
|
||||
|
||||
class Node < ActiveRecord::Base
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
@new_record = false
|
||||
end
|
||||
|
||||
class << self
|
||||
def name
|
||||
"DispatcherTest::Node"
|
||||
end
|
||||
|
||||
def columns(*args)
|
||||
[
|
||||
ActiveRecord::ConnectionAdapters::Column.new('id', 0, 'int'),
|
||||
ActiveRecord::ConnectionAdapters::Column.new('name', nil, 'string'),
|
||||
ActiveRecord::ConnectionAdapters::Column.new('description', nil, 'string'),
|
||||
]
|
||||
end
|
||||
|
||||
def connection
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Person < ActionWebService::Struct
|
||||
member :id, :int
|
||||
member :name, :string
|
||||
|
||||
def ==(other)
|
||||
self.id == other.id && self.name == other.name
|
||||
end
|
||||
end
|
||||
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :add, :expects => [:int, :int], :returns => [:int]
|
||||
api_method :interceptee
|
||||
api_method :struct_return, :returns => [[Node]]
|
||||
api_method :void
|
||||
end
|
||||
|
||||
class DirectAPI < ActionWebService::API::Base
|
||||
api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int]
|
||||
api_method :add2, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int]
|
||||
api_method :before_filtered
|
||||
api_method :after_filtered, :returns => [[:int]]
|
||||
api_method :struct_return, :returns => [[Node]]
|
||||
api_method :struct_pass, :expects => [{:person => Person}]
|
||||
api_method :base_struct_return, :returns => [[Person]]
|
||||
api_method :hash_struct_return, :returns => [[Person]]
|
||||
api_method :thrower
|
||||
api_method :void
|
||||
api_method :test_utf8, :returns => [:string]
|
||||
api_method :hex, :expects => [:base64], :returns => [:string]
|
||||
api_method :unhex, :expects => [:string], :returns => [:base64]
|
||||
api_method :time, :expects => [:time], :returns => [:time]
|
||||
end
|
||||
|
||||
class VirtualAPI < ActionWebService::API::Base
|
||||
default_api_method :fallback
|
||||
end
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
before_invocation :do_intercept, :only => [:interceptee]
|
||||
|
||||
attr :added
|
||||
attr :intercepted
|
||||
attr :void_called
|
||||
|
||||
def initialize
|
||||
@void_called = false
|
||||
end
|
||||
|
||||
def add(a, b)
|
||||
@added = a + b
|
||||
end
|
||||
|
||||
def interceptee
|
||||
@intercepted = false
|
||||
end
|
||||
|
||||
def struct_return
|
||||
n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1')
|
||||
n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2')
|
||||
[n1, n2]
|
||||
end
|
||||
|
||||
def void(*args)
|
||||
@void_called = args
|
||||
end
|
||||
|
||||
def do_intercept(name, args)
|
||||
[false, "permission denied"]
|
||||
end
|
||||
end
|
||||
|
||||
class MTAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
api_method :getCategories, :returns => [[:string]]
|
||||
api_method :bool, :returns => [:bool]
|
||||
api_method :alwaysFail
|
||||
api_method :person, :returns => [Person]
|
||||
end
|
||||
|
||||
class BloggerAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
api_method :getCategories, :returns => [[:string]]
|
||||
api_method :str, :expects => [:int], :returns => [:string]
|
||||
api_method :alwaysFail
|
||||
end
|
||||
|
||||
class MTService < ActionWebService::Base
|
||||
web_service_api MTAPI
|
||||
|
||||
def getCategories
|
||||
["mtCat1", "mtCat2"]
|
||||
end
|
||||
|
||||
def bool
|
||||
'y'
|
||||
end
|
||||
|
||||
def alwaysFail
|
||||
raise "MT AlwaysFail"
|
||||
end
|
||||
|
||||
def person
|
||||
Person.new('id' => 1, 'name' => 'person1')
|
||||
end
|
||||
end
|
||||
|
||||
class BloggerService < ActionWebService::Base
|
||||
web_service_api BloggerAPI
|
||||
|
||||
def getCategories
|
||||
["bloggerCat1", "bloggerCat2"]
|
||||
end
|
||||
|
||||
def str(int)
|
||||
unless int.is_a?(Integer)
|
||||
raise "Not an integer!"
|
||||
end
|
||||
500 + int
|
||||
end
|
||||
|
||||
def alwaysFail
|
||||
raise "Blogger AlwaysFail"
|
||||
end
|
||||
end
|
||||
|
||||
class AbstractController < ActionController::Base
|
||||
def generate_wsdl
|
||||
self.request ||= ::ActionController::TestRequest.new
|
||||
to_wsdl
|
||||
end
|
||||
end
|
||||
|
||||
class DelegatedController < AbstractController
|
||||
web_service_dispatching_mode :delegated
|
||||
wsdl_namespace WsdlNamespace
|
||||
|
||||
web_service(:test_service) { @service ||= Service.new; @service }
|
||||
end
|
||||
|
||||
class LayeredController < AbstractController
|
||||
web_service_dispatching_mode :layered
|
||||
wsdl_namespace WsdlNamespace
|
||||
|
||||
web_service(:mt) { @mt_service ||= MTService.new; @mt_service }
|
||||
web_service(:blogger) { @blogger_service ||= BloggerService.new; @blogger_service }
|
||||
end
|
||||
|
||||
class DirectController < AbstractController
|
||||
web_service_api DirectAPI
|
||||
web_service_dispatching_mode :direct
|
||||
wsdl_namespace WsdlNamespace
|
||||
|
||||
before_invocation :alwaysfail, :only => [:before_filtered]
|
||||
after_invocation :alwaysok, :only => [:after_filtered]
|
||||
|
||||
attr :added
|
||||
attr :added2
|
||||
attr :before_filter_called
|
||||
attr :before_filter_target_called
|
||||
attr :after_filter_called
|
||||
attr :after_filter_target_called
|
||||
attr :void_called
|
||||
attr :struct_pass_value
|
||||
|
||||
def initialize
|
||||
@before_filter_called = false
|
||||
@before_filter_target_called = false
|
||||
@after_filter_called = false
|
||||
@after_filter_target_called = false
|
||||
@void_called = false
|
||||
@struct_pass_value = false
|
||||
end
|
||||
|
||||
def add
|
||||
@added = params['a'] + params['b']
|
||||
end
|
||||
|
||||
def add2(a, b)
|
||||
@added2 = a + b
|
||||
end
|
||||
|
||||
def before_filtered
|
||||
@before_filter_target_called = true
|
||||
end
|
||||
|
||||
def after_filtered
|
||||
@after_filter_target_called = true
|
||||
[5, 6, 7]
|
||||
end
|
||||
|
||||
def thrower
|
||||
raise "Hi, I'm an exception"
|
||||
end
|
||||
|
||||
def struct_return
|
||||
n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1')
|
||||
n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2')
|
||||
[n1, n2]
|
||||
end
|
||||
|
||||
def struct_pass(person)
|
||||
@struct_pass_value = person
|
||||
end
|
||||
|
||||
def base_struct_return
|
||||
p1 = Person.new('id' => 1, 'name' => 'person1')
|
||||
p2 = Person.new('id' => 2, 'name' => 'person2')
|
||||
[p1, p2]
|
||||
end
|
||||
|
||||
def hash_struct_return
|
||||
p1 = { :id => '1', 'name' => 'test' }
|
||||
p2 = { 'id' => '2', :name => 'person2' }
|
||||
[p1, p2]
|
||||
end
|
||||
|
||||
def void
|
||||
@void_called = @method_params
|
||||
end
|
||||
|
||||
def test_utf8
|
||||
Utf8String
|
||||
end
|
||||
|
||||
def hex(s)
|
||||
return s.unpack("H*")[0]
|
||||
end
|
||||
|
||||
def unhex(s)
|
||||
return [s].pack("H*")
|
||||
end
|
||||
|
||||
def time(t)
|
||||
t
|
||||
end
|
||||
|
||||
protected
|
||||
def alwaysfail(method_name, params)
|
||||
@before_filter_called = true
|
||||
false
|
||||
end
|
||||
|
||||
def alwaysok(method_name, params, return_value)
|
||||
@after_filter_called = true
|
||||
end
|
||||
end
|
||||
|
||||
class VirtualController < AbstractController
|
||||
web_service_api VirtualAPI
|
||||
wsdl_namespace WsdlNamespace
|
||||
|
||||
def fallback
|
||||
"fallback!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module DispatcherCommonTests
|
||||
def test_direct_dispatching
|
||||
assert_equal(70, do_method_call(@direct_controller, 'Add', 20, 50))
|
||||
assert_equal(70, @direct_controller.added)
|
||||
assert_equal(50, do_method_call(@direct_controller, 'Add2', 25, 25))
|
||||
assert_equal(50, @direct_controller.added2)
|
||||
assert(@direct_controller.void_called == false)
|
||||
assert(do_method_call(@direct_controller, 'Void', 3, 4, 5).nil?)
|
||||
assert(@direct_controller.void_called == [])
|
||||
result = do_method_call(@direct_controller, 'BaseStructReturn')
|
||||
assert(result[0].is_a?(DispatcherTest::Person))
|
||||
assert(result[1].is_a?(DispatcherTest::Person))
|
||||
assert_equal("cafe", do_method_call(@direct_controller, 'Hex', "\xca\xfe"))
|
||||
assert_equal("\xca\xfe", do_method_call(@direct_controller, 'Unhex', "cafe"))
|
||||
time = Time.gm(1998, "Feb", 02, 15, 12, 01)
|
||||
assert_equal(time, do_method_call(@direct_controller, 'Time', time))
|
||||
end
|
||||
|
||||
def test_direct_entrypoint
|
||||
assert(@direct_controller.respond_to?(:api))
|
||||
end
|
||||
|
||||
def test_virtual_dispatching
|
||||
assert_equal("fallback!", do_method_call(@virtual_controller, 'VirtualOne'))
|
||||
assert_equal("fallback!", do_method_call(@virtual_controller, 'VirtualTwo'))
|
||||
end
|
||||
|
||||
def test_direct_filtering
|
||||
assert_equal(false, @direct_controller.before_filter_called)
|
||||
assert_equal(false, @direct_controller.before_filter_target_called)
|
||||
do_method_call(@direct_controller, 'BeforeFiltered')
|
||||
assert_equal(true, @direct_controller.before_filter_called)
|
||||
assert_equal(false, @direct_controller.before_filter_target_called)
|
||||
assert_equal(false, @direct_controller.after_filter_called)
|
||||
assert_equal(false, @direct_controller.after_filter_target_called)
|
||||
assert_equal([5, 6, 7], do_method_call(@direct_controller, 'AfterFiltered'))
|
||||
assert_equal(true, @direct_controller.after_filter_called)
|
||||
assert_equal(true, @direct_controller.after_filter_target_called)
|
||||
end
|
||||
|
||||
def test_delegated_dispatching
|
||||
assert_equal(130, do_method_call(@delegated_controller, 'Add', 50, 80))
|
||||
service = @delegated_controller.web_service_object(:test_service)
|
||||
assert_equal(130, service.added)
|
||||
@delegated_controller.web_service_exception_reporting = true
|
||||
assert(service.intercepted.nil?)
|
||||
result = do_method_call(@delegated_controller, 'Interceptee')
|
||||
assert(service.intercepted.nil?)
|
||||
assert(is_exception?(result))
|
||||
assert_match(/permission denied/, exception_message(result))
|
||||
result = do_method_call(@delegated_controller, 'NonExistentMethod')
|
||||
assert(is_exception?(result))
|
||||
assert_match(/NonExistentMethod/, exception_message(result))
|
||||
assert(service.void_called == false)
|
||||
assert(do_method_call(@delegated_controller, 'Void', 3, 4, 5).nil?)
|
||||
assert(service.void_called == [])
|
||||
end
|
||||
|
||||
def test_garbage_request
|
||||
[@direct_controller, @delegated_controller].each do |controller|
|
||||
controller.class.web_service_exception_reporting = true
|
||||
send_garbage_request = lambda do
|
||||
service_name = service_name(controller)
|
||||
request = protocol.encode_action_pack_request(service_name, 'broken, method, name!', 'broken request body', :request_class => ActionController::TestRequest)
|
||||
response = ActionController::TestResponse.new
|
||||
controller.process(request, response)
|
||||
# puts response.body
|
||||
assert(response.status =~ /^500/)
|
||||
end
|
||||
send_garbage_request.call
|
||||
controller.class.web_service_exception_reporting = false
|
||||
send_garbage_request.call
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_marshaling
|
||||
@direct_controller.web_service_exception_reporting = true
|
||||
result = do_method_call(@direct_controller, 'Thrower')
|
||||
assert(is_exception?(result))
|
||||
assert_equal("Hi, I'm an exception", exception_message(result))
|
||||
@direct_controller.web_service_exception_reporting = false
|
||||
result = do_method_call(@direct_controller, 'Thrower')
|
||||
assert(exception_message(result) != "Hi, I'm an exception")
|
||||
end
|
||||
|
||||
def test_ar_struct_return
|
||||
[@direct_controller, @delegated_controller].each do |controller|
|
||||
result = do_method_call(controller, 'StructReturn')
|
||||
assert(result[0].is_a?(DispatcherTest::Node))
|
||||
assert(result[1].is_a?(DispatcherTest::Node))
|
||||
assert_equal('node1', result[0].name)
|
||||
assert_equal('node2', result[1].name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_casting
|
||||
assert_equal 70, do_method_call(@direct_controller, 'Add', "50", "20")
|
||||
assert_equal false, @direct_controller.struct_pass_value
|
||||
person = DispatcherTest::Person.new(:id => 1, :name => 'test')
|
||||
result = do_method_call(@direct_controller, 'StructPass', person)
|
||||
assert(nil == result || true == result)
|
||||
assert_equal person, @direct_controller.struct_pass_value
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test'})
|
||||
case
|
||||
when soap?
|
||||
assert_equal(person, @direct_controller.struct_pass_value)
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
when xmlrpc?
|
||||
assert_equal(person, @direct_controller.struct_pass_value)
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
end
|
||||
assert_equal person, do_method_call(@direct_controller, 'HashStructReturn')[0]
|
||||
result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test', 'nonexistent_attribute' => 'value'})
|
||||
case
|
||||
when soap?
|
||||
assert_equal(person, @direct_controller.struct_pass_value)
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
when xmlrpc?
|
||||
assert_equal(person, @direct_controller.struct_pass_value)
|
||||
assert !person.equal?(@direct_controller.struct_pass_value)
|
||||
end
|
||||
end
|
||||
|
||||
def test_logging
|
||||
buf = ""
|
||||
ActionController::Base.logger = Logger.new(StringIO.new(buf))
|
||||
test_casting
|
||||
test_garbage_request
|
||||
test_exception_marshaling
|
||||
ActionController::Base.logger = nil
|
||||
assert_match /Web Service Response/, buf
|
||||
assert_match /Web Service Request/, buf
|
||||
end
|
||||
|
||||
def test_allowed_http_methods
|
||||
webservice_api = @direct_controller.class.web_service_api
|
||||
original_allowed_http_methods = webservice_api.allowed_http_methods
|
||||
|
||||
# check defaults
|
||||
assert_equal false, http_method_allowed?(:get)
|
||||
assert_equal false, http_method_allowed?(:head)
|
||||
assert_equal false, http_method_allowed?(:put)
|
||||
assert_equal false, http_method_allowed?(:delete)
|
||||
assert_equal true, http_method_allowed?(:post)
|
||||
|
||||
# allow get and post
|
||||
webservice_api.allowed_http_methods = [ :get, :post ]
|
||||
assert_equal true, http_method_allowed?(:get)
|
||||
assert_equal true, http_method_allowed?(:post)
|
||||
|
||||
# allow get only
|
||||
webservice_api.allowed_http_methods = [ :get ]
|
||||
assert_equal true, http_method_allowed?(:get)
|
||||
assert_equal false, http_method_allowed?(:post)
|
||||
|
||||
# allow delete only
|
||||
webservice_api.allowed_http_methods = [ 'DELETE' ]
|
||||
assert_equal false, http_method_allowed?(:get)
|
||||
assert_equal false, http_method_allowed?(:head)
|
||||
assert_equal false, http_method_allowed?(:post)
|
||||
assert_equal false, http_method_allowed?(:put)
|
||||
assert_equal true, http_method_allowed?(:delete)
|
||||
|
||||
ensure
|
||||
webservice_api.allowed_http_methods = original_allowed_http_methods
|
||||
end
|
||||
|
||||
protected
|
||||
def service_name(container)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def exception_message(obj)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def protocol
|
||||
@protocol
|
||||
end
|
||||
|
||||
def soap?
|
||||
protocol.is_a? ActionWebService::Protocol::Soap::SoapProtocol
|
||||
end
|
||||
|
||||
def xmlrpc?
|
||||
protocol.is_a? ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
|
||||
end
|
||||
|
||||
def do_method_call(container, public_method_name, *params)
|
||||
request_env = {}
|
||||
mode = container.web_service_dispatching_mode
|
||||
case mode
|
||||
when :direct
|
||||
service_name = service_name(container)
|
||||
api = container.class.web_service_api
|
||||
method = api.public_api_method_instance(public_method_name)
|
||||
when :delegated
|
||||
service_name = service_name(container)
|
||||
api = container.web_service_object(service_name).class.web_service_api
|
||||
method = api.public_api_method_instance(public_method_name)
|
||||
when :layered
|
||||
service_name = nil
|
||||
real_method_name = nil
|
||||
if public_method_name =~ /^([^\.]+)\.(.*)$/
|
||||
service_name = $1
|
||||
real_method_name = $2
|
||||
end
|
||||
if soap?
|
||||
public_method_name = real_method_name
|
||||
request_env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{real_method_name}"
|
||||
end
|
||||
api = container.web_service_object(service_name.to_sym).class.web_service_api rescue nil
|
||||
method = api.public_api_method_instance(real_method_name) rescue nil
|
||||
service_name = self.service_name(container)
|
||||
end
|
||||
protocol.register_api(api)
|
||||
virtual = false
|
||||
unless method
|
||||
virtual = true
|
||||
method ||= ActionWebService::API::Method.new(public_method_name.underscore.to_sym, public_method_name, nil, nil)
|
||||
end
|
||||
body = protocol.encode_request(public_method_name, params.dup, method.expects)
|
||||
# puts body
|
||||
ap_request = protocol.encode_action_pack_request(service_name, public_method_name, body, :request_class => ActionController::TestRequest)
|
||||
ap_request.env.update(request_env)
|
||||
ap_response = ActionController::TestResponse.new
|
||||
container.process(ap_request, ap_response)
|
||||
# puts ap_response.body
|
||||
@response_body = ap_response.body
|
||||
public_method_name, return_value = protocol.decode_response(ap_response.body)
|
||||
unless is_exception?(return_value) || virtual
|
||||
return_value = method.cast_returns(return_value)
|
||||
end
|
||||
if soap?
|
||||
# http://dev.rubyonrails.com/changeset/920
|
||||
assert_match(/Response$/, public_method_name) unless public_method_name == "fault"
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def http_method_allowed?(method)
|
||||
method = method.to_s.upcase
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.action = 'api'
|
||||
test_response = ActionController::TestResponse.new
|
||||
test_request.env['REQUEST_METHOD'] = method
|
||||
result = @direct_controller.process(test_request, test_response)
|
||||
result.body =~ /(GET|POST|PUT|DELETE|TRACE|CONNECT) not supported/ ? false : true
|
||||
end
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
$: << "#{File.dirname(__FILE__)}/../lib"
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
require 'rubygems'
|
||||
require 'test/unit'
|
||||
require 'action_web_service'
|
||||
require 'action_controller'
|
||||
require 'action_controller/test_case'
|
||||
require 'action_view'
|
||||
require 'action_view/test_case'
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
||||
|
||||
ActiveRecord::Base.logger = ActionController::Base.logger = Logger.new("debug.log")
|
||||
|
||||
begin
|
||||
require 'activerecord'
|
||||
require "active_record/test_case"
|
||||
require "active_record/fixtures" unless Object.const_defined?(:Fixtures)
|
||||
rescue LoadError => e
|
||||
fail "\nFailed to load activerecord: #{e}"
|
||||
end
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'mysql' => {
|
||||
:adapter => "mysql",
|
||||
:username => "root",
|
||||
:encoding => "utf8",
|
||||
:database => "actionwebservice_unittest"
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'mysql'
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
include ActiveRecord::TestFixtures
|
||||
self.fixture_path = "#{File.dirname(__FILE__)}/fixtures/"
|
||||
end
|
|
@ -1,102 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module APITest
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :void
|
||||
api_method :expects_and_returns, :expects_and_returns => [:string]
|
||||
api_method :expects, :expects => [:int, :bool]
|
||||
api_method :returns, :returns => [:int, [:string]]
|
||||
api_method :named_signature, :expects => [{:appkey=>:int}, {:publish=>:bool}]
|
||||
api_method :string_types, :expects => ['int', 'string', 'bool', 'base64']
|
||||
api_method :class_types, :expects => [TrueClass, Bignum, String]
|
||||
end
|
||||
end
|
||||
|
||||
class TC_API < ActiveSupport::TestCase
|
||||
API = APITest::API
|
||||
|
||||
def test_api_method_declaration
|
||||
%w(
|
||||
void
|
||||
expects_and_returns
|
||||
expects
|
||||
returns
|
||||
named_signature
|
||||
string_types
|
||||
class_types
|
||||
).each do |name|
|
||||
name = name.to_sym
|
||||
public_name = API.public_api_method_name(name)
|
||||
assert(API.has_api_method?(name))
|
||||
assert(API.has_public_api_method?(public_name))
|
||||
assert(API.api_method_name(public_name) == name)
|
||||
assert(API.api_methods.has_key?(name))
|
||||
end
|
||||
end
|
||||
|
||||
def test_signature_canonicalization
|
||||
assert_equal(nil, API.api_methods[:void].expects)
|
||||
assert_equal(nil, API.api_methods[:void].returns)
|
||||
assert_equal([String], API.api_methods[:expects_and_returns].expects.map{|x| x.type_class})
|
||||
assert_equal([String], API.api_methods[:expects_and_returns].returns.map{|x| x.type_class})
|
||||
assert_equal([Integer, TrueClass], API.api_methods[:expects].expects.map{|x| x.type_class})
|
||||
assert_equal(nil, API.api_methods[:expects].returns)
|
||||
assert_equal(nil, API.api_methods[:returns].expects)
|
||||
assert_equal([Integer, [String]], API.api_methods[:returns].returns.map{|x| x.array?? [x.element_type.type_class] : x.type_class})
|
||||
assert_equal([[:appkey, Integer], [:publish, TrueClass]], API.api_methods[:named_signature].expects.map{|x| [x.name, x.type_class]})
|
||||
assert_equal(nil, API.api_methods[:named_signature].returns)
|
||||
assert_equal([Integer, String, TrueClass, ActionWebService::Base64], API.api_methods[:string_types].expects.map{|x| x.type_class})
|
||||
assert_equal(nil, API.api_methods[:string_types].returns)
|
||||
assert_equal([TrueClass, Integer, String], API.api_methods[:class_types].expects.map{|x| x.type_class})
|
||||
assert_equal(nil, API.api_methods[:class_types].returns)
|
||||
end
|
||||
|
||||
def test_not_instantiable
|
||||
assert_raises(NoMethodError) do
|
||||
API.new
|
||||
end
|
||||
end
|
||||
|
||||
def test_api_errors
|
||||
assert_raises(ActionWebService::ActionWebServiceError) do
|
||||
klass = Class.new(ActionWebService::API::Base) do
|
||||
api_method :test, :expects => [ActiveRecord::Base]
|
||||
end
|
||||
end
|
||||
klass = Class.new(ActionWebService::API::Base) do
|
||||
allow_active_record_expects true
|
||||
api_method :test2, :expects => [ActiveRecord::Base]
|
||||
end
|
||||
assert_raises(ActionWebService::ActionWebServiceError) do
|
||||
klass = Class.new(ActionWebService::API::Base) do
|
||||
api_method :test, :invalid => [:int]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_parameter_names
|
||||
method = API.api_methods[:named_signature]
|
||||
assert_equal 0, method.expects_index_of(:appkey)
|
||||
assert_equal 1, method.expects_index_of(:publish)
|
||||
assert_equal 1, method.expects_index_of('publish')
|
||||
assert_equal 0, method.expects_index_of('appkey')
|
||||
assert_equal -1, method.expects_index_of('blah')
|
||||
assert_equal -1, method.expects_index_of(:missing)
|
||||
assert_equal -1, API.api_methods[:void].expects_index_of('test')
|
||||
end
|
||||
|
||||
def test_parameter_hash
|
||||
method = API.api_methods[:named_signature]
|
||||
hash = method.expects_to_hash([5, false])
|
||||
assert_equal({:appkey => 5, :publish => false}, hash)
|
||||
end
|
||||
|
||||
def test_api_methods_compat
|
||||
sig = API.api_methods[:named_signature][:expects]
|
||||
assert_equal [{:appkey=>Integer}, {:publish=>TrueClass}], sig
|
||||
end
|
||||
|
||||
def test_to_s
|
||||
assert_equal 'void Expects(int param0, bool param1)', APITest::API.api_methods[:expects].to_s
|
||||
end
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
class AutoLoadAPI < ActionWebService::API::Base
|
||||
api_method :void
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module BaseTest
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :add, :expects => [:int, :int], :returns => [:int]
|
||||
api_method :void
|
||||
end
|
||||
|
||||
class PristineAPI < ActionWebService::API::Base
|
||||
inflect_names false
|
||||
|
||||
api_method :add
|
||||
api_method :under_score
|
||||
end
|
||||
|
||||
class Service < ActionWebService::Base
|
||||
web_service_api API
|
||||
|
||||
def add(a, b)
|
||||
end
|
||||
|
||||
def void
|
||||
end
|
||||
end
|
||||
|
||||
class PristineService < ActionWebService::Base
|
||||
web_service_api PristineAPI
|
||||
|
||||
def add
|
||||
end
|
||||
|
||||
def under_score
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Base < ActiveSupport::TestCase
|
||||
def test_options
|
||||
assert(BaseTest::PristineService.web_service_api.inflect_names == false)
|
||||
assert(BaseTest::Service.web_service_api.inflect_names == true)
|
||||
end
|
||||
end
|
|
@ -1,94 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module CastingTest
|
||||
class A < ActionWebService::Struct; end
|
||||
class B < A; end
|
||||
class API < ActionWebService::API::Base
|
||||
api_method :int, :expects => [:int]
|
||||
api_method :str, :expects => [:string]
|
||||
api_method :base64, :expects => [:base64]
|
||||
api_method :bool, :expects => [:bool]
|
||||
api_method :float, :expects => [:float]
|
||||
api_method :time, :expects => [:time]
|
||||
api_method :datetime, :expects => [:datetime]
|
||||
api_method :date, :expects => [:date]
|
||||
|
||||
api_method :int_array, :expects => [[:int]]
|
||||
api_method :str_array, :expects => [[:string]]
|
||||
api_method :bool_array, :expects => [[:bool]]
|
||||
|
||||
api_method :a, :expects => [A]
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Casting < Test::Unit::TestCase
|
||||
include CastingTest
|
||||
|
||||
def test_base_type_casting_valid
|
||||
assert_equal 10000, cast_expects(:int, '10000')[0]
|
||||
assert_equal '10000', cast_expects(:str, 10000)[0]
|
||||
base64 = cast_expects(:base64, 10000)[0]
|
||||
assert_equal '10000', base64
|
||||
assert_instance_of ActionWebService::Base64, base64
|
||||
[1, '1', 'true', 'y', 'yes'].each do |val|
|
||||
assert_equal true, cast_expects(:bool, val)[0]
|
||||
end
|
||||
[0, '0', 'false', 'n', 'no'].each do |val|
|
||||
assert_equal false, cast_expects(:bool, val)[0]
|
||||
end
|
||||
assert_equal 3.14159, cast_expects(:float, '3.14159')[0]
|
||||
now = Time.at(Time.now.tv_sec)
|
||||
casted = cast_expects(:time, now.to_s)[0]
|
||||
assert_equal now, casted
|
||||
now = DateTime.now
|
||||
assert_equal now.to_s, cast_expects(:datetime, now.to_s)[0].to_s
|
||||
today = Date.today
|
||||
assert_equal today, cast_expects(:date, today.to_s)[0]
|
||||
end
|
||||
|
||||
def test_base_type_casting_invalid
|
||||
assert_raises ArgumentError do
|
||||
cast_expects(:int, 'this is not a number')
|
||||
end
|
||||
assert_raises ActionWebService::Casting::CastingError do
|
||||
# neither true or false ;)
|
||||
cast_expects(:bool, 'i always lie')
|
||||
end
|
||||
assert_raises ArgumentError do
|
||||
cast_expects(:float, 'not a float')
|
||||
end
|
||||
assert_raises ArgumentError do
|
||||
cast_expects(:time, '111111111111111111111111111111111')
|
||||
end
|
||||
assert_raises ArgumentError do
|
||||
cast_expects(:datetime, '-1')
|
||||
end
|
||||
assert_raises ArgumentError do
|
||||
cast_expects(:date, '')
|
||||
end
|
||||
end
|
||||
|
||||
def test_array_type_casting
|
||||
assert_equal [1, 2, 3213992, 4], cast_expects(:int_array, ['1', '2', '3213992', '4'])[0]
|
||||
assert_equal ['one', 'two', '5.0', '200', nil, 'true'], cast_expects(:str_array, [:one, 'two', 5.0, 200, nil, true])[0]
|
||||
assert_equal [true, nil, true, true, false], cast_expects(:bool_array, ['1', nil, 'y', true, 'false'])[0]
|
||||
end
|
||||
|
||||
def test_array_type_casting_failure
|
||||
assert_raises ActionWebService::Casting::CastingError do
|
||||
cast_expects(:bool_array, ['false', 'blahblah'])
|
||||
end
|
||||
assert_raises ArgumentError do
|
||||
cast_expects(:int_array, ['1', '2.021', '4'])
|
||||
end
|
||||
end
|
||||
|
||||
def test_structured_type_casting_with_polymorphism
|
||||
assert cast_expects(:a, B.new)[0].is_a?(B)
|
||||
end
|
||||
|
||||
private
|
||||
def cast_expects(method_name, *args)
|
||||
API.api_method_instance(method_name.to_sym).cast_expects([*args])
|
||||
end
|
||||
end
|
|
@ -1,155 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_client'
|
||||
|
||||
|
||||
module ClientSoapTest
|
||||
PORT = 8998
|
||||
|
||||
class SoapClientLet < ClientTest::AbstractClientLet
|
||||
def do_POST(req, res)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = req.path.gsub(/^\//, '').split(/\//)[1]
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
||||
test_request.env['HTTP_SOAPACTION'] = req.header['soapaction'][0]
|
||||
test_request.env['RAW_POST_DATA'] = req.body
|
||||
response = ActionController::TestResponse.new
|
||||
@controller.process(test_request, response)
|
||||
res.header['content-type'] = 'text/xml'
|
||||
res.body = response.body
|
||||
rescue Exception => e
|
||||
$stderr.puts e.message
|
||||
$stderr.puts e.backtrace.join("\n")
|
||||
ensure
|
||||
ActiveRecord::Base.clear_active_connections!
|
||||
end
|
||||
end
|
||||
|
||||
class ClientContainer < ActionController::Base
|
||||
web_client_api :client, :soap, "http://localhost:#{PORT}/client/api", :api => ClientTest::API
|
||||
web_client_api :invalid, :null, "", :api => true
|
||||
|
||||
def get_client
|
||||
client
|
||||
end
|
||||
|
||||
def get_invalid
|
||||
invalid
|
||||
end
|
||||
end
|
||||
|
||||
class SoapServer < ClientTest::AbstractServer
|
||||
def create_clientlet(controller)
|
||||
SoapClientLet.new(controller)
|
||||
end
|
||||
|
||||
def server_port
|
||||
PORT
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TC_ClientSoap < ActiveSupport::TestCase
|
||||
include ClientTest
|
||||
include ClientSoapTest
|
||||
|
||||
fixtures :users
|
||||
|
||||
def setup
|
||||
@server = SoapServer.instance
|
||||
@container = @server.container
|
||||
@client = ActionWebService::Client::Soap.new(API, "http://localhost:#{@server.server_port}/client/api")
|
||||
end
|
||||
|
||||
def test_void
|
||||
assert(@container.value_void.nil?)
|
||||
@client.void
|
||||
assert(!@container.value_void.nil?)
|
||||
end
|
||||
|
||||
def test_normal
|
||||
assert(@container.value_normal.nil?)
|
||||
assert_equal(5, @client.normal(5, 6))
|
||||
assert_equal([5, 6], @container.value_normal)
|
||||
assert_equal(5, @client.normal("7", "8"))
|
||||
assert_equal([7, 8], @container.value_normal)
|
||||
assert_equal(5, @client.normal(true, false))
|
||||
end
|
||||
|
||||
def test_array_return
|
||||
assert(@container.value_array_return.nil?)
|
||||
new_person = Person.new
|
||||
new_person.firstnames = ["one", "two"]
|
||||
new_person.lastname = "last"
|
||||
assert_equal([new_person], @client.array_return)
|
||||
assert_equal([new_person], @container.value_array_return)
|
||||
end
|
||||
|
||||
def test_struct_pass
|
||||
assert(@container.value_struct_pass.nil?)
|
||||
new_person = Person.new
|
||||
new_person.firstnames = ["one", "two"]
|
||||
new_person.lastname = "last"
|
||||
assert_equal(true, @client.struct_pass([new_person]))
|
||||
assert_equal([[new_person]], @container.value_struct_pass)
|
||||
end
|
||||
|
||||
def test_nil_struct_return
|
||||
assert_nil @client.nil_struct_return
|
||||
end
|
||||
|
||||
def test_inner_nil
|
||||
outer = @client.inner_nil
|
||||
assert_equal 'outer', outer.name
|
||||
assert_nil outer.inner
|
||||
end
|
||||
|
||||
def test_client_container
|
||||
assert_equal(50, ClientContainer.new.get_client.client_container)
|
||||
assert(ClientContainer.new.get_invalid.nil?)
|
||||
end
|
||||
|
||||
def test_named_parameters
|
||||
assert(@container.value_named_parameters.nil?)
|
||||
assert(@client.named_parameters("key", 5).nil?)
|
||||
assert_equal(["key", 5], @container.value_named_parameters)
|
||||
end
|
||||
|
||||
def test_capitalized_method_name
|
||||
@container.value_normal = nil
|
||||
assert_equal(5, @client.Normal(5, 6))
|
||||
assert_equal([5, 6], @container.value_normal)
|
||||
@container.value_normal = nil
|
||||
end
|
||||
|
||||
def test_model_return
|
||||
user = @client.user_return
|
||||
assert_equal 1, user.id
|
||||
assert_equal 'Kent', user.name
|
||||
assert user.active?
|
||||
assert_kind_of Date, user.created_on
|
||||
assert_equal Date.today, user.created_on
|
||||
assert_equal BigDecimal('12.2'), user.balance
|
||||
end
|
||||
|
||||
def test_with_model
|
||||
with_model = @client.with_model_return
|
||||
assert_equal 'Kent', with_model.user.name
|
||||
assert_equal 2, with_model.users.size
|
||||
with_model.users.each do |user|
|
||||
assert_kind_of User, user
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_model_return
|
||||
scoped_model = @client.scoped_model_return
|
||||
assert_kind_of Accounting::User, scoped_model
|
||||
assert_equal 'Kent', scoped_model.name
|
||||
end
|
||||
|
||||
def test_multi_dim_return
|
||||
md_struct = @client.multi_dim_return
|
||||
assert_kind_of Array, md_struct.pref
|
||||
assert_equal 2, md_struct.pref.size
|
||||
assert_kind_of Array, md_struct.pref[0]
|
||||
end
|
||||
end
|
|
@ -1,153 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_client'
|
||||
|
||||
|
||||
module ClientXmlRpcTest
|
||||
PORT = 8999
|
||||
|
||||
class XmlRpcClientLet < ClientTest::AbstractClientLet
|
||||
def do_POST(req, res)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.request_parameters['action'] = req.path.gsub(/^\//, '').split(/\//)[1]
|
||||
test_request.env['REQUEST_METHOD'] = "POST"
|
||||
test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
||||
test_request.env['RAW_POST_DATA'] = req.body
|
||||
response = ActionController::TestResponse.new
|
||||
@controller.process(test_request, response)
|
||||
res.header['content-type'] = 'text/xml'
|
||||
res.body = response.body
|
||||
rescue Exception => e
|
||||
$stderr.puts e.message
|
||||
$stderr.puts e.backtrace.join("\n")
|
||||
ensure
|
||||
ActiveRecord::Base.clear_active_connections!
|
||||
end
|
||||
end
|
||||
|
||||
class ClientContainer < ActionController::Base
|
||||
web_client_api :client, :xmlrpc, "http://localhost:#{PORT}/client/api", :api => ClientTest::API
|
||||
|
||||
def get_client
|
||||
client
|
||||
end
|
||||
end
|
||||
|
||||
class XmlRpcServer < ClientTest::AbstractServer
|
||||
def create_clientlet(controller)
|
||||
XmlRpcClientLet.new(controller)
|
||||
end
|
||||
|
||||
def server_port
|
||||
PORT
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TC_ClientXmlRpc < ActiveSupport::TestCase
|
||||
include ClientTest
|
||||
include ClientXmlRpcTest
|
||||
|
||||
fixtures :users
|
||||
|
||||
def setup
|
||||
@server = XmlRpcServer.instance
|
||||
@container = @server.container
|
||||
@client = ActionWebService::Client::XmlRpc.new(API, "http://localhost:#{@server.server_port}/client/api")
|
||||
end
|
||||
|
||||
def test_void
|
||||
assert(@container.value_void.nil?)
|
||||
@client.void
|
||||
assert(!@container.value_void.nil?)
|
||||
end
|
||||
|
||||
def test_normal
|
||||
assert(@container.value_normal.nil?)
|
||||
assert_equal(5, @client.normal(5, 6))
|
||||
assert_equal([5, 6], @container.value_normal)
|
||||
assert_equal(5, @client.normal("7", "8"))
|
||||
assert_equal([7, 8], @container.value_normal)
|
||||
assert_equal(5, @client.normal(true, false))
|
||||
end
|
||||
|
||||
def test_array_return
|
||||
assert(@container.value_array_return.nil?)
|
||||
new_person = Person.new
|
||||
new_person.firstnames = ["one", "two"]
|
||||
new_person.lastname = "last"
|
||||
assert_equal([new_person], @client.array_return)
|
||||
assert_equal([new_person], @container.value_array_return)
|
||||
end
|
||||
|
||||
def test_struct_pass
|
||||
assert(@container.value_struct_pass.nil?)
|
||||
new_person = Person.new
|
||||
new_person.firstnames = ["one", "two"]
|
||||
new_person.lastname = "last"
|
||||
assert_equal(true, @client.struct_pass([new_person]))
|
||||
assert_equal([[new_person]], @container.value_struct_pass)
|
||||
end
|
||||
|
||||
def test_nil_struct_return
|
||||
assert_equal false, @client.nil_struct_return
|
||||
end
|
||||
|
||||
def test_inner_nil
|
||||
outer = @client.inner_nil
|
||||
assert_equal 'outer', outer.name
|
||||
assert_nil outer.inner
|
||||
end
|
||||
|
||||
def test_client_container
|
||||
assert_equal(50, ClientContainer.new.get_client.client_container)
|
||||
end
|
||||
|
||||
def test_named_parameters
|
||||
assert(@container.value_named_parameters.nil?)
|
||||
assert_equal(false, @client.named_parameters("xxx", 7))
|
||||
assert_equal(["xxx", 7], @container.value_named_parameters)
|
||||
end
|
||||
|
||||
def test_exception
|
||||
assert_raises(ActionWebService::Client::ClientError) do
|
||||
assert(@client.thrower)
|
||||
end
|
||||
end
|
||||
|
||||
def test_invalid_signature
|
||||
assert_raises(ArgumentError) do
|
||||
@client.normal
|
||||
end
|
||||
end
|
||||
|
||||
def test_model_return
|
||||
user = @client.user_return
|
||||
assert_equal 1, user.id
|
||||
assert_equal 'Kent', user.name
|
||||
assert user.active?
|
||||
assert_kind_of Time, user.created_on
|
||||
assert_equal Time.utc(Time.now.year, Time.now.month, Time.now.day), user.created_on
|
||||
assert_equal BigDecimal('12.2'), user.balance
|
||||
end
|
||||
|
||||
def test_with_model
|
||||
with_model = @client.with_model_return
|
||||
assert_equal 'Kent', with_model.user.name
|
||||
assert_equal 2, with_model.users.size
|
||||
with_model.users.each do |user|
|
||||
assert_kind_of User, user
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_model_return
|
||||
scoped_model = @client.scoped_model_return
|
||||
assert_kind_of Accounting::User, scoped_model
|
||||
assert_equal 'Kent', scoped_model.name
|
||||
end
|
||||
|
||||
def test_multi_dim_return
|
||||
md_struct = @client.multi_dim_return
|
||||
assert_kind_of Array, md_struct.pref
|
||||
assert_equal 2, md_struct.pref.size
|
||||
assert_kind_of Array, md_struct.pref[0]
|
||||
end
|
||||
end
|
|
@ -1,73 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module ContainerTest
|
||||
$immediate_service = Object.new
|
||||
$deferred_service = Object.new
|
||||
|
||||
class DelegateContainer < ActionController::Base
|
||||
web_service_dispatching_mode :delegated
|
||||
|
||||
attr :flag
|
||||
attr :previous_flag
|
||||
|
||||
def initialize
|
||||
@previous_flag = nil
|
||||
@flag = true
|
||||
end
|
||||
|
||||
web_service :immediate_service, $immediate_service
|
||||
web_service(:deferred_service) { @previous_flag = @flag; @flag = false; $deferred_service }
|
||||
end
|
||||
|
||||
class DirectContainer < ActionController::Base
|
||||
web_service_dispatching_mode :direct
|
||||
end
|
||||
|
||||
class InvalidContainer
|
||||
include ActionWebService::Container::Direct
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Container < Test::Unit::TestCase
|
||||
include ContainerTest
|
||||
|
||||
def setup
|
||||
@delegate_container = DelegateContainer.new
|
||||
@direct_container = DirectContainer.new
|
||||
end
|
||||
|
||||
def test_registration
|
||||
assert(DelegateContainer.has_web_service?(:immediate_service))
|
||||
assert(DelegateContainer.has_web_service?(:deferred_service))
|
||||
assert(!DelegateContainer.has_web_service?(:fake_service))
|
||||
assert_raises(ActionWebService::Container::Delegated::ContainerError) do
|
||||
DelegateContainer.web_service('invalid')
|
||||
end
|
||||
end
|
||||
|
||||
def test_service_object
|
||||
assert_raises(ActionWebService::Container::Delegated::ContainerError) do
|
||||
@delegate_container.web_service_object(:nonexistent)
|
||||
end
|
||||
assert(@delegate_container.flag == true)
|
||||
assert(@delegate_container.web_service_object(:immediate_service) == $immediate_service)
|
||||
assert(@delegate_container.previous_flag.nil?)
|
||||
assert(@delegate_container.flag == true)
|
||||
assert(@delegate_container.web_service_object(:deferred_service) == $deferred_service)
|
||||
assert(@delegate_container.previous_flag == true)
|
||||
assert(@delegate_container.flag == false)
|
||||
end
|
||||
|
||||
def test_direct_container
|
||||
assert(DirectContainer.web_service_dispatching_mode == :direct)
|
||||
end
|
||||
|
||||
def test_validity
|
||||
assert_raises(ActionWebService::Container::Direct::ContainerError) do
|
||||
InvalidContainer.web_service_api :test
|
||||
end
|
||||
assert_raises(ActionWebService::Container::Direct::ContainerError) do
|
||||
InvalidContainer.web_service_api 50.0
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,138 +0,0 @@
|
|||
$:.unshift(File.dirname(__FILE__) + '/apis')
|
||||
require File.dirname(__FILE__) + '/abstract_dispatcher'
|
||||
require 'wsdl/parser'
|
||||
|
||||
class ActionController::Base
|
||||
class << self
|
||||
alias :inherited_without_name_error :inherited
|
||||
def inherited(child)
|
||||
begin
|
||||
inherited_without_name_error(child)
|
||||
rescue NameError => e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class AutoLoadController < ActionController::Base; end
|
||||
class FailingAutoLoadController < ActionController::Base; end
|
||||
class BrokenAutoLoadController < ActionController::Base; end
|
||||
|
||||
class TC_DispatcherActionControllerSoap < Test::Unit::TestCase
|
||||
include DispatcherTest
|
||||
include DispatcherCommonTests
|
||||
|
||||
def setup
|
||||
@direct_controller = DirectController.new
|
||||
@delegated_controller = DelegatedController.new
|
||||
@virtual_controller = VirtualController.new
|
||||
@layered_controller = LayeredController.new
|
||||
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.create(@direct_controller)
|
||||
end
|
||||
|
||||
def test_wsdl_generation
|
||||
ensure_valid_wsdl_generation DelegatedController.new, DispatcherTest::WsdlNamespace
|
||||
ensure_valid_wsdl_generation DirectController.new, DispatcherTest::WsdlNamespace
|
||||
end
|
||||
|
||||
def test_wsdl_action
|
||||
delegated_types = ensure_valid_wsdl_action DelegatedController.new
|
||||
delegated_names = delegated_types.map{|x| x.name.name}
|
||||
assert(delegated_names.include?('DispatcherTest..NodeArray'))
|
||||
assert(delegated_names.include?('DispatcherTest..Node'))
|
||||
direct_types = ensure_valid_wsdl_action DirectController.new
|
||||
direct_names = direct_types.map{|x| x.name.name}
|
||||
assert(direct_names.include?('DispatcherTest..NodeArray'))
|
||||
assert(direct_names.include?('DispatcherTest..Node'))
|
||||
assert(direct_names.include?('IntegerArray'))
|
||||
end
|
||||
|
||||
def test_autoloading
|
||||
assert(!AutoLoadController.web_service_api.nil?)
|
||||
assert(AutoLoadController.web_service_api.has_public_api_method?('Void'))
|
||||
assert(FailingAutoLoadController.web_service_api.nil?)
|
||||
assert_raises(MissingSourceFile) do
|
||||
FailingAutoLoadController.require_web_service_api :blah
|
||||
end
|
||||
assert_raises(ArgumentError) do
|
||||
FailingAutoLoadController.require_web_service_api 50.0
|
||||
end
|
||||
assert(BrokenAutoLoadController.web_service_api.nil?)
|
||||
end
|
||||
|
||||
def test_layered_dispatching
|
||||
mt_cats = do_method_call(@layered_controller, 'mt.getCategories')
|
||||
assert_equal(["mtCat1", "mtCat2"], mt_cats)
|
||||
blogger_cats = do_method_call(@layered_controller, 'blogger.getCategories')
|
||||
assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
|
||||
end
|
||||
|
||||
def test_utf8
|
||||
@direct_controller.web_service_exception_reporting = true
|
||||
$KCODE = 'u'
|
||||
assert_equal(Utf8String, do_method_call(@direct_controller, 'TestUtf8'))
|
||||
retval = SOAP::Processor.unmarshal(@response_body).body.response
|
||||
assert retval.is_a?(SOAP::SOAPString)
|
||||
|
||||
# If $KCODE is not set to UTF-8, any strings with non-ASCII UTF-8 data
|
||||
# will be sent back as base64 by SOAP4R. By the time we get it here though,
|
||||
# it will be decoded back into a string. So lets read the base64 value
|
||||
# from the message body directly.
|
||||
$KCODE = 'NONE'
|
||||
do_method_call(@direct_controller, 'TestUtf8')
|
||||
retval = SOAP::Processor.unmarshal(@response_body).body.response
|
||||
assert retval.is_a?(SOAP::SOAPBase64)
|
||||
assert_equal "T25lIFdvcmxkIENhZsOp", retval.data.to_s
|
||||
end
|
||||
|
||||
protected
|
||||
def exception_message(soap_fault_exception)
|
||||
soap_fault_exception.detail.cause.message
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \
|
||||
obj.detail.cause.is_a?(Exception)
|
||||
end
|
||||
|
||||
def service_name(container)
|
||||
container.is_a?(DelegatedController) ? 'test_service' : 'api'
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl_generation(controller, expected_namespace)
|
||||
wsdl = controller.generate_wsdl
|
||||
ensure_valid_wsdl(controller, wsdl, expected_namespace)
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl(controller, wsdl, expected_namespace)
|
||||
definitions = WSDL::Parser.new.parse(wsdl)
|
||||
assert(definitions.is_a?(WSDL::Definitions))
|
||||
definitions.bindings.each do |binding|
|
||||
assert(binding.name.name.index(':').nil?)
|
||||
end
|
||||
definitions.services.each do |service|
|
||||
service.ports.each do |port|
|
||||
assert(port.name.name.index(':').nil?)
|
||||
end
|
||||
end
|
||||
types = definitions.collect_complextypes.map{|x| x.name}
|
||||
types.each do |type|
|
||||
assert(type.namespace == expected_namespace)
|
||||
end
|
||||
location = definitions.services[0].ports[0].soap_address.location
|
||||
if controller.is_a?(DelegatedController)
|
||||
assert_match %r{http://test.host/dispatcher_test/delegated/test_service$}, location
|
||||
elsif controller.is_a?(DirectController)
|
||||
assert_match %r{http://test.host/dispatcher_test/direct/api$}, location
|
||||
end
|
||||
definitions.collect_complextypes
|
||||
end
|
||||
|
||||
def ensure_valid_wsdl_action(controller)
|
||||
test_request = ActionController::TestRequest.new
|
||||
test_request.action = 'wsdl'
|
||||
test_response = ActionController::TestResponse.new
|
||||
wsdl = controller.process(test_request, test_response).body
|
||||
ensure_valid_wsdl(controller, wsdl, DispatcherTest::WsdlNamespace)
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_dispatcher'
|
||||
|
||||
class TC_DispatcherActionControllerXmlRpc < Test::Unit::TestCase
|
||||
include DispatcherTest
|
||||
include DispatcherCommonTests
|
||||
|
||||
def setup
|
||||
@direct_controller = DirectController.new
|
||||
@delegated_controller = DelegatedController.new
|
||||
@layered_controller = LayeredController.new
|
||||
@virtual_controller = VirtualController.new
|
||||
@protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.create(@direct_controller)
|
||||
end
|
||||
|
||||
def test_layered_dispatching
|
||||
mt_cats = do_method_call(@layered_controller, 'mt.getCategories')
|
||||
assert_equal(["mtCat1", "mtCat2"], mt_cats)
|
||||
blogger_cats = do_method_call(@layered_controller, 'blogger.getCategories')
|
||||
assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
|
||||
end
|
||||
|
||||
def test_multicall
|
||||
response = do_method_call(@layered_controller, 'system.multicall', [
|
||||
{'methodName' => 'mt.getCategories'},
|
||||
{'methodName' => 'blogger.getCategories'},
|
||||
{'methodName' => 'mt.bool'},
|
||||
{'methodName' => 'blogger.str', 'params' => ['2000']},
|
||||
{'methodName' => 'mt.alwaysFail'},
|
||||
{'methodName' => 'blogger.alwaysFail'},
|
||||
{'methodName' => 'mt.blah'},
|
||||
{'methodName' => 'blah.blah'},
|
||||
{'methodName' => 'mt.person'}
|
||||
])
|
||||
assert_equal [
|
||||
[["mtCat1", "mtCat2"]],
|
||||
[["bloggerCat1", "bloggerCat2"]],
|
||||
[true],
|
||||
["2500"],
|
||||
{"faultCode" => 3, "faultString" => "MT AlwaysFail"},
|
||||
{"faultCode" => 3, "faultString" => "Blogger AlwaysFail"},
|
||||
{"faultCode" => 4, "faultMessage" => "no such method 'blah' on API DispatcherTest::MTAPI"},
|
||||
{"faultCode" => 4, "faultMessage" => "no such web service 'blah'"},
|
||||
[{"name"=>"person1", "id"=>1}]
|
||||
], response
|
||||
end
|
||||
|
||||
protected
|
||||
def exception_message(xmlrpc_fault_exception)
|
||||
xmlrpc_fault_exception.faultString
|
||||
end
|
||||
|
||||
def is_exception?(obj)
|
||||
obj.is_a?(XMLRPC::FaultException)
|
||||
end
|
||||
|
||||
def service_name(container)
|
||||
container.is_a?(DelegatedController) ? 'test_service' : 'api'
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
CREATE TABLE `users` (
|
||||
`id` int(11) NOT NULL auto_increment,
|
||||
`name` varchar(30) default NULL,
|
||||
`active` tinyint(4) default NULL,
|
||||
`balance` decimal(5, 2) default NULL,
|
||||
`created_on` date default NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
|
@ -1,12 +0,0 @@
|
|||
user1:
|
||||
id: 1
|
||||
name: Kent
|
||||
active: 1
|
||||
balance: 12.2
|
||||
created_on: <%= Date.today %>
|
||||
user2:
|
||||
id: 2
|
||||
name: David
|
||||
active: 1
|
||||
balance: 16.4
|
||||
created_on: <%= Date.today %>
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
rcov -x '.*_test\.rb,rubygems,abstract_,/run,/apis' ./run
|
|
@ -1,185 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module InvocationTest
|
||||
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
|
||||
api_method :fail_generic
|
||||
api_method :no_before
|
||||
api_method :no_after
|
||||
api_method :only_one
|
||||
api_method :only_two
|
||||
end
|
||||
|
||||
class Interceptor
|
||||
attr :args
|
||||
|
||||
def initialize
|
||||
@args = nil
|
||||
end
|
||||
|
||||
def intercept(*args)
|
||||
@args = args
|
||||
end
|
||||
end
|
||||
|
||||
InterceptorClass = Interceptor.new
|
||||
|
||||
class Service < ActionController::Base
|
||||
web_service_api API
|
||||
|
||||
before_invocation :intercept_before, :except => [:no_before]
|
||||
after_invocation :intercept_after, :except => [:no_after]
|
||||
prepend_after_invocation :intercept_after_first, :except => [:no_after]
|
||||
prepend_before_invocation :intercept_only, :only => [:only_one, :only_two]
|
||||
after_invocation(:only => [:only_one]) do |*args|
|
||||
args[0].instance_variable_set('@block_invoked', args[1])
|
||||
end
|
||||
after_invocation InterceptorClass, :only => [:only_one]
|
||||
|
||||
attr_accessor :before_invoked
|
||||
attr_accessor :after_invoked
|
||||
attr_accessor :after_first_invoked
|
||||
attr_accessor :only_invoked
|
||||
attr_accessor :block_invoked
|
||||
attr_accessor :invocation_result
|
||||
|
||||
def initialize
|
||||
@before_invoked = nil
|
||||
@after_invoked = nil
|
||||
@after_first_invoked = nil
|
||||
@only_invoked = nil
|
||||
@invocation_result = nil
|
||||
@block_invoked = nil
|
||||
end
|
||||
|
||||
def add(a, b)
|
||||
a + b
|
||||
end
|
||||
|
||||
def transmogrify(str)
|
||||
str.upcase
|
||||
end
|
||||
|
||||
def fail_with_reason
|
||||
end
|
||||
|
||||
def fail_generic
|
||||
end
|
||||
|
||||
def no_before
|
||||
5
|
||||
end
|
||||
|
||||
def no_after
|
||||
end
|
||||
|
||||
def only_one
|
||||
end
|
||||
|
||||
def only_two
|
||||
end
|
||||
|
||||
protected
|
||||
def intercept_before(name, args)
|
||||
@before_invoked = name
|
||||
return [false, "permission denied"] if name == :fail_with_reason
|
||||
return false if name == :fail_generic
|
||||
end
|
||||
|
||||
def intercept_after(name, args, result)
|
||||
@after_invoked = name
|
||||
@invocation_result = result
|
||||
end
|
||||
|
||||
def intercept_after_first(name, args, result)
|
||||
@after_first_invoked = name
|
||||
end
|
||||
|
||||
def intercept_only(name, args)
|
||||
raise "Interception error" unless name == :only_one || name == :only_two
|
||||
@only_invoked = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Invocation < Test::Unit::TestCase
|
||||
include ActionWebService::Invocation
|
||||
|
||||
def setup
|
||||
@service = InvocationTest::Service.new
|
||||
end
|
||||
|
||||
def test_invocation
|
||||
assert(perform_invocation(:add, 5, 10) == 15)
|
||||
assert(perform_invocation(:transmogrify, "hello") == "HELLO")
|
||||
assert_raises(NoMethodError) do
|
||||
perform_invocation(:nonexistent_method_xyzzy)
|
||||
end
|
||||
end
|
||||
|
||||
def test_interceptor_registration
|
||||
assert(InvocationTest::Service.before_invocation_interceptors.length == 2)
|
||||
assert(InvocationTest::Service.after_invocation_interceptors.length == 4)
|
||||
assert_equal(:intercept_only, InvocationTest::Service.before_invocation_interceptors[0])
|
||||
assert_equal(:intercept_after_first, InvocationTest::Service.after_invocation_interceptors[0])
|
||||
end
|
||||
|
||||
def test_interception
|
||||
assert(@service.before_invoked.nil?)
|
||||
assert(@service.after_invoked.nil?)
|
||||
assert(@service.only_invoked.nil?)
|
||||
assert(@service.block_invoked.nil?)
|
||||
assert(@service.invocation_result.nil?)
|
||||
perform_invocation(:add, 20, 50)
|
||||
assert(@service.before_invoked == :add)
|
||||
assert(@service.after_invoked == :add)
|
||||
assert(@service.invocation_result == 70)
|
||||
end
|
||||
|
||||
def test_interception_canceling
|
||||
reason = nil
|
||||
perform_invocation(:fail_with_reason){|r| reason = r}
|
||||
assert(@service.before_invoked == :fail_with_reason)
|
||||
assert(@service.after_invoked.nil?)
|
||||
assert(@service.invocation_result.nil?)
|
||||
assert(reason == "permission denied")
|
||||
reason = true
|
||||
@service.before_invoked = @service.after_invoked = @service.invocation_result = nil
|
||||
perform_invocation(:fail_generic){|r| reason = r}
|
||||
assert(@service.before_invoked == :fail_generic)
|
||||
assert(@service.after_invoked.nil?)
|
||||
assert(@service.invocation_result.nil?)
|
||||
assert(reason == true)
|
||||
end
|
||||
|
||||
def test_interception_except_conditions
|
||||
perform_invocation(:no_before)
|
||||
assert(@service.before_invoked.nil?)
|
||||
assert(@service.after_first_invoked == :no_before)
|
||||
assert(@service.after_invoked == :no_before)
|
||||
assert(@service.invocation_result == 5)
|
||||
@service.before_invoked = @service.after_invoked = @service.invocation_result = nil
|
||||
perform_invocation(:no_after)
|
||||
assert(@service.before_invoked == :no_after)
|
||||
assert(@service.after_invoked.nil?)
|
||||
assert(@service.invocation_result.nil?)
|
||||
end
|
||||
|
||||
def test_interception_only_conditions
|
||||
assert(@service.only_invoked.nil?)
|
||||
perform_invocation(:only_one)
|
||||
assert(@service.only_invoked == :only_one)
|
||||
assert(@service.block_invoked == :only_one)
|
||||
assert(InvocationTest::InterceptorClass.args[1] == :only_one)
|
||||
@service.only_invoked = nil
|
||||
perform_invocation(:only_two)
|
||||
assert(@service.only_invoked == :only_two)
|
||||
end
|
||||
|
||||
private
|
||||
def perform_invocation(method_name, *args, &block)
|
||||
@service.perform_invocation(method_name, args, &block)
|
||||
end
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'test/unit'
|
||||
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||
args = Dir[File.join(File.dirname(__FILE__), '*_test.rb')] + Dir[File.join(File.dirname(__FILE__), 'ws/*_test.rb')]
|
||||
(r = Test::Unit::AutoRunner.new(true)).process_args(args)
|
||||
exit r.run
|
|
@ -1,146 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect '', :controller => 'scaffolded'
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
ActionController::Base.view_paths = [ '.' ]
|
||||
|
||||
class ScaffoldPerson < ActionWebService::Struct
|
||||
member :id, :int
|
||||
member :name, :string
|
||||
member :birth, :date
|
||||
|
||||
def ==(other)
|
||||
self.id == other.id && self.name == other.name
|
||||
end
|
||||
end
|
||||
|
||||
class ScaffoldedControllerTestAPI < ActionWebService::API::Base
|
||||
api_method :hello, :expects => [{:integer=>:int}, :string], :returns => [:bool]
|
||||
api_method :hello_struct_param, :expects => [{:person => ScaffoldPerson}], :returns => [:bool]
|
||||
api_method :date_of_birth, :expects => [ScaffoldPerson], :returns => [:string]
|
||||
api_method :bye, :returns => [[ScaffoldPerson]]
|
||||
api_method :date_diff, :expects => [{:start_date => :date}, {:end_date => :date}], :returns => [:int]
|
||||
api_method :time_diff, :expects => [{:start_time => :time}, {:end_time => :time}], :returns => [:int]
|
||||
api_method :base64_upcase, :expects => [:base64], :returns => [:base64]
|
||||
end
|
||||
|
||||
class ScaffoldedController < ActionController::Base
|
||||
web_service_api ScaffoldedControllerTestAPI
|
||||
web_service_scaffold :scaffold_invoke
|
||||
|
||||
def hello(int, string)
|
||||
0
|
||||
end
|
||||
|
||||
def hello_struct_param(person)
|
||||
0
|
||||
end
|
||||
|
||||
def date_of_birth(person)
|
||||
person.birth.to_s
|
||||
end
|
||||
|
||||
def bye
|
||||
[ScaffoldPerson.new(:id => 1, :name => "leon"), ScaffoldPerson.new(:id => 2, :name => "paul")]
|
||||
end
|
||||
|
||||
def rescue_action(e)
|
||||
raise e
|
||||
end
|
||||
|
||||
def date_diff(start_date, end_date)
|
||||
end_date - start_date
|
||||
end
|
||||
|
||||
def time_diff(start_time, end_time)
|
||||
end_time - start_time
|
||||
end
|
||||
|
||||
def base64_upcase(data)
|
||||
data.upcase
|
||||
end
|
||||
end
|
||||
|
||||
class ScaffoldedControllerTest < ActionController::TestCase
|
||||
# def setup
|
||||
# @controller = ScaffoldedController.new
|
||||
# @request = ActionController::TestRequest.new
|
||||
# @response = ActionController::TestResponse.new
|
||||
# end
|
||||
|
||||
def test_scaffold_invoke
|
||||
get :scaffold_invoke
|
||||
assert_template 'methods.html.erb'
|
||||
end
|
||||
|
||||
def test_scaffold_invoke_method_params
|
||||
get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'Hello'
|
||||
assert_template 'parameters.html.erb'
|
||||
end
|
||||
|
||||
def test_scaffold_invoke_method_params_with_struct
|
||||
get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'HelloStructParam'
|
||||
assert_template 'parameters.html.erb'
|
||||
assert_tag :tag => 'form'
|
||||
assert_tag :tag => 'input', :attributes => {:name => "method_params[0][name]"}
|
||||
end
|
||||
|
||||
def test_scaffold_invoke_submit_hello
|
||||
post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'Hello', :method_params => {'0' => '5', '1' => 'hello world'}
|
||||
assert_template 'result.html.erb'
|
||||
assert_equal false, @controller.instance_eval{ @method_return_value }
|
||||
end
|
||||
|
||||
def test_scaffold_invoke_submit_bye
|
||||
post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'Bye'
|
||||
assert_template 'result.html.erb'
|
||||
persons = [ScaffoldPerson.new(:id => 1, :name => "leon"), ScaffoldPerson.new(:id => 2, :name => "paul")]
|
||||
assert_equal persons, @controller.instance_eval{ @method_return_value }
|
||||
end
|
||||
|
||||
def test_scaffold_date_params
|
||||
get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'DateDiff'
|
||||
(0..1).each do |param|
|
||||
(1..3).each do |date_part|
|
||||
assert_tag :tag => 'select', :attributes => {:name => "method_params[#{param}][#{date_part}]"},
|
||||
:children => {:greater_than => 1, :only => {:tag => 'option'}}
|
||||
end
|
||||
end
|
||||
|
||||
post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'DateDiff',
|
||||
:method_params => {'0' => {'1' => '2006', '2' => '2', '3' => '1'}, '1' => {'1' => '2006', '2' => '2', '3' => '2'}}
|
||||
assert_equal 1, @controller.instance_eval{ @method_return_value }
|
||||
end
|
||||
|
||||
def test_scaffold_struct_date_params
|
||||
post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'DateOfBirth',
|
||||
:method_params => {'0' => {'birth' => {'1' => '2006', '2' => '2', '3' => '1'}, 'id' => '1', 'name' => 'person'}}
|
||||
assert_equal '2006-02-01', @controller.instance_eval{ @method_return_value }
|
||||
end
|
||||
|
||||
def test_scaffold_time_params
|
||||
get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'TimeDiff'
|
||||
(0..1).each do |param|
|
||||
(1..6).each do |date_part|
|
||||
assert_tag :tag => 'select', :attributes => {:name => "method_params[#{param}][#{date_part}]"},
|
||||
:children => {:greater_than => 1, :only => {:tag => 'option'}}
|
||||
end
|
||||
end
|
||||
|
||||
post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'TimeDiff',
|
||||
:method_params => {'0' => {'1' => '2006', '2' => '2', '3' => '1', '4' => '1', '5' => '1', '6' => '1'},
|
||||
'1' => {'1' => '2006', '2' => '2', '3' => '2', '4' => '1', '5' => '1', '6' => '1'}}
|
||||
assert_equal 86400, @controller.instance_eval{ @method_return_value }
|
||||
end
|
||||
|
||||
def test_scaffold_base64
|
||||
get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'Base64Upcase'
|
||||
assert_tag :tag => 'textarea', :attributes => {:name => 'method_params[0]'}
|
||||
|
||||
post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'Base64Upcase', :method_params => {'0' => 'scaffold'}
|
||||
assert_equal 'SCAFFOLD', @controller.instance_eval{ @method_return_value }
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
|
||||
module StructTest
|
||||
class Struct < ActionWebService::Struct
|
||||
member :id, Integer
|
||||
member :name, String
|
||||
member :items, [String]
|
||||
member :deleted, :bool
|
||||
member :emails, [:string]
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Struct < Test::Unit::TestCase
|
||||
include StructTest
|
||||
|
||||
def setup
|
||||
@struct = Struct.new(:id => 5,
|
||||
:name => 'hello',
|
||||
:items => ['one', 'two'],
|
||||
:deleted => true,
|
||||
:emails => ['test@test.com'])
|
||||
end
|
||||
|
||||
def test_members
|
||||
assert_equal(5, Struct.members.size)
|
||||
assert_equal(Integer, Struct.members[:id].type_class)
|
||||
assert_equal(String, Struct.members[:name].type_class)
|
||||
assert_equal(String, Struct.members[:items].element_type.type_class)
|
||||
assert_equal(TrueClass, Struct.members[:deleted].type_class)
|
||||
assert_equal(String, Struct.members[:emails].element_type.type_class)
|
||||
end
|
||||
|
||||
def test_initializer_and_lookup
|
||||
assert_equal(5, @struct.id)
|
||||
assert_equal('hello', @struct.name)
|
||||
assert_equal(['one', 'two'], @struct.items)
|
||||
assert_equal(true, @struct.deleted)
|
||||
assert_equal(['test@test.com'], @struct.emails)
|
||||
assert_equal(5, @struct['id'])
|
||||
assert_equal('hello', @struct['name'])
|
||||
assert_equal(['one', 'two'], @struct['items'])
|
||||
assert_equal(true, @struct['deleted'])
|
||||
assert_equal(['test@test.com'], @struct['emails'])
|
||||
end
|
||||
|
||||
def test_each_pair
|
||||
@struct.each_pair do |name, value|
|
||||
assert_equal @struct.__send__(name), value
|
||||
assert_equal @struct[name], value
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,112 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/abstract_unit'
|
||||
require 'action_web_service/test_invoke'
|
||||
|
||||
class TestInvokeAPI < ActionWebService::API::Base
|
||||
api_method :null
|
||||
api_method :add, :expects => [:int, :int], :returns => [:int]
|
||||
end
|
||||
|
||||
class TestInvokeService < ActionWebService::Base
|
||||
web_service_api TestInvokeAPI
|
||||
|
||||
attr :invoked
|
||||
|
||||
def add(a, b)
|
||||
@invoked = true
|
||||
a + b
|
||||
end
|
||||
|
||||
def null
|
||||
end
|
||||
end
|
||||
|
||||
class TestController < ActionController::Base
|
||||
def rescue_action(e); raise e; end
|
||||
end
|
||||
|
||||
class TestInvokeDirectController < TestController
|
||||
web_service_api TestInvokeAPI
|
||||
|
||||
attr :invoked
|
||||
|
||||
def add
|
||||
@invoked = true
|
||||
@method_params[0] + @method_params[1]
|
||||
end
|
||||
|
||||
def null
|
||||
end
|
||||
end
|
||||
|
||||
class TestInvokeDelegatedController < TestController
|
||||
web_service_dispatching_mode :delegated
|
||||
web_service :service, TestInvokeService.new
|
||||
end
|
||||
|
||||
class TestInvokeLayeredController < TestController
|
||||
web_service_dispatching_mode :layered
|
||||
web_service(:one) { @service_one ||= TestInvokeService.new }
|
||||
web_service(:two) { @service_two ||= TestInvokeService.new }
|
||||
end
|
||||
|
||||
class TestInvokeTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_direct_add
|
||||
@controller = TestInvokeDirectController.new
|
||||
assert_equal nil, @controller.invoked
|
||||
result = invoke :add, 25, 25
|
||||
assert_equal 50, result
|
||||
assert_equal true, @controller.invoked
|
||||
end
|
||||
|
||||
def test_delegated_add
|
||||
@controller = TestInvokeDelegatedController.new
|
||||
assert_equal nil, @controller.web_service_object(:service).invoked
|
||||
result = invoke_delegated :service, :add, 100, 50
|
||||
assert_equal 150, result
|
||||
assert_equal true, @controller.web_service_object(:service).invoked
|
||||
end
|
||||
|
||||
def test_layered_add
|
||||
[:soap, :xmlrpc].each do |protocol|
|
||||
@protocol = protocol
|
||||
[:one, :two].each do |service|
|
||||
@controller = TestInvokeLayeredController.new
|
||||
assert_equal nil, @controller.web_service_object(service).invoked
|
||||
result = invoke_layered service, :add, 200, -50
|
||||
assert_equal 150, result
|
||||
assert_equal true, @controller.web_service_object(service).invoked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_layered_fail_with_wrong_number_of_arguments
|
||||
[:soap, :xmlrpc].each do |protocol|
|
||||
@protocol = protocol
|
||||
[:one, :two].each do |service|
|
||||
@controller = TestInvokeLayeredController.new
|
||||
assert_raise(ArgumentError) { invoke_layered service, :add, 1 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_delegated_fail_with_wrong_number_of_arguments
|
||||
@controller = TestInvokeDelegatedController.new
|
||||
assert_raise(ArgumentError) { invoke_delegated :service, :add, 1 }
|
||||
end
|
||||
|
||||
def test_direct_fail_with_wrong_number_of_arguments
|
||||
@controller = TestInvokeDirectController.new
|
||||
assert_raise(ArgumentError) { invoke :add, 1 }
|
||||
end
|
||||
|
||||
def test_with_no_parameters_declared
|
||||
@controller = TestInvokeDirectController.new
|
||||
assert_nil invoke(:null)
|
||||
end
|
||||
|
||||
end
|
309
vendor/gems/has_many_polymorphs-2.13/.specification
vendored
309
vendor/gems/has_many_polymorphs-2.13/.specification
vendored
|
@ -1,309 +0,0 @@
|
|||
--- !ruby/object:Gem::Specification
|
||||
name: has_many_polymorphs
|
||||
version: !ruby/object:Gem::Version
|
||||
hash: 25
|
||||
prerelease: false
|
||||
segments:
|
||||
- 2
|
||||
- 13
|
||||
version: "2.13"
|
||||
platform: ruby
|
||||
authors:
|
||||
- ""
|
||||
autorequire:
|
||||
bindir: bin
|
||||
cert_chain:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDLjCCAhagAwIBAgIBADANBgkqhkiG9w0BAQUFADA9MQ0wCwYDVQQDDARldmFu
|
||||
MRgwFgYKCZImiZPyLGQBGRYIY2xvdWRidXIxEjAQBgoJkiaJk/IsZAEZFgJzdDAe
|
||||
Fw0wNzA5MTYxMDMzMDBaFw0wODA5MTUxMDMzMDBaMD0xDTALBgNVBAMMBGV2YW4x
|
||||
GDAWBgoJkiaJk/IsZAEZFghjbG91ZGJ1cjESMBAGCgmSJomT8ixkARkWAnN0MIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5C0Io89nyApnr+PvbNFge9Vs
|
||||
yRWAlGBUEMahpXp28VrrfXZT0rAW7JBo4PlCE3jl4nE4dzE6gAdItSycjTosrw7A
|
||||
Ir5+xoyl4Vb35adv56TIQQXvNz+BzlqnkAY5JN0CSBRTQb6mxS3hFyD/h4qgDosj
|
||||
R2RFVzHqSxCS8xq4Ny8uzOwOi+Xyu4w67fI5JvnPvMxqrlR1eaIQHmxnf76RzC46
|
||||
QO5QhufjAYGGXd960XzbQsQyTDUYJzrvT7AdOfiyZzKQykKt8dEpDn+QPjFTnGnT
|
||||
QmgJBX5WJN0lHF2l1sbv3gh4Kn1tZu+kTUqeXY6ShAoDTyvZRiFqQdwh8w2lTQID
|
||||
AQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU+WqJz3xQ
|
||||
XSea1hRvvHWcIMgeeC4wDQYJKoZIhvcNAQEFBQADggEBAGLZ75jfOEW8Nsl26CTt
|
||||
JFrWxQTcQT/UljeefVE3xYr7lc9oQjbqO3FOyued3qW7TaNEtZfSHoYeUSMYbpw1
|
||||
XAwocIPuSRFDGM4B+hgQGVDx8PMGiJKom4qLXjO40UZsR7QyN/u869Vj45LURm6h
|
||||
MBcPeqCASI+WNprj9+uZa2kmHiitrFqqfMBNlm5IFbn9XeYSta9AHVvs5QQqV2m5
|
||||
hIPfLqCyxsn/YgOGvo6iwyQTWyTswamaAC3HRWZxIS1sfn/Ssqa7E7oQMkv5FAXr
|
||||
x5rKePfXINf8XTJczkl9OBEYdE9aNdJsJpXD0asLgGVwBICS5Bjohp6mizJcDC1+
|
||||
yZ0=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
date: 2009-02-02 00:00:00 +01:00
|
||||
default_executable:
|
||||
dependencies:
|
||||
- !ruby/object:Gem::Dependency
|
||||
name: activerecord
|
||||
prerelease: false
|
||||
requirement: &id001 !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 3
|
||||
segments:
|
||||
- 0
|
||||
version: "0"
|
||||
type: :runtime
|
||||
version_requirements: *id001
|
||||
- !ruby/object:Gem::Dependency
|
||||
name: echoe
|
||||
prerelease: false
|
||||
requirement: &id002 !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 3
|
||||
segments:
|
||||
- 0
|
||||
version: "0"
|
||||
type: :development
|
||||
version_requirements: *id002
|
||||
description: An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
|
||||
email: ""
|
||||
executables: []
|
||||
|
||||
extensions: []
|
||||
|
||||
extra_rdoc_files:
|
||||
- CHANGELOG
|
||||
- generators/tagging/templates/migration.rb
|
||||
- generators/tagging/templates/tag.rb
|
||||
- generators/tagging/templates/tagging.rb
|
||||
- generators/tagging/templates/tagging_extensions.rb
|
||||
- lib/has_many_polymorphs/association.rb
|
||||
- lib/has_many_polymorphs/autoload.rb
|
||||
- lib/has_many_polymorphs/class_methods.rb
|
||||
- lib/has_many_polymorphs/configuration.rb
|
||||
- lib/has_many_polymorphs/reflection.rb
|
||||
- LICENSE
|
||||
- README
|
||||
- test/integration/app/doc/README_FOR_APP
|
||||
- test/integration/app/README
|
||||
- TODO
|
||||
files:
|
||||
- CHANGELOG
|
||||
- examples/hmph.rb
|
||||
- generators/tagging/tagging_generator.rb
|
||||
- generators/tagging/templates/migration.rb
|
||||
- generators/tagging/templates/tag.rb
|
||||
- generators/tagging/templates/tag_test.rb
|
||||
- generators/tagging/templates/tagging.rb
|
||||
- generators/tagging/templates/tagging_extensions.rb
|
||||
- generators/tagging/templates/tagging_test.rb
|
||||
- generators/tagging/templates/taggings.yml
|
||||
- generators/tagging/templates/tags.yml
|
||||
- init.rb
|
||||
- lib/has_many_polymorphs/association.rb
|
||||
- lib/has_many_polymorphs/autoload.rb
|
||||
- lib/has_many_polymorphs/base.rb
|
||||
- lib/has_many_polymorphs/class_methods.rb
|
||||
- lib/has_many_polymorphs/configuration.rb
|
||||
- lib/has_many_polymorphs/debugging_tools.rb
|
||||
- lib/has_many_polymorphs/rake_task_redefine_task.rb
|
||||
- lib/has_many_polymorphs/reflection.rb
|
||||
- lib/has_many_polymorphs/support_methods.rb
|
||||
- lib/has_many_polymorphs.rb
|
||||
- LICENSE
|
||||
- Manifest
|
||||
- Rakefile
|
||||
- README
|
||||
- test/fixtures/bow_wows.yml
|
||||
- test/fixtures/cats.yml
|
||||
- test/fixtures/eaters_foodstuffs.yml
|
||||
- test/fixtures/fish.yml
|
||||
- test/fixtures/frogs.yml
|
||||
- test/fixtures/keep_your_enemies_close.yml
|
||||
- test/fixtures/little_whale_pupils.yml
|
||||
- test/fixtures/people.yml
|
||||
- test/fixtures/petfoods.yml
|
||||
- test/fixtures/whales.yml
|
||||
- test/fixtures/wild_boars.yml
|
||||
- test/generator/tagging_generator_test.rb
|
||||
- test/integration/app/app/controllers/application.rb
|
||||
- test/integration/app/app/controllers/bones_controller.rb
|
||||
- test/integration/app/app/helpers/addresses_helper.rb
|
||||
- test/integration/app/app/helpers/application_helper.rb
|
||||
- test/integration/app/app/helpers/bones_helper.rb
|
||||
- test/integration/app/app/helpers/sellers_helper.rb
|
||||
- test/integration/app/app/helpers/states_helper.rb
|
||||
- test/integration/app/app/helpers/users_helper.rb
|
||||
- test/integration/app/app/models/bone.rb
|
||||
- test/integration/app/app/models/double_sti_parent.rb
|
||||
- test/integration/app/app/models/double_sti_parent_relationship.rb
|
||||
- test/integration/app/app/models/organic_substance.rb
|
||||
- test/integration/app/app/models/single_sti_parent.rb
|
||||
- test/integration/app/app/models/single_sti_parent_relationship.rb
|
||||
- test/integration/app/app/models/stick.rb
|
||||
- test/integration/app/app/models/stone.rb
|
||||
- test/integration/app/app/views/addresses/edit.html.erb
|
||||
- test/integration/app/app/views/addresses/index.html.erb
|
||||
- test/integration/app/app/views/addresses/new.html.erb
|
||||
- test/integration/app/app/views/addresses/show.html.erb
|
||||
- test/integration/app/app/views/bones/index.rhtml
|
||||
- test/integration/app/app/views/layouts/addresses.html.erb
|
||||
- test/integration/app/app/views/layouts/sellers.html.erb
|
||||
- test/integration/app/app/views/layouts/states.html.erb
|
||||
- test/integration/app/app/views/layouts/users.html.erb
|
||||
- test/integration/app/app/views/sellers/edit.html.erb
|
||||
- test/integration/app/app/views/sellers/index.html.erb
|
||||
- test/integration/app/app/views/sellers/new.html.erb
|
||||
- test/integration/app/app/views/sellers/show.html.erb
|
||||
- test/integration/app/app/views/states/edit.html.erb
|
||||
- test/integration/app/app/views/states/index.html.erb
|
||||
- test/integration/app/app/views/states/new.html.erb
|
||||
- test/integration/app/app/views/states/show.html.erb
|
||||
- test/integration/app/app/views/users/edit.html.erb
|
||||
- test/integration/app/app/views/users/index.html.erb
|
||||
- test/integration/app/app/views/users/new.html.erb
|
||||
- test/integration/app/app/views/users/show.html.erb
|
||||
- test/integration/app/config/boot.rb
|
||||
- test/integration/app/config/database.yml
|
||||
- test/integration/app/config/environment.rb
|
||||
- test/integration/app/config/environment.rb.canonical
|
||||
- test/integration/app/config/environments/development.rb
|
||||
- test/integration/app/config/environments/production.rb
|
||||
- test/integration/app/config/environments/test.rb
|
||||
- test/integration/app/config/locomotive.yml
|
||||
- test/integration/app/config/routes.rb
|
||||
- test/integration/app/config/ultrasphinx/default.base
|
||||
- test/integration/app/config/ultrasphinx/development.conf.canonical
|
||||
- test/integration/app/db/migrate/001_create_sticks.rb
|
||||
- test/integration/app/db/migrate/002_create_stones.rb
|
||||
- test/integration/app/db/migrate/003_create_organic_substances.rb
|
||||
- test/integration/app/db/migrate/004_create_bones.rb
|
||||
- test/integration/app/db/migrate/005_create_single_sti_parents.rb
|
||||
- test/integration/app/db/migrate/006_create_double_sti_parents.rb
|
||||
- test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb
|
||||
- test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb
|
||||
- test/integration/app/db/migrate/009_create_library_model.rb
|
||||
- test/integration/app/doc/README_FOR_APP
|
||||
- test/integration/app/generators/commenting_generator_test.rb
|
||||
- test/integration/app/lib/library_model.rb
|
||||
- test/integration/app/public/404.html
|
||||
- test/integration/app/public/500.html
|
||||
- test/integration/app/public/dispatch.cgi
|
||||
- test/integration/app/public/dispatch.fcgi
|
||||
- test/integration/app/public/dispatch.rb
|
||||
- test/integration/app/public/favicon.ico
|
||||
- test/integration/app/public/images/rails.png
|
||||
- test/integration/app/public/index.html
|
||||
- test/integration/app/public/javascripts/application.js
|
||||
- test/integration/app/public/javascripts/controls.js
|
||||
- test/integration/app/public/javascripts/dragdrop.js
|
||||
- test/integration/app/public/javascripts/effects.js
|
||||
- test/integration/app/public/javascripts/prototype.js
|
||||
- test/integration/app/public/robots.txt
|
||||
- test/integration/app/public/stylesheets/scaffold.css
|
||||
- test/integration/app/Rakefile
|
||||
- test/integration/app/README
|
||||
- test/integration/app/script/about
|
||||
- test/integration/app/script/breakpointer
|
||||
- test/integration/app/script/console
|
||||
- test/integration/app/script/destroy
|
||||
- test/integration/app/script/generate
|
||||
- test/integration/app/script/performance/benchmarker
|
||||
- test/integration/app/script/performance/profiler
|
||||
- test/integration/app/script/plugin
|
||||
- test/integration/app/script/process/inspector
|
||||
- test/integration/app/script/process/reaper
|
||||
- test/integration/app/script/process/spawner
|
||||
- test/integration/app/script/runner
|
||||
- test/integration/app/script/server
|
||||
- test/integration/app/test/fixtures/double_sti_parent_relationships.yml
|
||||
- test/integration/app/test/fixtures/double_sti_parents.yml
|
||||
- test/integration/app/test/fixtures/organic_substances.yml
|
||||
- test/integration/app/test/fixtures/single_sti_parent_relationships.yml
|
||||
- test/integration/app/test/fixtures/single_sti_parents.yml
|
||||
- test/integration/app/test/fixtures/sticks.yml
|
||||
- test/integration/app/test/fixtures/stones.yml
|
||||
- test/integration/app/test/functional/addresses_controller_test.rb
|
||||
- test/integration/app/test/functional/bones_controller_test.rb
|
||||
- test/integration/app/test/functional/sellers_controller_test.rb
|
||||
- test/integration/app/test/functional/states_controller_test.rb
|
||||
- test/integration/app/test/functional/users_controller_test.rb
|
||||
- test/integration/app/test/test_helper.rb
|
||||
- test/integration/app/test/unit/bone_test.rb
|
||||
- test/integration/app/test/unit/double_sti_parent_relationship_test.rb
|
||||
- test/integration/app/test/unit/double_sti_parent_test.rb
|
||||
- test/integration/app/test/unit/organic_substance_test.rb
|
||||
- test/integration/app/test/unit/single_sti_parent_relationship_test.rb
|
||||
- test/integration/app/test/unit/single_sti_parent_test.rb
|
||||
- test/integration/app/test/unit/stick_test.rb
|
||||
- test/integration/app/test/unit/stone_test.rb
|
||||
- test/integration/server_test.rb
|
||||
- test/models/aquatic/fish.rb
|
||||
- test/models/aquatic/pupils_whale.rb
|
||||
- test/models/aquatic/whale.rb
|
||||
- test/models/beautiful_fight_relationship.rb
|
||||
- test/models/canine.rb
|
||||
- test/models/cat.rb
|
||||
- test/models/dog.rb
|
||||
- test/models/eaters_foodstuff.rb
|
||||
- test/models/frog.rb
|
||||
- test/models/kitten.rb
|
||||
- test/models/parentship.rb
|
||||
- test/models/person.rb
|
||||
- test/models/petfood.rb
|
||||
- test/models/tabby.rb
|
||||
- test/models/wild_boar.rb
|
||||
- test/modules/extension_module.rb
|
||||
- test/modules/other_extension_module.rb
|
||||
- test/patches/symlinked_plugins_1.2.6.diff
|
||||
- test/schema.rb
|
||||
- test/setup.rb
|
||||
- test/test_helper.rb
|
||||
- test/unit/has_many_polymorphs_test.rb
|
||||
- TODO
|
||||
- has_many_polymorphs.gemspec
|
||||
has_rdoc: true
|
||||
homepage: http://blog.evanweaver.com/files/doc/fauna/has_many_polymorphs/
|
||||
licenses: []
|
||||
|
||||
post_install_message:
|
||||
rdoc_options:
|
||||
- --line-numbers
|
||||
- --inline-source
|
||||
- --title
|
||||
- Has_many_polymorphs
|
||||
- --main
|
||||
- README
|
||||
require_paths:
|
||||
- lib
|
||||
required_ruby_version: !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 3
|
||||
segments:
|
||||
- 0
|
||||
version: "0"
|
||||
required_rubygems_version: !ruby/object:Gem::Requirement
|
||||
none: false
|
||||
requirements:
|
||||
- - ">="
|
||||
- !ruby/object:Gem::Version
|
||||
hash: 11
|
||||
segments:
|
||||
- 1
|
||||
- 2
|
||||
version: "1.2"
|
||||
requirements: []
|
||||
|
||||
rubyforge_project: fauna
|
||||
rubygems_version: 1.3.7
|
||||
signing_key:
|
||||
specification_version: 2
|
||||
summary: An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
|
||||
test_files:
|
||||
- test/generator/tagging_generator_test.rb
|
||||
- test/integration/server_test.rb
|
||||
- test/unit/has_many_polymorphs_test.rb
|
84
vendor/gems/has_many_polymorphs-2.13/CHANGELOG
vendored
84
vendor/gems/has_many_polymorphs-2.13/CHANGELOG
vendored
|
@ -1,84 +0,0 @@
|
|||
v2.13. Merge various fixes for Rails 2.2.2.
|
||||
|
||||
v2.12. Improvements to the test suite; bugfixes for STI children (rsl). Remove fancy dependency system in favor of using Dispatcher::to_prepare every time.
|
||||
|
||||
v2.11. Rails 1.2.6 tagging generator compatibility; change test suite to use included integration app.
|
||||
|
||||
v2.10. Add :parent_conditions option; bugfix for nullified conditions; bugfix for self-referential tagging generator; allow setting of has_many_polymorphs_options hash in Configuration's after_initialize if you need to adjust the autoload behavior; clear error message on missing or improperly namespaced models; fix .build on double-sided relationships; add :namespace key for easier set up of Camping apps or other unusual class structures.
|
||||
|
||||
v2.9. Gem version renumbering; my apologies if this messes anyone up.
|
||||
|
||||
v2.8. RDoc documentation; repository relocation; Rakefile cleanup; remove deprecated plugin-specific class caching.
|
||||
|
||||
v2.7.5. Various bugfixes; Postgres problems may remain on edge.
|
||||
|
||||
v2.7.3. Use new :source and :source_type options in 1.2.3 (David Lemstra); fix pluralization bug; add some tests; experimental tagging generator.
|
||||
|
||||
v2.7.2. Deprecate has_many_polymorphs_cache_classes= option because it doesn't really work. Use config.cache_classes= instead to cache all reloadable items.
|
||||
|
||||
v2.7.1. Dispatcher.to_prepare didn't fire in the console; now using a config.after_initialize wrapper instead.
|
||||
|
||||
v2.7. Dependency injection framework elimates having to care about load order.
|
||||
|
||||
v2.6. Make the logger act sane for the gem version.
|
||||
|
||||
v2.5.2. Allow :skip_duplicates on double relationships.
|
||||
|
||||
v2.5.1. Renamed :ignore_duplicates to :skip_duplicates to better express its non-passive behavior; made sure not to load target set on push unless necessary.
|
||||
|
||||
v2.5. Activerecord compatibility branch becomes trunk: extra options now supported for double polymorphism; conditions nulled-out and propogated to child relationships; more tests; new :ignore_duplicates option on macro can be set to false if you want << to push duplicate associations.
|
||||
|
||||
v2.4.1. Code split into multiple files; tests added for pluralization check; Rails 1.1.6 no longer supported.
|
||||
|
||||
v2.4. Unlimited mixed class association extensions for both single and double targets and joins.
|
||||
|
||||
v2.3. Gem version
|
||||
|
||||
v2.2. API change; prefix on methods is now singular when using :rename_individual_collections.
|
||||
|
||||
v2.1. Add configuration option to cache polymorphic classes in development mode.
|
||||
|
||||
v2.0. Collection methods (push, delete, clear) now on individual collections.
|
||||
|
||||
v1.9.2. Disjoint collection sides bugfix, don't raise on new records.
|
||||
|
||||
v1.9.1. Double classify bugfix.
|
||||
|
||||
v1.9. Large changes to properly support double polymorphism.
|
||||
|
||||
v1.8.2. Bugfix to make sure the type gets checked on doubly polymorphic parents.
|
||||
|
||||
v1.8.1. Bugfix for sqlite3 child attribute retrieval.
|
||||
|
||||
v1.8. Bugfix for instantiating attributes of namespaced models.
|
||||
|
||||
v1.7.1. Bugfix for double polymorphic relationships.
|
||||
|
||||
v1.7. Double polymorphic relationships (includes new API method).
|
||||
|
||||
v1.6. Namespaced model support.
|
||||
|
||||
v1.5. Bugfix for Postgres and Mysql under 1.1.6; refactored tests (hildofur); properly handles legacy table names set with set_table_name().
|
||||
|
||||
v1.4. STI support added (use the child class names, not the base class).
|
||||
|
||||
v1.3. Bug regarding table names with underscores in SQL query fixed.
|
||||
|
||||
v1.2. License change, again.
|
||||
|
||||
v1.1. File_column bug fixed.
|
||||
|
||||
v1.0. Tests written; after_find and after_initialize now correctly called.
|
||||
|
||||
v0.5. SQL performance enhancements added.
|
||||
|
||||
v0.4. Rewrote singletons as full-fledged proxy class so that marshalling works (e.g. in the session).
|
||||
|
||||
v0.3. Caching added.
|
||||
|
||||
v0.2. Fixed dependency reloading problem in development mode.
|
||||
|
||||
v0.1. License change.
|
||||
|
||||
v0. Added :dependent support on the join table; no changelog before this version.
|
||||
|
184
vendor/gems/has_many_polymorphs-2.13/LICENSE
vendored
184
vendor/gems/has_many_polymorphs-2.13/LICENSE
vendored
|
@ -1,184 +0,0 @@
|
|||
Academic Free License (AFL) v. 3.0
|
||||
|
||||
This Academic Free License (the "License") applies to any original work
|
||||
of authorship (the "Original Work") whose owner (the "Licensor") has
|
||||
placed the following licensing notice adjacent to the copyright notice
|
||||
for the Original Work:
|
||||
|
||||
Licensed under the Academic Free License version 3.0
|
||||
|
||||
1) Grant of Copyright License. Licensor grants You a worldwide,
|
||||
royalty-free, non-exclusive, sublicensable license, for the duration of
|
||||
the copyright, to do the following:
|
||||
|
||||
a) to reproduce the Original Work in copies, either alone or as part of
|
||||
a collective work;
|
||||
|
||||
b) to translate, adapt, alter, transform, modify, or arrange the
|
||||
Original Work, thereby creating derivative works ("Derivative Works")
|
||||
based upon the Original Work;
|
||||
|
||||
c) to distribute or communicate copies of the Original Work and
|
||||
Derivative Works to the public, under any license of your choice that
|
||||
does not contradict the terms and conditions, including Licensor's
|
||||
reserved rights and remedies, in this Academic Free License;
|
||||
|
||||
d) to perform the Original Work publicly; and
|
||||
|
||||
e) to display the Original Work publicly.
|
||||
|
||||
2) Grant of Patent License. Licensor grants You a worldwide,
|
||||
royalty-free, non-exclusive, sublicensable license, under patent claims
|
||||
owned or controlled by the Licensor that are embodied in the Original
|
||||
Work as furnished by the Licensor, for the duration of the patents, to
|
||||
make, use, sell, offer for sale, have made, and import the Original Work
|
||||
and Derivative Works.
|
||||
|
||||
3) Grant of Source Code License. The term "Source Code" means the
|
||||
preferred form of the Original Work for making modifications to it and
|
||||
all available documentation describing how to modify the Original Work.
|
||||
Licensor agrees to provide a machine-readable copy of the Source Code of
|
||||
the Original Work along with each copy of the Original Work that
|
||||
Licensor distributes. Licensor reserves the right to satisfy this
|
||||
obligation by placing a machine-readable copy of the Source Code in an
|
||||
information repository reasonably calculated to permit inexpensive and
|
||||
convenient access by You for as long as Licensor continues to distribute
|
||||
the Original Work.
|
||||
|
||||
4) Exclusions From License Grant. Neither the names of Licensor, nor the
|
||||
names of any contributors to the Original Work, nor any of their
|
||||
trademarks or service marks, may be used to endorse or promote products
|
||||
derived from this Original Work without express prior permission of the
|
||||
Licensor. Except as expressly stated herein, nothing in this License
|
||||
grants any license to Licensor's trademarks, copyrights, patents, trade
|
||||
secrets or any other intellectual property. No patent license is granted
|
||||
to make, use, sell, offer for sale, have made, or import embodiments of
|
||||
any patent claims other than the licensed claims defined in Section 2.
|
||||
No license is granted to the trademarks of Licensor even if such marks
|
||||
are included in the Original Work. Nothing in this License shall be
|
||||
interpreted to prohibit Licensor from licensing under terms different
|
||||
from this License any Original Work that Licensor otherwise would have a
|
||||
right to license.
|
||||
|
||||
5) External Deployment. The term "External Deployment" means the use,
|
||||
distribution, or communication of the Original Work or Derivative Works
|
||||
in any way such that the Original Work or Derivative Works may be used
|
||||
by anyone other than You, whether those works are distributed or
|
||||
communicated to those persons or made available as an application
|
||||
intended for use over a network. As an express condition for the grants
|
||||
of license hereunder, You must treat any External Deployment by You of
|
||||
the Original Work or a Derivative Work as a distribution under section
|
||||
1(c).
|
||||
|
||||
6) Attribution Rights. You must retain, in the Source Code of any
|
||||
Derivative Works that You create, all copyright, patent, or trademark
|
||||
notices from the Source Code of the Original Work, as well as any
|
||||
notices of licensing and any descriptive text identified therein as an
|
||||
"Attribution Notice." You must cause the Source Code for any Derivative
|
||||
Works that You create to carry a prominent Attribution Notice reasonably
|
||||
calculated to inform recipients that You have modified the Original
|
||||
Work.
|
||||
|
||||
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants
|
||||
that the copyright in and to the Original Work and the patent rights
|
||||
granted herein by Licensor are owned by the Licensor or are sublicensed
|
||||
to You under the terms of this License with the permission of the
|
||||
contributor(s) of those copyrights and patent rights. Except as
|
||||
expressly stated in the immediately preceding sentence, the Original
|
||||
Work is provided under this License on an "AS IS" BASIS and WITHOUT
|
||||
WARRANTY, either express or implied, including, without limitation, the
|
||||
warranties of non-infringement, merchantability or fitness for a
|
||||
particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
|
||||
WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential
|
||||
part of this License. No license to the Original Work is granted by this
|
||||
License except under this disclaimer.
|
||||
|
||||
8) Limitation of Liability. Under no circumstances and under no legal
|
||||
theory, whether in tort (including negligence), contract, or otherwise,
|
||||
shall the Licensor be liable to anyone for any indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or the use of the Original Work including,
|
||||
without limitation, damages for loss of goodwill, work stoppage,
|
||||
computer failure or malfunction, or any and all other commercial damages
|
||||
or losses. This limitation of liability shall not apply to the extent
|
||||
applicable law prohibits such limitation.
|
||||
|
||||
9) Acceptance and Termination. If, at any time, You expressly assented
|
||||
to this License, that assent indicates your clear and irrevocable
|
||||
acceptance of this License and all of its terms and conditions. If You
|
||||
distribute or communicate copies of the Original Work or a Derivative
|
||||
Work, You must make a reasonable effort under the circumstances to
|
||||
obtain the express assent of recipients to the terms of this License.
|
||||
This License conditions your rights to undertake the activities listed
|
||||
in Section 1, including your right to create Derivative Works based upon
|
||||
the Original Work, and doing so without honoring these terms and
|
||||
conditions is prohibited by copyright law and international treaty.
|
||||
Nothing in this License is intended to affect copyright exceptions and
|
||||
limitations (including "fair use" or "fair dealing"). This License shall
|
||||
terminate immediately and You may no longer exercise any of the rights
|
||||
granted to You by this License upon your failure to honor the conditions
|
||||
in Section 1(c).
|
||||
|
||||
10) Termination for Patent Action. This License shall terminate
|
||||
automatically and You may no longer exercise any of the rights granted
|
||||
to You by this License as of the date You commence an action, including
|
||||
a cross-claim or counterclaim, against Licensor or any licensee alleging
|
||||
that the Original Work infringes a patent. This termination provision
|
||||
shall not apply for an action alleging patent infringement by
|
||||
combinations of the Original Work with other software or hardware.
|
||||
|
||||
11) Jurisdiction, Venue and Governing Law. Any action or suit relating
|
||||
to this License may be brought only in the courts of a jurisdiction
|
||||
wherein the Licensor resides or in which Licensor conducts its primary
|
||||
business, and under the laws of that jurisdiction excluding its
|
||||
conflict-of-law provisions. The application of the United Nations
|
||||
Convention on Contracts for the International Sale of Goods is expressly
|
||||
excluded. Any use of the Original Work outside the scope of this License
|
||||
or after its termination shall be subject to the requirements and
|
||||
penalties of copyright or patent law in the appropriate jurisdiction.
|
||||
This section shall survive the termination of this License.
|
||||
|
||||
12) Attorneys' Fees. In any action to enforce the terms of this License
|
||||
or seeking damages relating thereto, the prevailing party shall be
|
||||
entitled to recover its costs and expenses, including, without
|
||||
limitation, reasonable attorneys' fees and costs incurred in connection
|
||||
with such action, including any appeal of such action. This section
|
||||
shall survive the termination of this License.
|
||||
|
||||
13) Miscellaneous. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable.
|
||||
|
||||
14) Definition of "You" in This License. "You" throughout this License,
|
||||
whether in upper or lower case, means an individual or a legal entity
|
||||
exercising rights under, and complying with all of the terms of, this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with you. For purposes of this
|
||||
definition, "control" means (i) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
15) Right to Use. You may use the Original Work in all ways not
|
||||
otherwise restricted or conditioned by this License or by law, and
|
||||
Licensor promises not to interfere with or be responsible for such uses
|
||||
by You.
|
||||
|
||||
16) Modification of This License. This License is Copyright (c) 2005
|
||||
Lawrence Rosen. Permission is granted to copy, distribute, or
|
||||
communicate this License without modification. Nothing in this License
|
||||
permits You to modify this License as applied to the Original Work or to
|
||||
Derivative Works. However, You may modify the text of this License and
|
||||
copy, distribute or communicate your modified version (the "Modified
|
||||
License") and apply it to other original works of authorship subject to
|
||||
the following conditions: (i) You may not indicate in any way that your
|
||||
Modified License is the "Academic Free License" or "AFL" and you may not
|
||||
use those names in the name of your Modified License; (ii) You must
|
||||
replace the notice specified in the first paragraph above with the
|
||||
notice "Licensed under <insert your license name here>" or with a notice
|
||||
of your own that is not confusingly similar to the notice in this
|
||||
License; and (iii) You may not claim that your original works are open
|
||||
source software unless your Modified License has been approved by Open
|
||||
Source Initiative (OSI) and You comply with its license review and
|
||||
certification process.
|
||||
|
173
vendor/gems/has_many_polymorphs-2.13/Manifest
vendored
173
vendor/gems/has_many_polymorphs-2.13/Manifest
vendored
|
@ -1,173 +0,0 @@
|
|||
CHANGELOG
|
||||
examples/hmph.rb
|
||||
generators/tagging/tagging_generator.rb
|
||||
generators/tagging/templates/migration.rb
|
||||
generators/tagging/templates/tag.rb
|
||||
generators/tagging/templates/tag_test.rb
|
||||
generators/tagging/templates/tagging.rb
|
||||
generators/tagging/templates/tagging_extensions.rb
|
||||
generators/tagging/templates/tagging_test.rb
|
||||
generators/tagging/templates/taggings.yml
|
||||
generators/tagging/templates/tags.yml
|
||||
init.rb
|
||||
lib/has_many_polymorphs/association.rb
|
||||
lib/has_many_polymorphs/autoload.rb
|
||||
lib/has_many_polymorphs/base.rb
|
||||
lib/has_many_polymorphs/class_methods.rb
|
||||
lib/has_many_polymorphs/configuration.rb
|
||||
lib/has_many_polymorphs/debugging_tools.rb
|
||||
lib/has_many_polymorphs/rake_task_redefine_task.rb
|
||||
lib/has_many_polymorphs/reflection.rb
|
||||
lib/has_many_polymorphs/support_methods.rb
|
||||
lib/has_many_polymorphs.rb
|
||||
LICENSE
|
||||
Manifest
|
||||
Rakefile
|
||||
README
|
||||
test/fixtures/bow_wows.yml
|
||||
test/fixtures/cats.yml
|
||||
test/fixtures/eaters_foodstuffs.yml
|
||||
test/fixtures/fish.yml
|
||||
test/fixtures/frogs.yml
|
||||
test/fixtures/keep_your_enemies_close.yml
|
||||
test/fixtures/little_whale_pupils.yml
|
||||
test/fixtures/people.yml
|
||||
test/fixtures/petfoods.yml
|
||||
test/fixtures/whales.yml
|
||||
test/fixtures/wild_boars.yml
|
||||
test/generator/tagging_generator_test.rb
|
||||
test/integration/app/app/controllers/application.rb
|
||||
test/integration/app/app/controllers/bones_controller.rb
|
||||
test/integration/app/app/helpers/addresses_helper.rb
|
||||
test/integration/app/app/helpers/application_helper.rb
|
||||
test/integration/app/app/helpers/bones_helper.rb
|
||||
test/integration/app/app/helpers/sellers_helper.rb
|
||||
test/integration/app/app/helpers/states_helper.rb
|
||||
test/integration/app/app/helpers/users_helper.rb
|
||||
test/integration/app/app/models/bone.rb
|
||||
test/integration/app/app/models/double_sti_parent.rb
|
||||
test/integration/app/app/models/double_sti_parent_relationship.rb
|
||||
test/integration/app/app/models/organic_substance.rb
|
||||
test/integration/app/app/models/single_sti_parent.rb
|
||||
test/integration/app/app/models/single_sti_parent_relationship.rb
|
||||
test/integration/app/app/models/stick.rb
|
||||
test/integration/app/app/models/stone.rb
|
||||
test/integration/app/app/views/addresses/edit.html.erb
|
||||
test/integration/app/app/views/addresses/index.html.erb
|
||||
test/integration/app/app/views/addresses/new.html.erb
|
||||
test/integration/app/app/views/addresses/show.html.erb
|
||||
test/integration/app/app/views/bones/index.rhtml
|
||||
test/integration/app/app/views/layouts/addresses.html.erb
|
||||
test/integration/app/app/views/layouts/sellers.html.erb
|
||||
test/integration/app/app/views/layouts/states.html.erb
|
||||
test/integration/app/app/views/layouts/users.html.erb
|
||||
test/integration/app/app/views/sellers/edit.html.erb
|
||||
test/integration/app/app/views/sellers/index.html.erb
|
||||
test/integration/app/app/views/sellers/new.html.erb
|
||||
test/integration/app/app/views/sellers/show.html.erb
|
||||
test/integration/app/app/views/states/edit.html.erb
|
||||
test/integration/app/app/views/states/index.html.erb
|
||||
test/integration/app/app/views/states/new.html.erb
|
||||
test/integration/app/app/views/states/show.html.erb
|
||||
test/integration/app/app/views/users/edit.html.erb
|
||||
test/integration/app/app/views/users/index.html.erb
|
||||
test/integration/app/app/views/users/new.html.erb
|
||||
test/integration/app/app/views/users/show.html.erb
|
||||
test/integration/app/config/boot.rb
|
||||
test/integration/app/config/database.yml
|
||||
test/integration/app/config/environment.rb
|
||||
test/integration/app/config/environment.rb.canonical
|
||||
test/integration/app/config/environments/development.rb
|
||||
test/integration/app/config/environments/production.rb
|
||||
test/integration/app/config/environments/test.rb
|
||||
test/integration/app/config/locomotive.yml
|
||||
test/integration/app/config/routes.rb
|
||||
test/integration/app/config/ultrasphinx/default.base
|
||||
test/integration/app/config/ultrasphinx/development.conf.canonical
|
||||
test/integration/app/db/migrate/001_create_sticks.rb
|
||||
test/integration/app/db/migrate/002_create_stones.rb
|
||||
test/integration/app/db/migrate/003_create_organic_substances.rb
|
||||
test/integration/app/db/migrate/004_create_bones.rb
|
||||
test/integration/app/db/migrate/005_create_single_sti_parents.rb
|
||||
test/integration/app/db/migrate/006_create_double_sti_parents.rb
|
||||
test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb
|
||||
test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb
|
||||
test/integration/app/db/migrate/009_create_library_model.rb
|
||||
test/integration/app/doc/README_FOR_APP
|
||||
test/integration/app/generators/commenting_generator_test.rb
|
||||
test/integration/app/lib/library_model.rb
|
||||
test/integration/app/public/404.html
|
||||
test/integration/app/public/500.html
|
||||
test/integration/app/public/dispatch.cgi
|
||||
test/integration/app/public/dispatch.fcgi
|
||||
test/integration/app/public/dispatch.rb
|
||||
test/integration/app/public/favicon.ico
|
||||
test/integration/app/public/images/rails.png
|
||||
test/integration/app/public/index.html
|
||||
test/integration/app/public/javascripts/application.js
|
||||
test/integration/app/public/javascripts/controls.js
|
||||
test/integration/app/public/javascripts/dragdrop.js
|
||||
test/integration/app/public/javascripts/effects.js
|
||||
test/integration/app/public/javascripts/prototype.js
|
||||
test/integration/app/public/robots.txt
|
||||
test/integration/app/public/stylesheets/scaffold.css
|
||||
test/integration/app/Rakefile
|
||||
test/integration/app/README
|
||||
test/integration/app/script/about
|
||||
test/integration/app/script/breakpointer
|
||||
test/integration/app/script/console
|
||||
test/integration/app/script/destroy
|
||||
test/integration/app/script/generate
|
||||
test/integration/app/script/performance/benchmarker
|
||||
test/integration/app/script/performance/profiler
|
||||
test/integration/app/script/plugin
|
||||
test/integration/app/script/process/inspector
|
||||
test/integration/app/script/process/reaper
|
||||
test/integration/app/script/process/spawner
|
||||
test/integration/app/script/runner
|
||||
test/integration/app/script/server
|
||||
test/integration/app/test/fixtures/double_sti_parent_relationships.yml
|
||||
test/integration/app/test/fixtures/double_sti_parents.yml
|
||||
test/integration/app/test/fixtures/organic_substances.yml
|
||||
test/integration/app/test/fixtures/single_sti_parent_relationships.yml
|
||||
test/integration/app/test/fixtures/single_sti_parents.yml
|
||||
test/integration/app/test/fixtures/sticks.yml
|
||||
test/integration/app/test/fixtures/stones.yml
|
||||
test/integration/app/test/functional/addresses_controller_test.rb
|
||||
test/integration/app/test/functional/bones_controller_test.rb
|
||||
test/integration/app/test/functional/sellers_controller_test.rb
|
||||
test/integration/app/test/functional/states_controller_test.rb
|
||||
test/integration/app/test/functional/users_controller_test.rb
|
||||
test/integration/app/test/test_helper.rb
|
||||
test/integration/app/test/unit/bone_test.rb
|
||||
test/integration/app/test/unit/double_sti_parent_relationship_test.rb
|
||||
test/integration/app/test/unit/double_sti_parent_test.rb
|
||||
test/integration/app/test/unit/organic_substance_test.rb
|
||||
test/integration/app/test/unit/single_sti_parent_relationship_test.rb
|
||||
test/integration/app/test/unit/single_sti_parent_test.rb
|
||||
test/integration/app/test/unit/stick_test.rb
|
||||
test/integration/app/test/unit/stone_test.rb
|
||||
test/integration/server_test.rb
|
||||
test/models/aquatic/fish.rb
|
||||
test/models/aquatic/pupils_whale.rb
|
||||
test/models/aquatic/whale.rb
|
||||
test/models/beautiful_fight_relationship.rb
|
||||
test/models/canine.rb
|
||||
test/models/cat.rb
|
||||
test/models/dog.rb
|
||||
test/models/eaters_foodstuff.rb
|
||||
test/models/frog.rb
|
||||
test/models/kitten.rb
|
||||
test/models/parentship.rb
|
||||
test/models/person.rb
|
||||
test/models/petfood.rb
|
||||
test/models/tabby.rb
|
||||
test/models/wild_boar.rb
|
||||
test/modules/extension_module.rb
|
||||
test/modules/other_extension_module.rb
|
||||
test/patches/symlinked_plugins_1.2.6.diff
|
||||
test/schema.rb
|
||||
test/setup.rb
|
||||
test/test_helper.rb
|
||||
test/unit/has_many_polymorphs_test.rb
|
||||
TODO
|
205
vendor/gems/has_many_polymorphs-2.13/README
vendored
205
vendor/gems/has_many_polymorphs-2.13/README
vendored
|
@ -1,205 +0,0 @@
|
|||
Has_many_polymorphs
|
||||
|
||||
An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
|
||||
|
||||
== License
|
||||
|
||||
Copyright 2006-2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file.
|
||||
|
||||
The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
|
||||
|
||||
If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
|
||||
|
||||
== Description
|
||||
|
||||
This plugin lets you define self-referential and double-sided polymorphic associations in your models. It is an extension of <tt>has_many :through</tt>.
|
||||
|
||||
“Polymorphic” means an association can freely point to any of several unrelated model classes, instead of being tied to one particular class.
|
||||
|
||||
== Features
|
||||
|
||||
* self-references
|
||||
* double-sided polymorphism
|
||||
* efficient database usage
|
||||
* STI support
|
||||
* namespace support
|
||||
* automatic individual and reverse associations
|
||||
|
||||
The plugin also includes a generator for a tagging system, a common use case (see below).
|
||||
|
||||
== Requirements
|
||||
|
||||
* Rails 2.2.2 or greater
|
||||
|
||||
= Usage
|
||||
|
||||
== Installation
|
||||
|
||||
To install the Rails plugin, run:
|
||||
script/plugin install git://github.com/fauna/has_many_polymorphs.git
|
||||
|
||||
There's also a gem version. To install it instead, run:
|
||||
sudo gem install has_many_polymorphs
|
||||
|
||||
If you are using the gem, make sure to add <tt>require 'has_many_polymorphs'</tt> to <tt>environment.rb</tt>, before Rails::Initializer block.
|
||||
|
||||
== Configuration
|
||||
|
||||
Setup the parent model as so:
|
||||
|
||||
class Kennel < ActiveRecord::Base
|
||||
has_many_polymorphs :guests, :from => [:dogs, :cats, :birds]
|
||||
end
|
||||
|
||||
The join model:
|
||||
|
||||
class GuestsKennel < ActiveRecord::Base
|
||||
belongs_to :kennel
|
||||
belongs_to :guest, :polymorphic => true
|
||||
end
|
||||
|
||||
One of the child models:
|
||||
|
||||
class Dog < ActiveRecord::Base
|
||||
# nothing
|
||||
end
|
||||
|
||||
For your parent and child models, you don't need any special fields in your migration. For the join model (GuestsKennel), use a migration like so:
|
||||
|
||||
class CreateGuestsKennels < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :guests_kennels do |t|
|
||||
t.references :guest, :polymorphic => true
|
||||
t.references :kennel
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :guests_kennels
|
||||
end
|
||||
end
|
||||
|
||||
See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.
|
||||
|
||||
== Helper methods example
|
||||
|
||||
>> k = Kennel.find(1)
|
||||
#<Kennel id: 1, name: "Happy Paws">
|
||||
>> k.guests.map(&:class)
|
||||
[Dog, Cat, Cat, Bird]
|
||||
|
||||
>> k.guests.push(Cat.create); k.cats.size
|
||||
3
|
||||
>> k.guests << Cat.create; k.cats.size
|
||||
4
|
||||
>> k.guests.size
|
||||
6
|
||||
|
||||
>> d = k.dogs.first
|
||||
#<Dog id: 3, name: "Rover">
|
||||
>> d.kennels
|
||||
[#<Kennel id: 1, name: "Happy Paws">]
|
||||
|
||||
>> k.guests.delete(d); k.dogs.size
|
||||
0
|
||||
>> k.guests.size
|
||||
5
|
||||
|
||||
Note that the parent method is always plural, even if there is only one parent (<tt>Dog#kennels</tt>, not <tt>Dog#kennel</tt>).
|
||||
|
||||
See ActiveRecord::Associations::PolymorphicAssociation for more helper method details.
|
||||
|
||||
= Extras
|
||||
|
||||
== Double-sided polymorphism
|
||||
|
||||
Double-sided relationships are defined on the join model:
|
||||
|
||||
class Devouring < ActiveRecord::Base
|
||||
belongs_to :guest, :polymorphic => true
|
||||
belongs_to :eaten, :polymorphic => true
|
||||
|
||||
acts_as_double_polymorphic_join(
|
||||
:guests =>[:dogs, :cats],
|
||||
:eatens => [:cats, :birds]
|
||||
)
|
||||
end
|
||||
|
||||
Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't <tt>guests</tt>) and dogs can't be eaten by anything (since they aren't <tt>eatens</tt>). The keys stand for what the models are, not what they do.
|
||||
|
||||
In this case, each guest/eaten relationship is called a Devouring.
|
||||
|
||||
In your migration, you need to declare both sides as polymorphic:
|
||||
|
||||
class CreateDevourings < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :devourings do |t|
|
||||
t.references :guest, :polymorphic => true
|
||||
t.references :eaten, :polymorphic => true
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :devourings
|
||||
end
|
||||
end
|
||||
|
||||
See ActiveRecord::Associations::PolymorphicClassMethods for more.
|
||||
|
||||
== Tagging generator
|
||||
|
||||
Has_many_polymorphs includes a tagging system generator. Run:
|
||||
script/generate tagging Dog Cat [...MoreModels...]
|
||||
|
||||
This adds a migration and new Tag and Tagging models in <tt>app/models</tt>. It configures Tag with an appropriate <tt>has_many_polymorphs</tt> call against the models you list at the command line. It also adds the file <tt>lib/tagging_extensions.rb</tt> and <tt>requires</tt> it in <tt>environment.rb</tt>.
|
||||
|
||||
Tests will also be generated.
|
||||
|
||||
Once you've run the generator, you can tag records as follows:
|
||||
|
||||
>> d = Dog.create(:name => "Rover")
|
||||
#<Dog id: 3, name: "Rover">
|
||||
>> d.tag_list
|
||||
""
|
||||
>> d.tag_with "fierce loud"
|
||||
#<Dog id: 3, name: "Rover">
|
||||
>> d.tag_list
|
||||
"fierce loud"
|
||||
>> c = Cat.create(:name => "Chloe")
|
||||
#<Cat id: 1, name: "Chloe">
|
||||
>> c.tag_with "fierce cute"
|
||||
#<Cat id: 1, name: "Chloe">
|
||||
>> c.tag_list
|
||||
"cute fierce"
|
||||
>> Tag.find_by_name("fierce").taggables
|
||||
[#<Cat id: 1, name: "Chloe">, #<Dog id: 3, name: "Rover">]
|
||||
|
||||
The generator accepts the optional flag <tt>--skip-migration</tt> to skip generating a migration (for example, if you are converting from <tt>acts_as_taggable</tt>). It also accepts the flag <tt>--self-referential</tt> if you want to be able to tag tags.
|
||||
|
||||
See ActiveRecord::Base::TaggingExtensions, Tag, and Tagging for more.
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
Some debugging tools are available in <tt>lib/has_many_polymorphs/debugging_tools.rb</tt>.
|
||||
|
||||
If you are having trouble, think very carefully about how your model classes, key columns, and table names relate. You may have to explicitly specify options on your join model such as <tt>:class_name</tt>, <tt>:foreign_key</tt>, or <tt>:as</tt>. The included tests are a good place to look for examples.
|
||||
|
||||
Note that because of the way Rails reloads model classes, the plugin can sometimes bog down your development server. Set <tt>config.cache_classes = true</tt> in <tt>config/environments/development.rb</tt> to avoid this.
|
||||
|
||||
== Reporting problems
|
||||
|
||||
The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=16450].
|
||||
|
||||
Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
|
||||
|
||||
== Further resources
|
||||
|
||||
* http://blog.evanweaver.com/articles/2007/08/15/polymorphs-tutorial
|
||||
* http://blog.evanweaver.com/articles/2007/02/22/polymorphs-25-total-insanity-branch
|
||||
* http://blog.evanweaver.com/articles/2007/02/09/how-to-find-the-most-popular-tags
|
||||
* http://blog.evanweaver.com/articles/2007/01/13/growing-up-your-acts_as_taggable
|
||||
* http://blog.evanweaver.com/articles/2006/12/02/polymorphs-19
|
||||
* http://blog.evanweaver.com/articles/2006/11/05/directed-double-polymorphic-associations
|
||||
* http://blog.evanweaver.com/articles/2006/11/04/namespaced-model-support-in-has_many_polymorphs
|
||||
* http://blog.evanweaver.com/articles/2006/09/26/sti-support-in-has_many_polymorphs
|
||||
* http://blog.evanweaver.com/articles/2006/09/11/make-polymorphic-children-belong-to-only-one-parent
|
28
vendor/gems/has_many_polymorphs-2.13/Rakefile
vendored
28
vendor/gems/has_many_polymorphs-2.13/Rakefile
vendored
|
@ -1,28 +0,0 @@
|
|||
|
||||
require 'echoe'
|
||||
|
||||
Echoe.new("has_many_polymorphs") do |p|
|
||||
p.project = "fauna"
|
||||
p.summary = "An ActiveRecord plugin for self-referential and double-sided polymorphic associations."
|
||||
p.url = "http://blog.evanweaver.com/files/doc/fauna/has_many_polymorphs/"
|
||||
p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
|
||||
p.dependencies = ["activerecord"]
|
||||
p.rdoc_pattern = /polymorphs\/association|polymorphs\/class_methods|polymorphs\/reflection|polymorphs\/autoload|polymorphs\/configuration|README|CHANGELOG|TODO|LICENSE|templates\/migration\.rb|templates\/tag\.rb|templates\/tagging\.rb|templates\/tagging_extensions\.rb/
|
||||
p.require_signed = true
|
||||
p.clean_pattern += ["**/ruby_sess*", "**/generated_models/**"]
|
||||
p.test_pattern = ["test/unit/*_test.rb", "test/integration/*_test.rb", "test/generator/*_test.rb"]
|
||||
end
|
||||
|
||||
desc "Run all the tests for every database adapter"
|
||||
task "test_all" do
|
||||
['mysql', 'postgresql', 'sqlite3'].each do |adapter|
|
||||
ENV['DB'] = adapter
|
||||
ENV['PRODUCTION'] = nil
|
||||
STDERR.puts "#{'='*80}\nDevelopment mode for #{adapter}\n#{'='*80}"
|
||||
system("rake test:multi_rails:all")
|
||||
|
||||
ENV['PRODUCTION'] = '1'
|
||||
STDERR.puts "#{'='*80}\nProduction mode for #{adapter}\n#{'='*80}"
|
||||
system("rake test:multi_rails:all")
|
||||
end
|
||||
end
|
2
vendor/gems/has_many_polymorphs-2.13/TODO
vendored
2
vendor/gems/has_many_polymorphs-2.13/TODO
vendored
|
@ -1,2 +0,0 @@
|
|||
|
||||
* Tag cloud method
|
|
@ -1,69 +0,0 @@
|
|||
require 'camping'
|
||||
require 'has_many_polymorphs'
|
||||
|
||||
Camping.goes :Hmph
|
||||
|
||||
module Hmph::Models
|
||||
class GuestsKennel < Base
|
||||
belongs_to :kennel
|
||||
belongs_to :guest, :polymorphic => true
|
||||
end
|
||||
|
||||
class Dog < Base
|
||||
end
|
||||
|
||||
class Cat < Base
|
||||
end
|
||||
|
||||
class Bird < Base
|
||||
end
|
||||
|
||||
class Kennel < Base
|
||||
has_many_polymorphs :guests,
|
||||
:from => [:dogs, :cats, :birds],
|
||||
:through => :guests_kennels,
|
||||
:namespace => :"hmph/models/"
|
||||
end
|
||||
|
||||
class InitialSchema < V 1.0
|
||||
def self.up
|
||||
create_table :hmph_kennels do |t|
|
||||
t.column :created_at, :datetime
|
||||
t.column :modified_at, :datetime
|
||||
t.column :name, :string, :default => 'Anonymous Kennel'
|
||||
end
|
||||
|
||||
create_table :hmph_guests_kennels do |t|
|
||||
t.column :guest_id, :integer
|
||||
t.column :guest_type, :string
|
||||
t.column :kennel_id, :integer
|
||||
end
|
||||
|
||||
create_table :hmph_dogs do |t|
|
||||
t.column :name, :string, :default => 'Fido'
|
||||
end
|
||||
|
||||
create_table :hmph_cats do |t|
|
||||
t.column :name, :string, :default => 'Morris'
|
||||
end
|
||||
|
||||
create_table :hmph_birds do |t|
|
||||
t.column :name, :string, :default => 'Polly'
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :hmph_kennels
|
||||
drop_table :hmph_guests_kennels
|
||||
drop_table :hmph_dogs
|
||||
drop_table :hmph_cats
|
||||
drop_table :hmph_birds
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Hmph::Controllers
|
||||
end
|
||||
|
||||
module Hmph::Views
|
||||
end
|
|
@ -1,97 +0,0 @@
|
|||
require 'ruby-debug' and Debugger.start if ENV['USER'] == 'eweaver'
|
||||
|
||||
class TaggingGenerator < Rails::Generator::NamedBase
|
||||
default_options :skip_migration => false
|
||||
default_options :self_referential => false
|
||||
attr_reader :parent_association_name
|
||||
attr_reader :taggable_models
|
||||
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
parse!(runtime_args, runtime_options)
|
||||
|
||||
@parent_association_name = (runtime_args.include?("--self-referential") ? "tagger" : "tag")
|
||||
@taggable_models = runtime_args.reject{|opt| opt =~ /^--/}.map do |taggable|
|
||||
":" + taggable.underscore.pluralize
|
||||
end
|
||||
@taggable_models += [":tags"] if runtime_args.include?("--self-referential")
|
||||
@taggable_models.uniq!
|
||||
|
||||
verify @taggable_models
|
||||
hacks
|
||||
runtime_args.unshift("placeholder")
|
||||
super
|
||||
end
|
||||
|
||||
def verify models
|
||||
puts "** Warning: only one taggable model specified; tests may not run properly." if models.size < 2
|
||||
models.each do |model|
|
||||
model = model[1..-1].classify
|
||||
next if model == "Tag" # don't load ourselves when --self-referential is used
|
||||
self.class.const_get(model) rescue puts "** Error: model #{model[1..-1].classify} could not be loaded." or exit
|
||||
end
|
||||
end
|
||||
|
||||
def hacks
|
||||
# add the extension require in environment.rb
|
||||
phrase = "require 'tagging_extensions'"
|
||||
filename = "#{RAILS_ROOT}/config/environment.rb"
|
||||
unless (open(filename) do |file|
|
||||
file.grep(/#{Regexp.escape phrase}/).any?
|
||||
end)
|
||||
open(filename, 'a+') do |file|
|
||||
file.puts "\n" + phrase + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.class_collisions class_path, class_name, "#{class_name}Test"
|
||||
|
||||
m.directory File.join('app/models', class_path)
|
||||
m.directory File.join('test/unit', class_path)
|
||||
m.directory File.join('test/fixtures', class_path)
|
||||
m.directory File.join('test/fixtures', class_path)
|
||||
m.directory File.join('lib')
|
||||
|
||||
m.template 'tag.rb', File.join('app/models', class_path, "tag.rb")
|
||||
m.template 'tag_test.rb', File.join('test/unit', class_path, "tag_test.rb")
|
||||
m.template 'tags.yml', File.join('test/fixtures', class_path, "tags.yml")
|
||||
|
||||
m.template 'tagging.rb', File.join('app/models', class_path, "tagging.rb")
|
||||
m.template 'tagging_test.rb', File.join('test/unit', class_path, "tagging_test.rb")
|
||||
m.template 'taggings.yml', File.join('test/fixtures', class_path, "taggings.yml")
|
||||
|
||||
m.template 'tagging_extensions.rb', File.join('lib', 'tagging_extensions.rb')
|
||||
|
||||
unless options[:skip_migration]
|
||||
m.migration_template 'migration.rb', 'db/migrate',
|
||||
:migration_file_name => "create_tags_and_taggings"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def banner
|
||||
"Usage: #{$0} generate tagging [TaggableModelA TaggableModelB ...]"
|
||||
end
|
||||
|
||||
def add_options!(opt)
|
||||
opt.separator ''
|
||||
opt.separator 'Options:'
|
||||
opt.on("--skip-migration",
|
||||
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
||||
opt.on("--self-referential",
|
||||
"Allow tags to tag themselves.") { |v| options[:self_referential] = v }
|
||||
end
|
||||
|
||||
# Useful for generating tests/fixtures
|
||||
def model_one
|
||||
taggable_models[0][1..-1].classify
|
||||
end
|
||||
|
||||
def model_two
|
||||
taggable_models[1][1..-1].classify rescue model_one
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
# A migration to add tables for Tag and Tagging. This file is automatically generated and added to your app if you run the tagging generator included with has_many_polymorphs.
|
||||
|
||||
class CreateTagsAndTaggings < ActiveRecord::Migration
|
||||
|
||||
# Add the new tables.
|
||||
def self.up
|
||||
create_table :tags do |t|
|
||||
t.column :name, :string, :null => false
|
||||
end
|
||||
add_index :tags, :name, :unique => true
|
||||
|
||||
create_table :taggings do |t|
|
||||
t.column :<%= parent_association_name -%>_id, :integer, :null => false
|
||||
t.column :taggable_id, :integer, :null => false
|
||||
t.column :taggable_type, :string, :null => false
|
||||
# t.column :position, :integer # Uncomment this if you need to use <tt>acts_as_list</tt>.
|
||||
end
|
||||
add_index :taggings, [:<%= parent_association_name -%>_id, :taggable_id, :taggable_type], :unique => true
|
||||
end
|
||||
|
||||
# Remove the tables.
|
||||
def self.down
|
||||
drop_table :tags
|
||||
drop_table :taggings
|
||||
end
|
||||
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
|
||||
# The Tag model. This model is automatically generated and added to your app if you run the tagging generator included with has_many_polymorphs.
|
||||
|
||||
class Tag < ActiveRecord::Base
|
||||
|
||||
DELIMITER = " " # Controls how to split and join tagnames from strings. You may need to change the <tt>validates_format_of parameters</tt> if you change this.
|
||||
|
||||
# If database speed becomes an issue, you could remove these validations and rescue the ActiveRecord database constraint errors instead.
|
||||
validates_presence_of :name
|
||||
validates_uniqueness_of :name, :case_sensitive => false
|
||||
|
||||
# Change this validation if you need more complex tag names.
|
||||
validates_format_of :name, :with => /^[a-zA-Z0-9\_\-]+$/, :message => "can not contain special characters"
|
||||
|
||||
# Set up the polymorphic relationship.
|
||||
has_many_polymorphs :taggables,
|
||||
:from => [<%= taggable_models.join(", ") %>],
|
||||
:through => :taggings,
|
||||
:dependent => :destroy,
|
||||
<% if options[:self_referential] -%> :as => :<%= parent_association_name -%>,
|
||||
<% end -%>
|
||||
:skip_duplicates => false,
|
||||
:parent_extend => proc {
|
||||
# Defined on the taggable models, not on Tag itself. Return the tagnames associated with this record as a string.
|
||||
def to_s
|
||||
self.map(&:name).sort.join(Tag::DELIMITER)
|
||||
end
|
||||
}
|
||||
|
||||
# Callback to strip extra spaces from the tagname before saving it. If you allow tags to be renamed later, you might want to use the <tt>before_save</tt> callback instead.
|
||||
def before_create
|
||||
self.name = name.downcase.strip.squeeze(" ")
|
||||
end
|
||||
|
||||
# Tag::Error class. Raised by ActiveRecord::Base::TaggingExtensions if something goes wrong.
|
||||
class Error < StandardError
|
||||
end
|
||||
|
||||
end
|
|
@ -1,15 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TagTest < Test::Unit::TestCase
|
||||
fixtures <%= taggable_models[0..1].join(", ") -%>
|
||||
|
||||
def setup
|
||||
@obj = <%= model_two %>.find(:first)
|
||||
@obj.tag_with "pale imperial"
|
||||
end
|
||||
|
||||
def test_to_s
|
||||
assert_equal "imperial pale", <%= model_two -%>.find(:first).tags.to_s
|
||||
end
|
||||
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
# The Tagging join model. This model is automatically generated and added to your app if you run the tagging generator included with has_many_polymorphs.
|
||||
|
||||
class Tagging < ActiveRecord::Base
|
||||
|
||||
belongs_to :<%= parent_association_name -%><%= ", :foreign_key => \"#{parent_association_name}_id\", :class_name => \"Tag\"" if options[:self_referential] %>
|
||||
belongs_to :taggable, :polymorphic => true
|
||||
|
||||
# If you also need to use <tt>acts_as_list</tt>, you will have to manage the tagging positions manually by creating decorated join records when you associate Tags with taggables.
|
||||
# acts_as_list :scope => :taggable
|
||||
|
||||
# This callback makes sure that an orphaned <tt>Tag</tt> is deleted if it no longer tags anything.
|
||||
def after_destroy
|
||||
<%= parent_association_name -%>.destroy_without_callbacks if <%= parent_association_name -%> and <%= parent_association_name -%>.taggings.count == 0
|
||||
end
|
||||
end
|
|
@ -1,203 +0,0 @@
|
|||
class ActiveRecord::Base #:nodoc:
|
||||
|
||||
# These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs.
|
||||
module TaggingExtensions
|
||||
|
||||
# Add tags to <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
#
|
||||
# We need to avoid name conflicts with the built-in ActiveRecord association methods, thus the underscores.
|
||||
def _add_tags incoming
|
||||
taggable?(true)
|
||||
tag_cast_to_string(incoming).each do |tag_name|
|
||||
begin
|
||||
tag = Tag.find_or_create_by_name(tag_name)
|
||||
raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record?
|
||||
tags << tag
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
raise unless e.to_s =~ /duplicate/i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Removes tags from <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def _remove_tags outgoing
|
||||
taggable?(true)
|
||||
outgoing = tag_cast_to_string(outgoing)
|
||||
<% if options[:self_referential] %>
|
||||
# because of http://dev.rubyonrails.org/ticket/6466
|
||||
taggings.destroy(*(taggings.find(:all, :include => :<%= parent_association_name -%>).select do |tagging|
|
||||
outgoing.include? tagging.<%= parent_association_name -%>.name
|
||||
end))
|
||||
<% else -%>
|
||||
<%= parent_association_name -%>s.delete(*(<%= parent_association_name -%>s.select do |tag|
|
||||
outgoing.include? tag.name
|
||||
end))
|
||||
<% end -%>
|
||||
end
|
||||
|
||||
# Returns the tags on <tt>self</tt> as a string.
|
||||
def tag_list
|
||||
# Redefined later to avoid an RDoc parse error.
|
||||
end
|
||||
|
||||
# Replace the existing tags on <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def tag_with list
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
list = tag_cast_to_string(list)
|
||||
|
||||
# Transactions may not be ideal for you here; be aware.
|
||||
Tag.transaction do
|
||||
current = <%= parent_association_name -%>s.map(&:name)
|
||||
_add_tags(list - current)
|
||||
_remove_tags(current - list)
|
||||
end
|
||||
|
||||
self
|
||||
#:startdoc:
|
||||
end
|
||||
|
||||
# Returns the tags on <tt>self</tt> as a string.
|
||||
def tag_list #:nodoc:
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
<%= parent_association_name -%>s.reload
|
||||
<%= parent_association_name -%>s.to_s
|
||||
#:startdoc:
|
||||
end
|
||||
|
||||
def tag_list=(value)
|
||||
tag_with(value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_cast_to_string obj #:nodoc:
|
||||
case obj
|
||||
when Array
|
||||
obj.map! do |item|
|
||||
case item
|
||||
when /^\d+$/, Fixnum then Tag.find(item).name # This will be slow if you use ids a lot.
|
||||
when Tag then item.name
|
||||
when String then item
|
||||
else
|
||||
raise "Invalid type"
|
||||
end
|
||||
end
|
||||
when String
|
||||
obj = obj.split(Tag::DELIMITER).map do |tag_name|
|
||||
tag_name.strip.squeeze(" ")
|
||||
end
|
||||
else
|
||||
raise "Invalid object of class #{obj.class} as tagging method parameter"
|
||||
end.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
# Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model.
|
||||
def taggable?(should_raise = false) #:nodoc:
|
||||
unless flag = respond_to?(:<%= parent_association_name -%>s)
|
||||
raise "#{self.class} is not a taggable model" if should_raise
|
||||
end
|
||||
flag
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module TaggingFinders
|
||||
# Find all the objects tagged with the supplied list of tags
|
||||
#
|
||||
# Usage : Model.tagged_with("ruby")
|
||||
# Model.tagged_with("hello", "world")
|
||||
# Model.tagged_with("hello", "world", :limit => 10)
|
||||
#
|
||||
# XXX This query strategy is not performant, and needs to be rewritten as an inverted join or a series of unions
|
||||
#
|
||||
def tagged_with(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, tags, taggings"
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.tag_id = tags.id "
|
||||
|
||||
tag_list_condition = tag_list.map {|name| "'#{name}'"}.join(", ")
|
||||
|
||||
sql << "AND (tags.name IN (#{sanitize_sql(tag_list_condition)})) "
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
sql << "HAVING COUNT(taggings.tag_id) = #{tag_list.size}"
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
def self.tagged_with_any(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, meta_tags, taggings"
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options, scope)
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.meta_tag_id = meta_tags.id "
|
||||
|
||||
sql << "AND ("
|
||||
or_options = []
|
||||
tag_list.each do |name|
|
||||
or_options << "meta_tags.name = '#{name}'"
|
||||
end
|
||||
or_options_joined = or_options.join(" OR ")
|
||||
sql << "#{or_options_joined}) "
|
||||
|
||||
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
def parse_tags(tags)
|
||||
return [] if tags.blank?
|
||||
tags = Array(tags).first
|
||||
tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(Tag::DELIMITER)
|
||||
tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
include TaggingExtensions
|
||||
extend TaggingFinders
|
||||
end
|
|
@ -1,85 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TaggingTest < Test::Unit::TestCase
|
||||
fixtures :tags, :taggings, <%= taggable_models[0..1].join(", ") -%>
|
||||
|
||||
def setup
|
||||
@objs = <%= model_two %>.find(:all, :limit => 2)
|
||||
|
||||
@obj1 = @objs[0]
|
||||
@obj1.tag_with("pale")
|
||||
@obj1.reload
|
||||
|
||||
@obj2 = @objs[1]
|
||||
@obj2.tag_with("pale imperial")
|
||||
@obj2.reload
|
||||
|
||||
<% if taggable_models.size > 1 -%>
|
||||
@obj3 = <%= model_one -%>.find(:first)
|
||||
<% end -%>
|
||||
@tag1 = Tag.find(1)
|
||||
@tag2 = Tag.find(2)
|
||||
@tagging1 = Tagging.find(1)
|
||||
end
|
||||
|
||||
def test_tag_with
|
||||
@obj2.tag_with "hoppy pilsner"
|
||||
assert_equal "hoppy pilsner", @obj2.tag_list
|
||||
end
|
||||
|
||||
def test_find_tagged_with
|
||||
@obj1.tag_with "seasonal lager ipa"
|
||||
@obj2.tag_with ["lager", "stout", "fruity", "seasonal"]
|
||||
|
||||
result1 = [@obj1]
|
||||
assert_equal <%= model_two %>.tagged_with("ipa"), result1
|
||||
assert_equal <%= model_two %>.tagged_with("ipa lager"), result1
|
||||
assert_equal <%= model_two %>.tagged_with("ipa", "lager"), result1
|
||||
|
||||
result2 = [@obj1.id, @obj2.id].sort
|
||||
assert_equal <%= model_two %>.tagged_with("seasonal").map(&:id).sort, result2
|
||||
assert_equal <%= model_two %>.tagged_with("seasonal lager").map(&:id).sort, result2
|
||||
assert_equal <%= model_two %>.tagged_with("seasonal", "lager").map(&:id).sort, result2
|
||||
end
|
||||
|
||||
<% if options[:self_referential] -%>
|
||||
def test_self_referential_tag_with
|
||||
@tag1.tag_with [1, 2]
|
||||
assert @tag1.tags.include?(@tag1)
|
||||
assert !@tag2.tags.include?(@tag1)
|
||||
end
|
||||
|
||||
<% end -%>
|
||||
def test__add_tags
|
||||
@obj1._add_tags "porter longneck"
|
||||
assert Tag.find_by_name("porter").taggables.include?(@obj1)
|
||||
assert Tag.find_by_name("longneck").taggables.include?(@obj1)
|
||||
assert_equal "longneck pale porter", @obj1.tag_list
|
||||
|
||||
@obj1._add_tags [2]
|
||||
assert_equal "imperial longneck pale porter", @obj1.tag_list
|
||||
end
|
||||
|
||||
def test__remove_tags
|
||||
@obj2._remove_tags ["2", @tag1]
|
||||
assert @obj2.tags.empty?
|
||||
end
|
||||
|
||||
def test_tag_list
|
||||
assert_equal "imperial pale", @obj2.tag_list
|
||||
end
|
||||
|
||||
def test_taggable
|
||||
assert_raises(RuntimeError) do
|
||||
@tagging1.send(:taggable?, true)
|
||||
end
|
||||
assert !@tagging1.send(:taggable?)
|
||||
<% if taggable_models.size > 1 -%>
|
||||
assert @obj3.send(:taggable?)
|
||||
<% end -%>
|
||||
<% if options[:self_referential] -%>
|
||||
assert @tag1.send(:taggable?)
|
||||
<% end -%>
|
||||
end
|
||||
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
<% if taggable_models.size > 1 -%>
|
||||
taggings_003:
|
||||
<%= parent_association_name -%>_id: "2"
|
||||
id: "3"
|
||||
taggable_type: <%= model_one %>
|
||||
taggable_id: "1"
|
||||
<% end -%>
|
||||
taggings_004:
|
||||
<%= parent_association_name -%>_id: "2"
|
||||
id: "4"
|
||||
taggable_type: <%= model_two %>
|
||||
taggable_id: "2"
|
||||
taggings_001:
|
||||
<%= parent_association_name -%>_id: "1"
|
||||
id: "1"
|
||||
taggable_type: <%= model_two %>
|
||||
taggable_id: "1"
|
||||
taggings_002:
|
||||
<%= parent_association_name -%>_id: "1"
|
||||
id: "2"
|
||||
taggable_type: <%= model_two %>
|
||||
taggable_id: "2"
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
tags_001:
|
||||
name: pale
|
||||
id: "1"
|
||||
tags_002:
|
||||
name: imperial
|
||||
id: "2"
|
File diff suppressed because one or more lines are too long
2
vendor/gems/has_many_polymorphs-2.13/init.rb
vendored
2
vendor/gems/has_many_polymorphs-2.13/init.rb
vendored
|
@ -1,2 +0,0 @@
|
|||
|
||||
require 'has_many_polymorphs'
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
require 'active_record'
|
||||
|
||||
RAILS_DEFAULT_LOGGER = nil unless defined? RAILS_DEFAULT_LOGGER
|
||||
|
||||
require 'has_many_polymorphs/reflection'
|
||||
require 'has_many_polymorphs/association'
|
||||
require 'has_many_polymorphs/class_methods'
|
||||
|
||||
require 'has_many_polymorphs/support_methods'
|
||||
require 'has_many_polymorphs/base'
|
||||
|
||||
class ActiveRecord::Base
|
||||
extend ActiveRecord::Associations::PolymorphicClassMethods
|
||||
end
|
||||
|
||||
if ENV['HMP_DEBUG'] || ENV['RAILS_ENV'] =~ /development|test/ && ENV['USER'] == 'eweaver'
|
||||
require 'has_many_polymorphs/debugging_tools'
|
||||
end
|
||||
|
||||
if defined? Rails and RAILS_ENV and RAILS_ROOT
|
||||
_logger_warn "rails environment detected"
|
||||
require 'has_many_polymorphs/configuration'
|
||||
require 'has_many_polymorphs/autoload'
|
||||
end
|
||||
|
||||
_logger_debug "loaded ok"
|
|
@ -1,159 +0,0 @@
|
|||
module ActiveRecord #:nodoc:
|
||||
module Associations #:nodoc:
|
||||
|
||||
class PolymorphicError < ActiveRecordError #:nodoc:
|
||||
end
|
||||
|
||||
class PolymorphicMethodNotSupportedError < ActiveRecordError #:nodoc:
|
||||
end
|
||||
|
||||
# The association class for a <tt>has_many_polymorphs</tt> association.
|
||||
class PolymorphicAssociation < HasManyThroughAssociation
|
||||
|
||||
# Push a record onto the association. Triggers a database load for a uniqueness check only if <tt>:skip_duplicates</tt> is <tt>true</tt>. Return value is undefined.
|
||||
def <<(*records)
|
||||
return if records.empty?
|
||||
|
||||
if @reflection.options[:skip_duplicates]
|
||||
_logger_debug "Loading instances for polymorphic duplicate push check; use :skip_duplicates => false and perhaps a database constraint to avoid this possible performance issue"
|
||||
load_target
|
||||
end
|
||||
|
||||
@reflection.klass.transaction do
|
||||
flatten_deeper(records).each do |record|
|
||||
if @owner.new_record? or not record.respond_to?(:new_record?) or record.new_record?
|
||||
raise PolymorphicError, "You can't associate unsaved records."
|
||||
end
|
||||
next if @reflection.options[:skip_duplicates] and @target.include? record
|
||||
@owner.send(@reflection.through_reflection.name).proxy_target << @reflection.klass.create!(construct_join_attributes(record))
|
||||
@target << record if loaded?
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
alias :push :<<
|
||||
alias :concat :<<
|
||||
|
||||
# Runs a <tt>find</tt> against the association contents, returning the matched records. All regular <tt>find</tt> options except <tt>:include</tt> are supported.
|
||||
def find(*args)
|
||||
opts = args._extract_options!
|
||||
opts.delete :include
|
||||
super(*(args + [opts]))
|
||||
end
|
||||
|
||||
def construct_scope
|
||||
_logger_warn "Warning; not all usage scenarios for polymorphic scopes are supported yet."
|
||||
super
|
||||
end
|
||||
|
||||
# Deletes a record from the association. Return value is undefined.
|
||||
def delete(*records)
|
||||
records = flatten_deeper(records)
|
||||
records.reject! {|record| @target.delete(record) if record.new_record?}
|
||||
return if records.empty?
|
||||
|
||||
@reflection.klass.transaction do
|
||||
records.each do |record|
|
||||
joins = @reflection.through_reflection.name
|
||||
@owner.send(joins).delete(@owner.send(joins).select do |join|
|
||||
join.send(@reflection.options[:polymorphic_key]) == record.id and
|
||||
join.send(@reflection.options[:polymorphic_type_key]) == "#{record.class.base_class}"
|
||||
end)
|
||||
@target.delete(record)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Clears all records from the association. Returns an empty array.
|
||||
def clear(klass = nil)
|
||||
load_target
|
||||
return if @target.empty?
|
||||
|
||||
if klass
|
||||
delete(@target.select {|r| r.is_a? klass })
|
||||
else
|
||||
@owner.send(@reflection.through_reflection.name).clear
|
||||
@target.clear
|
||||
end
|
||||
[]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# undef :sum
|
||||
# undef :create!
|
||||
|
||||
def construct_quoted_owner_attributes(*args) #:nodoc:
|
||||
# no access to returning() here? why not?
|
||||
type_key = @reflection.options[:foreign_type_key]
|
||||
{@reflection.primary_key_name => @owner.id,
|
||||
type_key=> (@owner.class.base_class.name if type_key)}
|
||||
end
|
||||
|
||||
def construct_from #:nodoc:
|
||||
# build the FROM part of the query, in this case, the polymorphic join table
|
||||
@reflection.klass.table_name
|
||||
end
|
||||
|
||||
def construct_owner #:nodoc:
|
||||
# the table name for the owner object's class
|
||||
@owner.class.table_name
|
||||
end
|
||||
|
||||
def construct_owner_key #:nodoc:
|
||||
# the primary key field for the owner object
|
||||
@owner.class.primary_key
|
||||
end
|
||||
|
||||
def construct_select(custom_select = nil) #:nodoc:
|
||||
# build the select query
|
||||
selected = custom_select || @reflection.options[:select]
|
||||
end
|
||||
|
||||
def construct_joins(custom_joins = nil) #:nodoc:
|
||||
# build the string of default joins
|
||||
"JOIN #{construct_owner} polymorphic_parent ON #{construct_from}.#{@reflection.options[:foreign_key]} = polymorphic_parent.#{construct_owner_key} " +
|
||||
@reflection.options[:from].map do |plural|
|
||||
klass = plural._as_class
|
||||
"LEFT JOIN #{klass.table_name} ON #{construct_from}.#{@reflection.options[:polymorphic_key]} = #{klass.table_name}.#{klass.primary_key} AND #{construct_from}.#{@reflection.options[:polymorphic_type_key]} = #{@reflection.klass.quote_value(klass.base_class.name)}"
|
||||
end.uniq.join(" ") + " #{custom_joins}"
|
||||
end
|
||||
|
||||
def construct_conditions #:nodoc:
|
||||
# build the fully realized condition string
|
||||
conditions = construct_quoted_owner_attributes.map do |field, value|
|
||||
"#{construct_from}.#{field} = #{@reflection.klass.quote_value(value)}" if value
|
||||
end
|
||||
conditions << custom_conditions if custom_conditions
|
||||
"(" + conditions.compact.join(') AND (') + ")"
|
||||
end
|
||||
|
||||
def custom_conditions #:nodoc:
|
||||
# custom conditions... not as messy as has_many :through because our joins are a little smarter
|
||||
if @reflection.options[:conditions]
|
||||
"(" + interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) + ")"
|
||||
end
|
||||
end
|
||||
|
||||
alias :construct_owner_attributes :construct_quoted_owner_attributes
|
||||
alias :conditions :custom_conditions # XXX possibly not necessary
|
||||
alias :sql_conditions :custom_conditions # XXX ditto
|
||||
|
||||
# construct attributes for join for a particular record
|
||||
def construct_join_attributes(record) #:nodoc:
|
||||
{@reflection.options[:polymorphic_key] => record.id,
|
||||
@reflection.options[:polymorphic_type_key] => "#{record.class.base_class}",
|
||||
@reflection.options[:foreign_key] => @owner.id}.merge(@reflection.options[:foreign_type_key] ?
|
||||
{@reflection.options[:foreign_type_key] => "#{@owner.class.base_class}"} : {}) # for double-sided relationships
|
||||
end
|
||||
|
||||
def build(attrs = nil) #:nodoc:
|
||||
raise PolymorphicMethodNotSupportedError, "You can't associate new records."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue