actionservice 0.2.102 → 0.3.0

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.
@@ -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|