In Ruby, is there a library to make command line scripts more user friendly, prompting me what to run? - ruby

So what I do now is my Ruby scripts have a bunch of methods, and I have to comment out which method calls I don't want to call, and un-comment the one I want to fire in my command line script.
Is there a Ruby library that makes it easy to prompt the user to which function to fire?

If you want code that will selectively execute a particular method in a source file, you'll need to write a dispatch table to take the input and call whichever routine is desired.
It's all pretty standard stuff for a programmer and easily done with OptionParser and some creative use of case/when statements.

You can use ARGV to iterate over your program's arguments. There is also Getopt::Declare, which is a library for dealing with command line arguments in a similar fashion to traditional Unix utilities.

If you need something that would really prompt user during the execution of the script, you can wrap the methods you need to prompt about:
module Confirmable
def confirm_first *methods
methods.each do |meth|
alias_method "orig_#{meth}", meth
define_method meth do |*args, &block|
print "Execute #{meth}?[Yn] "
s = gets.chomp
return if s.downcase == 'n'
send "orig_#{meth}", *args, &block
end
end
end
end
class MyClass
extend Confirmable
def foo arg
puts "starting foo"
puts arg
end
confirm_first :foo
end
c = MyClass.new
c.foo "bar"
This way, each method that you mark with confirm_first will first ask you for confirmation before actually executing.

Related

Use a command line option to call a method

So I guess this is kind of related to my last question, but I was wondering if there was a way to call a method by using a command line option. Say you had a method like this:
def b
puts "Hello brian"
end
is there a way to write something like this:
ruby mine.rb -b
and get this
Hello brian
I already tried looking for this online and discovered OptionParser but I have yet to discover anything involving the OptionParser calling a method previously created.
There are a lot of ways to do this, depending on the use case. The below code is taken from the Ruby docs with the extra method added.
Realistically you'd probably want a class that handles the different options and encapsulates the method instead of having them at the file scope.
#!/usr/bin/env ruby
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-b", "Run method 'b'") do |v|
options[:b] = true
end
end.parse!
def b
puts "Hello Brian"
end
if options[:b]
b
end
I've also added a shebang at the top that will automatically call ruby. As long as your script file is executable you can call it directly (ie. ./mine.rb -b).

A ruby script to run other ruby scripts

If I want to run a bunch of ruby scripts (super similar, with maybe a number changed as a commandline argument) and still have them output to stdout, is there a way to do this?
i.e a script to run these:
ruby program1.rb input_1.txt
ruby program1.rb input_2.txt
ruby program1.rb input_3.txt
like
(1..3).each do |i|
ruby program1.rb input_#{i}'
end
in another script, so I can just run that script and see the output in a terminal from all 3 runs?
EDIT:
I'm struggling to implement the second highest voted suggested answer.
I don't have a main function within my program1.rb, whereas the suggested answer has one.
I've tried this, for script.rb:
require "program1.rb"
(1..6).each do |i|
driver("cmd_line_arg_#{i}","cmd_line_arg2")
end
but no luck. Is that right?
You can use load to run the script you need (the difference between load and require is that require will not run the script again if it has already been loaded).
To make each run have different arguments (given that they are read from the ARGV variable), you need to override the ARGV variable:
(1..6).each do |i|
ARGV = ["cmd_line_arg_#{i}","cmd_line_arg2"]
load 'program1.rb'
end
# script_runner.rb
require_relative 'program_1'
module ScriptRunner
class << self
def run
ARGV.each do | file |
SomeClass.new(file).process
end
end
end
end
ScriptRunner.run
.
# programe_1.rb
class SomeClass
attr_reader :file_path
def initialize(file_path)
#file_path = file_path
end
def process
puts File.open(file_path).read
end
end
Doing something like the code shown above would allow you to run:
ruby script_runner.rb input_1.txt input_2.txt input_3.txt
from the command line - useful if your input files change. Or even:
ruby script_runner.rb *.txt
if you want to run it on all text files. Or:
ruby script_runner.rb inputs/*
if you want to run it on all files in a specific dir (in this case called 'inputs').
This assumes whatever is in program_1.rb is not just a block of procedural code and instead provides some object (class) that encapsulates the logic you want to use on each file,meaning you can require program_1.rb once and then use the object it provides as many times as you like, otherwise you'll need to use #load:
# script_runner.rb
module ScriptRunner
class << self
def run
ARGV.each do | file |
load('program_1.rb', file)
end
end
end
end
ScriptRunner.run
.
# program_1.rb
puts File.open(ARGV[0]).read

Thor Executable - Ignore Task Name

The thor wiki page, Making an Exectable, shows you how to create a thor powered CLI command that looks something like this:
bash
./mythorcommand foo
This requires you to pass in the thor task foo as the first argument.
I can also run a thor executable without any arguments using thor's default_method:
bash
./mythorcommand
However, I'd like to pass in a variable string as the first argument:
bash
./mythorcommand "somevalue"
This doesn't work because thor commands expect the first argument to the be a task name. Is there a way to ignore the task name and send the first argument to a default method?
If this functionality doesn't exist, I think it would be very useful to add a method that would pass all commandline arguments into one task/method:
class MyThorCommand < Thor
only_method :default
def default(*args)
puts args.inpsect
end
end
MyThorCommand.start
You should extend from Thor::Group and that call start method
class Test < Thor::Group
desc "Act description"
def act
puts "do smth"
end
end
Test.start
I found a rather 'strange' solution for this problem that is working quite well with me.
You add a default task to Thor. Than you add the method_missing so that you can trick Thor into passing the default method as an argument if there are parameters to your application.
Taking from your example, the solution would look like this:
class MyThorCommand < Thor
default_task :my_default
desc "my_default", "A simple default"
def my_default(*args)
puts args.inspect
end
def method_missing(method, *args)
args = ["my_default", method.to_s] + args
MyThorCommand.start(args)
end
end
MyThorCommand.start(ARGV)
If this is in the file "my_thor.rb" an execution "ruby my_thor.rb foo bar" would show '["foo", "bar"]' as a result.
Hope it helps.
Though this does not exactly solve your problem, one alternative might be using Thor.map to invoke a command by only giving an option flag:
map '-F' => 'foo'
Now you can also pass parameters
mythorcommand -F bar # => invokes foo("bar")

Can I write Ruby code that is executed only when my script is run, but not when it is required?

I want to write a Ruby script something like this:
class Foo
# instance methods here
def self.run
foo = Foo.new
# do stuff here
end
end
# This code should only be executed when run as a script, but not when required into another file
unless required_in? # <-- not a real Kernel method
Foo.run
end
# ------------------------------------------------------------------------------------------
I want to be able to unit test it, which is why I don't want the code outside of the class to run unless I execute the script directly, i.e. ruby foo_it_up.rb.
I know I can simply put the Foo class in another file and require 'foo' in my script. In fact, that is probably a better way to do it, just in case Foo's functionality is needed somewhere else. So my question is more academic than anything, but I'd still be interested in knowing how to do this in Ruby.
This is usually done with
if __FILE__ == $0
Foo.run
end
but I prefer
if File.identical?(__FILE__, $0)
Foo.run
end
because programs like ruby-prof can make $0 not equal __FILE__ even when you use --replace-progname.
$0 refers to the name of the program ($PROGRAM_NAME), while __FILE__ is the name of the current source file.

Ruby - redefining instance methods not working

My simple attempt to redefine instance methods are not working
class File
alias_method :old_atime, :atime
def atime(*args)
puts "helllllo"
old_atime(*args)
end
end
f = File.new("C:\\abc.txt","w")
puts f.atime
Any idea why?
I'm attempting to print "helllllo" everytime File#atime is called. Even alias old_atime atime is not working.
Is there something I'm doing wrong here?
Above code works perfectly as it should be. Puts "helllllo" writes "helllllo" in to your opened file. Puts inside the file instance meant for writing.
Just call f.close and open your file in text editor. You can see the content.
Yep, Ramesh is right. Try this:
class File
alias_method :old_atime, :atime
def atime(*args)
Kernel.puts "helllllo" # <---- Kernel method
old_atime(*args)
end
end
f = File.new("C:\\abc.txt","w")
puts f.atime
The issue is that 'puts' is defined in File for writing to files. You want the Kernel one which is used unless defined in a more specific scope.
This should work fine, but IO#puts writes to the IO object itself, not STDOUT. In other words, it's writing to the file.
Call f.atime a few times and then f.close within irb and you should see it printing helllllo to the file for each call to atime.
To print to STDOUT, you could use $stdout.puts or Kernel.puts.

Resources