Using thor for complex command line tool - ruby

i want to create a command line tool in Ruby using Thor. This tool should be packaged as a gem so that it is easily installed and uninstalled.
Creating and publishing the gem, I have done. I also created several Thor scripts which also work. However, I do not know how to combine them.
My aim is to be able to call my tool the following way:
mytool task param --options
mytool taskgroup:task param --options
I know how to make one Thor script to be executable. However, how do I make a bunch of thor scripts accessible throw one command?

According to the relevant Gem documentation, you could specify (in your .gemspec):
spec.executables = ['bin/foo', 'bin/bar']
spec.default_executable = 'bin/bar'
and have your gem install a bunch of executables (foo and bar). Or you write a wrapper for all your Thor scripts and specify:
spec.executables = ['bin/wrapper']
and have your gem install only one executable (wrapper).

The teletype gem (https://github.com/piotrmurach/tty) does an amazing job at setting up all the scaffolding for this. Create your project with teletype and then just fill in the implementation.

Related

Use "global" Thorfile

I want to use Thor with thor-scmversion to bump the version of my Chef cookbooks. The normal way of using Thor is to create a Thorfile in the project folder and have tasks implemented in there. For my use case I would have to create a Thorfile in every cookbook I write containing only
require 'thor/scmversion'
My question is, whether I can somehow put such a file in a "global" directory and point to it, when I run thor on the command line from within my project folder.
Edit
I followed #coderanger's advice and used thor install Thorfile. This creates a directory .thor in the user's home directory (in my case: /var/lib/jenkins/.thor) and a thor.yml file that includes a reference to the Thorfile I added with the upper command.
When I now run thor list, I get
$ thor list
version
-------
thor version:bump TYPE [PRERELEASE_TYPE] # Bump version number (type is major, minor, patch, prerelease or auto)
thor version:current # Show current SCM tagged version
but running thor version:current, I get the following error:
Could not find command "version:current".
So probably I missed something?
You can use thor install to set up a system-wide task. Bear in mind this might make things more difficult for other people since it won't be set up in git for them.

Creating Ruby gems with same name executables?

I want to have a Ruby Gem that will have the same executable as another Gem.
When called with command args it will either do something, or pass the command on to the other Gem.
The first problem I have is that it isn't able to run two same named executables. I get this error:
Bundler is using a binstub that was created for a different gem. This is deprecated, in future versions you may need to bundle binstub yourgem to work around a system/bundle conflict.
How can I have Gems with the same named executables and ensure that the target one executes?
You cannot rely on Bundler or Rubygems to manage this for you. All it does it copy an executable that you specified in your gem spec to its bin/ directory.
The first problem you'll have is that the executable that runs may be dependent on the order in which the gems were installed which you can't guarantee.
Another problem that you'll have is that you cannot execute code on gem installation so you will be unable to run code that would try to automate this set up for people who install your gem.
I believe your gem should provide a non-conflicting executable. You can then supply post install instructions in your gem spec that are displayed to a user installing the gem, in the README, in a blog post, etc. you can tell the user that they need to set up an alias that points to your executable. In all shells that I'm aware of aliases will be executed before filesystem executables.
For the times when people want to bypass your alias and execute the original executable you can tell people to escape the command, e.g. \original-gem. That bypasses alias and function lookup in most shells and will allow users to have your super awesome version as the default (thru the alias) and a way to easily access the original.

How to develop a Ruby GEM without having to install it first?

I'm developing a GEM that I've forked and I'm trying to modify it slightly for my app.
I'm finding it difficult and time consuming because for every change I make I have to
uninstall
build
re-install
run the app
Is there an easier way of which doesn't require repeating all steps above?
To use it in some app using bundler
If what you mean is for using it in a app to test it / use it, you can just specify a path for your gem or even point to a git repo in the Gemfile http://gembundler.com/gemfile.html
Like
gem "mygem", :path => "~/code/gems/mygem"
To use it as a standalone gem. i.e: like rspec or rake that can run outside of an app.
Just specify the path to your gem binary when running the gem command, like:
$ ~/path_to_my_gem/bin/mygem some args
If you can execute inside your gem directory (i.e: the command does not create files in the current directory, or needs any specific files from the current directory), just do this:
$ ./bin/mygem some args
Note that this last one is just for future reference, I think it's not applicable in the OP context.
use require_relative to include your files:
require_relative 'yourgem/yourclass'
This is the documentation for the function.

Are bundle exec and require 'bundler/setup' equivalent?

Do these things accomplish exactly the same?
starting a ruby process with bundle exec ruby foo.rb
having require "bundler/setup" as the first line of foo.rb
In your specific example they can be considered the same, however in reality they are not the same.
bundle exec makes some changes to the environment that bundler/setup does not make. If your foo.rb never runs a subshell, or never tries to run other ruby executables in subshells, then both versions are equivalent (they will both load bundled gems correctly and work exactly the same).
The whole idea with bundle exec is to enable you to run executables that were not originally designed with bundler in mind. Like rspec, rails, rackup. If your own app (foo.rb) does not try to run such executables that might be dependent on your bundles, then it makes no difference either way. Since all you want to make sure with bundler is that you load the correct gems, and for that bundler/setup works exactly as expected in your case.
From the bundler docs when talking about running ruby system executables:
In some cases, running executables without bundle exec may work, if the executable happens to be installed in your system and does not pull in any gems that conflict with your bundle.
However, this is unreliable and is the source of considerable pain. Even if it looks like it works, it may not work in the future or on another machine.
Then from the manpage of bundle exec you can get some additional clues as to what bundle exec actually does:
ENVIRONMENT MODIFICATIONS
make sure that it's still possible to shell out to bundle from inside a command invoked by bundle exec (using $BUNDLE_BIN_PATH)
put the directory containing executables (like rails, rspec, rackup) for your bundle on $PATH
make sure that if bundler is invoked in the subshell, it uses the same Gemfile (by setting BUNDLE_GEMFILE)
add -rbundler/setup to $RUBYOPT, which makes sure that Ruby programs invoked in the subshell can see the gems in the bundle
So if you build your app with bundler support in mind, then you never need to bundle exec your app.
But if you need to use other tools that load your app code that might load gems before they load your app code (which then might pull in a wrong non-bundled gem), then you need to use bundle exec.

Rake vs. Thor for automation scripts?

I want to automate things like:
Creating a new Ruby on Rails application with pre-selected database, Git initialize it, create a Heroku project, commit all files, etc.
Upload all files in folder to another computer through SSH, but do not overwrite files.
Upgrade Ubuntu, install all basic packages through apt-get.
From what I understand, tools for this are Rake and Thor, however, which one should I use?
Rake seems to me more de-facto and popular. I have heard people recommending Thor.
How do these stand to each other in a rundown?
Rake and Thor serve different purposes.
Rake is a general build script tool that is project-specific. In other words, you put your rakefile into your project folder and in your project's source control, and you can create, build and do other automation tasks that are specific to your project in that rakefile. Rake requires a rakefile to run.
Thor is a general purpose command line scripting tool that makes it very easy to re-use scripts across many projects and to do project setup, etc., like you are suggesting. Thor allows you to "install" an executable script that you can call from anywhere on your system, similar to calling "ruby", "gem" or "rake" command lines. However, Thor's scripts are more suited to general purpose, cross-application automation because the Thor script does not rely on a file sitting in your project-specific folder. A Thor script is the entire script, packed and installed for re-use anywhere.
Based on your stated needs, you are better off using Thor because you will be able to install your script in one location and have it work anywhere on your system. You will not be bound to where a Rake file is sitting or anything like that.
By the way, Rails 3 uses Thor for pretty much everything that is not project specific. You still have a Rake file and you still run things like "rake db:migrate" or "rake test:units". Thor is used for things like "rails new ...", "rails server" and "rails generate ..." The use of Thor AND Rake in Rails 3 is the perfect illustration of where each of these tools is best suited.
For setting up Ubuntu chores, Chef might be a better option.
From their web site:
Chef is an open source systems integration framework, built to bring the benefits of server configuration management to your entire infrastructure.
It's written in Ruby and there are tons of Chef recipes/cookbooks. Chef will handle setting up Ubuntu and installing packages, servers, etc.
I don't know if you are working with virtual machines, but Vagrant will set up a virtual machine and then use Chef to configure it.
There is something important to mention here.
http://guides.rubyonrails.org/generators.html in its section 8 Application Templates.
You can execute git commands, select gems, capify project.
And you could also execute system commands to satisfy your last point: Upgrade Ubuntu, install all basic packages through apt-get.
I would go with puppet.
By the way, maybe vagrant is useful to you?

Resources