protobuf 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -169,6 +169,69 @@ end
169
169
 
170
170
  This means that the client's `on_failure` callback will be invoked instead of the `on_success` callback. Read more below on client callbacks. One drawback to the `rpc_failed` approach is that it does not short-circuit the rest of the method. This means that you must explicitly return from the method if you do not wish the remainder to be executed.
171
171
 
172
+ ### Service Filters
173
+
174
+ Service Filters provides ActionController-style filter support to service instances, specifically adding `before_filter`, `after_filter`, and `around_filter`.
175
+
176
+ ```ruby
177
+ class Foo::UserService < ::Protobuf::Rpc::Service
178
+ before_filter :start_request_timer
179
+ after_filter :end_request_timer
180
+ around_filter :benchmark_request
181
+
182
+ # Provide a list of rpc methods to call (or exclude calling) for the given filter(s).
183
+ # The following two filters are essentially equivalent.
184
+ before_filter :verify_user_present, :only => [ :update, :delete ]
185
+ before_filter :verify_user_present, :except => [ :find, :create ]
186
+
187
+ # Using if/unless filters options to achieve the same goal, reporting a login after the login has been processed.
188
+ # Note that you can provide a method name or lambda, but you must return a boolean value.
189
+ after_filter :report_login, :only => :login, :if => :user_found?
190
+ after_filter :report_login, :only => :login, :if => lambda { |service| service.response.user_guid.present? }
191
+ after_filter :report_login, :only => :login, :unless => :user_missing?
192
+ after_filter :report_login, :only => :login, :unless => lambda { |service| service.response.user_guid.empty? }
193
+
194
+ #... rpc instance methods
195
+
196
+ private
197
+
198
+ def start_request_timer
199
+ @time_start = Time.now
200
+ end
201
+
202
+ def end_request_timer
203
+ @time_end = Time.now
204
+ log_info { ... }
205
+ end
206
+
207
+ def benchmark_request
208
+ Benchmark.benchmark do
209
+ yield
210
+ end
211
+ end
212
+ end
213
+ ```
214
+
215
+ #### Halting execution of rpc request inside a filter
216
+
217
+ __Around Filters__ – Inside of an around filter, if you wish to halt request processing and return, simply do not `yield` the block. Since the filter is implemented as an instance method, you can use `rpc_failed` or `respond_with` just like you can in the endpoint methods.
218
+
219
+ __Before Filters__ – Returning `false` from a before filter will cancel any other filter calls which would run afterwards, as well as canceling invocation of the service method. Note: You must actually return false, not just a "falsey" value such as nil.
220
+
221
+ __After Filters__ – There is no request shortcutting since the after filter runs after the request. Duh.
222
+
223
+ #### Filter options
224
+
225
+ The following options can be applied to any of the filters as the final argument in the filter configuration. (See example above).
226
+
227
+ __:if__ – The object supplied to `:if` can either be a symbol/string indicating the instance method to call, or, an object that responds to `call`. The method or callable should return true or false indicating if the filter should be invoked or not. Akin to the `if` keyword.
228
+
229
+ __:unless__ – The opposite of the `:if` option is `:unless`. Accepts the same object types. The method or callable should return true or false indicating if the filter should be invoked or not. Akin to the `unless` keyword.
230
+
231
+ __:only__ – A string/symbol or Array of strings/symbols values that reference instance methods. The names of these methods should be the rpc method you wish to invoke the filter for. Methods not identified in this list would not have the filter applied.
232
+
233
+ __:except__ – The opposite of the `:only` option. A string/symbol or Array of strings/symbols values that reference instance methods. The names of these methods should be the rpc method you wish to skip invokation of the given filter. Methods not identified in this list would have the filter applied.
234
+
172
235
  ### Servers
173
236
 
174
237
  A service is nothing without being hooked up to a socket. It's the nerdy kid waiting by the telephone for someone to call without knowing that the phone company disconnected their house. Sad and pathetic. So hook the phone lines!
@@ -34,7 +34,6 @@ module Protobuf
34
34
  option :print_deprecation_warnings, :type => :boolean, :default => true, :desc => 'Cause use of deprecated fields to be printed or ignored.'
35
35
 
36
36
  def start(app_file)
37
- puts 'RUNNING FROM LOCAL'
38
37
  debug_say 'Configuring the rpc_server process'
39
38
  @start_aborted = false
40
39
 
@@ -159,7 +158,7 @@ module Protobuf
159
158
  { :host => options.host,
160
159
  :port => options.port,
161
160
  :backlog => options.backlog,
162
- :treshold => options.threshold,
161
+ :threshold => options.threshold,
163
162
  :threads => options.threads }
164
163
  end
165
164
 
@@ -168,6 +167,9 @@ module Protobuf
168
167
 
169
168
  ::Protobuf::Logger.error { message }
170
169
  if exception
170
+ $stderr.puts "[#{exception.class.name}] #{exception.message}"
171
+ $stderr.puts exception.backtrace.join("\n")
172
+
171
173
  ::Protobuf::Logger.error { "[#{exception.class.name}] #{exception.message}" }
172
174
  ::Protobuf::Logger.debug { exception.backtrace.join("\n") }
173
175
  end
@@ -103,14 +103,14 @@ module Protobuf
103
103
  # c.on_failure {|err| ... }
104
104
  # end
105
105
  #
106
- def method_missing(method, *params)
106
+ def method_missing(method_name, *params)
107
107
  service = options[:service]
108
- unless service.rpc_method?(method)
109
- log_error { sign_message("#{service.name}##{method.to_s} not rpc method, passing to super") }
110
- super(method, *params)
108
+ unless service.rpc_method?(method_name)
109
+ log_error { sign_message("#{service.name}##{method_name.to_s} not rpc method, passing to super") }
110
+ super(method_name, *params)
111
111
  else
112
- log_debug { sign_message("#{service.name}##{method.to_s}") }
113
- rpc = service.rpcs[method.to_sym]
112
+ log_debug { sign_message("#{service.name}##{method_name.to_s}") }
113
+ rpc = service.rpcs[method_name.to_sym]
114
114
 
