Use a command line option to call a method - ruby

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).

Related

How to require one of two possible options in a Ruby script?

I'm trying to configure a couple options in a Ruby script using OptionParser. I'd like them both to be optional but require that at least one is used. One option would allow for a single value to be passed from the command line. The other option will tell the script to use a file containing multiple values to be iterated over.
This is all I've been able to come up with:
options = {}
parser = OptionParser.new do |opts|
opts.banner = "Usage: sat_server_delete.rb [options]"
opts.on('-f', '--file file', 'File') do |file|
options[:file] = file;
end
opts.on('-s', '--server server', 'Server') do |server|
options[:server] = server
end
end.parse!
I haven't worked out the logic each will use yet. Right now I'd be happy to just see an error indicating that neither have been provided. So far I'm not getting even that. The script simply runs to completion and returns to a prompt.
I looked at the Ruby Doc for OptionParser, but It isn't clear to me how to accomplish what I'm trying. Is it even possible to have an either/or situation with options? I'm not even sure if what I already have makes sense. I'm basically just attempting to copy the logic without fully understanding what it is doing.
You can check the options hash after the parse! as in
if options[:file] && options[:server]
puts "may have only one of -f and -s"
exit
elsif !options[:file] && !options[:server]
puts "must have at least one of -f and -s"
exit
end

Call ruby function from command-line

How can I directly call a ruby function from the command-line?
Imagine, I would have this script test.rb:
class TestClass
def self.test_function(some_var)
puts "I got the following variable: #{some_var}"
end
end
If this script is run from the command-line (ruby test.rb), nothing happens (as intended).
Is there something like ruby test.rb TestClass.test_function('someTextString')?
I want to get the following output: I got the following variable: someTextString.
First the name of the class needs to start with a capital letter, and since you really want to use a static method, the function name definition needs to start with self..
class TestClass
def self.test_function(someVar)
puts "I got the following variable: " + someVar
end
end
Then to invoke that from the command line you can do:
ruby -r "./test.rb" -e "TestClass.test_function 'hi'"
If you instead had test_function as an instance method, you'd have:
class TestClass
def test_function(someVar)
puts "I got the following variable: " + someVar
end
end
then you'd invoke it with:
ruby -r "./test.rb" -e "TestClass.new.test_function 'hi'"
Here's another variation, if you find that typing ruby syntax at the command line is awkward and you really just want to pass args to ruby. Here's test.rb:
#!/usr/bin/env ruby
class TestClass
def self.test_function(some_var)
puts "I got the following variable: #{some_var}"
end
end
TestClass.test_function(ARGV[0])
Make test.rb executable and run it like this:
./test.rb "Some Value"
Or run it like this:
ruby test.rb "Some Value"
This works because ruby automatically sets the ARGV array to the arguments passed to the script. You could use ARGV[0] or ARGV.first to get the first argument, or you could combine the args into a single string, separated by spaces, using ARGV.join(' ').
If you're doing lots of command-line stuff, you may eventually have a use for Shellwords, which is in the standard ruby lib.
If you have multiple arguments to call in a example like this:
class TestClass
def self.test_function(some_var1, some_var2)
puts "I got the following variables: #{some_var1}, #{some_var2}"
end
end
run it like this (the arguments need to be comma separated in this case)
ruby -r "./test.rb" -e "TestClass.new.test_function 'hi','Mike'"
#!/usr/bin/env ruby
class A
def run
p :Hello_world
end
self
end.new.run
The usual way to script Ruby is to just use the top level execution environment called main. You can just start defining methods and code you write outside of a class, and these will be executed directly. (BTW, code inside a class but outside any method will run "by itself" also.)
Anyway, I'm with you ... I like writing all code in a named class and instantiating that class, so you can combine the techniques .. have the class return its own object .. and then use just a little of that top level code to allocate, initialize, and then dot into the class.
With this, you can just type $ ruby test.rb and it will do what you want. Or you can chmod +x test.rb; ./test.rb since we did add a shebang.
If you are working on a command line interface, then I would suggest to have a look at thor.
Thor directly maps your commands to methods within the defined class, see the thor wiki for an example.
Just an extension to Ingenu's answer for the case that the function does not print something out, but does return something.
We would have the following test.rb
class TestClass
def self.test_function(some_var)
return "I got the following variable: " + some_var
end
end
Then to invoke that from the command line and get the return value:
ruby -r "./test.rb" -e "puts TestClass.test_function('hi')"
If you know that how to call an rb file from commandline
ruby yourfile.rb
This can do the whole trick for you.
What you have done is, just defined your methods in the class. Now you can call it below the definition. If you still want to read, wide open your eyes
class TestClass
def self.test_function(some_var)
puts "I got the following variable: #{some_var}"
end
test_function(var)
end

Is there a "main" method in Ruby like in C?

