passing blocks to be used in OptionParser on method - ruby

I want to wrap OptionParser in another function that defines some defaults, but allows
me to overwrite the banner, add separators, and add new options. I am having a hard time passing the relevant information to the on method.
Any advice on how to fix the line options.each { |o, p| opts.on(o, &p) }?
def my_opts (banner: "Usage: ruby #{File::basename($0)} [options]", separators: [], options: {})
option_parser = OptionParser.new do |opts|
opts.banner = banner
separators.each {|s| opts.separator s }
opts.separator ''
opts.separator 'Options:'
opts.on('-u', '--user USER', 'Username for authentication.') { |user| #user = user }
options.each { |o, p| opts.on(o, &p) }
opts.on_tail('--help', 'Print this help message.') { puts opts; exit }
end
return option_parser
end
opts = {['-f', '--file FILE', 'File to read']=>Proc.new { |f| #f=file } }
stdopts = my_opts( options: opts)

HA! figured it out! the line in question needs to be: options.each { |o,p| opts.on(*o, &p) }

Related

faye-websocket holding until block finishes

When I send messages via a faye-websocket server as middleware, I find the message doesn't send until after the block finishes.
Here's a few examples I've tried:
No threading
require 'faye/websocket'
require 'eventmachine'
require 'json'
Faye::WebSocket.load_adapter('thin')
module SocketTest
class Websocket
def initialize(app)
#app = app
end
def long_function()
sleep 20
"foo"
end
def call(env)
EM.run {
if Faye::WebSocket.websocket?(env)
ws = Faye::WebSocket.new(env, nil, {ping: 15 })
ws.on :open do |event|
response = {
:responseCode => 100,
:message => "Connection opened"
}
$logger.info "< #{response.to_json}"
ws.send(response.to_json)
end
ws.on :message do |event|
response = {
:responseCode => 100,
:message => "Received request, running slow function"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
long_function_result = long_function()
response = {
:responseCode => 200,
:message => "Long function ran, result is #{long_function_result}"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
ws.close()
end
ws.on :close do |event|
$logger.info "< CLOSE"
ws = nil
end
# Return async Rack response
ws.rack_response
else
#app.call(env)
end
}
end
end
end
Chrome output
5:25:09pm WebSocket Connection Established
5:25:09pm {"request":"test"}
5:25:09pm {"responseCode":100,"message":"Connection opened"}
5:25:30pm {"responseCode":100,"message":"Received request, running slow function"}
5:25:30pm {"responseCode":200,"message":"Long function ran, result is foo"}
5:25:30pm Connection Close Frame
5:25:30pm Connection Close Frame
Console output
I, [2020-01-15T17:25:10.006006 #25394] INFO -- : < {"responseCode":100,"message":"Connection opened"}
I, [2020-01-15T17:25:10.024170 #25394] INFO -- : < {"responseCode":100,"message":"Received request, running slow function"}
I, [2020-01-15T17:25:30.034885 #25394] INFO -- : < {"responseCode":200,"message":"Long function ran, result is foo"}I, [2020-01-15T06:25:30.189606 #25394] INFO -- : < CLOSE
As we can see above, the logger output is is immediate (17:25:09) for "Received request, running slow function", however the additional 20 seconds are taken for the response to be sent to the client. I've noticed the same thing with PING/PONG messages - nothing comes from the WebSocket server until the blocking sleep finishes.
I've also tried a modified version like this:
Threading off
# Just the call function, all other parts are the same
def call(env)
EM.run {
if Faye::WebSocket.websocket?(env)
ws = Faye::WebSocket.new(env, nil, {ping: 15 })
ws.on :open do |event|
response = {
:responseCode => 100,
:message => "Connection opened"
}
$logger.info "< #{response.to_json}"
ws.send(response.to_json)
end
ws.on :message do |event|
response = {
:responseCode => 100,
:message => "Received request, running slow function"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
long_function = Thread.new { long_function() }
until (long_function.alive? == false) do
response = {
:responseCode => 100,
:message => "Waiting for long function to complete"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
sleep 5
end
response = {
:responseCode => 200,
:message => "Long function ran, result is #{long_function.value}"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
ws.close()
end
ws.on :close do |event|
$logger.info "< CLOSE"
ws = nil
end
# Return async Rack response
ws.rack_response
else
#app.call(env)
end
}
end
Chrome output
5:34:33pm WebSocket Connection Established
5:34:33pm {"request":"test"}
5:34:33pm {"responseCode":100,"message":"Connection opened"}
5:34:53pm {"responseCode":100,"message":"Received request, running slow function"}
5:34:53pm {"responseCode":100,"message":"Waiting for long function to complete"}
5:34:53pm {"responseCode":100,"message":"Waiting for long function to complete"}
5:34:53pm {"responseCode":100,"message":"Waiting for long function to complete"}
5:34:53pm {"responseCode":100,"message":"Waiting for long function to complete"}
5:34:53pm {"responseCode":200,"message":"Long function ran, result is foo"}
5:34:53pm Connection Close Frame
5:34:53pm Connection Close Frame
Console output
I, [2020-01-15T17:34:33.473295 #25729] INFO -- : < {"responseCode":100,"message":"Connection opened"}
I, [2020-01-15T17:34:33.489433 #25729] INFO -- : < {"responseCode":100,"message":"Received request, running slow function"}
I, [2020-01-15T17:34:33.489638 #25729] INFO -- : < {"responseCode":100,"message":"Waiting for long function to complete"}
I, [2020-01-15T17:34:38.490995 #25729] INFO -- : < {"responseCode":100,"message":"Waiting for long function to complete"}
I, [2020-01-15T17:34:43.491927 #25729] INFO -- : < {"responseCode":100,"message":"Waiting for long function to complete"}
I, [2020-01-15T17:34:48.497281 #25729] INFO -- : < {"responseCode":100,"message":"Waiting for long function to complete"}
I, [2020-01-15T17:34:53.499446 #25729] INFO -- : < {"responseCode":200,"message":"Long function ran, result is foo"}
I, [2020-01-15T17:34:53.632997 #25729] INFO -- : < CLOSE
The result is the same again, although the $logger object continues to operate whilst waiting for the thread result (indicating to me we are no longer blocking), it appears the WebSocket is still holding and waiting for its buffer. How can I force this buffer out?
Using the add_timer EventMachine function that #max_pleaner suggested I was able to build a working version of the code with internal callbacks to the function to create a loop:
require 'faye/websocket'
require 'eventmachine'
require 'json'
Faye::WebSocket.load_adapter('thin')
module SocketTest
class Websocket
def initialize(app)
#app = app
end
def call(env)
EM.run {
if Faye::WebSocket.websocket?(env)
ws = Faye::WebSocket.new(env, nil, {ping: 15 })
ws.on :open do |event|
response = {
:responseCode => 100,
:message => "Connection opened"
}
$logger.info "< #{response.to_json}"
ws.send(response.to_json)
end
ws.on :message do |event|
response = {
:responseCode => 100,
:message => "Received request, running slow function"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
total_runs = 4
def long_function(ws, count, total_runs)
if count > total_runs then
# Error out
puts "Reached full count, exiting"
ws.close()
return
end
# Logic here
if count == 3 then
response = {
:responseCode => 200,
:message => "Long function ran"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
ws.close()
else
response = {
:responseCode => 100,
:message => "Waiting for long function to complete"
}
ws.send(response.to_json)
$logger.info "< #{response.to_json}"
EventMachine.add_timer(5) {
long_function(ws, count + 1, total_runs)
}
end
end
long_function(ws, 1, total_runs)
end
ws.on :close do |event|
$logger.info "< CLOSE"
ws = nil
end
# Return async Rack response
ws.rack_response
else
#app.call(env)
end
}
end
end
end

Setting variable outside a block from inside a block?

I have following code:
def test_callback_interface
with_temp_stdio do |stdin, stdout|
stdin.write("hello\n")
stdin.close
stdout.flush
line = nil
replace_stdio(stdin.path, stdout.path) {
Readline.handler_install("> ", true) { |l| line = l }
6.times { Readline.read_char }
Readline.handler_remove
}
assert_equal("hello", line) <------ FAIL here
assert_equal(true, line.tainted?)
stdout.rewind
assert_equal("> ", stdout.read(2))
assert_equal(1, Readline::HISTORY.length)
assert_equal("hello", Readline::HISTORY[0])
end
assert_equal(true, false)
end
it fails on the line assert_equal("hello", line) saying that the line is nil. However, I'm sure that the callback is called (I verified it by putting raise in there). So I must be missing something fundamental about scopes here. Could someone please enlighten me how to get the value of l to the line variable?
Thanks
EDIT:
How do I call the callback inside handler_install/read_char?
static VALUE readline_callback_ensure(VALUE val) {
free(readline_callback_line);
readline_callback_line = NULL;
return Qnil;
}
static VALUE readline_callback_call(VALUE line) {
VALUE proc = rb_attr_get(mReadline, read_char_cb_proc);
rb_funcall(proc, id_call, 1, line);
return Qnil;
}
static void readline_callback_callback(char * line) {
if (readline_callback_add_history && line) {
add_history(line);
}
readline_callback_line = line;
rb_ensure(
readline_callback_call, line ? rb_str_new_cstr(line) : Qnil,
readline_callback_ensure, Qnil
);
}
static VALUE readline_callback_handler_install(int argc, VALUE * argv, VALUE self) {
VALUE tmp, add_hist, block;
char * prompt = NULL;
rb_need_block();
if (rb_scan_args(argc, argv, "02&", &tmp, &add_hist, &block) > 0) {
prompt = RSTRING_PTR(tmp);
}
if (RTEST(add_hist)) {
readline_callback_add_history = true;
} else {
readline_callback_add_history = false;
}
rb_ivar_set(mReadline, read_char_cb_proc, block);
rl_callback_handler_install(prompt, readline_callback_callback);
return Qnil;
}
static VALUE readline_callback_read_char(VALUE self) {
VALUE proc = rb_attr_get(mReadline, read_char_cb_proc);
if (NIL_P(proc)) {
rb_raise(rb_eRuntimeError, "No handler installed.");
}
rl_callback_read_char();
return Qnil;
}
So basically read_char calls rl_callback_read_char (gnu readline function), which on detecting complete line invokes my installed handler readline_callback_callback which invoked stored block supplied by user.
Solved, not an scope issue. GNU Readline calls my code once more with NULL on EOF, complete forgot about that.

mongoid-bcrypt-ruby returning invalid hash when trying to persist a new user

I am trying to setup a simple password encryption using mongoid-bcrypt-ruby mongoid and sinatra i am also developing on a windows machine
unfortunately whenever I try to persist a new user i get an error from my User.create!() call saying invalid hash
# gemfile
ruby "2.2.4"
source "http://rubygems.org"
gem "sinatra", require: "sinatra/base"
gem "sinatra-contrib", require: "sinatra/namespace"
gem "rack"
gem "thin"
gem "mongoid"
gem "mongoid-bcrypt-ruby"
user model
class User
include Mongoid::Document
include Mongoid::Timestamps
store_in collection: :users
field :first_name, type: String
field :last_name, type: String
field :email, type: String
field :phone_number, type: String
field :password, type: BCrypt::Password
field :is_active, type: Boolean, default: false
field :is_admin, type: Boolean, default: false
# ....
end
class Routes
# ....
post '/submit' do
User.create!(
first_name: params['first_name'],
last_name: params['last_name'],
email: params['email'],
phone_number: params['phone_number'],
password: params['password']
)
end
end
Trace from Sinatra's error page:
BCrypt::Errors::InvalidHash at /api/v1/new_user
invalid hash
file: password.rb location: initialize line: 60
Full Backtrace from Sinatra's error page:
C:/Ruby22/lib/ruby/gems/2.2.0/gems/bcrypt-3.1.11-x86-mingw32/lib/bcrypt/password.rb in initialize
raise Errors::InvalidHash.new("invalid hash")
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-bcrypt-ruby-0.0.2/lib/mongoid/bcrypt/ruby.rb in new
when String then self.new(password)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-bcrypt-ruby-0.0.2/lib/mongoid/bcrypt/ruby.rb in demongoize
when String then self.new(password)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/fields/standard.rb in demongoize
delegate :demongoize, :evolve, :mongoize, to: :type
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/fields.rb in block (2 levels) in create_field_getter
value = field.demongoize(raw)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/validatable.rb in read_attribute_for_validation
send(attr)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validator.rb in block in validate
value = record.read_attribute_for_validation(attribute)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validator.rb in each
attributes.each do |attribute|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validator.rb in validate
attributes.each do |attribute|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in public_send
filter.public_send method_to_call, target, &blk
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in block in make_lambda
filter.public_send method_to_call, target, &blk
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in call
result_lambda = -> { user_callback.call target, value }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in block (2 levels) in halting
result_lambda = -> { user_callback.call target, value }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in call
result_lambda.call if result_lambda.is_a?(Proc)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in block (2 levels) in default_terminator
result_lambda.call if result_lambda.is_a?(Proc)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in catch
catch(:abort) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in block in default_terminator
catch(:abort) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in call
env.halted = halted_lambda.call(target, result_lambda)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in block in halting
env.halted = halted_lambda.call(target, result_lambda)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in call
#before.each { |b| b.call(arg) }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in block in call
#before.each { |b| b.call(arg) }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in each
#before.each { |b| b.call(arg) }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in call
#before.each { |b| b.call(arg) }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in __run_callbacks__
runner.call(e).value
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in _run_validate_callbacks
__run_callbacks__(_#{name}_callbacks, &block)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validations.rb in run_validations!
_run_validate_callbacks
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validations/callbacks.rb in block in run_validations!
_run_validation_callbacks { super }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in __run_callbacks__
yield if block_given?
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activesupport-5.0.0.1/lib/active_support/callbacks.rb in _run_validation_callbacks
__run_callbacks__(_#{name}_callbacks, &block)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validations/callbacks.rb in run_validations!
_run_validation_callbacks { super }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validations.rb in valid?
run_validations!
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/validatable.rb in valid?
super context ? context : (new_record? ? :create : :update)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/activemodel-5.0.0.1/lib/active_model/validations.rb in invalid?
!valid?(context)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/persistable/creatable.rb in prepare_insert
invalid?(options[:context] || :create)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/persistable/creatable.rb in insert
prepare_insert(options) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/persistable/creatable.rb in block in create!
doc.fail_due_to_validation! unless doc.insert.errors.empty?
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/threaded/lifecycle.rb in _creating
yield
C:/Ruby22/lib/ruby/gems/2.2.0/gems/mongoid-6.0.2/lib/mongoid/persistable/creatable.rb in create!
_creating do
C:/Users/ALilland/Documents/macros/experiments/mongoid/mongoid_heroku/db_core_app/models/user.rb in block (2 levels) in <class:Routes>
User.create!(
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
proc { |a,p| unbound_method.bind(a).call }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in compile!
proc { |a,p| unbound_method.bind(a).call }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in []
route_eval { block[*args] }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block (3 levels) in route!
route_eval { block[*args] }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in route_eval
throw :halt, yield
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block (2 levels) in route!
route_eval { block[*args] }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in process_route
block ? block[self, values] : yield(self, values)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in catch
catch(:pass) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in process_route
catch(:pass) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in route!
returned_pass_block = process_route(pattern, keys, conditions) do |*args|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in each
routes.each do |pattern, keys, conditions, block|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in route!
routes.each do |pattern, keys, conditions, block|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in dispatch!
route!
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in invoke
res = catch(:halt) { yield }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in catch
res = catch(:halt) { yield }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in invoke
res = catch(:halt) { yield }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in dispatch!
invoke do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in call!
invoke { dispatch! }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in invoke
res = catch(:halt) { yield }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in catch
res = catch(:halt) { yield }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in invoke
res = catch(:halt) { yield }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call!
invoke { dispatch! }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
dup.call!(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/xss_header.rb in call
status, headers, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb in call
result or app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb in call
result or app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/path_traversal.rb in call
app.call env
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/json_csrf.rb in call
status, headers, body = app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb in call
result or app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/base.rb in call
result or app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-protection-1.5.3/lib/rack/protection/frame_options.rb in call
status, headers, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/session/abstract/id.rb in context
status, headers, body = app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/session/abstract/id.rb in call
context(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/logger.rb in call
#app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
env['sinatra.commonlogger'] ? #app.call(env) : super
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/head.rb in call
status, headers, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/show_exceptions.rb in call
#app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
result, callback = app.call(env), env['async.callback']
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
#stack.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in block in call
synchronize { prototype.call(env) }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in synchronize
yield
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
synchronize { prototype.call(env) }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/cascade.rb in block in call
result = app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/cascade.rb in each
#apps.each do |app|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/cascade.rb in call
#apps.each do |app|
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/tempfile_reaper.rb in call
status, headers, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/lint.rb in _call
status, headers, #body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/lint.rb in call
dup._call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/showexceptions.rb in call
#app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/commonlogger.rb in call
status, header, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/sinatra-1.4.7/lib/sinatra/base.rb in call
call_without_check(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/chunked.rb in call
status, headers, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/content_length.rb in call
status, headers, body = #app.call(env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/connection.rb in block in pre_process
response = #app.call(#request.env)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/connection.rb in catch
catch(:async) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/connection.rb in pre_process
catch(:async) do
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/connection.rb in process
post_process(pre_process)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/connection.rb in receive_data
process if #request.parse(data)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/eventmachine-1.2.1-x86-mingw32/lib/eventmachine.rb in run_machine
run_machine
C:/Ruby22/lib/ruby/gems/2.2.0/gems/eventmachine-1.2.1-x86-mingw32/lib/eventmachine.rb in run
run_machine
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/backends/base.rb in start
EventMachine.run(&starter)
C:/Ruby22/lib/ruby/gems/2.2.0/gems/thin-1.7.0/lib/thin/server.rb in start
#backend.start { setup_signals if #setup_signals }
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/handler/thin.rb in run
server.start
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/server.rb in start
server.run wrapped_app, options, &blk
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/lib/rack/server.rb in start
new(options).start
C:/Ruby22/lib/ruby/gems/2.2.0/gems/rack-1.6.5/bin/rackup in <top (required)>
Rack::Server.start
C:/Ruby22/bin/rackup in load
load Gem.bin_path('rack', 'rackup', version)
C:/Ruby22/bin/rackup in <main>
load Gem.bin_path('rack', 'rackup', version)
I had this problem myself. The below worked for me.
You can see from the trace that the problem is the line:
when String then self.new(password)
Change that line to the following:
when String then self.create(password)

Rabbitmq RPC implementation share the same reply queue

I am trying to build RPC by using rabbitmq.
According to the tutorial for building RPC through rabbitmq http://www.rabbitmq.com/tutorials/tutorial-six-ruby.html, we can use the one reply-queue for each client and use correlation_id to map the response and request. I am confused about how the correlation_id is used?
Here is the issue I am running with, I would like to create two rpc calls synchronously from one client, using the same reply-queue with two different correlation id. However I don't know if this is the correct use case, since from what I read in the tutorial it seems to assume that each client is making the rpc call sequentially. (In this case, it becomes more confusing that why would we need the correlation_id here).
Here is the code example (the rpc_server.rb will be the same as the tutorial), for what I am trying to achieve. Hopefully it makes my question more clear.
The code block below wouldn't work, since the correlation_id is being overwrite by thr2 when we set it in thr1.
I wonder if there is anyway to modify it, to make it work? If we try to move the #reply_queue.subscribe block out of initialization and pass in different call_id, it still doesn't work since it seem like the #reply-queue will be locked while waiting for thr1 to finish.
Please let me know if the question is uncleared, thanks in advance for any of your response.
#!/usr/bin/env ruby
# encoding: utf-8
require "bunny"
require "thread"
conn = Bunny.new(:automatically_recover => false)
conn.start
ch = conn.create_channel
class FibonacciClient
attr_reader :reply_queue
attr_accessor :response, :call_id
attr_reader :lock, :condition
def initialize(ch, server_queue)
#ch = ch
#x = ch.default_exchange
#server_queue = server_queue
#reply_queue = ch.queue("", :exclusive => true)
#lock = Mutex.new
#condition = ConditionVariable.new
that = self
#reply_queue.subscribe do |delivery_info, properties, payload|
if properties[:correlation_id] == that.call_id
that.response = payload.to_i
that.lock.synchronize{that.condition.signal}
end
end
end
def call(n)
self.call_id = self.generate_uuid
#x.publish(n.to_s,
:routing_key => #server_queue,
:correlation_id => call_id,
:reply_to => #reply_queue.name)
lock.synchronize{condition.wait(lock)}
response
end
protected
def generate_uuid
# very naive but good enough for code
# examples
"#{rand}#{rand}#{rand}"
end
end
client = FibonacciClient.new(ch, "rpc_queue")
thr1 = Thread.new{
response1 = client.call(30)
puts response1
}
thr2 = Thread.new{
response2 = client.call(40)
puts response2
}
ch.close
conn.close
same problem here.
'use strict';
const config = require('./config')
const amqp = require('amqplib').connect('amqp://' + config.username + ':' + config.password + '#ali3')
const co = require('co')
const args = process.argv.slice(2)
if (args.length == 0) {
console.log("Usage: rpc_client.js num");
process.exit(1)
}
function generateUuid() {
return Math.random().toString() +
Math.random().toString() +
Math.random().toString()
}
function* init(){
let conn = yield amqp
let ch = yield conn.createChannel()
let cbQueue = yield ch.assertQueue('', {exclusive: true})
return {"conn": conn, "channel": ch, "cbQueue": cbQueue}
}
function* sender(initConfig, msg, resHandler) {
try {
let ch = initConfig.channel
let conn = initConfig.conn
let cbQueue = initConfig.cbQueue
const corr = generateUuid()
console.log(' [x] [%s] Requesting fib(%d)',corr, msg)
ch.consume(cbQueue.queue, (resultMsg) => {
resHandler(resultMsg, corr, conn)
})
ch.sendToQueue('rpc_queue', new Buffer(msg.toString()), {"correlationId": corr, "replyTo": cbQueue.queue})
}
catch (ex) {
console.warn("ex:", ex)
}
}
function responseHandler(res, corr, conn) {
console.log("corr: %s - %s", corr, res.content.toString());//important debug info
if (res.properties.correlationId == corr)
{
console.log(' [.] Got %s', res.content.toString());
//setTimeout( () => {
// conn.close()
// process.exit(0)
//}, 500);
}
};
function onerror(err) {
console.error(err.stack);
}
co(function*() {
let num = parseInt(args[0])
let initConfig = yield init();
//let initConfig2 = yield init();
yield [
sender(initConfig, num.toString(), responseHandler),
sender(initConfig, (num+3).toString(), responseHandler)
]
}, onerror)
dengwei#RMBAP:~/projects/github/rabbitmq-demo/rpc$ node rpc_client_gen.js 5
[x] [0.64227353665046390.20330130192451180.5467283953912556] Requesting fib(5)
[x] [0.461023105075582860.22911424539051950.9930733679793775] Requesting fib(8)
corr: 0.64227353665046390.20330130192451180.5467283953912556 - 5
[.] Got 5
corr: 0.461023105075582860.22911424539051950.9930733679793775 - 21
[.] Got 21
from the important debug here we can see:
message has been sent from server to callback queue , and consume by client, check the uuid is set to the first message. so :
if (res.properties.correlationId == corr)
this line of code ignore the result.
seems that we can only init each time before send a single message.

Store ruby code in a let() variable

I want to store the code will be run inside each_with_index block:
describe '#each_with_index' do
subject { [:a,:b].each_with_index{ block.to_ruby } }
context 'when the block prints v to console' do
let(:block) { '|v| puts v' }
specify { expect { subject }.to output(':a :b').to_stdout }
end
context 'when the block prints i to console' do
let(:block) { '|v,i| puts i' }
specify { expect { subject }.to output('0 1').to_stdout }
end
context 'when the block prints v and i to console' do
let(:block) { '|v,i| puts "#{v} and {i}"' }
specify { expect { subject }.to output(':a and 0 :b and 1').to_stdout }
end
end
I don't need the code stored as a string, it is just a way to show you what I mean. I want to do this with in-block code, pipes, and everything. I've got a feeling we can use Proc.new but the pipes are tripping me up.
Something like:
let(:block) { Proc.new{ |v| puts v } }
subject { [:a,:b].each_with_index { |*args| block.call args } }

Resources