actionservice 0.2.102 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,50 @@
1
1
  require 'action_service/support/class_inheritable_options'
2
2
 
3
- module ActionService
4
- class ActionServiceError < StandardError
3
+ module ActionService # :nodoc:
4
+ class ActionServiceError < StandardError # :nodoc:
5
5
  end
6
6
 
7
+ # Action Service objects implement an exported API. Used in controllers
8
+ # operating in _Delegated_ dispatching mode.
9
+ #
10
+ # An ActionService::Base derivative is the <em>public interface</em>
11
+ # of an API.
12
+ #
13
+ # === Example
14
+ #
15
+ # class SearchCriteria < ActionStruct::Base
16
+ # member :firstname, String
17
+ # member :lastname, String
18
+ # member :email, String
19
+ # end
20
+ #
21
+ # class PersonApi < ActionService::Base
22
+ # def find_person(criteria)
23
+ # Person.find_all [...]
24
+ # end
25
+ #
26
+ # def delete_person(id)
27
+ # Person.find_by_id(id).destroy
28
+ # end
29
+ #
30
+ # export :find_person, :expects => [SearchCriteria], :returns => [[Person]]
31
+ # export :delete_person, :expects => [Integer]
32
+ # end
33
+ #
34
+ # See ActionService::Exporting::ClassMethods for more details on the syntax
35
+ # for declaring exported methods.
7
36
  class Base
8
37
  # Whether to send back a detailed stack trace to the remote caller
9
38
  # when an exception is thrown on the server
10
39
  class_inheritable_option :report_exceptions, true
40
+
41
+ # Whether to mangle method names into friendly camelized names
42
+ # when declaring them to remote callers
43
+ class_inheritable_option :export_name_mangling, true
44
+
45
+ # If present, the name of a method to call when the remote caller
46
+ # tried to call a nonexistent method. Semantically equivalent to
47
+ # +method_missing+.
48
+ class_inheritable_option :default_export
11
49
  end
12
50
  end
@@ -1,12 +1,12 @@
1
- module ActionService
2
- module Container
1
+ module ActionService # :nodoc:
2
+ module Container # :nodoc:
3
3
  DirectDispatching = :direct
4
4
  DelegatedDispatching = :delegated
5
5
 
6
- class ContainerError < ActionService::ActionServiceError
6
+ class ContainerError < ActionService::ActionServiceError # :nodoc:
7
7
  end
8
8
 
9
- def self.append_features(base)
9
+ def self.append_features(base) # :nodoc:
10
10
  super
11
11
  base.class_inheritable_option(:service_dispatching_mode, DirectDispatching)
12
12
  base.extend(ClassMethods)
@@ -14,7 +14,36 @@ module ActionService
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- def service(name, object=nil, options={}, &block)
17
+ # Declares a service that will provides access to the exported API
18
+ # of the given service +object+. +object+ must be an ActionService::Base
19
+ # derivative.
20
+ #
21
+ # === Example
22
+ #
23
+ # class ApiController < ApplicationController
24
+ # service_dispatching_mode :delegated
25
+ #
26
+ # service :person, PersonApi.new
27
+ # end
28
+ #
29
+ # This will generate a <tt>/api/person</tt> action in the controller.
30
+ # All exported methods of +object+ will be available for invocation by
31
+ # clients sending protocol messages to <tt>/api/person</tt> with HTTP POST.
32
+ #
33
+ # You can also declare a service that defers the service +object+
34
+ # creation until request time. To do this, pass a block instead of a
35
+ # service object instance. The block will be executed in instance
36
+ # context, so it will have access to controller instance variables. Its
37
+ # return value should be the service object.
38
+ #
39
+ # === Deferred Example
40
+ #
41
+ # class ApiController < ApplicationController
42
+ # service_dispatching_mode :delegated
43
+ #
44
+ # service(:person) { PersonApi.new }
45
+ # end
46
+ def service(name, object=nil, &block)
18
47
  if (object && block_given?) || (object.nil? && block.nil?)
19
48
  raise(ContainerError, "either service, or a block must be given")
20
49
  end
@@ -28,18 +57,20 @@ module ActionService
28
57
  call_service_definition_callbacks(self, name, info)
29
58
  end
30
59
 
31
- def add_service_definition_callback(&block)
32
- write_inheritable_array("service_definition_callbacks", [block])
33
- end
34
-
60
+ # Returns +true+ if #service was used to declare a service with
61
+ # the given name.
35
62
  def has_service?(name)