115
115
  options[:request_type] = rpc.request_type
116
116
  log_debug { sign_message("Request Type: #{options[:request_type].name}") }
@@ -118,7 +118,7 @@ module Protobuf
118
118
  options[:response_type] = rpc.response_type
119
119
  log_debug { sign_message("Response Type: #{options[:response_type].name}") }
120
120
 
121
- options[:method] = method.to_s
121
+ options[:method] = method_name.to_s
122
122
  options[:request] = params[0].is_a?(Hash) ? options[:request_type].new(params[0]) : params[0]
123
123
  log_debug { sign_message("Request Data: #{options[:request].inspect}") }
124
124
 
@@ -51,7 +51,7 @@ module Protobuf
51
51
  @stats = Protobuf::Rpc::Stat.new(:CLIENT)
52
52
  @stats.server = [@options[:port], @options[:host]]
53
53
  @stats.service = @options[:service].name
54
- @stats.method = @options[:method].to_s
54
+ @stats.method_name = @options[:method].to_s
55
55
  rescue => ex
56
56
  log_exception(ex)
57
57
  fail(:RPC_ERROR, "Invalid stats configuration. #{ex.message}")
@@ -1,17 +1,21 @@
1
1
  require 'protobuf/logger'
2
2
  require 'protobuf/rpc/client'
3
3
  require 'protobuf/rpc/error'
4
+ require 'protobuf/rpc/service_filters'
4
5
 
5
6
  module Protobuf
6
7
  module Rpc
8
+
7
9
  # Object to encapsulate the request/response types for a given service method
8
10
  #
9
11
  RpcMethod = Struct.new("RpcMethod", :method, :request_type, :response_type)
10
12
 
11
13
  class Service
14
+ include Protobuf::Rpc::ServiceFilters
12
15
  include Protobuf::Logger::LogMethods
13
16
 
14
- attr_reader :response
17
+
18
+ attr_reader :response, :rpc
15
19
 
16
20
  DEFAULT_HOST = '127.0.0.1'.freeze
17
21
  DEFAULT_PORT = 9399
@@ -130,6 +134,15 @@ module Protobuf
130
134
  self.class.rpcs
131
135
  end
132
136
 
137
+ # Get a callable object that will be used by the dispatcher
138
+ # to invoke the specified rpc method. Facilitates callback dispatch.
139
+ # The returned lambda is expected to be called at a later time (which
140
+ # is why we wrap the method call).
141
+ #
142
+ def callable_rpc_method(method_name)
143
+ lambda { run_filters(method_name) }
144
+ end
145
+
133
146
  private
134
147
 
135
148
  # Request object for this rpc cycle. Not assignable.
@@ -73,12 +73,14 @@ module Protobuf
73
73
  method_name = outer_request.method_name.underscore.to_sym
74
74
  if service_klass.rpc_method?(method_name)
75
75
  self.service = service_klass.new(method_name, outer_request.request_proto)
76
- self.callable_method = service.method(method_name)
76
+ self.callable_method = service.callable_rpc_method(method_name)
77
77
  self.definition = service.rpcs[method_name]
78
78
  else
79
79
  assign_error(MethodNotFound, "#{service.class.name}##{method_name} is not a defined rpc method.")
80
80
  end
81
81
  rescue NameError => e
82
+ # FIXME I think this is no longer applicable since the method extract
83
+ # is now wrapped in a lambda (@see Service#callable_rpc_method).
82
84
  log_exception(e)
83
85
  assign_error(MethodNotFound, "#{service.class.name}##{method_name} is not implemented.")
84
86
  end
