I am trying to deliver an exec file with my ruby gem but am not sure how to go about it.
I have a ruby script called pfparser like:
#!/usr/bin/env ruby
ENV['path'] = ARGV[0]
require 'pfparser'
in my pfparser.rb file I want to accept the parameter but it doesn't seem to pass over. The parameter should be a file path that the pfparser.rb file should then parse and return an output. I feel like I am not doing this correctly but I am not sure how to go about it.
This feels like overkill, I want to deliver one file that the user can then execute with a parameter.
I assume pfparser.rb contains a class definition, that handles all the stuff. So I would go with something like that:
lib/pfparser.rb
class PfParser
def initialize path
#path = path
end
def do_job
# change to real stuff
puts "Hi, path is: #{#path}"
end
end
bin/pfparser
#!/usr/bin/env ruby
require 'pfparser'
PfParser.new(ARGV[0]).do_job
And now:
$ bin/pfparser PATH
#⇒ Hi, path is: PATH
Related
I am creating a gem that is a Rack application, so I assume my application is going to be instantiated in a config.ru file. I expect certain paths to be relative to this config.ru file. So how can I get and set the path when the app is initialized?
For example:
Hidden away in my gem:
class MyApp
def initialize
#base_path = get_the_base_path_here
end
def call(env)
html = render_view(#base_path + '/views/index.erb')
end
end
User of the gem's config.ru:
require 'my_app'
run MyApp.new
...and their views directory:
/views
index.erb
Update:
One way to achieve this is to pass in the base path as an argument, but I would like to find a way to achieve this without passing it as an argument.
require 'my_app'
run MyApp.new(File.dirname(__FILE__))
Absolute Path of Current File
In general, you can simply use File.expand_path(__FILE__) to find the absolute path of the current file, which you can then store a variable or global if you like. For example:
$file_path = File.expand_path(__FILE__)
Absolute Path of Current Program
File.expand_path($0) is similar, but returns the program that was called. The distinction is sometimes subtle, but can be useful from time to time.
Creating an Absolute Path to a File in the Same Base Directory
If you want to use the directory name of the location of the current file to address another file, you can use File#join. For example:
File.join File.dirname(File.expand_path(__FILE__)), '.X11-unix'
=> "/tmp/.X11-unix"
Probably not the best way but you can find config.ru with:
$:.find{|path| File.exists? "#{path}/config.ru"}
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
I'm trying to create an application that will primarily consist of ruby scripts that will be run from the command-line (cron, specifically). I want to have a libs folder, so I can put encapsulated, reusable classes/modules in there, and be able to access them from any script.
I want to be able to put my scripts into a "bin" folder.
What is the best way to give them access to the libs folder? I know I can add to the load path via command-line argument, or at the top of each command-line script. In PHP, it sometimes made more sense to create a custom .ini file and point the cli to the ini file, so you got them all in one pop.
Anything similar for ruby? Based on your experience, what's the best way to go here?
At the top of each bin/executable, you can put this at the top
#!/usr/bin/env ruby
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')
require 'libfile'
[etc.]
Were you looking for something different?
If you turn your application into a Ruby gem and install the gem on your system, you don't even need to put this stuff at the top. The require statement would suffice in that case.
Sean,
There is no way to not have to require a library, that I know of. I guess if you want to personalize your Ruby so much you could "roll your own" using eval.
The script below basically works as the interpreter. You can add your own functions and include libraries. Give the file executable permissions and put it in /usr/bin if you really want. Then just use
$ myruby <source>
Here's the code for a very minimal one. As an example I've included the md5 digest library and created a custom function called md5()
#!/usr/bin/ruby -w
require 'digest/md5';
def executeCode(file)
handle = File.open(file,'r');
for line in handle.readlines()
line = line.strip();
begin
eval(line);
rescue Exception => e
print "Problem with script '" + file + "'\n";
print e + "\n";
end
end
end
def checkFile(file)
if !File.exists?(file)
print "No such source file '" + file + "'\n";
exit(1);
elsif !File.readable?(file)
print "Cannot read from source file '" + file + "'\n";
exit(1);
else
executeCode(file);
end
end
# My custom function for our "interpreter"
def md5(key=nil)
if key.nil?
raise "md5 requires 1 parameter, 0 given!\n";
else
return Digest::MD5.hexdigest(key)
end
end
if ARGV[0].nil?
print "No input file specified!\n"
exit(1);
else
checkFile(ARGV[0]);
end
Save that as myruby or myruby.rb and give it executable permissions (755). Now you're ready to create a normal ruby source file
puts "I will now generate a md5 digest for mypass using the md5() function"
puts md5('mypass')
Save that and run it as you would a normal ruby script but with our new interpreter. You'll notice I didn't need to include any libraries or write the function in the source code because it's all defined in our interpreter.
It's probably not the most ideal method, but it's the only one I can come up with.
Cheers
There is a RUBYLIB environment variable that can be set to any folder on the system
If you want to use your classes/modules globally, why not just move them to your main Ruby lib directory? eg: /usr/lib/ruby/1.8/ ?
Eg:
$ cat > /usr/lib/ruby/1.8/mymodule.rb
module HelloWorld
def hello
puts("Hello, World!");
end
end
We have our module in the main lib directory - should be able to
require it from anywhere in the system now.
$ irb
irb(main):001:0> require 'mymodule'
=> true
irb(main):002:0> include HelloWorld
=> Object
irb(main):003:0> hello
Hello, World!
=> nil
Is there a shorter way to require a file located in the same directory (as the script being executed)?
require File.expand_path(File.dirname(__FILE__) + '/some_other_script')
I read that require "my_script" and require "./my_script" will actually load the script twice (ruby will not recognize that it is actually the same script), and this is the reason why File.expand_path is recommended: if it is used every time the script is required, then it will only be loaded once.
It seems weird to me that a concise language like Ruby does not seem to have a shorter solution. For example, python simply has this:
import .some_other_module_in_the_same_directory
I guess I could monkey-patch require... but that's just evil! ;-)
Since ruby 1.9 you can use require_relative.
Check the latest doc for require_relative or another version of the Core API.
Just require filename.
Yes, it will import it twice if you specify it as filename and ./filename, so don't do that. You're not specifying the .rb, so don't specify the path. I usually put the bulk of my application logic into a file in lib, and then have a script in bin that looks something like this:
#!/usr/bin/env ruby
$: << File.join(File.dirname(__FILE__), "/../lib")
require 'app.rb'
App.new.run(ARGV)
Another advantage is that I find it easier to do unit testing if the loading the application logic doesn't automatically start executing it.
The above will work even when you're running the script from some other directory.
However, inside the same directory the shorter forms you refer to work as expected and at least for ruby 1.9 won't result in a double-require.
testa.rb
puts "start test A"
require 'testb'
require './testb'
puts "finish test A"
testb.rb
puts "start test B"
puts "finish test B"
running 'ruby testa.rb' will result in:
start test A
start test B
finish test B
finish test A
However, the longer form will work even from another directory (eg. ruby somedir/script.rb)
Put this in a standard library directory (somewhere that's already in your default loadpath $:):
# push-loadpath.rb
if caller.first
$: << File.expand_path(File.dirname(caller.first))
end
Then, this should work
% ls /path/to/
bin.rb lib1.rb lib2.rb #...
% cat /path/to/bin.rb
load 'push-loadpath.rb'
require 'lib1'
require 'lib2'
#...
caller gives you access to the current callstack, and tells you what file and where, so push-loadpath.rb uses that to add the file that load'd it to the loadpath.
Note that you should load the file, rather than require it, so the body can be invoked multiple times (once for each time you want to alter the loadpath).
Alternately, you could wrap the body in a method,
# push-loadpath.rb
def push_loadpath
$: << File.expand_path(File.dirname(caller.first))
end
This would allow you to require it, and use it this way:
% ls /path/to/
bin.rb lib1.rb lib2.rb #...
% cat /path/to/bin.rb
require 'push-loadpath'
push_loadpath
require 'lib1'
require 'lib2'
#...
In ruby, if I do "require foo", is there a way to subsequently determine the name of the module or modules defined in foo.rb?
For example, say I have a ruby file named foo.rb that looks like this:
# foo.rb
module MyModule
def self.summary
"this does something useful"
end
...
end
In another script, after I do "require foo", how can I determine that I now have a module named MyModule?
Ultimate what I'm after is to be able to do something like this:
file = someComputedFileName()
require file
puts "summary of #{file}: #{???::summary}
While I could force myself to make module names and file names identical, I'd rather not. I want a bit more freedom in making short filenames but more expressive module names. However, I am guaranteeing to myself that each file will only define a single module (think: plugins).
I don't know if this is the best solution, but off the top of my head this seems to work:
all_constants = Object.constants
require 'foo'
foo_constants = Object.constants - all_constants
foo_constants should give you only the modules, classes or other constants that were defined by foo.rb.
One approach would be to use ObjectSpace.each_object(Module) to find all defined modules before requiring the file. Then, after you require the file, loop over all defined modules again and see if there are any new ones.
require "set"
old_modules = SortedSet.new
ObjectSpace.each_object(Module) {|m| old_modules.add(m) }
file = someComputedFileName()
require file
new_modules = SortedSet.new
ObjectSpace.each_object(Module) {|m| new_modules.add(m) unless old_modules.include?(m) }
puts "summary of #{file}: #{new_modules.to_a.map{|m|m.summary}.join(',')}"
That would also let you define more than one module in the file.