36
63
  services.has_key?(name.to_sym)
37
64
  end
38
65
 
39
- def services
66
+ def services # :nodoc:
40
67
  read_inheritable_attribute("action_services") || {}
41
68
  end
42
69
 
70
+ def add_service_definition_callback(&block) # :nodoc:
71
+ write_inheritable_array("service_definition_callbacks", [block])
72
+ end
73
+
43
74
  private
44
75
  def call_service_definition_callbacks(container_class, service_name, service_info)
45
76
  (read_inheritable_attribute("service_definition_callbacks") || []).each do |block|
@@ -48,7 +79,7 @@ module ActionService
48
79
  end
49
80
  end
50
81
 
51
- module InstanceMethods
82
+ module InstanceMethods # :nodoc:
52
83
  def service_object(service_name)
53
84
  info = self.class.services[service_name.to_sym]
54
85
  unless info
@@ -73,13 +104,29 @@ module ActionService
73
104
  def dispatch_direct_service_request(protocol_request)
74
105
  public_method_name = protocol_request.public_method_name
75
106
  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]
107
+ block = nil
108
+ expects = nil
109
+ if method_name
110
+ export_info = self.class.exports[method_name]
82
111
  expects = export_info[:expects]
112
+ protocol_request.type = Protocol::CheckedMessage
113
+ protocol_request.signature = expects
114
+ protocol_request.return_signature = export_info[:returns]
115
+ else
116
+ protocol_request.type = Protocol::UncheckedMessage
117
+ system_exports = self.class.read_inheritable_attribute('default_system_exports') || {}
118
+ protocol = protocol_request.protocol
119
+ block = system_exports[protocol.class]
120
+ unless block
121
+ method_name = service_class.default_export
122
+ unless method_name && service.respond_to?(method_name)
123
+ raise(ContainerError, "no such method /#{service_name}##{public_method_name}")
124
+ end
125
+ end
126
+ end
127
+
128
+ @invocation_params = protocol_request.unmarshal
129
+ if expects
83
130
  @params ||= {}
84
131
  (1..@invocation_params.size).each do |i|
85
132
  i -= 1
@@ -90,7 +137,29 @@ module ActionService
90
137
  end
91
138
  end
92
139
  end
93
- result = send(method_name)
140
+
141
+ perform_invoke = lambda do
142
+ if block
143
+ result = block.call(public_method_name, self.class, *@invocation_params)
144
+ else
145
+ result = send(method_name)
146
+ end
147
+ end
148
+ try_default = true
149
+ result = nil
150
+ catch(:try_default_export) do
151
+ result = perform_invoke.call
152
+ try_default = false
153
+ end
154
+ if try_default
155
+ method_name = self.class.default_export
156
+ if method_name
157
+ protocol_request.type = Protocol::UncheckedMessage
158
+ else
159
+ raise(ContainerError, "no such method ##{public_method_name}")
160
+ end
161
+ result = perform_invoke.call
162
+ end
94
163
  protocol_request.marshal(result)
95
164
  end
96
165
 
@@ -1,19 +1,13 @@
1
- module ActionService
2
- module Exporting
3
- class ExportError < ActionService::ActionServiceError
1
+ module ActionService # :nodoc:
2
+ module Exporting # :nodoc:
3
+ class ExportError < ActionService::ActionServiceError # :nodoc:
4
4
  end
5
5
 
6
- def self.append_features(base)
6
+ def self.append_features(base) # :nodoc:
7
7
  super
8
- # Whether to mangle method names into friendly camelized names
9
- # when declaring them to remote callers
8
+ base.class_inheritable_option :report_exceptions, true
10
9
  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
10
  base.class_inheritable_option :default_export
16
-
17
11
  base.extend(ClassMethods)
18
12
  end
19
13
 
@@ -38,7 +32,7 @@ module ActionService
38
32
  # A type annotation is an Array, containing as elements one or more of:
39
33
  #
40
34
  # * A Class object for the desired type.
41
- # * An Array containing a Class object. Declares that the argument at that
35
+ # * An Array containing a Class object. Indicates that the argument at that
42
36
  # position is to be an Array containing values of type Class.
43
37
  # * A Hash containing the argument name as the key, and one of the above as
44
38
  # value.
@@ -1,10 +1,10 @@
1
- module ActionService
2
- module Invocation
1
+ module ActionService # :nodoc:
2
+ module Invocation # :nodoc:
3
3
  ConcreteInvocation = :concrete
