Strategies for writing Ruby applications to be easily distributed with RubyGems

Hello,

This is mostly a repost of ruby-talk[229723] with some clarifications
and an appeal for ideas.

I have recently attempted to write a RubyGem for the Saikuro project.
Unfortunately, I have learned that there is a mismatch between how I
wrote Saikuro and how RubyGems expects executables to work.

I would like to know is are there any better or commonly followed ways
of writing executables that work with RubyGems and other installation
programs such as setup.rb ?

Below are the differences between how I wrote Saikuro and what
RubyGems does.

1)
Saikuro uses the "if __FILE__ == $0" check before doing the
"application work". When RubyGems creates a new gemified saikuro
wrapper to call the real saikuro bin "$0" is the gemified saikuro and
not the real one. Thus, by default nothing happens.

For fun I cheated and commented out the "if __FILE__" check and
replaced it with "if true". Saikuro will work with this change, but I
then ran "saikuro -?" to get a usage message and found the next
problem:

2)
I was reading through the Programming Ruby book and found a part that
showed how to use rdoc to create usage messages. I figured this might
be better than my normal technique so I used it in Saikuro. This has
caused a number of problems actually.

* With the above cheat I get the gem message:

···

--
/usr/bin/saikuro: invalid option -- ?
This file was generated by RubyGems.

The application 'Saikuro' is installed as part of a gem, and this file
is here to facilitate running it.
--

Which is not the usage message I wanted.

My original goal was to keep Saikuro simple so that as a single file
nothing complicated was needed. But now I suppose that should change.

I am planning to move all logic above and "if __FILE__ == $0" block
into a separate library file. Then add a require to this newly made
file and remove the "if __FILE__" check logic to make the insides of
the block automatically run.

Next I will stop using rdoc usage and simply put the usage message in
a method that outputs a String.

Does this sound like a good strategy?

Thanks,
Zev

This is mostly a repost of ruby-talk[229723] with some clarifications
and an appeal for ideas.

I have recently attempted to write a RubyGem for the Saikuro project.
Unfortunately, I have learned that there is a mismatch between how I
wrote Saikuro and how RubyGems expects executables to work.

I would like to know is are there any better or commonly followed ways
of writing executables that work with RubyGems and other installation
programs such as setup.rb ?

My executables typically consist of three lines:

#!/usr/local/bin/ruby -w
require 'some_file'
SomeFile.run

Where SomeFile::run is something like:

class SomeFile
   def self.run(args = ARGV)
     options = process_args args
     new(option[:foo]).run
   end
end

2)
I was reading through the Programming Ruby book and found a part that
showed how to use rdoc to create usage messages. I figured this might
be better than my normal technique so I used it in Saikuro. This has
caused a number of problems actually.

What does this technique look like? I'm not familiar with it.

My original goal was to keep Saikuro simple so that as a single file
nothing complicated was needed. But now I suppose that should change.

I am planning to move all logic above and "if __FILE__ == $0" block
into a separate library file. Then add a require to this newly made
file and remove the "if __FILE__" check logic to make the insides of
the block automatically run.

I keep these together, but put the logic that would normally go in "if __FILE__ == $0" into its own method or methods. That keeps the executable as simple as possible.

···

On Dec 18, 2006, at 22:34, Zev Blut wrote:

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

I would like to know is are there any better or commonly followed ways
of writing executables that work with RubyGems and other installation
programs such as setup.rb ?

My executables typically consist of three lines:

#!/usr/local/bin/ruby -w
require 'some_file'
SomeFile.run

Where SomeFile::run is something like:

class SomeFile
   def self.run(args = ARGV)
     options = process_args args
     new(option[:foo]).run
   end
end

Yes that is a good way to solve this problem.

2)
I was reading through the Programming Ruby book and found a part that
showed how to use rdoc to create usage messages. I figured this might
be better than my normal technique so I used it in Saikuro. This has
caused a number of problems actually.

What does this technique look like? I'm not familiar with it.

http://ruby-doc.org/stdlib/libdoc/rdoc/rdoc/classes/RDoc.html#M001338

It is something like this:

···

On Tue, 19 Dec 2006 16:31:48 +0900, Eric Hodel <drbrain@segment7.net> wrote:

On Dec 18, 2006, at 22:34, Zev Blut wrote:

