actionservice 0.2.100 → 0.2.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ChangeLog ADDED
@@ -0,0 +1,3 @@
1
+ TBD
2
+
3
+ * First public release
data/README CHANGED
@@ -1,29 +1,54 @@
1
- = Action Service -- Web service integration with Action Pack
1
+ = Action Service -- Serving APIs on rails
2
+
3
+ Action Service provides a way to publish interoperablel web service APIs with
4
+ Rails without wasting time delving into protocol details.
5
+
6
+
7
+ == Features
8
+
9
+ * SOAP RPC protocol support
10
+ * Dynamic WSDL generation
11
+ * XML-RPC protocol support
12
+ * Strong type signature hints to improve interoperability with static
13
+ languages
14
+ * Using Active Record derivatives in return signatures automatically generates
15
+ a structured type equivalent that can be sent over the wire
16
+
17
+
18
+ == Integration with Action Pack
19
+
20
+ Action Service can be integrated in two different dispatching modes, _Direct_ and
21
+ _Delegated_.
22
+
23
+
24
+ * _Direct_ mode refers to a dispatching mode where one controller represents a
25
+ single API service, and actions in the controller represent API methods. This is
26
+ the default mode.
27
+ * _Delegated_ mode refers to a dispatching mode where a controller is a
28
+ container for one or more API services, and an action on the controller
29
+ serves as the entry point for an associated service.
2
30
 
3
- FIXME
4
31
 
5
32
  == Dependencies
6
33
 
7
- Action Service requires that the Action Pack is either available to be required immediately
8
- or is accessible as a GEM.
34
+ Action Service requires that the Action Pack and Active Record are either
35
+ available to be required immediately or are accessible as GEMs.
9
36
 
10
- It also requires a version of Ruby that includes SOAP support in the standard library. At
11
- least version 1.8.2 final is recommended.
37
+ It also requires a version of Ruby that includes SOAP support in the standard
38
+ library. At least version 1.8.2 final (2004-12-25) of Ruby is recommended.
12
39
 
13
40
 
14
41
  == Download
15
42
 
16
-
17
- FIXME
43
+ The latest Action Service version can be downloaded from
44
+ http://rubyforge.org/projects/actionservice
18
45
 
19
46
 
20
47
  == Installation
21
48
 
22
49
  You can install Action Service with the following command.
23
50
 
24
- % [sudo] ruby install.rb
25
-
26
- from its distribution directory.
51
+ % [sudo] ruby setup.rb
27
52
 
28
53
 
29
54
  == License
@@ -33,4 +58,6 @@ Action Service is released under the MIT license.
33
58
 
34
59
  == Support
35
60
 