4
4
  VirtualInvocation = :virtual
5
5
  UnexportedConcreteInvocation = :unexported_concrete
6
6
 
7
- class InvocationError < ActionService::ActionServiceError
7
+ class InvocationError < ActionService::ActionServiceError # :nodoc:
8
8
  end
9
9
 
10
10
  def self.append_features(base) # :nodoc:
@@ -13,7 +13,43 @@ module ActionService
13
13
  base.send(:include, ActionService::Invocation::InstanceMethods)
14
14
  end
15
15
 
16
+ # Invocation interceptors provide a means to execute custom code before
17
+ # and after API method invocations on ActionService::Base objects when
18
+ # running in _Delegated_ dispatching mode.
19
+ #
20
+ # When running in _Direct_ mode, ActionController filters should be used
21
+ # instead.
22
+ #
23
+ # The semantics of invocation interceptors are the same as ActionController
24
+ # filters, and accept the same parameters and options.
25
+ #
26
+ # A _before_ interceptor can also cancel execution by returning +false+,
27
+ # or returning a <tt>[false, "cancel reason"]</tt> array if it wishes to supply
28
+ # a reason for canceling the request.
29
+ #
30
+ # === Example
31
+ #
32
+ # class CustomService < ActionService::Base
33
+ # before_invocation :intercept_add, :only => [:add]
34
+ #
35
+ # def add(a, b)
36
+ # a + b
37
+ # end
38
+ #
39
+ # export :add, :expects => [Integer, Integer], :returns => [Integer]
40
+ #
41
+ # private
42
+ # def intercept_add
43
+ # return [false, "permission denied"] # cancel it
44
+ # end
45
+ # end
46
+ #
47
+ # Options:
48
+ # [<tt>:except</tt>] A list of exports to which the interceptor will NOT apply
49
+ # [<tt>:only</tt>] A list of exports to which the interceptor will apply
16
50
  module ClassMethods
51
+ # Appends the given +interceptors+ to be called
52
+ # _before_ method invocation.
17
53
  def append_before_invocation(*interceptors, &block)
18
54
  conditions = extract_conditions!(interceptors)
19
55
  interceptors << block if block_given?
@@ -21,6 +57,8 @@ module ActionService
21
57
  append_interceptors_to_chain("before", interceptors)
22
58
  end
23
59
 
60
+ # Prepends the given +interceptors+ to be called
61
+ # _before_ method invocation.
24
62
  def prepend_before_invocation(*interceptors, &block)
25
63
  conditions = extract_conditions!(interceptors)
26
64
  interceptors << block if block_given?
@@ -30,6 +68,8 @@ module ActionService
30
68
 
31
69
  alias :before_invocation :append_before_invocation
32
70
 
71
+ # Appends the given +interceptors+ to be called
72
+ # _after_ method invocation.
33
73
  def append_after_invocation(*interceptors, &block)
34
74
  conditions = extract_conditions!(interceptors)
35
75
  interceptors << block if block_given?
@@ -37,6 +77,8 @@ module ActionService
37
77
  append_interceptors_to_chain("after", interceptors)
38
78
  end
39
79
 
80
+ # Prepends the given +interceptors+ to be called
81
+ # _after_ method invocation.
40
82
  def prepend_after_invocation(*interceptors, &block)
41
83
  conditions = extract_conditions!(interceptors)
42
84
  interceptors << block if block_given?
@@ -46,24 +88,23 @@ module ActionService
46
88
 
47
89
  alias :after_invocation :append_after_invocation
48
90
 
49
- def before_invocation_interceptors
91
+ def before_invocation_interceptors # :nodoc:
50
92
  read_inheritable_attribute("before_invocation_interceptors")
51
93
  end
52
94
 
53
- def after_invocation_interceptors
95
+ def after_invocation_interceptors # :nodoc:
54
96
  read_inheritable_attribute("after_invocation_interceptors")
55
97
  end
56
98
 
57
- def included_intercepted_methods
99
+ def included_intercepted_methods # :nodoc:
58
100
  read_inheritable_attribute("included_intercepted_methods") || {}
59
101
  end
60
102
 
61
- def excluded_intercepted_methods
103
+ def excluded_intercepted_methods # :nodoc:
62
104
  read_inheritable_attribute("excluded_intercepted_methods") || {}
63
105
  end
64
106
 
65
107
  private
66
-
67
108
  def append_interceptors_to_chain(condition, interceptors)
68
109
  write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
69
110
  end
