ruby problems for methods - ruby

I have the following code. I want to use the methods for myserver1 and myserver2 and pass them the addresses the have been iterated through in the send_to_servers method. I can't seem to do that. Please help. Unless I can have these two receive addresses on an altering basis, I won't be able to do what I need to do. Thanks in advance.
class Addresses
def add
#addresses = %w(me#gmail.com me2#gmail.com me3#gmail.com)
end
def myserver1
puts "Sending email from myserver1 with address #{#address}"
end
def myserver2
puts "Sending email from myserver2 with address #{#address}"
end
def servers
serv = [myserver1, myserver2]
end
# def servers
# serv = (1..2).to_a # Your list of servers goes here
# end
def send_to_servers(servers)
#addresses.each.with_index do |address, i|
server = servers[i % servers.length]
puts "Sending address #{address} to server #{server}"
#address = address
end
end
end
a = Addresses.new
a.add
servers = a.servers
a.send_to_servers(servers)

It's unclear how you might want to structure it, but here is a concise implementation that I think meets your description.
def myserver1(address)
# Do something with address
end
def myserver2(address)
# Do something with address
end
addresses = %w(me#gmail.com me2#gmail.com me3#gmail.com)
servers = %w(myserver1 myserver2).cycle
addresses.each do |address|
send(servers.next, address)
end
Apologies if I'm missing something crucial to your problem with this. Please feel free to comment on what extra functionality is required to help firm up the specification.

Your problem is that your server methods do not return anything:
def myserver1
puts "Sending email from myserver1 with address #{#address}"
end
This method prints the message out and returns nil. puts always returns nil.
Thus, when you do [myserver1, myserver2], it prints out two messages and returns [nil, nil].
Servers are things, they should probably be objects, not methods. Methods are actions that do and/or return something. Try something like this:
class Server
def initialize(name)
#name = name
end
def send_address(address)
puts "Sending email from #{#name} with address #{address}"
end
end
addresses = %w(me#gmail.com me2#gmail.com me3#gmail.com)
servers = [Server.new("server one"), Server.new("server two")]
addresses.each_with_index do |address, i|
server = servers[i % servers.length]
server.send_address(address)
end

I believe your code should be refactored to be closer to the real world.
You want to loop through a list of email addresses and send each iteration to myserver2 and myserver2.
This means you need to have the cartesian product of emails and servers and do "send" to that pair.
require 'net/smtp'
emails = %w{email1#email.com email2#email.com}
servers = %w{server1 server2}
emails.product(servers).each do |address, server|
Net::SMTP.start(server) do |smtp|
smtp.send_message 'Body', 'from#example.com', [address]
end
end

Related

How to check variable in another file in Ruby?

I am new to Ruby and writing an IRC server using some existing code. I have searched online for a solution but since I don't really know the exact phrases I want to search for, I'm having little luck so I'll explain it as best as I can here hoping that someone can help me.
I have a file ircclient.rb containing:
when 'oper'
name = args.any? && args.shift.downcase
pass = args.shift
#oline = ServerConfig.opers.find do |oper|
oper['login'].downcase == name && oper['pass'] == pass
end
if #oline
#oper = true
send_numeric 381, 'You are now an IRC operator'
join ServerConfig.oper_channel if ServerConfig.oper_channel
else
send_numeric 491, 'Login Failed'
end
This code is within:
class IRCClient < LineConnection
end
My question is how can I check whether a user is #oper or not but from another file?
For example I tried:
def join client
#opers << client if #opered?
#users << client
send_to_all client.path, :join, #name
end
but it causes the program to crash.
Update
Thank you for the answer, I appreciate it.
In the existing ircclient.rb class IRCClient < LineConnection I have at the start:
attr_reader :nick, :ident, :realname, :conn, :addr, :ip, :host, :dead, :umodes, :server
attr_accessor :oper, :away, :created_at, :modified_at
In the ircchannel.rb I tried to access whether a user is #oper or not using:
def join client
#ops << client if client.opered?
#users << client
send_to_all client.path, :join, #name
end
but this caused the program to crash. I also tried:
def join client
#ops << client if user.opered?
#users << client
send_to_all client.path, :join, #name
end
I guess I am making a simple mistake?
Since you alreday have attr_accessor :oper in your class, you can just write:
# in the `join` method
#ops << client if client.oper

Assert_equal undefined local variable LRTHW ex52

Hi I made it to the lase exercise os Learn Ruby The Hard Way, and I come at the wall...
Here is the test code:
def test_gothon_map()
assert_equal(START.go('shoot!'), generic_death)
assert_equal(START.go('dodge!'), generic_death)
room = START.go("tell a joke")
assert_equal(room, laser_weapon_armory)
end
And here is the code of the file it should test:
class Room
attr_accessor :name, :description, :paths
def initialize(name, description)
#name = name
#description = description
#paths = {}
end
def ==(other)
self.name==other.name&&self.description==other.description&&self.paths==other.paths
end
def go(direction)
#paths[direction]
end
def add_paths(paths)
#paths.update(paths)
end
end
generic_death = Room.new("death", "You died.")
And when I try to launch the test file I get an error:
generic_death = Room.new("death", "You died.")
I tried to set the "generic_death = Room.new("death", "You died.")" in test_gothon_map method and it worked but the problem is that description of the next object is extremely long, so my questions are:
why assertion doesn't not respond to defined object?
can it be done different way then by putting whole object to testing method, since description of the next object is extremely long...
The nature of local variable is that they are, well, local. This means that they are not available outside the scope they were defined.
That's why ruby does not know what generic_death means in your test.
You can solve this in a couple of ways:
define rooms as constants in the Room class:
class Room
# ...
GENERIC_DEATH = Room.new("death", "You died.")
LASER_WEAPON_ARMORY = Room.new(...)
end
def test_gothon_map()
assert_equal(Room::START.go('shoot!'), Room::GENERIC_DEATH)
assert_equal(Room::START.go('dodge!'), Room::GENERIC_DEATH)
room = Room::START.go("tell a joke")
assert_equal(room, Room::LASER_WEAPON_ARMORY)
end
assert the room by its name, or some other identifier:
def test_gothon_map()
assert_equal(START.go('shoot!').name, "death")
assert_equal(START.go('dodge!').name, "death")
room = START.go("tell a joke")
assert_equal(room.name, "laser weapon armory")
end

How to test that a block is called within a thread?

I am working on wrapping the ruby-mqtt gem into a class which implements a subscribe and publish method. The subscribe method connects to the server and listens in a separate thread because this call is synchronous.
module PubSub
class MQTT
attr_accessor :host, :port, :username, :password
def initialize(params = {})
params.each do |attr, value|
self.public_send("#{attr}=", value)
end if params
super()
end
def connection_options
{
remote_host: self.host,
remote_port: self.port,
username: self.username,
password: self.password,
}
end
def subscribe(name, &block)
channel = name
connect_opts = connection_options
code_block = block
::Thread.new do
::MQTT::Client.connect(connect_opts) do |c|
c.get(channel) do |topic, message|
puts "channel: #{topic} data: #{message.inspect}"
code_block.call topic, message
end
end
end
end
def publish(channel = nil, data)
::MQTT::Client.connect(connection_options) do |c|
c.publish(channel, data)
end
end
end
end
I have a test that I have written using rspec to test the class but it does not pass.
mqtt = ::PubSub::MQTT.new({host: "localhost",port: 1883})
block = lambda { |channel, data| puts "channel: #{channel} data: #{data.inspect}"}
block.should_receive(:call).with("channel", {"some" => "data"})
thr = mqtt.subscribe("channel", &block)
mqtt.publish("channel", {"some" => "data"})
When I run the following ruby-mqtt-example I have now problems at all.
uri = URI.parse ENV['CLOUDMQTT_URL'] || 'mqtt://localhost:1883'
conn_opts = {
remote_host: uri.host,
remote_port: uri.port,
username: uri.user,
password: uri.password,
}
# Subscribe example
Thread.new do
puts conn_opts
MQTT::Client.connect(conn_opts) do |c|
# The block will be called when you messages arrive to the topic
c.get('test') do |topic, message|
puts "#{topic}: #{message}"
end
end
end
# Publish example
puts conn_opts
MQTT::Client.connect(conn_opts) do |c|
# publish a message to the topic 'test'
loop do
c.publish('test', 'Hello World')
sleep 1
end
end
So my question is, what am I doing wrong when I simply create a class and separate out the publish and subscribe logic? My guess is that it has something to do with Threading in the function call but I can't seem to figure it out. Any help is much appreciated.
UPDATE
I believe I know why the test is not passing and it is because when I pass a lambda in to subscribe expecting it to receive a call it actually will not receive the call when it exits the method or until publish is called. So I would like to rephrase the question to: How do I test that a block is called within a thread? If someone answers, "you don't", then the question is: How do you test that block is being called in an infinite loop like in the example of calling get within ruby-mqtt gem.
The RSpec expectations machinery will work fine with threads, as evidenced by the following example, which passes:
def foo(&block)
block.call(42)
end
describe "" do
it "" do
l = lambda {}
expect(l).to receive(:call).with(42)
Thread.new { foo(&l) }.join
end
end
The join waits for the thread(s) to finish before going further.

Uninitialized Constant Error from Ruby EventMachine Chat Server

I'm trying to build a chat server in ruby using EventManager. Needless to day, I'm new to Ruby and feeling a little over my head with the current error I am getting, as I have no clue what it means and a search doesn't return anything valuable. Here's some of the logistics-
(ive only implemented LOGIN and REGISTER so I'll only include those..)
user can enter-
REGISTER username password - registers user
LOGIN username password - logins user
I'm taking in the string of data the user sends, splitting it into an array called msg, and then acting on the data based on msg[0] (as its the command, like REGISTER, LOGIN, etc)
Here is my code, all contained in a single file- chatserver.rb (explanation follows):
require 'rubygems'
require 'eventmachine'
class Server
attr_accessor :clients, :channels, :userCreds, :userChannels
def initialize
#clients = [] #list of clients connected e.g. [192.168.1.2, 192.168.1.3]
#users = {} #list of users 'logged in' e.g. [tom, sam, jerry]
#channels = [] #list of channels e.g. [a, b, c]
#userCreds = {} #user credentials hash e.g. { tom: password1, sam: password2, etc }
#userChanels = {} #users and their channels e.g. { tom: a, sam: a, jerry: b }
end
def start
#signature = EventMachine.start_server("127.0.0.1", 3200, Client) do |con|
con.server = self
end
end
def stop
EventMachine.stop_server(#signature)
unless wait_for_connections_and_stop
EventMachine.add_periodic.timer(1) { wait_for_connections_and_stop }
end
end
# Does the username already exist?
def has_username?(name)
#userCreds.has_key?(name)
end
# Is the user already logged in?
def logged_in?(name)
if #users[name] == 1
true
else
false
end
end
# Did the user enter the correct pwd?
def correct_pass?(pass)
if #userCreds[name] == pass
true
else
false
end
end
private
def wait_for_connections_and_stop
if #clients.empty?
EventMachine.stop
true
else
puts "Waiting for #{#clients.size} client(s) to stop"
false
end
end
end
class Connection < EventMachine::Connection
attr_accessor :server, :name, :msg
def initialize
#name = nil
#msg = []
end
# First thing the user sees when they connect to the server.
def post_init
send_data("Welcome to the lobby.\nRegister or Login with REGISTER/LOGIN username password\nOr try HELP if you get stuck!")
end
# Start parsing incoming data
def receive_data(data)
data.strip!
msg = data.split("") #split data by spaces and throw it in array msg[]
if data.empty? #the user entered nothing?
send_data("You didn't type anything! Try HELP.")
return
elsif msg[0] == "REGISTER"
handle_register(msg) #send msg to handle_register method
else
hanlde_login(msg) #send msg to handle_login method
end
end
def unbind
#server.clients.each { |client| client.send_data("#{#name} has just left") }
puts("#{#name} has just left")
#server.clients.delete(self)
end
private
def handle_register(msg)
if #server.has_username? msg[1] #user trying to register with a name that already exists?
send_data("That username is already taken! Choose another or login.")
return
else
#name = msg[1] #set name to username
#userCreds[name] = msg[2] #add username and password to user credentials hash
send_data("OK") #send user OK message
end
end
end
EventMachine::run do
s = Server.new
s.start #start server
puts "Server listening"
end
Whew, okay, it's only the beginning, so not that complicated. Since I'm new to Ruby I have a feeling I'm just not declaring variable or using scope correctly. Here's the error output:
chatserver.rb:16:in start': uninitialized constant Server::Client
(NameError) from chatserver.rb:110:inblock in ' from
/Users/meth/.rvm/gems/ruby-1.9.3-p392#rails3tutorial2ndEd/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in
call' from
/Users/meth/.rvm/gems/ruby-1.9.3-p392#rails3tutorial2ndEd/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in
run_machine' from
/Users/meth/.rvm/gems/ruby-1.9.3-p392#rails3tutorial2ndEd/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in
run' from chatserver.rb:108:in<\main>'
ignore the slash in main in that last line.
line 108 is the last function- EventMachine::run do etc.
Any help would be appreciated, if I didn't provide enough info just let me know.
I would think that when you call EventMachine::start_server you need to give it your Connection class as the handler. Client is not defined anywhere.

Ruby EventMachine & functions

I'm reading a Redis set within an EventMachine reactor loop using a suitable Redis EM gem ('em-hiredis' in my case) and have to check if some Redis sets contain members in a cascade. My aim is to get the name of the set which is not empty:
require 'eventmachine'
require 'em-hiredis'
def fetch_queue
#redis.scard('todo').callback do |scard_todo|
if scard_todo.zero?
#redis.scard('failed_1').callback do |scard_failed_1|
if scard_failed_1.zero?
#redis.scard('failed_2').callback do |scard_failed_2|
if scard_failed_2.zero?
#redis.scard('failed_3').callback do |scard_failed_3|
if scard_failed_3.zero?
EM.stop
else
queue = 'failed_3'
end
end
else
queue = 'failed_2'
end
end
else
queue = 'failed_1'
end
end
else
queue = 'todo'
end
end
end
EM.run do
#redis = EM::Hiredis.connect "redis://#{HOST}:#{PORT}"
# How to get the value of fetch_queue?
foo = fetch_queue
puts foo
end
My question is: how can I tell EM to return the value of 'queue' in 'fetch_queue' to use it in the reactor loop? a simple "return queue = 'todo'", "return queue = 'failed_1'" etc. in fetch_queue results in "unexpected return (LocalJumpError)" error message.
Please for the love of debugging use some more methods, you wouldn't factor other code like this, would you?
Anyway, this is essentially what you probably want to do, so you can both factor and test your code:
require 'eventmachine'
require 'em-hiredis'
# This is a simple class that represents an extremely simple, linear state
# machine. It just walks the "from" parameter one by one, until it finds a
# non-empty set by that name. When a non-empty set is found, the given callback
# is called with the name of the set.
class Finder
def initialize(redis, from, &callback)
#redis = redis
#from = from.dup
#callback = callback
end
def do_next
# If the from list is empty, we terminate, as we have no more steps
unless #current = #from.shift
EM.stop # or callback.call :error, whatever
end
#redis.scard(#current).callback do |scard|
if scard.zero?
do_next
else
#callback.call #current
end
end
end
alias go do_next
end
EM.run do
#redis = EM::Hiredis.connect "redis://#{HOST}:#{PORT}"
finder = Finder.new(redis, %w[todo failed_1 failed_2 failed_3]) do |name|
puts "Found non-empty set: #{name}"
end
finder.go
end

Resources