36
- FIXME
61
+ The Ruby on Rails mailing list
62
+
63
+ Or, to contact the author, send mail to bitserf@gmail.com
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'actionservice'
11
- PKG_VERSION = '0.2.100' + PKG_BUILD
11
+ PKG_VERSION = '0.2.102' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  desc "Default Task"
@@ -27,12 +27,13 @@ Rake::TestTask.new { |t|
27
27
  Rake::RDocTask.new { |rdoc|
28
28
  rdoc.rdoc_dir = 'doc'
29
29
  rdoc.title = "Action Service -- Web services for Action Pack"
30
- rdoc.options << '--line-numbers --inline-source --main README'
31
- rdoc.rdoc_files.include('README', 'CHANGELOG')
30
+ rdoc.options << '--line-numbers --inline-source --main README --accessor class_inheritable_option=RW'
31
+ rdoc.rdoc_files.include('README')
32
32
  rdoc.rdoc_files.include('lib/action_service.rb')
33
33
  rdoc.rdoc_files.include('lib/action_service/*.rb')
34
34
  rdoc.rdoc_files.include('lib/action_service/protocol/*.rb')
35
35
  rdoc.rdoc_files.include('lib/action_service/router/*.rb')
36
+ rdoc.rdoc_files.include('lib/action_service/support/*.rb')
36
37
  }
37
38
 
38
39
 
@@ -50,13 +51,14 @@ spec = Gem::Specification.new do |s|
50
51
  s.homepage = "http://rubyforge.org/projects/actionservice"
51
52
 
52
53
  s.add_dependency('actionpack', '>= 1.4.0')
54
+ s.add_dependency('activerecord', '>= 1.6.0')
53
55
 
54
56
  s.has_rdoc = true
55
57
  s.requirements << 'none'
56
58
  s.require_path = 'lib'
57
59
  s.autorequire = 'action_service'
58
60
 
59
- s.files = [ "Rakefile", "setup.rb", "README", "TODO", "HACKING", "CHANGELOG", "MIT-LICENSE" ]
61
+ s.files = [ "Rakefile", "setup.rb", "README", "TODO", "HACKING", "ChangeLog", "MIT-LICENSE" ]
60
62
  s.files = s.files + Dir.glob( "examples/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
61
63
  s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
62
64
  s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
@@ -96,6 +98,7 @@ task :lines do
96
98
  action_service
97
99
  action_service/protocol
98
100
  action_service/router
101
+ action_service/support
99
102
  }.each do |dir|
100
103
  Dir.foreach(File.join(prefix, dir)) { |file_name|
101
104
  next unless file_name =~ /.*rb$/
data/TODO CHANGED
@@ -1,30 +1,13 @@
1
- = Features Remaining
2
- - Support for ActiveRecord model classes as structure types as well, since
3
- we can reflect all the details to do proper mapping from model classes.
4
-
5
- = Ideas
6
- - <nextangle> Support a second dispatching mechanism, where incoming
7
- operation requests are mapped directly to a controller action.
8
-
9
- Advantages: No seperate API service class, the controller becomes the
10
- service class.
11
-
12
- Possible approaches:
13
- Create a new router that maps an 'api' action to do dispatching using
14
- itself as the target service.
15
-
16
- This dispatching would:
17
-
18
- * Unpack request parameters into @params
19
- * Execute the action
20
- * Use the return value of the action to send back to the client
21
-
22
- = Warts
23
- - Come up with a cleaner way for supporting XML-RPC introspection that doesn't
24
- involve throwing a SystemExportNotHandledError exception.
25
- - Come up with a cleaner approach for 'default_export' implementations.
26
- But NOT method_missing!
27
- - Create abstract SOAP test that provides helpers for doing SOAP method
28
- calls, get rid of the copy & paste SOAP crap in the SOAP protocol tests
1
+ = Tasks
2
+ - add better type mapping tests for XML-RPC
3
+ - add tests for ActiveRecord support (with mock objects?)
4
+
5
+ = Refactoring
6
+ - Find an alternative way to map interesting types for SOAP (like ActiveRecord
7
+ model classes) that doesn't require creation of a sanitized copy object with data
8
+ copied from the real one. Ideally this would let us get rid of
9
+ ActionService::Struct altogether and provide a block that would yield the
10
+ attributes and values. "Filters" ? Not sure how to integrate with SOAP though.
11
+
29
12
  - Don't have clean way to go from SOAP Class object to the xsd:NAME type
30
- string
13
+ string -- NaHi possibly looking at remedying this situation
@@ -2,7 +2,6 @@ require 'google_search_service'
2
2
 
3
3
  class SearchController < ApplicationController
4
4
  wsdl_service_name 'GoogleSearch'
5
+ service_dispatching_mode :delegated
5
6
  service :beta3, GoogleSearchService.new
6
-
7
- # self.action_service_debugging = true
8
7
  end
@@ -68,7 +68,10 @@ class GoogleSearchService < ActionService::Base
68
68
 
69
69
  # For Mono, we have to clone objects if they're referenced by more than one place, otherwise
70
70
  # the Ruby SOAP collapses them into one instance and uses references all over the
71
- # place, confusing Mono
71
+ # place, confusing Mono.
72
+ #
73
+ # This has recently been fixed:
74
+ # http://bugzilla.ximian.com/show_bug.cgi?id=72265
72
75
  result.directoryCategories = [
73
76
  category("Web Development", "UTF-8"),
74
77
  category("Programming", "US-ASCII"),
@@ -84,7 +87,7 @@ class GoogleSearchService < ActionService::Base
84
87
  cat
85
88
  end
86
89
 
87
- export :doGetCachedPage, :returns => [String], :expects => [String, String]
90
+ export :doGetCachedPage, :returns => [String], :expects => [{:key=>String}, {:url=>String}]
88
91
  export :doGetSpellingSuggestion, :returns => [String], :expects => [String, String]
89
92
  export :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [String, String, Integer, Integer, TrueClass, String, TrueClass, String, String, String]
90
93
  end
@@ -5,63 +5,8 @@ module ActionService
5
5
  end
6
6
 
7
7
  class Base
8
- class_inheritable_option :export_name_mangling, true
8
+ # Whether to send back a detailed stack trace to the remote caller
9
+ # when an exception is thrown on the server
9
10
  class_inheritable_option :report_exceptions, true
10
- class_inheritable_option :logger
11
- class_inheritable_option :default_export
12
-
13
- class << self
14
- def export(name, options={})
15
- validate_options([:expects, :returns, :expects_and_returns], options.keys)
16
- if options[:expects_and_returns]
17
- expects = options[:expects_and_returns]
18
- returns = options[:expects_and_returns]
19
- else
20
- expects = options[:expects]
21
- returns = options[:returns]
22
- end
23
- name = name.to_sym
24
- public_name = public_export_name(name)
25
- info = { :expects => expects, :returns => returns }
26
- write_inheritable_hash("action_service_exports", name => info)
27
- write_inheritable_hash("action_service_public_exports", public_name => name)
28
- end
29
-
30
- def has_export?(name)
31
- exports.has_key?(name)
32
- end
33
-
34
- def has_public_export?(name)
35
- public_exports.has_key?(name)
36
- end
37
-
38
- def public_export_name(export_name)
39
- if export_name_mangling
40
- Inflector.camelize(export_name.to_s)
41
- else
42
- export_name.to_s
43
- end
44
- end
45
-
46
- def internal_export_name(public_name)
47
- public_exports[public_name]
48
- end
49
-
50
- def exports
51
- read_inheritable_attribute("action_service_exports") || {}
52
- end
53
-
54
- private
55
- def public_exports
56
- read_inheritable_attribute("action_service_public_exports") || {}
57
- end
58
-
59
- def validate_options(valid_option_keys, supplied_option_keys)
60
- unknown_option_keys = supplied_option_keys - valid_option_keys
61
- unless unknown_option_keys.empty?
62
- raise(ActionServiceError, "Unknown options: #{unknown_option_keys}")
63
- end
64
- end
65
- end
66
11
  end
67
12
  end
@@ -1,10 +1,14 @@
1
1
  module ActionService
2
2
  module Container
3
+ DirectDispatching = :direct
4
+ DelegatedDispatching = :delegated
5
+
3
6
  class ContainerError < ActionService::ActionServiceError
4
7
  end
5
8
 
6
9
  def self.append_features(base)
7
10
  super
11
+ base.class_inheritable_option(:service_dispatching_mode, DirectDispatching)
8
12
  base.extend(ClassMethods)
9
13
  base.send(:include, ActionService::Container::InstanceMethods)
10
14
  end
@@ -25,7 +29,7 @@ module ActionService
25
29
  end
26
30
 
27
31
  def add_service_definition_callback(&block)
28
- write_inheritable_array("action_service_definition_callbacks", [block])
32
+ write_inheritable_array("service_definition_callbacks", [block])
29
33
  end
30
34
 
31
35
  def has_service?(name)
@@ -37,9 +41,9 @@ module ActionService
37
41
  end
38
42
 
39
43
  private
40
- def call_service_definition_callbacks(container_klass, service_name, service_info)
41
- (read_inheritable_attribute("action_service_definition_callbacks") || []).each do |block|
42
- block.call(container_klass, service_name, service_info)
44
+ def call_service_definition_callbacks(container_class, service_name, service_info)
45
+ (read_inheritable_attribute("service_definition_callbacks") || []).each do |block|
46
+ block.call(container_class, service_name, service_info)
43
47
  end
44
48
  end
45
49
  end
@@ -55,56 +59,104 @@ module ActionService
55
59
  end
56
60
 
57
61
  private
58
- def dispatch_service_invocation_request(protocol, info)
59
- service = service_object(info.service_name)
60
- service_klass = service.class
61
- method_name = service_klass.internal_export_name(info.public_method_name)
62
- opts = {}
63
- params = []
62
+ def dispatch_service_request(protocol_request)
63
+ case service_dispatching_mode
64
+ when DirectDispatching
65
+ dispatch_direct_service_request(protocol_request)
66
+ when DelegatedDispatching
67
+ dispatch_delegated_service_request(protocol_request)
68
+ else
69
+ raise(ContainerError, "unsupported dispatching mode '#{service_dispatching_mode}'")
70
+ end
71
+ end
72
+
73
+ def dispatch_direct_service_request(protocol_request)
74
+ public_method_name = protocol_request.public_method_name
75
+ method_name = self.class.internal_export_name(public_method_name)
76
+ export_info = self.class.exports[method_name]
77
+ protocol_request.type = Protocol::CheckedMessage
78
+ protocol_request.signature = export_info[:expects]
79
+ protocol_request.return_signature = export_info[:returns]
80
+ @invocation_params = protocol_request.unmarshal
81
+ if export_info[:expects]
82
+ expects = export_info[:expects]
83
+ @params ||= {}
84
+ (1..@invocation_params.size).each do |i|
85
+ i -= 1
86
+ if expects[i].is_a?(Hash)
87
+ @params[expects[i].keys.shift.to_s] = @invocation_params[i]
88
+ else
89
+ @params["param#{i}"] = @invocation_params[i]
90
+ end
91
+ end
92
+ end
93
+ result = send(method_name)
94
+ protocol_request.marshal(result)
95
+ end
96
+
97
+ def dispatch_delegated_service_request(protocol_request)
98
+ service_name = protocol_request.service_name
99
+ service = service_object(service_name)
100
+ service_class = service.class
101
+ public_method_name = protocol_request.public_method_name
102
+ method_name = service_class.internal_export_name(public_method_name)
103
+
104
+ invocation = ActionService::Invocation::InvocationRequest.new(
105
+ ActionService::Invocation::ConcreteInvocation,
106
+ public_method_name,
107
+ method_name)
108
+
64
109
  if method_name
65
- export_info = service_klass.exports[method_name]
66
- strict = true
110
+ protocol_request.type = Protocol::CheckedMessage
111
+ export_info = service_class.exports[method_name]
112
+ protocol_request.signature = export_info[:expects]
113
+ protocol_request.return_signature = export_info[:returns]
114
+ invocation.params = protocol_request.unmarshal
67
115
  else
116
+ protocol_request.type = Protocol::UncheckedMessage
117
+ invocation.type = ActionService::Invocation::VirtualInvocation
68
118
  system_exports = self.class.read_inheritable_attribute('default_system_exports') || {}
69
- method_name = system_exports[protocol.class]
70
- if method_name
71
- opts[:system_call] = [info.public_method_name, system_exports[protocol.class]]
72
- params.unshift service_klass
119
+ protocol = protocol_request.protocol
120
+ block = system_exports[protocol.class]
121
+ if block
122
+ invocation.block = block
123
+ invocation.block_params << service_class
73
124
  else
74
- method_name = service_klass.default_export
75
- method_name = method_name.to_sym if method_name
76
- unless method_name
77
- raise(ContainerError, "no such method /#{info.service_name}##{info.public_method_name}()")
125
+ method_name = service_class.default_export
126
+ if method_name && service.respond_to?(method_name)
127
+ invocation.params = protocol_request.unmarshal
128
+ invocation.method_name = method_name.to_sym
129
+ else
130
+ raise(ContainerError, "no such method /#{service_name}##{public_method_name}")
78
131
  end
79
132
  end
80
- export_info = {}
81
- strict = false
82
133
  end
83
- opts[:require_exported] = false unless strict
84
- params = params + protocol.unmarshal_request(info, export_info, strict)
134
+
85
135
  canceled_reason = nil
86
136
  canceled_block = lambda{|r| canceled_reason = r}
87
137
  perform_invoke = lambda do
88
- service.perform_invocation(method_name, params, opts, &canceled_block)
138
+ service.perform_invocation(invocation, &canceled_block)
89
139
  end
90
- begin
140
+ try_default = true
141
+ result = nil
142
+ catch(:try_default_export) do
91
143
  result = perform_invoke.call
92
- rescue ActionService::Protocol::SystemExportNotHandledError => e
93
- params.shift
94
- method_name = service_klass.default_export
95
- opts.delete(:system_call)
144
+ try_default = false
145
+ end
146
+ if try_default
147
+ method_name = service_class.default_export
96
148
  if method_name
97
- method_name = method_name.to_sym
98
- export_info = {}
99
- strict = false
149
+ protocol_request.type = Protocol::UncheckedMessage
150
+ invocation.params = protocol_request.unmarshal
151
+ invocation.method_name = method_name.to_sym
152
+ invocation.type = ActionService::Invocation::UnexportedConcreteInvocation
100
153
  else
101
- raise(ContainerError, "no such method /#{info.service_name}##{info.public_method_name}()")
154
+ raise(ContainerError, "no such method /#{service_name}##{public_method_name}")
102
155
  end
103
156
  result = perform_invoke.call
104
157
  end
105
- protocol.marshal_response(info, export_info, result, strict)
158
+ protocol_request.marshal(result)
106
159
  end
107
-
108
160
  end
109
161
  end
110
162
  end
@@ -0,0 +1,140 @@
1
+ module ActionService
2
+ module Exporting
3
+ class ExportError < ActionService::ActionServiceError
4
+ end
5
+
6
+ def self.append_features(base)
7
+ super
8
+ # Whether to mangle method names into friendly camelized names
9
+ # when declaring them to remote callers
10
+ base.class_inheritable_option :export_name_mangling, true
11
+
12
+ # If present, the name of a method to call when the remote caller
13
+ # tried to call a nonexistent method. Semantically equivalent to
14
+ # +method_missing+.
15
+ base.class_inheritable_option :default_export
16
+
17
+ base.extend(ClassMethods)
18
+ end
19
+
20
+ module ClassMethods
21
+ # Declares the named method to be available for remote execution.
22
+ #
23
+ # Example:
24
+ #
25
+ # class CalculatorService < ActionService::Base
26
+ # def add(a, b)
27
+ # a + b
28
+ # end
29
+ #
30
+ # export :add, :expects => [Integer, Integer], :returns => [Integer]
31
+ # end
32
+ #
33
+ #
34
+ # You will need to provide type annotation options if the method will be
35
+ # receiving arguments, or returning values that will be used by remote
36
+ # callers.
37
+ #
38
+ # A type annotation is an Array, containing as elements one or more of:
39
+ #
40
+ # * A Class object for the desired type.
41
+ # * An Array containing a Class object. Declares that the argument at that
42
+ # position is to be an Array containing values of type Class.
43
+ # * A Hash containing the argument name as the key, and one of the above as
44
+ # value.
45
+ #
46
+ # Valid annotations:
47
+ # [<tt>:expects</tt>] The arguments that will be received by the method
48
+ # [<tt>:returns</tt>] The type of the method return value. May contain only one element.
49
+ # [<tt>:expects_and_returns</tt>] Shortcut for specifying <tt>:expects</tt> and <tt>:returns</tt> with the same signature.
50
+ #
51
+ # The public name of the method (that is, the name that must be used by
52
+ # remote callers) will be a _camelized_ version of +name+. Camelization is
53
+ # performed using Rails inflector camelization rules.
54
+ #
55
+ # Method name camelization can be turned off for a class by using the
56
+ # +export_name_mangling+ class option:
57
+ #
58
+ # class MyService < ActionService::Base
59
+ # export_name_mangling false
60
+ # end
61
+ def export(name, options={})
62
+ validate_options([:expects, :returns, :expects_and_returns], options.keys)
63
+ if options[:expects_and_returns]
64
+ expects = options[:expects_and_returns]
65
+ returns = options[:expects_and_returns]
66
+ else
67
+ expects = options[:expects]
68
+ returns = options[:returns]
69
+ end
70
+ if expects
71
+ expects.each do |klass|
72
+ klass = klass.values.shift if klass.is_a?(Hash)
73
+ klass = klass[0] if klass.is_a?(Array)
74
+ if klass.ancestors.include?(ActiveRecord::Base)
75
+ raise(ActionServiceError, "ActiveRecord model classes not allowed in :expects")
76
+ end
77
+ end
78
+ end
79
+ name = name.to_sym
80
+ public_name = public_export_name(name)
81
+ info = { :expects => expects, :returns => returns }
82
+ write_inheritable_hash("action_service_exports", name => info)
83
+ write_inheritable_hash("action_service_public_exports", public_name => name)
84
+ call_export_definition_callbacks(self, name, info)
85
+ end
86
+
87
+ # Whether the given name is exported by this service
88
+ def has_export?(name)
89
+ exports.has_key?(name)
90
+ end
91
+
92
+ # Whether the given public method name (mangled) is exported by
93
+ # this service
94
+ def has_public_export?(name)
95
+ public_exports.has_key?(name)
96
+ end
97
+
98
+ # The mangled public method name for the given method name
99
+ def public_export_name(export_name)
100
+ if export_name_mangling
101
+ Inflector.camelize(export_name.to_s)
102
+ else
103
+ export_name.to_s
104
+ end
105
+ end
106
+
107
+ # The internal method name for the given public method name
108
+ def internal_export_name(public_name)
109
+ public_exports[public_name]
110
+ end
111
+
112
+ # A Hash of method name to export information
113
+ def exports
114
+ read_inheritable_attribute("action_service_exports") || {}
115
+ end
116
+
117
+ def add_export_definition_callback(&block) # :nodoc:
118
+ write_inheritable_array("export_definition_callbacks", [block])
119
+ end
120
+
121
+ private
122
+ def call_export_definition_callbacks(service_class, export_name, export_info)
123
+ (read_inheritable_attribute("export_definition_callbacks") || []).each do |block|
124
+ block.call(service_class, export_name, export_info)
125
+ end
126
+ end
127
+
128
+ def public_exports
129
+ read_inheritable_attribute("action_service_public_exports") || {}
130
+ end
131
+
132
+ def validate_options(valid_option_keys, supplied_option_keys)
133
+ unknown_option_keys = supplied_option_keys - valid_option_keys
134
+ unless unknown_option_keys.empty?
135
+ raise(ActionServiceError, "Unknown options: #{unknown_option_keys}")
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -1,5 +1,9 @@
1
1
  module ActionService
2
2
  module Invocation
3
+ ConcreteInvocation = :concrete
4
+ VirtualInvocation = :virtual
5
+ UnexportedConcreteInvocation = :unexported_concrete
6
+
3
7
  class InvocationError < ActionService::ActionServiceError
4
8
  end
5
9
 
@@ -95,29 +99,29 @@ module ActionService
95
99
  end
96
100
  end
97
101
 
98
- def perform_invocation_with_interception(name, args=nil, options={}, &block)
99
- return if before_invocation(name, args, &block) == false
100
- result = perform_invocation_without_interception(name, args, options)
101
- after_invocation(name, args, result)
102
+ def perform_invocation_with_interception(invocation, &block)
103
+ return if before_invocation(invocation.method_name, invocation.params, &block) == false
104
+ result = perform_invocation_without_interception(invocation)
105
+ after_invocation(invocation.method_name, invocation.params, result)
102
106
  result
103
107
  end
104
108
 
105
- def perform_invocation(name, args=nil, options={})
106
- unless options[:require_exported] == false
107
- unless self.respond_to?(name) && self.class.has_export?(name)
108
- raise InvocationError, "no such exported method '#{name}'"
109
+ def perform_invocation(invocation)
110
+ if invocation.concrete?
111
+ unless self.respond_to?(invocation.method_name) && self.class.has_export?(invocation.method_name)
112
+ raise InvocationError, "no such exported method '#{invocation.method_name}'"
109
113
  end
110
114
  end
111
- args ||= []
112
- if options[:system_call]
113
- name, block = options[:system_call]
114
- if block.respond_to?('call')
115
- block.call(name, *args)
115
+ params = invocation.params
116
+ if invocation.concrete? || invocation.unexported_concrete?
117
+ self.send(invocation.method_name, *params)
118
+ else
119
+ if invocation.block
120
+ params = invocation.block_params + params
121
+ invocation.block.call(invocation.public_method_name, *params)
116
122
  else
117
- self.send(block, *args)
123
+ self.send(invocation.method_name, *params)
118
124
  end
119
- else
120
- self.send(name, *args)
121
125
  end
122
126
  end
123
127
 
@@ -177,7 +181,33 @@ module ActionService
177
181
  self.class.excluded_intercepted_methods[interceptor].include?(method_name)
178
182
  end
179
183
  end
184
+ end
185
+
186
+ class InvocationRequest
187
+ attr_accessor :type
188
+ attr :public_method_name
189
+ attr_accessor :method_name
190
+ attr_accessor :params
191
+ attr_accessor :block
192
+ attr :block_params
193
+
194
+ def initialize(type, public_method_name, method_name, params=nil)
195
+ @type = type
196
+ @public_method_name = public_method_name
197
+ @method_name = method_name
198
+ @params = params || []
199
+ @block = nil
200
+ @block_params = []
201
+ end
202
+
203
+ def concrete?
204
+ @type == ConcreteInvocation ? true : false
205
+ end
180
206
 
207
+ def unexported_concrete?
208
+ @type == UnexportedConcreteInvocation ? true : false
209
+ end
181
210
  end
211
+
182
212
  end
183
213
  end