Best practices for hard-coded strings - ruby-on-rails

I am writing a ruby script where I read commands from command line and check if they are correct. If not I show the proportionate error.
My code looks like this:
if command == 0
puts "error one #{command}"
elsif command == 1
puts "other error two #{command}"
...
end
I have a lot of different error-strings and they have ruby code in it.
I was thinking to create a hash but I can not add ruby code in the error-string.
Is there any better way to manage (hard-coded) error-strings?

If the code is always going to be at the end, then this might work:
Errors = {
0 => "error one",
1 => "other error two",
}.freeze
# later...
command = 1
puts "#{Errors.fetch(command)} #{command}"
#=> other error two 1
Otherwise, you can add a custom placeholder and later substitute in the error code:
Errors = {
0 => "error %{code} one",
1 => "%{code} other error two",
}.freeze
def error_str_for_code(code)
Errors.fetch(code) % { code: code.to_s }
end
# later...
command = 1
puts error_str_for_code(command)
#=> 1 other error two

Related

Pass Params from select_tag in rails to one script.rb get this params

Hello how is it possible to pass web parameters to an rb script in rails?
I'm using select_tag where when choosing an option I send the value to a variable in script.rb
Could help me I'm studying ruby ​​and rails is very complicated for me to do this.
my controller.rb
#myvalue = ["OPT VALUE 1","OPT VALUE 2"]
my external script.rb
loop do
#give_my_param_from_rails = gets.chomp
case #give_my_param_from_rails
when '1'
puts "i get number 1"
when '2'
puts "i get number 2"
else
puts "dont get any value"
break
end
end
my html.erb
<%= select_tag "my_options", options_for_select(#myvalue) %>
In my external script I want the rails value selected in select_tag to be set in the #give_my_param_from_rails variable help me?
I've added $stdout.flush to the end of your script to flush the output for each line of input:
loop do
line = gets.chomp
case line
when '1'
puts "i get number 1"
when '2'
puts "i get number 2"
else
puts "dont get any value"
break
end
$stdout.flush # flush output after each line of input
end
To access the script in your controller you can now do:
# allow writing to io by using r+ mode
# https://ruby-doc.org/core-2.6.5/IO.html#method-c-new-label-IO+Open+Mode
File.popen('ruby /path/to/script.rb', 'r+') do |io|
io.puts params[:my_options] # assuming params[:my_options] is "1"
io.gets #=> "i get number 1\n"
io.puts 2
io.gets #=> "i get number 2\n"
io.puts "foo bar" # falling in the else scenario breaks the loop, exiting the script
io.gets #=> "dont get any value\n"
io.puts 1
io.gets #=> nil
io.puts "foo bar"
io.gets #=> nil
end
If you only need to supply one input you could use backticks instead:
input = params[:my_options] # assuming params[:my_options] is "foo bar"
# escape special characters in double quoted context
# https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
sanatized_input = input.gsub(/([$`\\"])/, '\\\\\1')
output = `echo "${sanatized_input}" | ruby /path/to/script.rb`
#=> "dont get any value\n"
Keep in mind that you don't handle the scenario where you supply 1 or 2 and then close the input. When this happens the next gets call will return nil.
`echo 1 | ruby /path/to/script.rb`
# script.rb:2:in `block in <main>': undefined method `chomp' for nil:NilClass (NoMethodError)
# from script.rb:1:in `loop'
# from script.rb:1:in `<main>'
#=> "i get number 1\n"
To resolve this you could do something like:
loop do
line = gets or break # break the loop if gets returns nil
line.chomp!
# ...
end

Ruby Telnet - Net::ReadTimeout: timed out while waiting for more data

I am trying to connect to printer using ruby telnet library. I can successfully connect to printer using telnet library but whenever I send the command using cmd keyword library returns the exception:
Net::ReadTimeout: timed out while waiting for more data
I am using below commands to connect to printer
localhost = Net::Telnet::new("Host" => "192.168.25.168","Port" => 20000, "Timeout" => 10)
status = ""
localhost.cmd("SELECTGROUP 1") {|c| status = c }
if status == "ok"
puts "success"
else
puts "failure"
end
The above localhost.cmd command returns string "ok" which I am expecting, or the above exception.
I want to know how to prevent cmd command from sending a timeout exception.
Raising an Exception is the standard behaviour for when a command "fails" - such as by reaching a timeout limit. What you are really looking to do, I believe, is gracefully handle the exception. You can do this by use of a rescue:
localhost = Net::Telnet::new("Host" => "192.168.25.168","Port" => 20000, "Timeout" => 10)
status = ""
begin
localhost.cmd("SELECTGROUP 1") {|c| status = c }
rescue Net::ReadTimeout
status = 'timeout'
end
if status == "ok"
puts "success"
else
puts "failure"
end
Note that Net::Telnet#cmd returns the received data as a string, so your above code could in fact be simplified to something like:
localhost = Net::Telnet::new("Host" => "192.168.25.168","Port" => 20000, "Timeout" => 10)
begin
if localhost.cmd("SELECTGROUP 1") == 'ok'
puts 'success'
else
puts 'failure'
end
rescue Net::ReadTimeout
puts 'timeout'
end

Not setting arguments properly in rails tasks?

I am curious as to why my args variable is always coming back as {} in the following task:
desc "Create an Api Key assuming one doesn't exist."
task :create_api_key, [:name] => :environment do | t, args |
if !ApiKey.find_by_application_name(args[:name])
binding.pry
if ApiKey.new(:application_name => args[:name], :api_key => SecureRandom.hex(32)).save!
puts "Your key is: " + ApiKey.find_by_application_name(args[:name]).api_key
else
puts "Could not create the api key, you might be missing an argument: Application Name."
end
else
puts "This application already contains an api key."
end
end
The following is a run of the task (Note the binding.pry):
$ bin/rake create_api_key "xaaron_test"
From: /Users/Adam/Documents/Rails-Projects/BlackBird/lib/tasks/create_api_key.rake # line 4 :
1: desc "Create an Api Key assuming one doesn't exist."
2: task :create_api_key, [:name] => :environment do | t, args |
3: if !ApiKey.find_by_application_name(args[:name])
=> 4: binding.pry
5: if ApiKey.new(:application_name => args[:name], :api_key => SecureRandom.hex(32)).save!
6: puts "Your key is: " + ApiKey.find_by_application_name(args[:name]).api_key
7: else
8: puts "Could not create the api key, you might be missing an argument: Application Name."
9: end
[1] pry(main)> args
=> {}
Even if I do bin/rake create_api_key xaaron_test I get the same issue. What is going on? is there some small mistake some where I forgot about?
Update
I also spit out t to see what was in there:
pry(main)> t
=> <Rake::Task create_api_key => [environment]>
You pass arguments to a task by enclosing them in [] directly after the task name.
e.g.
rake create_api_key[xaaron_test]
If you use zsh, you need to escape the opening [
e.g.
rake create_api_key\[xaaron_test]

How to make Rails add line numbers / time stamps to log messages?

I use tail -f to display the log file when developing my Rails app.
It shows the log messages (in color! :), which is great.
But with so much information in the 80-width console, it becomes difficult to track where a certain "set" of log messages started when, say, I clicked on a button to GET a resource.
It would be easier if there was a line number or even a time stamp at the start of each log message/line. This way I could remember that I need to start looking at the log "after line number 2365" or "after 2010/10/10 23:33:23:45".
Is this possible to do? Is there some Rails internal option for this ?
why don't you just edit your desired environment's log tags
development.rb
config.log_tags [ lambda {|r| DateTime.now } ]
If you wanted to get a time stamp:
class ApplicationController < ActionController::Base
# ...
before_filter :log_tracker
def log_tracker
Rails.logger.add(1, "Log Date: #{DateTime.now}")
end
end
And format the date however you see fit....
That would work for Rails 2.1 +, prior you could access the ActiveSupport::Buffered log object with the constant: RAILS_DEFAULT_LOGGER
Get access to the actual log file with Rails.logger.instance_values["log"]
Getting the number of lines is difficult because the logger only opens the file for writing, probably for economy. I get an IOError: not opened for reading when I try.
`
Thanks #scaney.
I found a solution here.
I modified that code to add my own coloring highlights (for development only of course!) and now I can see things like 'parameters' in yellow in the console and I'm very pleased now!
In case someone is interested, here is the code I put at the end of environment.rb.
Here is my current (dirty) implementation. Will probably fix this up later (maybe make a gem, but for now this serves me fine)
WARNING
DIRTY CODE FOLLOWS! Use at your own risk!
module ActiveSupport
class BufferedLogger
#define the ANSI escape codes for normal and bright colors
$my_my_ansi_colors = {
:normal => "\x1B[0m",
:black => "\x1B[30m",
:red => "\x1B[31m", #red
:green => "\x1B[32m",
:yellow => "\x1B[33m",
:blue => "\x1B[34m",
:magenta => "\x1B[35m",
:cyan => "\x1B[36m",
:white => "\x1B[37m",
:bred => "\x1B[1m\x1B[31m", #bright red
:bgreen => "\x1B[1m\x1B[32m",
:byellow => "\x1B[1m\x1B[33m",
:bblue => "\x1B[1m\x1B[34m",
:bmagenta => "\x1B[1m\x1B[35m",
:bcyan => "\x1B[1m\x1B[36m",
:bwhite => "\x1B[1m\x1B[37m",
}
#take a string and using the keys in the hash, replace the keys in the
#string but surround the keys with ANSI color codes
#No idea how to retain the case of the key!(TODO someday)
def my_highlight msgx,hash
return msgx if msgx.blank?
return msgx if hash.empty?
hash.each_pair do |k,v|
if not k.nil?
msgx.gsub! Regexp.new(k, Regexp::IGNORECASE), $my_my_ansi_colors[:normal]+$my_my_ansi_colors[v]+k.upcase+$my_my_ansi_colors[:normal]
end
end
msgx
end
def add(severity, message = nil, progname = nil, &block)
return if #level > severity
message = (message || (block && block.call) || progname).to_s
#INSERT BEGINS
if not $myownglobalnumbercounter.nil?
$myownglobalnumbercounter += 1
else
$myownglobalnumbercounter = 1
end
level = {
0 => "DEBUG",
1 => "INFO",
2 => "WARN",
3 => "ERROR",
4 => "FATAL"
}[severity] || "U"
message = "\x1B[0m[%d %s] : %s" % [$myownglobalnumbercounter,level,message]
message = my_highlight message, {
"debug" => :white,
"error" => :bred,
"info" => :bwhite,
"warning" => :byellow,
"warn" => :byellow ,
"parameters" => :byellow,
"#" => :bgreen,
"ms " => :bmagenta,
"GET " => :bmagenta,
"PUT " => :bmagenta,
"POST " => :bmagenta,
"DELETE " => :bmagenta
}
#INSERT ENDS
message = "#{message}\n" unless message[-1] == ?\n
buffer << message
auto_flush
message
end
end
end

Rails logger format string configuration

How can I configure the rails logger to output its log strings in another format? I would like to get something that is more informative like:
[Log Level] [Time] [Message]
Debug : 01-20-2008 13:11:03.00 : Method Called
This would really help me when I want to tail my development.log for messages that only come from a certain log level, like debug.
Did some digging and found this post in the RubyOnRails Talk google group.
So I modified it a little bit and put it at the end of my environment.rb:
module ActiveSupport
class BufferedLogger
def add(severity, message = nil, progname = nil, &block)
return if #level > severity
message = (message || (block && block.call) || progname).to_s
level = {
0 => "DEBUG",
1 => "INFO",
2 => "WARN",
3 => "ERROR",
4 => "FATAL"
}[severity] || "U"
message = "[%s: %s #%d] %s" % [level,
Time.now.strftime("%m%d %H:%M:%S"),
$$,
message]
message = "#{message}\n" unless message[-1] == ?\n
buffer << message
auto_flush
message
end
end
end
This results in a format string like this:
[DEBUG: 0121 10:35:26 #57078] Rendered layouts/_header (0.00089)
For rails 4 apps, I've put together a simple gem that not only adds support for basic tagging like time stamp and log level, but even adds color to the log messages themselves.
https://github.com/phallguy/shog
The problem with tags is that they clutter your logs to the point where they are unreadable.
I'd recommend something like timber. It automatically augments your logs with context (level, time, session id, etc) without sacrificing readability.
# config/initializers/rack_logger.rb
module Rails
module Rack
class Logger < ActiveSupport::LogSubscriber
# Add UserAgent
def started_request_message(request)
'Started %s "%s" for %s at %s by %s' % [
request.request_method,
request.filtered_path,
request.ip,
Time.now.to_default_s,
request.env['HTTP_USER_AGENT'] ]
end
end
end
end
source link

Resources