@@ -0,0 +1,243 @@
1
+ module Protobuf
2
+ module Rpc
3
+ module ServiceFilters
4
+
5
+ def self.included(other)
6
+ other.class_eval do
7
+ extend Protobuf::Rpc::ServiceFilters::ClassMethods
8
+ include Protobuf::Rpc::ServiceFilters::InstanceMethods
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ [:after, :around, :before].each do |type|
15
+ # Setter DSL method for given filter types.
16
+ #
17
+ define_method "#{type}_filter" do |*args|
18
+ set_filters(type, *args)
19
+ end
20
+ end
21
+
22
+ # Filters hash keyed based on filter type (e.g. :before, :after, :around),
23
+ # whose values are Sets.
24
+ #
25
+ def filters
26
+ @filters ||= Hash.new { |h,k| h[k] = [] }
27
+ end
28
+
29
+ private
30
+
31
+ def define_filter(type, filter, options = {})
32
+ return if filter_defined?(type, filter)
33
+ filters[type] << options.merge({ :callable => filter })
34
+ remember_filter(type, filter)
35
+ end
36
+
37
+ def defined_filters
38
+ @defined_filters ||= Hash.new { |h,k| h[k] = Set.new }
39
+ end
40
+
41
+ # Check to see if the filter has been defined.
42
+ #
43
+ def filter_defined?(type, filter)
44
+ defined_filters[type].include?(filter)
45
+ end
46
+
47
+ # Remember that we stored the filter.
48
+ #
49
+ def remember_filter(type, filter)
50
+ defined_filters[type] << filter
51
+ end
52
+
53
+ # Takes a list of actually (or potentially) callable objects.
54
+ # TODO add support for if/unless
55
+ # TODO add support for only/except sub-filters
56
+ #
57
+ def set_filters(type, *args)
58
+ options = args.last.is_a?(Hash) ? args.pop : {}
59
+ args.each do |filter|
60
+ define_filter(type, filter, options)
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ module InstanceMethods
67
+
68
+ private
69
+
70
+ # Get back to class filters.
71
+ #
72
+ def filters
73
+ self.class.filters
74
+ end
75
+
76
+ # Predicate which uses the filter options to determine if the filter
77
+ # should be called. Specifically checks the :if, :unless, :only, and :except
78
+ # options for every filter. Each option check is expected to return false
79
+ # if the filter should not be invoked, true if invocation should occur.
80
+ #
81
+ def invoke_filter?(rpc_method, filter)
82
+ return invoke_via_only?(rpc_method, filter) \
83
+ && invoke_via_except?(rpc_method, filter) \
84
+ && invoke_via_if?(rpc_method, filter) \
85
+ && invoke_via_unless?(rpc_method, filter)
86
+ end
87
+
88
+ # If the target rpc endpoint method is listed under an :except option,
89
+ # return false to indicate that the filter should not be invoked. Any
90
+ # other target rpc endpoint methods not listed should be invoked.
91
+ # This option is the opposite of :only.
92
+ #
93
+ # Value should be a symbol/string or an array of symbols/strings.
94
+ #
95
+ def invoke_via_except?(rpc_method, filter)
96
+ except = [ filter.fetch(:except) { [] } ].flatten
97
+ return except.empty? || ! except.include?(rpc_method)
98
+ end
99
+
100
+ # Invoke the given :if callable (if any) and return its return value.
101
+ # Used by `invoke_filter?` which expects a true/false
102
+ # return value to determine if we should invoke the target filter.
103
+ #
104
+ # Value can either be a symbol/string indicating an instance method to call
105
+ # or an object that responds to `call`.
106
+ #
107
+ def invoke_via_if?(rpc_method, filter)
108
+ if_check = filter.fetch(:if) { lambda { |service| return true } }
109
+ do_invoke = case
110
+ when if_check.nil? then
111
+ true
112
+ else
113
+ call_or_send(if_check)
114
+ end
115
+
116
+ return do_invoke
117
+ end
118
+
119
+ # If the target rpc endpoint method is listed in the :only option,
120
+ # it should be invoked. Any target rpc endpoint methods not listed in this
121
+ # option should not be invoked. This option is the opposite of :except.
122
+ #
123
+ # Value should be a symbol/string or an array of symbols/strings.
124
+ #
125
+ def invoke_via_only?(rpc_method, filter)
126
+ only = [ filter.fetch(:only) { [] } ].flatten
127
+ return only.empty? || only.include?(rpc_method)
128
+ end
129
+
130
+ # Invoke the given :unless callable (if any) and return the opposite
131
+ # of it's return value. Used by `invoke_filter?` which expects a true/false
132
+ # return value to determine if we should invoke the target filter.
133
+ #
134
+ # Value can either be a symbol/string indicating an instance method to call
135
+ # or an object that responds to `call`.
136
+ #
137
+ def invoke_via_unless?(rpc_method, filter)
138
+ unless_check = filter.fetch(:unless) { lambda { |service| return false } }
139
+ skip_invoke = case
140
+ when unless_check.nil? then
141
+ false
142
+ else
143
+ call_or_send(unless_check)
144
+ end
145
+
146
+ return ! skip_invoke
147
+ end
148
+
149
+ # Loop over the unwrapped filters and invoke them. An unwrapped filter
150
+ # is either a before or after filter, not an around filter.
151
+ #
152
+ def run_unwrapped_filters(unwrapped_filters, rpc_method, stop_on_false_return = false)
153
+ unwrapped_filters.each do |filter|
154
+ if invoke_filter?(rpc_method, filter)
155
+ rv = call_or_send(filter[:callable])
156
+ return false if stop_on_false_return && rv === false
157
+ end
158
+ end
159
+
160
+ return true
161
+ end
162
+
163
+ # Reverse build a chain of around filters. To implement an around chain,
164
+ # simply build a method that yields control when it expects the underlying
165
+ # method to be invoked. If the endpoint should not be run (due to some
166
+ # condition), simply do not yield.
167
+ #
168
+ # Around filters are invoked in the order they are defined, outer to inner,
169
+ # with the inner-most method called being the actual rpc endpoint.
170
+ #
171
+ # Let's say you have a class defined with the following filters:
172
+ #
173
+ # class MyService
174
+ # around_filter :filter1, :filter2, :filter3
175
+ #
176
+ # def my_endpoint
177
+ # # do stuff
178
+ # end
179
+ # end
180
+ #
181
+ # When the my_endpoint method is invoked using Service#callable_rpc_method,
182
+ # It is similar to this call chain:
183
+ #
184
+ # filter1 do
185
+ # filter2 do
186
+ # filter3 do
187
+ # my_endpoint
188
+ # end
189
+ # end
190
+ # end
191
+ #
192
+ def run_around_filters(rpc_method)
193
+ final = lambda { __send__(rpc_method) }
194
+ filters[:around].reverse.inject(final) { |previous, filter|
195
+ if invoke_filter?(rpc_method, filter)
196
+ lambda { call_or_send(filter[:callable], &previous) }
197
+ else
198
+ previous
199
+ end
200
+ }.call
201
+ end
202
+
203
+
204
+ # Entry method to call each filter type in the appropriate order. This should
205
+ # be used instead of the other run methods directly.
206
+ #
207
+ def run_filters(rpc_method)
208
+ continue = run_unwrapped_filters(filters[:before], rpc_method, true)
209
+ if continue
210
+ run_around_filters(rpc_method)
211
+ run_unwrapped_filters(filters[:after], rpc_method)
212
+ end
213
+ end
214
+
215
+ # Call the object if it is callable, otherwise invoke the method using
216
+ # __send__ assuming that we respond_to it. Return the call's return value.
217
+ #
218
+ def call_or_send(callable, &block)
219
+ rv = case
220
+ when callable.respond_to?(:call) then
221
+ if callable.arity == 1
222
+ callable.call(self, &block)
223
+ else
224
+ callable.call(&block)
225
+ end
226
+ when respond_to?(callable, true) then
227
+ if method(callable).arity == 1
228
+ __send__(callable, self, &block)
229
+ else
230
+ __send__(callable, &block)
231
+ end
232
+ else
233
+ raise "Object #{callable} is not callable"
234
+ end
235
+
236
+ return rv
237
+ end
238
+
239
+ end
240
+
241
+ end
242
+ end
243
+ end
@@ -5,7 +5,7 @@ module Protobuf
5
5
  module Rpc
