rjr 0.18.2 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +16 -9
  4. data/bin/rjr-server +2 -1
  5. data/examples/client.rb +21 -19
  6. data/examples/server.rb +1 -1
  7. data/examples/structured_server.rb +1 -0
  8. data/examples/tcp.rb +1 -0
  9. data/lib/rjr/common.rb +1 -226
  10. data/lib/rjr/core_ext.rb +63 -0
  11. data/lib/rjr/dispatcher.rb +75 -219
  12. data/lib/rjr/messages.rb +8 -0
  13. data/lib/rjr/messages/compressed.rb +264 -0
  14. data/lib/rjr/messages/notification.rb +95 -0
  15. data/lib/rjr/messages/request.rb +99 -0
  16. data/lib/rjr/messages/response.rb +128 -0
  17. data/lib/rjr/node.rb +100 -97
  18. data/lib/rjr/node_callback.rb +43 -0
  19. data/lib/rjr/nodes/amqp.rb +12 -11
  20. data/lib/rjr/nodes/easy.rb +4 -4
  21. data/lib/rjr/nodes/local.rb +13 -12
  22. data/lib/rjr/nodes/multi.rb +1 -1
  23. data/lib/rjr/nodes/tcp.rb +15 -13
  24. data/lib/rjr/nodes/template.rb +4 -4
  25. data/lib/rjr/nodes/unix.rb +15 -13
  26. data/lib/rjr/nodes/web.rb +15 -14
  27. data/lib/rjr/nodes/ws.rb +12 -11
  28. data/lib/rjr/request.rb +128 -0
  29. data/lib/rjr/result.rb +75 -0
  30. data/lib/rjr/util/args.rb +145 -0
  31. data/lib/rjr/{em_adapter.rb → util/em_adapter.rb} +0 -0
  32. data/lib/rjr/util/handles_methods.rb +115 -0
  33. data/lib/rjr/util/has_messages.rb +50 -0
  34. data/lib/rjr/{inspect.rb → util/inspect.rb} +1 -1
  35. data/lib/rjr/util/json_parser.rb +101 -0
  36. data/lib/rjr/util/logger.rb +128 -0
  37. data/lib/rjr/{thread_pool.rb → util/thread_pool.rb} +2 -0
  38. data/lib/rjr/version.rb +1 -1
  39. data/site/jrw.js +1 -1
  40. data/specs/args_spec.rb +144 -0
  41. data/specs/dispatcher_spec.rb +399 -211
  42. data/specs/em_adapter_spec.rb +31 -18
  43. data/specs/handles_methods_spec.rb +154 -0
  44. data/specs/has_messages_spec.rb +54 -0
  45. data/specs/inspect_spec.rb +1 -1
  46. data/specs/json_parser_spec.rb +169 -0
  47. data/specs/messages/notification_spec.rb +59 -0
  48. data/specs/messages/request_spec.rb +66 -0
  49. data/specs/messages/response_spec.rb +94 -0
  50. data/specs/node_callbacks_spec.rb +47 -0
  51. data/specs/node_spec.rb +465 -56
  52. data/specs/request_spec.rb +147 -0
  53. data/specs/result_spec.rb +144 -0
  54. data/specs/thread_pool_spec.rb +1 -1
  55. metadata +41 -11
  56. data/lib/rjr/errors.rb +0 -23
  57. data/lib/rjr/message.rb +0 -351
  58. data/lib/rjr/semaphore.rb +0 -58
  59. data/specs/message_spec.rb +0 -229
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d55a14977509a3c88cdd498f075666cb3e3bac3
4
- data.tar.gz: d41f1a19c4750ec55392b1530499ddfea7832dbc
3
+ metadata.gz: 517f79e571aae798dcc24be8feacd2ef5cbc3606
4
+ data.tar.gz: 340a496e1f47092a7b01a4b1363bac1db87b9e81
5
5
  SHA512:
6
- metadata.gz: 26d189aa6818b7b2046756be51fb112cabcd183e1a9d67ba7686ff75b99f459d70333c1504f049d995b2d5c518b86042a7b8542a16c16efc1c2e81304927cc86
7
- data.tar.gz: 87c4581787d211a26b8e8dc99eb53413183032eb151c02ceb7f1bafb07648b299a7c63b9ef9fd62b55f40d21fc31b3eb54f2366bb69b84556c26e279e7fc3120
6
+ metadata.gz: 6b5e056c47862523e346cf2a47923146e0b15a3fb856071f20a4fa226d55fdb6046e5ec463c8aea4e356d96d49e707279d5b477549df2537041943aaa04513f7
7
+ data.tar.gz: 325453d870c227bd35826a3bab5933fc6a62e644f1efbb2d5de3f762e917b24e38908aa15cfad2d7d3aae2d6d2f8af8da57a7bf527f9558570a78412747c5b60
data/Rakefile CHANGED
@@ -3,6 +3,8 @@
3
3
  # Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
4
4
  # Licensed under the Apache License, Version 2.0
5
5
 
6
+ # TODO fix travis build
7
+
6
8
  require "rspec/core/rake_task"
7
9
 
8
10
  desc "Run all specs"
@@ -5,8 +5,8 @@
5
5
  # Licensed under the Apache License, Version 2.0
6
6
 
7
7
  require 'optparse'
8
- require 'rjr/common'
9
- require 'rjr/message'
8
+ require 'rjr/core_ext'
9
+ require 'rjr/util/logger'
10
10
  require 'rjr/nodes/easy'
11
11
 
12
12
  RJR::Logger.log_level = ::Logger::DEBUG
@@ -27,7 +27,8 @@ config = { :mode => :msg,
27
27
  :msg_id => :rand,
28
28
  :block => false,
29
29
  :interval => 0,
30
- :disconnect => false}
30
+ :disconnect => false,
31
+ :class => 'Object'}
31
32
 
32
33
  optparse = OptionParser.new do |opts|
33
34
  opts.on('-h', '--help', 'Display this help screen') do
@@ -59,6 +60,10 @@ optparse = OptionParser.new do |opts|
59
60
  config[:block] = !ret
60
61
  end
61
62
 
63
+ opts.on('-c', '--class [value]', 'Ruby class to extract messages from (default Class)') do |cls|
64
+ config[:class] = cls
65
+ end
66
+
62
67
  opts.on('-n', '--num number_of_messages', 'Number of messages to send to server (may be a number, :rand, or :indefinite)') do |n|
63
68
  config[:num_msg] = case n.to_s.intern
64
69
  when :rand then rand(MAX_MESSAGES)
@@ -68,7 +73,7 @@ optparse = OptionParser.new do |opts|
68
73
  end
69
74
 
70
75
  opts.on('--message ID', 'Message to send to server (rand to select random)') do |mid|
71
- config[:msg_id] = mid.intern
76
+ config[:msg_id] = (mid == 'rand' ? :rand : mid)
72
77
  end
73
78
 
74
79
  opts.on('--interval seconds', 'Number of seconds after which to wait between requests (or rand)') do |s|
@@ -94,10 +99,13 @@ NODES = {config[:transport] => {:node_id => config[:node_id],
94
99
  :keep_alive => true} } # conditionally set keep alive?
95
100
 
96
101
  cdir = File.dirname(__FILE__)
97
- client_path = File.join(ENV['RJR_LOAD_PATH'] || File.join(cdir, '..', 'examples', 'client'))
102
+ client_path = File.join(ENV['RJR_LOAD_PATH'] ||
103
+ File.join(cdir, '..', 'examples', 'client'))
98
104
 
99
105
  ##########################################################
100
106
 
107
+ msg_class = config[:class].to_class
108
+
101
109
  node = RJR::Nodes::Easy.new(NODES)
102
110
  client_path.split(':').each { |cp|
103
111
  node.dispatcher.add_modules(cp)
@@ -112,13 +120,14 @@ if config[:disconnect]
112
120
  }
113
121
  end
114
122
 
123
+
115
124
  # invoke request(s)