-------------
#!/usr/bin/env ruby
# == Usage
# Some stuff on how to use a program.
# More stuff

require 'rdoc/usage'

RDoc.usage
-------------

Thanks for the advice!
Zev

Since RDoc.usage uses #caller to figure out which file to read you should be able to combine RDoc.usage with my method above, and the usage will live in the library file.

···

On Dec 18, 2006, at 23:49, Zev Blut wrote:

On Tue, 19 Dec 2006 16:31:48 +0900, Eric Hodel > <drbrain@segment7.net> wrote:

On Dec 18, 2006, at 22:34, Zev Blut wrote:

I would like to know is are there any better or commonly followed ways
of writing executables that work with RubyGems and other installation
programs such as setup.rb ?

My executables typically consist of three lines:

#!/usr/local/bin/ruby -w
require 'some_file'
SomeFile.run

Where SomeFile::run is something like:

class SomeFile
   def self.run(args = ARGV)
     options = process_args args
     new(option[:foo]).run
   end
end

Yes that is a good way to solve this problem.

2)
I was reading through the Programming Ruby book and found a part that
showed how to use rdoc to create usage messages. I figured this might
be better than my normal technique so I used it in Saikuro. This has
caused a number of problems actually.

What does this technique look like? I'm not familiar with it.

http://ruby-doc.org/stdlib/libdoc/rdoc/rdoc/classes/RDoc.html#M001338

It is something like this:

-------------
#!/usr/bin/env ruby
# == Usage
# Some stuff on how to use a program.
# More stuff

require 'rdoc/usage'

RDoc.usage

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

I have done a quick refactoring to create something similar to your
suggestions. The bin part works now, but the RDoc.usage still gives a
RubyGems message.

Just in case here is my ruby version
# ruby -v
ruby 1.8.5 (2006-08-25) [i686-linux]

I stuck some debugging puts in the RDoc.usage_no_exit method and this
is what I got:

# saikuro -?
/usr/bin/saikuro: invalid option -- ?
Caller main program file is /usr/bin/saikuro
Caller stack is
/usr/lib/ruby/1.8/rdoc/usage.rb:93:in `usage'
/usr/lib/ruby/gems/1.8/gems/Saikuro-1.0.0/bin/saikuro:175:in `run'
/usr/lib/ruby/gems/1.8/gems/Saikuro-1.0.0/bin/saikuro:200
/usr/bin/saikuro:18:in `load'
/usr/bin/saikuro:18
This file was generated by RubyGems.

The application 'Saikuro' is installed as part of a gem, and this file
is here to facilitate running it.

···

On Tue, 19 Dec 2006 17:36:43 +0900, Eric Hodel <drbrain@segment7.net> wrote:

On Dec 18, 2006, at 23:49, Zev Blut wrote:

On Tue, 19 Dec 2006 16:31:48 +0900, Eric Hodel <drbrain@segment7.net> >> wrote:

On Dec 18, 2006, at 22:34, Zev Blut wrote:

2)
I was reading through the Programming Ruby book and found a part that
showed how to use rdoc to create usage messages. I figured this might
be better than my normal technique so I used it in Saikuro. This has
caused a number of problems actually.

What does this technique look like? I'm not familiar with it.

http://ruby-doc.org/stdlib/libdoc/rdoc/rdoc/classes/RDoc.html#M001338

It is something like this:

-------------
#!/usr/bin/env ruby
# == Usage
# Some stuff on how to use a program.
# More stuff

require 'rdoc/usage'

RDoc.usage

Since RDoc.usage uses #caller to figure out which file to read you should be able to combine RDoc.usage with my method above, and the usage will live in the library file.

-----

Here is the specific piece of code in rdoc/usage usage_no_exit

  main_program_file = caller[-1].sub(/:\d+$/, '')

It appears that it assumes that the last caller on the stack is the
one who has the usage comments. But, in this current RubyGems bin
case the line above should be something like this:

  main_program_file = caller[1].sub(/:\d+(:.*)?$/, '')

If you read the synopsis of RDoc/usage it states that it will always
take the usage from the main program file. This appears to be in
conflict with RubyGems. I suppose one could hack the method to look
at the caller stack and if it sees a RubyGems /bin/ in the caller
stack uses the first instance as the main_program_file ?

Thanks,
Zev