6
6
  class Stat
7
7
  attr_accessor :mode, :start_time, :end_time, :request_size, :dispatcher
8
- attr_accessor :response_size, :client, :server, :service, :method
8
+ attr_accessor :response_size, :client, :server, :service, :method_name
9
9
 
10
10
  MODES = [:SERVER, :CLIENT].freeze
11
11
 
@@ -22,8 +22,8 @@ module Protobuf
22
22
  @client ? "#{@client[:ip]}:#{@client[:port]}" : nil
23
23
  end
24
24
 
25
- def method
26
- @method ||= @dispatcher.try(:callable_method).try(:name)
25
+ def method_name
26
+ @method_name ||= @dispatcher.try(:service).try(:rpc)
27
27
  end
28
28
 
29
29
  def server=(peer)
@@ -52,7 +52,7 @@ module Protobuf
52
52
  end
53
53
 
54
54
  def rpc
55
- service && method ? "#{service}##{method}" : nil
55
+ service && method_name ? "#{service}##{method_name}" : nil
56
56
  end
57
57
 
58
58
  def elapsed_time
@@ -1,4 +1,4 @@
1
1
  module Protobuf
2
- VERSION = '2.0.3'
2
+ VERSION = '2.1.0'
3
3
  PROTOC_VERSION = '2.4.1'
4
4
  end
@@ -23,7 +23,7 @@ describe ::Protobuf::CLI do
23
23
 
24
24
  it 'sends the host option to the runner' do
25
25
  ::Protobuf::Rpc::SocketRunner.should_receive(:run) do |options|
26
- options.host.should eq '123.123.123.123'
26
+ options[:host].should eq '123.123.123.123'
27
27
  end
28
28
  described_class.start(args)
29
29
  end
@@ -34,7 +34,18 @@ describe ::Protobuf::CLI do
34
34
 
35
35
  it 'sends the port option to the runner' do
36
36
  ::Protobuf::Rpc::SocketRunner.should_receive(:run) do |options|
37
- options.port.should eq 12345
37
+ options[:port].should eq 12345
38
+ end
39
+ described_class.start(args)
40
+ end
41
+ end
42
+
43
+ context 'threads option' do
44
+ let(:test_args) { [ '--threads=500' ] }
45
+
46
+ it 'sends the threads option to the runner' do
47
+ ::Protobuf::Rpc::SocketRunner.should_receive(:run) do |options|
48
+ options[:threads].should eq 500
38
49
  end
39
50
  described_class.start(args)
40
51
  end
@@ -45,7 +56,7 @@ describe ::Protobuf::CLI do
45
56
 
46
57
  it 'sends the backlog option to the runner' do
47
58
  ::Protobuf::Rpc::SocketRunner.should_receive(:run) do |options|
48
- options.backlog.should eq 500
59
+ options[:backlog].should eq 500
49
60
  end
50
61
  described_class.start(args)
51
62
  end
@@ -56,7 +67,7 @@ describe ::Protobuf::CLI do
56
67
 
57
68
  it 'sends the backlog option to the runner' do
58
69
  ::Protobuf::Rpc::SocketRunner.should_receive(:run) do |options|
59
- options.threshold.should eq 500
70
+ options[:threshold].should eq 500
60
71
  end
61
72
  described_class.start(args)
62
73
  end
@@ -96,6 +96,7 @@ describe Protobuf::Message do
96
96
 
97
97
  context 'when tag does not reference a field' do
98
98
  it 'returns nil' do
99
+ pending 'need to implement a range-limiting array sub-class for field access'
99
100
  subject.get_field_by_tag(-1).should be_nil
100
101
  end
101
102
  end
@@ -18,7 +18,7 @@ describe Protobuf::Rpc::ServiceDispatcher do
18
18
 
19
19
  context 'creating a new dispatcher' do
20
20
  its(:service) { should be_instance_of service_name.constantize }
21
- its(:callable_method) { should be_a(Method) }
21
+ its(:callable_method) { should respond_to(:call)}
22
22
  its(:outer_request) { should eq wrapper }
23
23
  its(:error) { should be_nil }
24
24
 