116
125
  0.upto(config[:num_msg]-1) { |i|
117
126
  # TODO implement mode == :rand
118
127
 
119
128
  # grab message (or rand message)
120
- msg = (config[:msg_id] == :rand ? RJR::MessageUtil.rand_msg(config[:transport]) :
121
- RJR::MessageUtil.message(config[:msg_id]))
129
+ msg = (config[:msg_id] == :rand ? msg_class.rand_message(config[:transport]) :
130
+ msg_class.message(config[:msg_id]))
122
131
 
123
132
  if msg.nil?
124
133
  puts "Invalid message id"
@@ -141,8 +150,6 @@ end
141
150
  res = node.invoke(config[:dst], msg[:method], *params)
142
151
 
143
152
  # verify and output result
144
- puts res
145
- puts msg[:result]
146
153
  ress = (msg[:result].nil? ? "" : (msg[:result].call(res) ? "passed" : "failed"))
147
154
  RJR::Logger.info "#{msg[:method]} result #{res} #{ress}"
148
155
 
@@ -62,7 +62,8 @@ NODES = {:amqp => {:node_id => config[:node_id], :broker => config[:broker]},
62
62
  :tcp => {:node_id => config[:node_id], :host => config[:host], :port => config[:tcp_port]}}
63
63
 
64
64
  cdir = File.dirname(__FILE__)
65
- server_path = File.join(ENV['RJR_LOAD_PATH'] || File.join(cdir, '..', 'examples', 'server'))
65
+ server_path = File.join(ENV['RJR_LOAD_PATH'] ||
66
+ File.join(cdir, '..', 'examples', 'server'))
66
67
 
67
68
  ##########################################################
68
69
 
@@ -3,30 +3,32 @@
3
3
  # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
4
4
  # Licensed under the Apache License, Version 2.0
5
5
 
6
- include RJR::MessageMixins
6
+ require 'rjr/util/has_messages'
7
7
 
8
- def dispatch_client(dispatcher)
8
+ include RJR::HasMessages
9
+
10
+ define_message "stress" do
11
+ { :method => 'stress',
12
+ :params => ["<CLIENT_ID>"],
13
+ :result => lambda { |r| r =~ /foobar.*/ } }
14
+ end
15
+
16
+ define_message "stress_callback" do
17
+ { :method => 'stress_callback',
18
+ :params => ["<CLIENT_ID>"],
19
+ :transports => [:tcp, :ws, :amqp],
20
+ :result => lambda { |r| r =~ /barfoo.*/ } }
21
+ end
22
+
23
+ define_message "messages" do
24
+ { :method => 'messages'}
25
+ end
26
+
27
+ def dispatch_examples_client(dispatcher)
9
28
  dispatcher.handle "client_callback" do |p|
10
29
  RJR::Logger.info "invoked client_callback method #{p}"
11
30
  #amqp_node.invoke_request('stress_test-queue', 'stress', "foozmoney#{client_id}")
12
31
  #amqp_node.stop
13
32
  nil
14
33
  end
15
-
16
- define_message "stress" do
17
- { :method => 'stress',
18
- :params => ["<CLIENT_ID>"],
19
- :result => lambda { |r| r =~ /foobar.*/ } }
20
- end
21
-
22
- define_message "stress_callback" do
23
- { :method => 'stress_callback',
24
- :params => ["<CLIENT_ID>"],
25
- :transports => [:tcp, :ws, :amqp],
26
- :result => lambda { |r| r =~ /barfoo.*/ } }
27
- end
28
-
29
- define_message "messages" do
30
- { :method => 'messages'}
31
- end
32
34
  end
@@ -3,7 +3,7 @@
3
3
  # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
4
4
  # Licensed under the Apache License, Version 2.0
5
5
 
6
- def dispatch_server(dispatcher)
6
+ def dispatch_examples_server(dispatcher)
7
7
  dispatcher.handle "messages" do |p|
8
8
  $messages.string.split("\n")
9
9
  end
@@ -0,0 +1 @@
1
+ # TODO server using HandlesMethods mixin
@@ -5,6 +5,7 @@
5
5
  # Licensed under the Apache License, Version 2.0
6
6
 
7
7
  require 'rjr/nodes/tcp'
8
+ #require 'rjr/messages/compressed'
8
9
 
9
10
  server = RJR::Nodes::TCP.new :host => 'localhost', :port => 9789, :node_id => "server"
10
11
  server.dispatcher.handle('method') { |i|
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Copyright (C) 2011-2013 Mohammed Morsi <mo@morsi.org>
6
6
  # Licensed under the Apache License, Version 2.0
7
- require 'logger'
7
+
8
8
  require 'json'
9
9
 
10
10
  # Return a random uuid
@@ -25,229 +25,4 @@ def self.persistent_nodes
25
25
  }.compact
26
26
  end
27
27
 
28
- # Logger helper class.
29
- #
30
- # Encapsulates the standard ruby logger in a thread safe manner. Dispatches
31
- # class methods to an internally tracked logger to provide global access.
32
- #
33
- # TODO handle logging errors (log size too big, logrotate, etc)
34
- #
35
- # @example
36
- # RJR::Logger.info 'my message'
37
- # RJR::Logger.warn 'my warning'
38
- class Logger
39
- private
40
- def self._instantiate_logger
41
- if @logger.nil?
42
- #STDOUT.sync = true
43
- output = @log_to || ENV['RJR_LOG'] || STDOUT
44
- @logger = ::Logger.new(output)
45
- @logger.level = @log_level || ::Logger::FATAL
46
- @logger_mutex = Mutex.new
47
- @filters = []
48
- @highlights = []
49
- end
50
- end
51
-
52
- public
53
-
54
- # Add method which to call on every log message to determine
55
- # if messages should be included/excluded
56
- def self.add_filter(filter)
57
- @logger_mutex.synchronize{
58
- @filters << filter
59
- }
60
- end
61
-
62
- # Add a method which to call on every log message to determine
63
- # if message should be highlighted
64
- def self.highlight(hlight)
65
- @logger_mutex.synchronize{
66
- @highlights << hlight
67
- }
68
- end
69
-
70
- def self.method_missing(method_id, *args)
71
- _instantiate_logger
72
- @logger_mutex.synchronize {
73
- args = args.first if args.first.is_a?(Array)
74
- args.each { |a|
75
- # run highlights / filters against output before
76
- # sending formatted output to logger
77
- # TODO allow user to customize highlight mechanism/text
78
- na = @highlights.any? { |h| h.call a } ?
79
- "\e[1m\e[31m#{a}\e[0m\e[0m" : a
80
- @logger.send(method_id, na) if @filters.all? { |f| f.call a }
81
- }
82
- }
83
- end
84
-
85
- def self.safe_exec(*args, &bl)
86
- _instantiate_logger
87
- @logger_mutex.synchronize {
88
- bl.call *args
89
- }
90
- end
91
-
92
- def self.logger
93
- _instantiate_logger
94
- @logger
95
- end
96
-
97
- # Set log destination
98
- # @param dst destination which to log to (file name, STDOUT, etc)
99
- def self.log_to(dst)
100
- @log_to = dst
101
- @logger = nil
102
- _instantiate_logger
103
- end
104
-
105
- # Set log level.
106
- # @param level one of the standard rails log levels (default fatal)
107
- def self.log_level=(level)
108
- _instantiate_logger
109
- if level.is_a?(String)
110
- level = case level
111
- when 'debug' then
112
- ::Logger::DEBUG
113
- when 'info' then
114
- ::Logger::INFO
115
- when 'warn' then
116
- ::Logger::WARN
117
- when 'error' then
118
- ::Logger::ERROR
119
- when 'fatal' then
120
- ::Logger::FATAL
121
- end
122
- end
123
- @log_level = level
124
- @logger.level = level
125
- end
126
-
127
- # Return true if log level is set to debug, else false
128
- def self.debug?
129
- @log_level == ::Logger::DEBUG
130
- end
131
- end
132
-
133
- end # module RJR
134
-
135
- # Serialized puts, uses logger lock to serialize puts output
136
- def sputs(*args)
137
- ::RJR::Logger.safe_exec {
138
- puts *args
139
- }
140
- end
141
-
142
- class Object
143
- def eigenclass
144
- class << self
145
- self
146
- end
147
- end
148
- end
149
-
150
- if RUBY_VERSION < "1.9"
151
- # We extend object in ruby 1.9 to define 'instance_exec'
152
- #
153
- # {http://blog.jayfields.com/2006/09/ruby-instanceexec-aka-instanceeval.html Further reference}
154
- class Object
155
- module InstanceExecHelper; end
156
- include InstanceExecHelper
157
- # Execute the specified block in the scope of the local object
158
- # @param [Array] args array of args to be passed to block
159
- # @param [Callable] block callable object to bind and invoke in the local namespace
160
- def instance_exec(*args, &block)
161
- begin
162
- old_critical, Thread.critical = Thread.critical, true
163
- n = 0
164
- n += 1 while respond_to?(mname="__instance_exec#{n}")
165
- InstanceExecHelper.module_eval{ define_method(mname, &block) }
166
- ensure
167
- Thread.critical = old_critical
168
- end
169
- begin
170
- ret = send(mname, *args)
171
- ensure
172
- InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
173
- end
174
- ret
175
- end
176
- end
177
- end
178
-
179
- # Two stage json parsing required, for more details
180
- # see json issue https://github.com/flori/json/issues/179
181
-
182
- # FIXME this will only work for json >= 1.7.6 where
183
- # create_additions is defined
184
-
185
- class Class
186
- class << self
187
- attr_accessor :whitelist_json_classes
188
- attr_accessor :permitted_json_classes
189
- end
190
-
191
- def permit_json_create
192
- Class.whitelist_json_classes = true
193
- Class.permitted_json_classes ||= []
194
- unless Class.permitted_json_classes.include?(self.name)
195
- Class.permitted_json_classes << self.name
196
- end
197
- end
198
- end
199
-
200
- module RJR
201
- def self.invalid_json_class?(jc)
202
- Class.whitelist_json_classes ||= false
203
-
204
- Class.whitelist_json_classes ?
205
- # only permit classes user explicitly authorizes
206
- !Class.permitted_json_classes.include?(jc) :
207
-
208
- # allow any class
209
- jc.to_s.split(/::/).inject(Object) do |p,c|
210
- case
211
- when c.empty? then p
212
- when p.constants.collect { |c| c.to_s }.include?(c)
213
- then p.const_get(c)
214
- else
215
- nil
216
- end
217
- end.nil?
218
- end
219
-
220
- def self.validate_json_hash(jh)
221
- jh.each { |k,v|
222
- if k == ::JSON.create_id && invalid_json_class?(v)
223
- raise ArgumentError, "can't create json class #{v}"
224
- elsif v.is_a?(Array)
225
- validate_json_array(v)
226
- elsif v.is_a?(Hash)
227
- validate_json_hash(v)
228
- end
229
- }
230
- end
231
-
232
- def self.validate_json_array(ja)
233
- ja.each { |jai|
234
- if jai.is_a?(Array)
235
- validate_json_array(jai)
236
- elsif jai.is_a?(Hash)
237
- validate_json_hash(jai)
238
- end
239
- }
240
- end
241
-
242
- def self.parse_json(js)
243
- jp = ::JSON.parse js, :create_additions => false
244
- if jp.is_a?(Array)
245
- validate_json_array(jp)
246
- elsif jp.is_a?(Hash)
247
- validate_json_hash(jp)
248
- else
249
- return jp
250
- end
251
- ::JSON.parse js, :create_additions => true
252
- end
253
28
  end
@@ -0,0 +1,63 @@
1
+ # RJR Ruby Core Extensions
2
+ #
3
+ # Copyright (C) 2011-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ class String
7
+ # Safely convert string to ruby class it represents
8
+ def to_class
9
+ split(/::/).inject(Object) do |p,c|
10
+ case
11
+ when c.empty? then p
12
+ when p.constants.collect { |c| c.to_s }.include?(c)
13
+ then p.const_get(c)
14
+ else
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ if RUBY_VERSION < "1.9"
22
+ # We extend object in ruby 1.9 to define 'instance_exec'
23
+ #
24
+ # {http://blog.jayfields.com/2006/09/ruby-instanceexec-aka-instanceeval.html Further reference}
25
+ class Object
26
+ module InstanceExecHelper; end
27
+ include InstanceExecHelper
28
+ # Execute the specified block in the scope of the local object
29
+ # @param [Array] args array of args to be passed to block
30
+ # @param [Callable] block callable object to bind and invoke in the local namespace
31
+ def instance_exec(*args, &block)
32
+ begin
33
+ old_critical, Thread.critical = Thread.critical, true
34
+ n = 0
35
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
36
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
37
+ ensure
38
+ Thread.critical = old_critical
39
+ end
40
+ begin
41
+ ret = send(mname, *args)
42
+ ensure
43
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
44
+ end
45
+ ret
46
+ end
47
+ end
48
+ end
49
+
50
+ class Class
51
+ class << self
52
+ attr_accessor :whitelist_json_classes
53
+ attr_accessor :permitted_json_classes
54
+ end
55
+
56
+ def permit_json_create
57
+ Class.whitelist_json_classes = true
58
+ Class.permitted_json_classes ||= []
59
+ unless Class.permitted_json_classes.include?(self.name)
60
+ Class.permitted_json_classes << self.name
61
+ end
62
+ end
63
+ end