I'm new to Ruby, so apologies if this sounds really silly.
I can't seem to figure out how to write a "main" code and have methods in the same file (similar to C). I end up with a "main" file which loads a seperate file that has all the methods. I appreciate any guidance on this.
I spotted the following SO post but I don't understand it:
Should I define a main method in my ruby scripts?
While it's not a big deal, it's just easier being able to see all the relevant code in the same file. Thank you.
[-EDIT-]
Thanks to everyone who responded - turns out you just need to define all the methods above the code. An example is below:
def callTest1
puts "in test 1"
end
def callTest2
puts "in test 2"
end
callTest1
callTest2
I think this makes sense as Ruby needs to know all methods beforehand. This is unlike C where there is a header file which clearly list the available functions and therefore, can define them beneath the main() function
Again, thanks to everyone who responded.
#Hauleth's answer is correct: there is no main method or structure in Ruby. I just want to provide a slightly different view here along with some explanation.
When you execute ruby somefile.rb, Ruby executes all of the code in somefile.rb. So if you have a very small project and want it to be self-contained in a single file, there's absolutely nothing wrong with doing something like this:
# somefile.rb
class MyClass
def say_hello
puts "Hello World"
end
end
def another_hello
puts "Hello World (from a method)"
end
c = MyClass.new
c.say_hello
another_hello
It's not that the first two blocks aren't executed, it's just that you don't see the effects until you actually use the corresponding class/method.
The if __FILE__ == $0 bit is just a way to block off code that you only want to run if this file is being run directly from the command line. __FILE__
is the name of the current file, $0 is the command that was executed by the shell (though it's smart enough to drop the ruby), so comparing the two tells you precisely that: is this the file that was executed from the command line? This is sometimes done by coders who want to define a class/module in a file and also provide a command-line utility that uses it. IMHO that's not very good project structure, but just like anything there are use cases where doing it makes perfect sense.
If you want to be able to execute your code directly, you can add a shebang line
#!/usr/bin/env ruby
# rest of somefile.rb
and make it executable with chmod +x somefile.rb (optionally rename it without the .rb extension). This doesn't really change your situation. The if __FILE__ == $0 still works and still probably isn't necessary.
Edit
As #steenslag correctly points out, the top-level scope in Ruby is an Object called main. It has slightly funky behavior, though:
irb
>> self
=> main
>> self.class
=> Object
>> main
NameError: undefined local variable or method `main' for main:Object
from (irb):8
Don't worry about this until you start to dig much deeper into the language. If you do want to learn lots more about this kind of stuff, Metaprogramming Ruby is a great read :)
No there isn't such structure. Of course you can define main function but it won't be called until you do so. Ruby execute line by line so if you want to print 'Hello World' you simply write:
puts 'Hello World'
The question that you mentioned is about using one file as module and executable, so if you write
if __FILE__ == $0
# your code
end
It will be called only if you run this file. If you only require it in other file then this code will never run. But IMHO it's bad idea, better option is using RubyGems and there add executables.
Actually there is a main, but it is not a method; it's the top-level object that is the initial execution context of a Ruby program.
class Foo
p self
end
#=> Foo
p self
#=> main
def foo
p self
end
foo
#=> main
There is no magic main function in Ruby. See http://en.wikipedia.org/wiki/Main_function#Ruby
If you wish to run Ruby scripts like C compiled files, do the following:
#!/usr/bin/env ruby
puts "Hello"
and then chmod a+x file_name.rb. Everything that is below the first line will be run, as if it was contents of main in C. Of course class and function definitions won't give you any results until they are instantiated/invoked (although the code inside class definitions is actually evaluated, so you could get some output but this is not expected in normal circumstances).
Another way to write main() method is:
class HelloWorld
def initialize(name)
#name = name
end
def sayHello()
print "Hello ##name!"
end
end
def main()
helloWorld = HelloWorld.new("Alice")
helloWorld.sayHello
end
main

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")

Require file without executing code?

Here I have two files:
file.rb
def method
puts "This won't be outputted."
end
puts "This will be outputted."
main.rb
require "./file"
When running main.rb it will load all the code inside file.rb so I will get "This will be outputted." on the screen.
Is it possible to load a file without having it to run the code?
Cause I want to load all the methods (in modules and classes too) without having to execute code outside these scopes.
Is it possible to load a file without having it to run the code?
No, everything in a ruby file is executable code, including class and method definitions (you can see this when you try to define a method inside an if-statement for example, which works just fine). So if you wouldn't execute anything in the file, nothing would be defined.
You can however tell ruby that certain code shall only execute if the file is run directly - not if it is required. For this simply put the code in question inside an if __FILE__ == $0 block. So for your example, this would work:
file.rb
def method
puts "This won't be outputted."
end
if __FILE__ == $0
puts "This will not be outputted."
end
main.rb
require "./file"
the if __FILE__ == $0 is nice, but a way more in keeping with ruby's Object Oriented approach is to put all the methods you want access to in a class (as class methods), and then call them from main.rb.
e.g.
file.rb
class MyUtils
def self.method
puts "this won't be outputted"
end
end
and then in main.rb
require "/.file.rb"
and when you want to use your utility methods:
MyUtils.method
I don't think modifying file is good idea - there are could be a lot of files like this one or these files belong to customer and a ton of another reasons.
Ruby is good at metaprogramming so why don't use this feature?
It could be like this.
Create file with fake module and put here the file.
File.open("mfile.rb","w") do |f|
f.write "module FakeModule
"
f.write File.open("file.rb").read
f.write "
end"
end
Then load this file:
require "/.mfile.rb
and accessing to the method:
FakeModule::method

Resources