@@ -0,0 +1,356 @@
1
+ require 'spec_helper'
2
+
3
+ class FilterTest
4
+ include Protobuf::Rpc::ServiceFilters
5
+
6
+ attr_accessor :called
7
+
8
+ # Initialize the hash keys as instance vars
9
+ def initialize(ivar_hash)
10
+ @called = []
11
+ ivar_hash.each_pair do |key, value|
12
+ self.class.class_eval do
13
+ attr_accessor key
14
+ end
15
+ __send__("#{key}=", value)
16
+ end
17
+ end
18
+
19
+ def endpoint
20
+ @called << :endpoint
21
+ end
22
+
23
+ def self.clear_filters!
24
+ @defined_filters = nil
25
+ @filters = nil
26
+ end
27
+ end
28
+
29
+ describe Protobuf::Rpc::ServiceFilters do
30
+ subject { FilterTest.new(params) }
31
+ after(:each) { FilterTest.clear_filters! }
32
+
33
+ describe '#before_filter' do
34
+ let(:params) { { :before_filter_calls => 0 } }
35
+
36
+ before(:all) do
37
+ class FilterTest
38
+ private
39
+ def verify_before
40
+ @called << :verify_before
41
+ @before_filter_calls += 1
42
+ end
43
+
44
+ def foo
45
+ @called << :foo
46
+ end
47
+ end
48
+ end
49
+
50
+ before do
51
+ FilterTest.before_filter(:verify_before)
52
+ FilterTest.before_filter(:verify_before)
53
+ FilterTest.before_filter(:foo)
54
+ end
55
+
56
+ it 'calls filters in the order they were defined' do
57
+ subject.__send__(:run_filters, :endpoint)
58
+ subject.called.should eq [ :verify_before, :foo, :endpoint ]
59
+ subject.before_filter_calls.should eq 1
60
+ end
61
+
62
+ context 'when filter is configured with "only"' do
63
+ before(:all) do
64
+ class FilterTest
65
+ private
66
+ def endpoint_with_verify
67
+ @called << :endpoint_with_verify
68
+ end
69
+ end
70
+ end
71
+
72
+ before do
73
+ FilterTest.clear_filters!
74
+ FilterTest.before_filter(:verify_before, :only => :endpoint_with_verify)
75
+ end
76
+
77
+ context 'when invoking a method defined in "only" option' do
78
+ it 'invokes the filter' do
79
+ subject.__send__(:run_filters, :endpoint_with_verify)
80
+ subject.called.should eq [ :verify_before, :endpoint_with_verify ]
81
+ end
82
+ end
83
+
84
+ context 'when invoking a method not defined by "only" option' do
85
+ it 'does not invoke the filter' do
86
+ subject.__send__(:run_filters, :endpoint)
87
+ subject.called.should eq [ :endpoint ]
88
+ end
89
+ end
90
+ end
91
+
92
+ context 'when filter is configured with "except"' do
93
+ before(:all) do
94
+ class FilterTest
95
+ private
96
+ def endpoint_without_verify
97
+ @called << :endpoint_without_verify
98
+ end
99
+ end
100
+ end
101
+
102
+ before do
103
+ FilterTest.clear_filters!
104
+ FilterTest.before_filter(:verify_before, :except => :endpoint_without_verify)
105
+ end
106
+
107
+ context 'when invoking a method not defined in "except" option' do
108
+ it 'invokes the filter' do
109
+ subject.__send__(:run_filters, :endpoint)
110
+ subject.called.should eq [ :verify_before, :endpoint ]
111
+ end
112
+ end
113
+
114
+ context 'when invoking a method defined by "except" option' do
115
+ it 'does not invoke the filter' do
116
+ subject.__send__(:run_filters, :endpoint_without_verify)
117
+ subject.called.should eq [ :endpoint_without_verify ]
118
+ end
119
+ end
120
+ end
121
+
122
+ context 'when filter is configured with "if"' do
123
+ before(:all) do
124
+ class FilterTest
125
+ private
126
+ def check_true; return true; end
127
+ def check_false; return false; end
128
+ def verify_before; @called << :verify_before; end
129
+ end
130
+ end
131
+
132
+ context 'when "if" option is a method that returns true' do
133
+ before do
134
+ FilterTest.clear_filters!
135
+ FilterTest.before_filter(:verify_before, :if => :check_true)
136
+ end
137
+
138
+ it 'invokes the filter' do
139
+ subject.__send__(:run_filters, :endpoint)
140
+ subject.called.should eq [ :verify_before, :endpoint ]
141
+ end
142
+ end
143
+
144
+ context 'when "if" option is a callable that returns true' do
145
+ before do
146
+ FilterTest.clear_filters!
147
+ FilterTest.before_filter(:verify_before, :if => lambda { |service| true })
148
+ end
149
+
150
+ it 'invokes the filter' do
151
+ subject.__send__(:run_filters, :endpoint)
152
+ subject.called.should eq [ :verify_before, :endpoint ]
153
+ end
154
+ end
155
+
156
+ context 'when "if" option is a method that returns false' do
157
+ before do
158
+ FilterTest.clear_filters!
159
+ FilterTest.before_filter(:verify_before, :if => :check_false)
160
+ end
161
+
162
+ it 'skips the filter' do
163
+ subject.__send__(:run_filters, :endpoint)
164
+ subject.called.should eq [ :endpoint ]
165
+ end
166
+ end
167
+
168
+ context 'when "if" option is a callable that returns false' do
169
+ before do
170
+ FilterTest.clear_filters!
171
+ FilterTest.before_filter(:verify_before, :if => lambda { |service| false })
172
+ end
173
+
174
+ it 'skips the filter' do
175
+ subject.__send__(:run_filters, :endpoint)
176
+ subject.called.should eq [ :endpoint ]
177
+ end
178
+ end
179
+ end
180
+
181
+ context 'when filter is configured with "unless"' do
182
+ before(:all) do
183
+ class FilterTest
184
+ private
185
+ def check_true; return true; end
186
+ def check_false; return false; end
187
+ def verify_before; @called << :verify_before; end
188
+ end
189
+ end
190
+
191
+ context 'when "unless" option is a method that returns false' do
192
+ before do
193
+ FilterTest.clear_filters!
194
+ FilterTest.before_filter(:verify_before, :unless => :check_false)
195
+ end
196
+
197
+ it 'invokes the filter' do
198
+ subject.__send__(:run_filters, :endpoint)
199
+ subject.called.should eq [ :verify_before, :endpoint ]
200
+ end
201
+ end
202
+
203
+ context 'when "unless" option is a callable that returns true' do
204
+ before do
205
+ FilterTest.clear_filters!
206
+ FilterTest.before_filter(:verify_before, :unless => lambda { |service| false })
207
+ end
208
+
209
+ it 'invokes the filter' do
210
+ subject.__send__(:run_filters, :endpoint)
211
+ subject.called.should eq [ :verify_before, :endpoint ]
212
+ end
213
+ end
214
+
215
+ context 'when "unless" option is a method that returns false' do
216
+ before do
217
+ FilterTest.clear_filters!
218
+ FilterTest.before_filter(:verify_before, :unless => :check_true)
219
+ end
220
+
221
+ it 'skips the filter' do
222
+ subject.__send__(:run_filters, :endpoint)
223
+ subject.called.should eq [ :endpoint ]
224
+ end
225
+ end
226
+
227
+ context 'when "unless" option is a callable that returns false' do
228
+ before do
229
+ FilterTest.clear_filters!
230
+ FilterTest.before_filter(:verify_before, :unless => lambda { |service| true })
231
+ end
232
+
233
+ it 'skips the filter' do
234
+ subject.__send__(:run_filters, :endpoint)
235
+ subject.called.should eq [ :endpoint ]
236
+ end
237
+ end
238
+ end
239
+
240
+ context 'when filter returns false' do
241
+ before(:all) do
242
+ class FilterTest
243
+ private
244
+ def short_circuit_filter
245
+ @called << :short_circuit_filter
246
+ return false
247
+ end
248
+ end
249
+ end
250
+
251
+ before do
252
+ FilterTest.clear_filters!
253
+ FilterTest.before_filter(:short_circuit_filter)
254
+ end
255
+
256
+ it 'does not invoke the rpc method' do
257
+ subject.should_not_receive(:endpoint)
258
+ subject.__send__(:run_filters, :endpoint)
259
+ subject.called.should eq [ :short_circuit_filter ]
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#after_filter' do
265
+ let(:params) { { :after_filter_calls => 0 } }
266
+
267
+ before(:all) do
268
+ class FilterTest
269
+ private
270
+ def verify_after
271
+ @called << :verify_after
272
+ @after_filter_calls += 1
273
+ end
274
+
275
+ def foo
276
+ @called << :foo
277
+ end
278
+ end
279
+ end
280
+
281
+ before do
282
+ FilterTest.after_filter(:verify_after)
283
+ FilterTest.after_filter(:verify_after)
284
+ FilterTest.after_filter(:foo)
285
+ end
286
+
287
+ it 'calls filters in the order they were defined' do
288
+ subject.__send__(:run_filters, :endpoint)
289
+ subject.called.should eq [ :endpoint, :verify_after, :foo ]
290
+ subject.after_filter_calls.should eq 1
291
+ end
292
+ end
293
+
294
+ describe '#around_filter' do
295
+ let(:params) { {} }
296
+
297
+ before(:all) do
298
+ class FilterTest
299
+ private
300
+ def outer_around
301
+ @called << :outer_around_top
302
+ yield
303
+ @called << :outer_around_bottom
304
+ end
305
+
306
+ def inner_around
307
+ @called << :inner_around_top
308
+ yield
309
+ @called << :inner_around_bottom
310
+ end
311
+ end
312
+ end
313
+
314
+ before do
315
+ FilterTest.around_filter(:outer_around)
316
+ FilterTest.around_filter(:inner_around)
317
+ FilterTest.around_filter(:outer_around)
318
+ FilterTest.around_filter(:inner_around)
319
+ end
320
+
321
+ it 'calls filters in the order they were defined' do
322
+ subject.__send__(:run_filters, :endpoint)
323
+ subject.called.should eq([ :outer_around_top,
324
+ :inner_around_top,
325
+ :endpoint,
326
+ :inner_around_bottom,
327
+ :outer_around_bottom ])
328
+ end
329
+
330
+ context 'when around_filter does not yield' do
331
+ before do
332
+ class FilterTest
333
+ private
334
+ def inner_around
335
+ @called << :inner_around
336
+ end
337
+ end
338
+ end
339
+
340
+ before do
341
+ FilterTest.around_filter(:outer_around)
342
+ FilterTest.around_filter(:inner_around)
343
+ end
344
+
345
+ it 'cancels calling the rest of the filters and the endpoint' do
346
+ subject.should_not_receive(:endpoint)
347
+ subject.__send__(:run_filters, :endpoint)
348
+ subject.called.should eq([ :outer_around_top,
349
+ :inner_around,
350
+ :outer_around_bottom ])
351
+ end
352
+
353
+ end
354
+ end
355
+
356
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protobuf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-30 00:00:00.000000000 Z
13
+ date: 2012-11-05 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: !ruby/object:Gem::Requirement
17
+ requirement: &2152184060 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,15 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
- requirements:
28
- - - ! '>='
29
- - !ruby/object:Gem::Version
30
- version: '0'
25
+ version_requirements: *2152184060
31
26
  - !ruby/object:Gem::Dependency
