rjr 0.18.2 → 0.19.1

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