i have a class named Foo, which uses pipe-in to read input from the command line, and it works well. i have another class named Bar, which invokes Foo, and has to feed (pipe-in) Foo in the same manner that Foo expects, but it does not seem to work for me.
see my snippet below.
i would appreciate any help.
note:
i know i can avoid doing so by passing object data from Bar to Foo, but i would like to use the pipes.
$ ls -x1
bar.rb
foo.rb
test.rb
$ cat *
# bar.rb
require "stringio"
class Bar
def self.pipe
input = StringIO.new
input.write "bar"
input.rewind
$stdin = input
Foo.print
$stdin = STDIN
end
end
# foo.rb
class Foo
##stdin = STDIN.tty? ? nil : $stdin.read #ok for cli pipe-in
def self.print
puts "stdin: #{##stdin}"
end
end
# test.rb
$:.unshift File.join(File.dirname(__FILE__))
require "foo"
require "bar"
Bar.pipe
$ echo "piped" | ruby test.rb
stdin: piped
$ ruby test.rb
stdin:
what is being done wrong, and why? a solution would be great.
i found my error. i modifying Foo as follow, but i broke its functionality.
$ cat foo.rb
# foo.rb
class Foo
def self.print
##stdin = STDIN.tty? ? nil : $stdin.read #ok for cli pipe-in
puts "stdin: #{##stdin}"
end
end
$ echo "piped" | ruby test.rb
stdin: bar
Related
I'm trying to create a command line program with sub-commands using OptionParser. I'm following "ruby's OptionParser to get subcommands".
The problem is that it does not allow for a use case like this:
ruby main.rb --version
#=> main.rb in `<main>': undefined method `order!' for nil:NilClass (NoMethodError)
But it does allow for this:
ruby main.rb foo --options
ruby main.rb --options foo
ruby main.rb --options foo --options
How would I be properly handle command line arguments, in the case that no subcommand is given.
My example code is:
global = OptionParser.new do |opts|
opts.banner = "Usage: opt.rb [options] [subcommand [options]]"
opts.on("-v", "--version", "Print the version") do |v|
options[:version] = v
end
opts.separator ""
opts.separator subtext
end
The lines with the error:
global.order!
command = ARGV.shift
subcommands[command].order!
If global.order! uses all of ARGV, then command is nil. So... check for that.
global.order!
command = ARGV.shift
unless command
STDERR.puts "ERROR: no subcommand"
STDERR.puts global # prints usage
exit(-1)
end
subcommands[command].order!
Maybe this'll help:
require 'optparse'
VERSION = '1.0.0'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--foo', 'Foo it') { |o| options[:foo] = o }
opt.on_tail('-v', '--version') do
puts VERSION
exit
end
end.parse!
puts options
Saving it as "test.rb" and running it with ruby test.rb returns:
{}
Running it with ruby test.rb -f or --foo returns:
{:foo=>true}
Running it with ruby test.rb -v or --version returns:
1.0.0
For more fun, running ruby test.rb -h or --help returns:
Usage: test [options]
-f, --foo Foo it
even though I didn't define -h or --help.
If I wanted the -v and --version flags to appear in the list then I'd change them from a on_tail method to a normal on method:
require 'optparse'
VERSION = '1.0.0'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--foo', 'Foo it') { |o| options[:foo] = o }
opt.on('-v', '--version', 'Returns the version') do
puts VERSION
exit
end
end.parse!
puts options
which would return:
Usage: test [options]
-f, --foo Foo it
-v, --version Returns the version
I can add:
puts ARGV
to the end of the script and see that OptionParser is correctly handling flags and parameters:
>ruby test.rb bar --foo
{:foo=>true}
bar
>ruby test.rb --foo bar
{:foo=>true}
bar
See "Pass variables to Ruby script via command line" for more information.
There is no way your example code will handle your sample inputs using --options. No handler for --options is defined. Nor is subtext. Your code returns:
undefined local variable or method `subtext' for main:Object (NameError)
Stripping the block to:
global = OptionParser.new do |opts|
opts.on("-v", "--version", "Print the version") do |v|
options[:version] = v
end
end
and running again returns:
invalid option: --options (OptionParser::InvalidOption)
So, again, your example doesn't match the results you say you're getting.
I'm a beginner in programming and wrote this little program:
Test.rb:
# encoding: utf-8
require 'open-uri'
require 'nokogiri'
def parse_file
doc = Nokogiri::XML(File.open("test.xml"))
parse_xml(doc)
end
def parse_xml(doc)
doc.root.elements.each do |node|
parse_tracks(node)
end
end
def parse_tracks(node)
if node.node_name.eql? 'kapitel'
puts 'New Kapitel'
end
end
I know how to execute this code:
ruby test.rb
But how can I call the def parse_file?
Simply add whatever you want to the end of your file. Ruby scripts are simply scripts, they are being interpreted:
…
end
parse_file # ⇐ HERE YOU GO
You can either call the method at the end of your test.rb file:
def parse_file
# ...
end
parse_file
And run it with
$ ruby test.rb
Or leave the file as it is, require it as a library and call the method manually:
$ ruby -r test.rb -e "parse_file"
Rather than hard-coding your file path, you can pass it as an argument when calling your script. Arguments can be accessed via the ARGV array:
def parse_file(file)
doc = Nokogiri::XML(File.open(file))
parse_xml(doc)
end
parse_file(ARGV.first)
Now you can run it with:
$ ruby test.rb test.xml
Another option is to make the script executable. Add a shebang as the first line of you file:
#!/usr/bin/env ruby
And set the execute flag:
$ chmod +x test.rb
Now you can run it with:
$ ./test.rb test.xml
just add
parse_file
in the end of your ruby file
# code.rb
def hello
puts "hello"
end
:$ ruby code.rb
Nothing is output on the console! I am using Ubuntu 13.04.
If I run the same code in IRB it works!
You have to call your code, you're just defining a method:
# code.rb
def hello
puts "hello"
end
hello
$ ruby code.rb
You define a method, but you're never calling it. Try this:
# code.rb
def hello
puts "hello"
end
hello
Run it:
:$ ruby code.rb
You need to call the method, in this case, hello in the script:
def hello
puts "hello"
end
hello
In my executable Ruby file I have the following:
#!/usr/bin/env ruby
require 'thor'
include Thor::Actions
class UI < Thor
# def self.source_root
# File.dirname(__FILE__)
# end
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcalmakecal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
In the terminal when I run the file as is I get an empty line as Thor's run command is returning a NilClass.
However, when I un-comment the puts `ls ~` and comment out Thor's run method I get an output of my home directory as expected.
I'm having trouble figuring out why I can't get Thor's run method to work like Ruby's ticks.
Any ideas where I may have went wrong?
Thanks for looking
I didn't put the include statement inside my class and that messed things up. The code should be:
#!/usr/bin/env ruby
require 'makecal'
class UI < Thor
include Thor::Actions
# def self.source_root
# File.dirname(__FILE__)
# end
#
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
Thor's documentation on this method is actually wrong and incomplete. It documents that it returns the "contents of the command" (which I assume means the standard output), but it, by defualt, does nothing.
But, you can, apparently, use the :capture option to get what you want:
unless options[:pretend]
config[:capture] ? `#{command}` : system("#{command}")
end
So, try doing
puts run("ls ~", :capture => true)
And see if that does it.
How do you check if a Ruby file was imported via "require" or "load" and not simply executed from the command line?
For example:
Contents of foo.rb:
puts "Hello"
Contents of bar.rb
require 'foo'
Output:
$ ./foo.rb
Hello
$ ./bar.rb
Hello
Basically, I'd like calling bar.rb to not execute the puts call.
Change foo.rb to read:
if __FILE__ == $0
puts "Hello"
end
That checks __FILE__ - the name of the current ruby file - against $0 - the name of the script which is running.
if __FILE__ != $0 #if the file is not the main script which is running
quit #then quit
end
Put this on top of all code in foo.rb
For better readability you can also use $PROGRAM_NAME
if __FILE__ == $PROGRAM_NAME
puts "Executed via CLI #{__FILE__}"
else
puts 'Referred'
end
More info: What does __FILE__ == $PROGRAM_NAME mean in ruby?