32
27
  name: ffi
33
- requirement: !ruby/object:Gem::Requirement
28
+ requirement: &2152179420 !ruby/object:Gem::Requirement
34
29
  none: false
35
30
  requirements:
36
31
  - - ! '>='
@@ -38,15 +33,10 @@ dependencies:
38
33
  version: '0'
39
34
  type: :runtime
40
35
  prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ! '>='
45
- - !ruby/object:Gem::Version
46
- version: '0'
36
+ version_requirements: *2152179420
47
37
  - !ruby/object:Gem::Dependency
48
38
  name: multi_json
49
- requirement: !ruby/object:Gem::Requirement
39
+ requirement: &2152175780 !ruby/object:Gem::Requirement
50
40
  none: false
51
41
  requirements:
52
42
  - - ! '>='
@@ -54,15 +44,10 @@ dependencies:
54
44
  version: '0'
55
45
  type: :runtime
56
46
  prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
62
- version: '0'
47
+ version_requirements: *2152175780
63
48
  - !ruby/object:Gem::Dependency
64
49
  name: thor
65
- requirement: !ruby/object:Gem::Requirement
50
+ requirement: &2152173860 !ruby/object:Gem::Requirement
66
51
  none: false
67
52
  requirements:
68
53
  - - ! '>='
@@ -70,15 +55,10 @@ dependencies:
70
55
  version: '0'
71
56
  type: :runtime
72
57
  prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ! '>='
77
- - !ruby/object:Gem::Version
78
- version: '0'
58
+ version_requirements: *2152173860
79
59
  - !ruby/object:Gem::Dependency
80
60
  name: eventmachine
81
- requirement: !ruby/object:Gem::Requirement
61
+ requirement: &2152172500 !ruby/object:Gem::Requirement
82
62
  none: false
83
63
  requirements:
84
64
  - - ! '>='
@@ -86,15 +66,10 @@ dependencies:
86
66
  version: '0'
87
67
  type: :development
88
68
  prerelease: false
89
- version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
- requirements:
92
- - - ! '>='
93
- - !ruby/object:Gem::Version
94
- version: '0'
69
+ version_requirements: *2152172500
95
70
  - !ruby/object:Gem::Dependency
96
71
  name: ffi-rzmq
97
- requirement: !ruby/object:Gem::Requirement
72
+ requirement: &2152170080 !ruby/object:Gem::Requirement
98
73
  none: false
