I do not understand what is going on in the line
print buggy_logger << "\n" # <- This insertion is the bug.
Why is that the variable status changes when the above line is called?
I am following this website http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings/
status = "peace"
buggy_logger = status
print "Status: "
print buggy_logger << "\n" # <- This insertion is the bug.
def launch_nukes?(status)
unless status == 'peace'
return true
else
return false
end
end
print "Nukes Launched: #{launch_nukes?(status)}\n"
OUTPUT IS:
=> Status: peace
=> Nukes Launched: true
Your question is "why does the variable change?"
The answer is because buggy_logger holds a reference to status. Easily proven by inspecting the object_id.
irb(main):001:0> a = "hi"
=> "hi"
irb(main):002:0> a.object_id
=> 24088560
irb(main):003:0> b = a
=> "hi"
irb(main):004:0> b.object_id
=> 24088560
irb(main):005:0>
To create a copy use + or any non-mutating operator. Don't use <<.
irb(main):010:0> c = a + " guys"
=> "hi guys"
irb(main):011:0> c.object_id
=> 26523040
irb(main):012:0>
Since status = "peace" is a string, when buggy_logger << "\n" is ran, it's updating the string of buggy_logger (and subsequently, status) to be "peace\n"
Therefore, when the method is ran, it returns true because status != "peace" anymore.
Now, if in the beginning you used a symbol status = :peace, it would not be able to be altered with the "\n" appendage. Therefore, the method would return false because status == :peace
Symbol version:
status = :peace
buggy_logger = status
print "Status: "
#print buggy_logger << "\n" # This is no longer possible. It will cause an error
def launch_nukes?(status)
unless status == :peace
return true
else
return false
end
end
print "Nukes Launched: #{launch_nukes?(status)}\n" # <- Returns FALSE
Related
When someone types "!disconnect", I want the bot to disconnect, using the response "PART ##{CHANNEL}".
The snippet below is not the full code, but there are end statements and everything.
CHANNEL = "SomeChannelHere"
prefix = "!"
message = "!disconnect"
commands = [
"disconnect" => "PART ##{CHANNEL}"]
commands.each do |command|
if message.include?(prefix + command)
response = commands[command]
How do I get the response?
Your commands is currently an array of hashes:
commands = ["disconnect" => "PART ##{CHANNEL}"]
#=> [{"disconnect"=>"PART #SomeChannelHere"}]
You have to use { ... } instead of [ ... ]:
commands = {"disconnect" => "PART ##{CHANNEL}"}
#=> {"disconnect"=>"PART #SomeChannelHere"}
Furthermore, the each block expects two arguments (key and value):
commands.each do |command, response|
# ...
end
You are missing the key of the hash:
commands.each do |command|
p command["disconnect"]
p command.has_key?("disconnect")
end
I am making a Ruby REPL to be used inside an application. I made code:
a = 1
b = 2
currentScope = []
Kernel.local_variables.each do |var|
currentScope << [var,Kernel.eval(var.to_s)]
end
launchREPL(currentScope)
Inside the REPL, I can execute the following code:
#a #=>1
#a+#b #=>3
Ideally I wouldn't have to write the four lines of code before I launch the REPL, and instead I would like to run them inside the launchREPL function. However this would require access to the previous scope from inside the launchREPL function.
Test1
Most notably I tried:
launchREPL(Kernel)
When I do the following:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
it is apparent that this method is not valid.
Test2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
Is there any way to do what I'm trying to do?
Edit: P.S. This is currently the code inside launchREPL:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "##{varDef[0].to_s}" , varDef[1]
__b.eval("##{varDef[0].to_s} = __b.instance_variable_get(:##{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:#pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
Although what you are trying to achieve is unclear and there are definitely many ways to do it properly, every ruby method might be called with Object#send approach:
def launchREPL(scope)
scope.send :local_variables #⇒ here you go
end
a = 42
launchREPL(binding).include?(:a)
#⇒ true
Sidenote: this is how your “4 lines” are usually written in ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
And this is how they should be written (note Binding#local_variable_get):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
The summing up:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#⇒ 42
This won’t fit the comment, so I would post it as an answer.
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "##{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * #a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
This is how your function should be written (I have just make it looking as ruby code.) The above works (currently it has no break at all, but you can see as it’s calculating 4200 infinitely.)
I'm trying to make nagios check for BBU (Battery Backup Unit), I have this ruby script called check_bbu
#!/usr/bin/ruby
require 'json'
output = %x{/usr/sbin/storcli /c0/bbu show j}
begin
j = JSON.parse(output)
result = j["Controllers"][0]["Command Status"]["Status"]
status = j["Controllers"][0]["Response Data"]["BBU_Info"][0]["State"]
rescue Exception => e
puts "CRITICAL: error reading BBU status: #{e}"
exit 2
end
if result != 'Success'
puts "CRITICAL: command not successful, result: #{result}"
exit 2
end
if status != 'Optimal'
puts "CRITICAL: BBU not optimal, status is #{status}"
exit 2
end
puts "OK: BBU is optimal"
But when I run this plugin I'm getting following error,
$ ./check_nrpe -u -t 30 -H foo.example.com -c check_bbu
CRITICAL: error reading BBU status: undefined method `[]' for nil:NilClass
What am I doing wrong in this ?
Seems your code break at
j = JSON.parse(output)
result = j["Controllers"][0]["Command Status"]["Status"]
status = j["Controllers"][0]["Response Data"]["BBU_Info"][0]["State"]
you should check which line
j == nil, and your want j["Controllers"]
result = j["Controllers"][0]["Command Status"]["Status"]
status = j["Controllers"][0]["Response Data"]["BBU_Info"][0]["State"]
cause undefined method '[]' for nil:NilClass by display j value first, and make sure json format match your code.
j = JSON.parse(output)
p j # display value
update:
your json format should match your code, otherwise it will raise error.
for example:
{
"Controllers": [
{
"Commandddd Status": { # format not match
"Status": "success"
}
}
]
}
# code and json format not match
j["Controllers"][0]["Command Status"] # nil
j["Controllers"][0]["Command Status"]["Status"]
=> nil["Status"] # boom!
For example to my comment, lets imagine that we have correct JSON:
j = {"Controllers" => [{"Comand Status" => {"status" => 'ok'}}]}
=> {"Controllers"=>[{"Comand Status"=>{"status"=>"ok"}}]}
so by typing your first line, it's should return correct result:
> j["Controllers"][0]["Comand Status"]["status"]
=> "ok"
But also you can get wrong JSON, for example:
j_e = {"Controllers" => []}
so now it's return error:
>j_e["Controllers"][0]["Comand Status"]["status"]
NoMethodError: undefined method `[]' for nil:NilClass
to solve it, you can use something like:
def try data, keys # there we take arguments: data == j, and keys == array with keys
return data if data.nil? # there we check: if data, that we send == nil, for example j = nil, array = ['first', etc..] we should stop this method and return nil as result
value = data[keys.shift] # on this line we try to get some data from j ##keys.shift will delete first element from array that we send to this method and return as `key` for `data`, for example data['Controllers'] so keys now will looks like: [0, 'Comand Status', 'status']
unless keys.empty? #on this line we check if on the pred line we was used the last key and keys now looks like: [] we: *return value and if it's not empty we just **call this method 1 more time
try(value, keys) #**value = [{"Comand Status" => {"status" => 'ok'}}] and keys = [0, 'Comand Status', 'status']
else
value #*nil or value
end
end
j = {"Controllers"=>[{"Comand Status"=>{"status"=>"ok"}}]}
try(j, ['Controllers', 0, 'Comand Status', 'status'])
>'ok'
try j, ['Controllers', 1, 'Comand Status', 'status']
> nil
in you code this should looks like:
require 'json'
def try data, keys
return data if data.nil?
value = data[keys.shift]
unless keys.empty?
try(value, keys)
else
value
end
end
output = %x{/usr/sbin/storcli /c0/bbu show j}
begin
j = JSON.parse(output)
result = try(j, ["Controllers", 0, "Command Status", "Status"])
status = try(j, ["Controllers", 0, "Response Data", "BBU_Info", 0, "State"])
rescue Exception => e
puts "CRITICAL: error reading BBU status: #{e}"
exit 2
end
if result != 'Success'
puts "CRITICAL: command not successful, result: #{result}"
exit 2
end
if status != 'Optimal'
puts "CRITICAL: BBU not optimal, status is #{status}"
exit 2
end
puts "OK: BBU is optimal"
Also, for Ruby 2.3.0+
it's much easier, just:
j.dig("Controllers", 0, "Comand Status", "status")
I'm following this tutorial to learn about creating shapes and colors on a canvas. Here is the issue I'm running into: When I try to run the command in the run_command method and I take the first letter of my command (command[0]), it is returning the number 98 to me. I am trying to match the first letter of the command to a letter of the alphabet, but am unable to do so. What's strange though, is that when I remove the first letter with "command.delete "b"", the letter is removed and I'm free to use the rest of the string as I please.
Here is my code:
require 'ruby-processing'
class ProcessArtist < Processing::App
def setup
background(0, 0, 0)
end
def draw
# Do Stuff
end
def key_pressed
if #queue.nil?
#queue = ""
end
if key != "\n"
#queue = #queue + key
else
warn "Time to run the command: #{#queue}"
run_command(#queue)
#queue = ""
end
end
def run_command(command)
puts "Running command: #{command}"
puts command[0]
if command[0] == "b"
command.delete "b"
command.split(",")
background(command[0].to_i,command[1].to_i,command[2].to_i)
else
puts command[0]
command.delete "b"
command.split(",")
background(command[0].to_i,command[1].to_i,command[2].to_i)
end
end
end
ProcessArtist.new(:width => 800, :height => 800,
:title => "ProcessArtist", :full_screen => false)
Ah, I see what I did wrong. It should have been:
def run_command(command)
puts "Running command: #{command}"
puts command[0]
if command[0] = "b"
command.delete "b"
command.split(",")
background(command[0].to_i,command[1].to_i,command[2].to_i)
else
puts command[0]
command.delete "b"
command.split(",")
background(command[0].to_i,command[1].to_i,command[2].to_i)
end
end
It seems like you're using ruby version older than 1.9.
In old version of ruby (1.8-), String#\[\] return Fixnum object representing ASCII value, not String object.
>> RUBY_VERSION
=> "1.8.7"
>> 'bcd'[0]
=> 98
To get string back, use one of followings:
>> 'bcd'[0,1]
=> "b"
>> 'bcd'[0..0]
=> "b"
>> 'bcd'[0].chr # this will not work in Ruby 1.9+, so not recommended.
=> "b"
For comparison:
>> 'bcd'[0] == 'b'
=> false
>> 'bcd'[0] == ?b
=> true
>> 'bcd'.start_with? 'b'
=> true
I am just learning ruby and this seems to be an easy mistake I am doing here right?
def palindromic(str)
str.to_s
if str.reverse == str
puts "it is a palindromic number!"
end
end
palindromic(500)
Instead I am getting an error
Project4.rb:5:in `palindromic': undefined method `reverse' for 500:Fixnum (NoMet
hodError)
from Project4.rb:10:in `<main>'
You need to change the line str.to_s to str=str.to_s. One example to show you why so is below :
num = 12
num.to_s # => "12"
num # => 12
num=num.to_s
num # => "12"
Basically String#to_s change the receiver instance to the instance of String.But if the receiver is already the String instance,in that case receiver itself will be returned.
ar = [1,2]
ar.object_id # => 77603090
ar.to_s.object_id # => 77602480
str = 'Hello'
str.object_id # => 77601890
str.to_s.object_id # => 77601890