@@ -90,7 +131,7 @@ module ActionService
90
131
  end
91
132
  end
92
133
 
93
- module InstanceMethods
134
+ module InstanceMethods # :nodoc:
94
135
  def self.append_features(base)
95
136
  super
96
137
  base.class_eval do
@@ -183,7 +224,7 @@ module ActionService
183
224
  end
184
225
  end
185
226
 
186
- class InvocationRequest
227
+ class InvocationRequest # :nodoc:
187
228
  attr_accessor :type
188
229
  attr :public_method_name
189
230
  attr_accessor :method_name
@@ -1,12 +1,12 @@
1
- module ActionService
2
- module Protocol
1
+ module ActionService # :nodoc:
2
+ module Protocol # :nodoc:
3
3
  CheckedMessage = :checked
4
4
  UncheckedMessage = :unchecked
5
5
 
6
- class ProtocolError < ActionService::ActionServiceError
6
+ class ProtocolError < ActionService::ActionServiceError # :nodoc:
7
7
  end
8
8
 
9
- class AbstractProtocol
9
+ class AbstractProtocol # :nodoc:
10
10
  attr :container_class
11
11
 
12
12
  def initialize(container_class)
@@ -30,14 +30,16 @@ module ActionService
30
30
  end
31
31
  end
32
32
 
33
- class AbstractProtocolMessage
33
+ class AbstractProtocolMessage # :nodoc:
34
34
  attr_accessor :signature
35
35
  attr_accessor :return_signature
36
36
  attr_accessor :type
37
+ attr :options
37
38
 
38
39
  def initialize(options={})
39
40
  @signature = @return_signature = nil
40
- @type = options[:type] || CheckedMessage
41
+ @options = options
42
+ @type = @options[:type] || CheckedMessage
41
43
  end
42
44
 
43
45
  def signature=(value)
@@ -79,14 +81,13 @@ module ActionService
79
81
  end
80
82
  end
81
83
 
82
- class ProtocolRequest < AbstractProtocolMessage
84
+ class ProtocolRequest < AbstractProtocolMessage # :nodoc:
83
85
  attr :protocol
84
86
  attr :raw_body
85
87
 
86
88
  attr_accessor :service_name
87
89
  attr_accessor :public_method_name
88
90
  attr_accessor :content_type
89
- attr_accessor :values
90
91
 
91
92
  def initialize(protocol, raw_body, service_name, public_method_name, content_type, options={})
92
93
  super(options)
@@ -95,11 +96,10 @@ module ActionService
95
96
  @service_name = service_name
96
97
  @public_method_name = public_method_name
97
98
  @content_type = content_type
98
- @values = nil
99
99
  end
100
100
 
101
101
  def unmarshal
102
- @values ||= @protocol.unmarshal_request(self)
102
+ @protocol.unmarshal_request(self)
103
103
  end
104
104
 
105
105
  def marshal(return_value)
@@ -107,7 +107,7 @@ module ActionService
107
107
  end
108
108
  end
109
109
 
110
- class ProtocolResponse < AbstractProtocolMessage
110
+ class ProtocolResponse < AbstractProtocolMessage # :nodoc:
111
111
  attr :protocol
112
112
  attr :raw_body
113
113
 
@@ -1,17 +1,17 @@
1
- module ActionService
2
- module Protocol
1
+ module ActionService # :nodoc:
2
+ module Protocol # :nodoc:
3
3
  HeaderAndBody = :header_and_body
4
4
  BodyOnly = :body_only
5
5
 
6
- module Registry
7
- def self.append_features(base)
6
+ module Registry # :nodoc:
7
+ def self.append_features(base) # :nodoc:
8
8
  super
9
9
  base.extend(ClassMethods)
10
10
  base.send(:include, ActionService::Protocol::Registry::InstanceMethods)
11
11
  end
12
12
 
13
- module ClassMethods
14
- def register_protocol(type, klass)
13
+ module ClassMethods # :nodoc:
14
+ def register_protocol(type, klass) # :nodoc:
15
15
  case type
16
16
  when HeaderAndBody
17
17
  write_inheritable_array("header_and_body_protocols", [klass])
@@ -23,7 +23,7 @@ module ActionService
23
23
  end
24
24
  end
25
25
 
26
- module InstanceMethods
26
+ module InstanceMethods # :nodoc:
27
27
  private
28
28
  def probe_request_protocol(action_pack_request)
29
29
  (header_and_body_protocols + body_only_protocols).each do |protocol|