99
74
  requirements:
100
75
  - - ! '>='
@@ -102,15 +77,10 @@ dependencies:
102
77
  version: '0'
103
78
  type: :development
104
79
  prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
- requirements:
108
- - - ! '>='
109
- - !ruby/object:Gem::Version
110
- version: '0'
80
+ version_requirements: *2152170080
111
81
  - !ruby/object:Gem::Dependency
112
82
  name: perftools.rb
113
- requirement: !ruby/object:Gem::Requirement
83
+ requirement: &2152151800 !ruby/object:Gem::Requirement
114
84
  none: false
115
85
  requirements:
116
86
  - - ! '>='
@@ -118,15 +88,10 @@ dependencies:
118
88
  version: '0'
119
89
  type: :development
120
90
  prerelease: false
121
- version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
- requirements:
124
- - - ! '>='
125
- - !ruby/object:Gem::Version
126
- version: '0'
91
+ version_requirements: *2152151800
127
92
  - !ruby/object:Gem::Dependency
128
93
  name: pry
129
- requirement: !ruby/object:Gem::Requirement
94
+ requirement: &2152144100 !ruby/object:Gem::Requirement
130
95
  none: false
131
96
  requirements:
132
97
  - - ! '>='
@@ -134,15 +99,10 @@ dependencies:
134
99
  version: '0'
135
100
  type: :development
136
101
  prerelease: false
137
- version_requirements: !ruby/object:Gem::Requirement
138
- none: false
139
- requirements:
140
- - - ! '>='
141
- - !ruby/object:Gem::Version
142
- version: '0'
102
+ version_requirements: *2152144100
143
103
  - !ruby/object:Gem::Dependency
144
104
  name: pry-nav
145
- requirement: !ruby/object:Gem::Requirement
105
+ requirement: &2152143260 !ruby/object:Gem::Requirement
146
106
  none: false
147
107
  requirements:
148
108
  - - ! '>='
@@ -150,15 +110,10 @@ dependencies:
150
110
  version: '0'
151
111
  type: :development
152
112
  prerelease: false
153
- version_requirements: !ruby/object:Gem::Requirement
154
- none: false
155
- requirements:
156
- - - ! '>='
157
- - !ruby/object:Gem::Version
158
- version: '0'
113
+ version_requirements: *2152143260
159
114
  - !ruby/object:Gem::Dependency
160
115
  name: rake
161
- requirement: !ruby/object:Gem::Requirement
116
+ requirement: &2152142540 !ruby/object:Gem::Requirement
162
117
  none: false
163
118
  requirements:
164
119
  - - ! '>='
@@ -166,15 +121,10 @@ dependencies:
166
121
  version: '0'
167
122
  type: :development
168
123
  prerelease: false
169
- version_requirements: !ruby/object:Gem::Requirement
170
- none: false
171
- requirements:
172
- - - ! '>='
173
- - !ruby/object:Gem::Version
174
- version: '0'
124
+ version_requirements: *2152142540
175
125
  - !ruby/object:Gem::Dependency
176
126
  name: rake-compiler
177
- requirement: !ruby/object:Gem::Requirement
127
+ requirement: &2152141860 !ruby/object:Gem::Requirement
178
128
  none: false
179
129
  requirements:
180
130
  - - ! '>='
@@ -182,15 +132,10 @@ dependencies:
182
132
  version: '0'
183
133
  type: :development
184
134
  prerelease: false
185
- version_requirements: !ruby/object:Gem::Requirement
186
- none: false
187
- requirements:
188
- - - ! '>='
189
- - !ruby/object:Gem::Version
190
- version: '0'
135
+ version_requirements: *2152141860
191
136
  - !ruby/object:Gem::Dependency
192
137
  name: rspec
193
- requirement: !ruby/object:Gem::Requirement
138
+ requirement: &2152140260 !ruby/object:Gem::Requirement
194
139
  none: false
195
140
  requirements:
196
141
  - - ! '>='
@@ -198,15 +143,10 @@ dependencies:
198
143
  version: '0'
199
144
  type: :development
200
145
  prerelease: false
201
- version_requirements: !ruby/object:Gem::Requirement
202
- none: false
203
- requirements:
204
- - - ! '>='
205
- - !ruby/object:Gem::Version
206
- version: '0'
146
+ version_requirements: *2152140260
207
147
  - !ruby/object:Gem::Dependency
208
148
  name: simplecov
209
- requirement: !ruby/object:Gem::Requirement
149
+ requirement: &2152139460 !ruby/object:Gem::Requirement
210
150
  none: false
211
151
  requirements:
212
152
  - - ! '>='
@@ -214,15 +154,10 @@ dependencies:
214
154
  version: '0'
215
155
  type: :development
216
156
  prerelease: false
217
- version_requirements: !ruby/object:Gem::Requirement
218
- none: false
219
- requirements:
220
- - - ! '>='
221
- - !ruby/object:Gem::Version
222
- version: '0'
157
+ version_requirements: *2152139460
223
158
  - !ruby/object:Gem::Dependency
224
159
  name: yard
225
- requirement: !ruby/object:Gem::Requirement
160
+ requirement: &2152138580 !ruby/object:Gem::Requirement
226
161
  none: false
227
162
  requirements:
228
163
  - - ! '>='
@@ -230,12 +165,7 @@ dependencies:
230
165
  version: '0'
231
166
  type: :development
232
167
  prerelease: false
233
- version_requirements: !ruby/object:Gem::Requirement
234
- none: false
235
- requirements:
236
- - - ! '>='
237
- - !ruby/object:Gem::Version
238
- version: '0'
168
+ version_requirements: *2152138580
239
169
  description: Google Protocol Buffers v2.4.1 Serialization and RPC implementation for
240
170
  Ruby.
241
171
  email:
@@ -413,6 +343,7 @@ files:
413
343
  - lib/protobuf/rpc/servers/zmq_runner.rb
414
344
  - lib/protobuf/rpc/service.rb
415
345
  - lib/protobuf/rpc/service_dispatcher.rb
346
+ - lib/protobuf/rpc/service_filters.rb
416
347
  - lib/protobuf/rpc/stat.rb
417
348
  - lib/protobuf/socket.rb
418
349
  - lib/protobuf/version.rb
@@ -445,6 +376,7 @@ files:
445
376
  - spec/lib/protobuf/rpc/servers/zmq/util_spec.rb
446
377
  - spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb
447
378
  - spec/lib/protobuf/rpc/service_dispatcher_spec.rb
379
+ - spec/lib/protobuf/rpc/service_filters_spec.rb
448
380
  - spec/lib/protobuf/rpc/service_spec.rb
449
381
  - spec/lib/protobuf_spec.rb
450
382
  - spec/spec_helper.rb
@@ -514,7 +446,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
514
446
  version: '0'
515
447
  segments:
516
448
  - 0
517
- hash: -107413867811287521
449
+ hash: 2922389583391730892
518
450
  required_rubygems_version: !ruby/object:Gem::Requirement
519
451
  none: false
520
452
  requirements:
@@ -523,12 +455,92 @@ required_rubygems_version: !ruby/object:Gem::Requirement
523
455
  version: '0'
524
456
  segments:
525
457
  - 0
526
- hash: -107413867811287521
458
+ hash: 2922389583391730892
527
459
  requirements: []
528
460
  rubyforge_project:
529
- rubygems_version: 1.8.24
461
+ rubygems_version: 1.8.15
530
462
  signing_key:
531
463
  specification_version: 3
532
464
  summary: Google Protocol Buffers v2.4.1 Serialization and RPC implementation for Ruby.
533
- test_files: []
465
+ test_files:
466
+ - spec/benchmark/tasks.rb
467
+ - spec/functional/embedded_service_spec.rb
468
+ - spec/functional/evented_server_spec.rb
469
+ - spec/functional/socket_server_spec.rb
470
+ - spec/functional/zmq_server_spec.rb
471
+ - spec/lib/protobuf/cli_spec.rb
472
+ - spec/lib/protobuf/enum_spec.rb
473
+ - spec/lib/protobuf/enum_value_spec.rb
474
+ - spec/lib/protobuf/logger_spec.rb
475
+ - spec/lib/protobuf/message/encoder_spec.rb
476
+ - spec/lib/protobuf/message_spec.rb
477
+ - spec/lib/protobuf/rpc/client_spec.rb
478
+ - spec/lib/protobuf/rpc/connector_spec.rb
479
+ - spec/lib/protobuf/rpc/connectors/base_spec.rb
480
+ - spec/lib/protobuf/rpc/connectors/common_spec.rb
481
+ - spec/lib/protobuf/rpc/connectors/socket_spec.rb
482
+ - spec/lib/protobuf/rpc/connectors/zmq_spec.rb
483
+ - spec/lib/protobuf/rpc/servers/evented_server_spec.rb
484
+ - spec/lib/protobuf/rpc/servers/socket_server_spec.rb
485
+ - spec/lib/protobuf/rpc/servers/zmq/broker_spec.rb
486
+ - spec/lib/protobuf/rpc/servers/zmq/server_spec.rb
487
+ - spec/lib/protobuf/rpc/servers/zmq/util_spec.rb
488
+ - spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb
489
+ - spec/lib/protobuf/rpc/service_dispatcher_spec.rb
490
+ - spec/lib/protobuf/rpc/service_filters_spec.rb
491
+ - spec/lib/protobuf/rpc/service_spec.rb
492
+ - spec/lib/protobuf_spec.rb
493
+ - spec/spec_helper.rb
494
+ - spec/support/all.rb
495
+ - spec/support/server.rb
496
+ - spec/support/test/enum.pb.rb
497
+ - spec/support/test/enum.proto
498
+ - spec/support/test/resource.pb.rb
499
+ - spec/support/test/resource.proto
500
+ - spec/support/test/resource_service.rb
501
+ - spec/support/test_app_file.rb
502
+ - spec/support/tolerance_matcher.rb
503
+ - test/data/data.bin
504
+ - test/data/data_source.py
505
+ - test/data/types.bin
506
+ - test/data/types_source.py
507
+ - test/data/unk.png
508
+ - test/proto/addressbook.pb.rb
509
+ - test/proto/addressbook.proto
510
+ - test/proto/addressbook_base.pb.rb
511
+ - test/proto/addressbook_base.proto
512
+ - test/proto/addressbook_ext.pb.rb
513
+ - test/proto/addressbook_ext.proto
514
+ - test/proto/collision.pb.rb
515
+ - test/proto/collision.proto
516
+ - test/proto/ext_collision.pb.rb
517
+ - test/proto/ext_collision.proto
518
+ - test/proto/ext_range.pb.rb
519
+ - test/proto/ext_range.proto
520
+ - test/proto/float_default.proto
521
+ - test/proto/lowercase.pb.rb
522
+ - test/proto/lowercase.proto
523
+ - test/proto/merge.pb.rb
524
+ - test/proto/merge.proto
525
+ - test/proto/nested.pb.rb
526
+ - test/proto/nested.proto
527
+ - test/proto/optional_field.pb.rb
528
+ - test/proto/optional_field.proto
529
+ - test/proto/packed.pb.rb
530
+ - test/proto/packed.proto
531
+ - test/proto/rpc.proto
532
+ - test/proto/types.pb.rb
533
+ - test/proto/types.proto
534
+ - test/test_addressbook.rb
535
+ - test/test_enum_value.rb
536
+ - test/test_extension.rb
537
+ - test/test_lowercase.rb
538
+ - test/test_message.rb
539
+ - test/test_optional_field.rb
540
+ - test/test_packed_field.rb
541
+ - test/test_parse.rb
542
+ - test/test_repeated_types.rb
543
+ - test/test_serialize.rb
544
+ - test/test_standard_message.rb
545
+ - test/test_types.rb
534
546
  has_rdoc: