commit 84189ac33a9dc57aa4da969c5b5a1910129748c6 Author: Michael Chapman Date: Fri May 31 16:11:04 2013 +1000 Initial non-working diff --git a/.vagrant b/.vagrant new file mode 100644 index 0000000..974f36c --- /dev/null +++ b/.vagrant @@ -0,0 +1 @@ +{"active":{"build":"b1db3058-5d8a-4211-a238-bbadc7486980","control_basevm":"ac323559-4a20-4bec-bea4-df8c51b84e82"}} \ No newline at end of file diff --git a/01apt-cacher-ng-proxy b/01apt-cacher-ng-proxy new file mode 100644 index 0000000..f51557f --- /dev/null +++ b/01apt-cacher-ng-proxy @@ -0,0 +1 @@ +Acquire::http { Proxy "http://192.168.242.99:3142"; }; diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..e1a696f --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" +gem "vagrant", "~>1.0" +gem " librarian-puppet-simple" diff --git a/Puppetfile b/Puppetfile new file mode 100644 index 0000000..2f28e5c --- /dev/null +++ b/Puppetfile @@ -0,0 +1,105 @@ +# the account where the Openstack modules should come from +# +# this file also accepts a few environment variables +# +git_protocol=ENV['git_protocol'] || 'git' + +# +# this modulefile has been configured to use two sets of repos. +# The downstream repos that Cisco has forked, or the upstream repos +# that they are derived from (and should be maintained in sync with) +# + + +# +# this is just targeting the upstream stackforge modules +# right now, and the logic for using downstream does not +# work yet +# + +if ENV['repos_to_use'] == 'downstream' + # this assumes downstream which is the Cisco branches + branch_name = 'origin/grizzly' + openstack_module_branch = branch_name + openstack_module_account = 'CiscoSystems' +else + # use the upstream modules where they exist + branch_name = 'origin/grizzly' + openstack_module_branch = 'master' + openstack_module_account = 'stackforge' +end + +base_url = "#{git_protocol}://github.com" + +# +# Installer Manifests +# +user_name = 'CiscoSystems' +release = 'grizzly' +manifest_branch = 'multi-node' +mod 'manifests', :git => "#{base_url}/#{user_name}/#{release}-manifests", :ref => manifest_branch + +# +# the stackforge openstack modules +# + +openstack_repo_prefix = "#{base_url}/#{openstack_module_account}/puppet" + +mod 'stackforge/openstack', :git => "#{openstack_repo_prefix}-openstack", :ref => openstack_module_branch +# openstack core modules +mod 'stackforge/cinder', :git => "#{openstack_repo_prefix}-cinder", :ref => openstack_module_branch +mod 'stackforge/glance', :git => "#{openstack_repo_prefix}-glance", :ref => openstack_module_branch +mod 'stackforge/keystone', :git => "#{openstack_repo_prefix}-keystone", :ref => openstack_module_branch +mod 'stackforge/horizon', :git => "#{openstack_repo_prefix}-horizon", :ref => openstack_module_branch +mod 'stackforge/nova', :git => "#{openstack_repo_prefix}-nova", :ref => openstack_module_branch +mod 'stackforge/quantum', :git => "#{openstack_repo_prefix}-quantum", :ref => openstack_module_branch +mod 'stackforge/swift', :git => "#{openstack_repo_prefix}-swift", :ref => openstack_module_branch + +# +# the rest of the modules just come straight from their respective Cisco branches at the moment. +# + +# +# coe specific modules +# +mod 'CiscoSystems/coe', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-coe", :ref => 'origin/grizzly' +mod 'CiscoSystems/openstack_admin', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-openstack_admin", :ref => 'origin/grizzly' + + +# middleware modules +mod 'CiscoSystems/apache', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-apache", :ref => branch_name +mod 'CiscoSystems/memcached', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-memcached", :ref => branch_name +# +# I cannot remember if this is necessary +# +mod 'CiscoSystems/mysql', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-mysql", :ref => 'master' +mod 'CiscoSystems/rabbitmq', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-rabbitmq", :ref => branch_name + +# linux tools +mod 'CiscoSystems/apt', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-apt", :ref => branch_name +mod 'CiscoSystems/apt-cacher-ng', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-apt-cacher-ng", :ref => branch_name +mod 'CiscoSystems/cobbler', :git => "#{git_protocol}://github.com/bodepd/puppet-cobbler", :ref => 'origin/fix_cobbler_sync_issue' +mod 'CiscoSystems/collectd', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-collectd", :ref => branch_name +mod 'CiscoSystems/corosync', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-corosync", :ref => branch_name +mod 'CiscoSystems/dnsmasq', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-dnsmasq", :ref => branch_name +mod 'CiscoSystems/drbd', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-drbd", :ref => branch_name +mod 'CiscoSystems/graphite', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-graphite", :ref => branch_name +mod 'CiscoSystems/monit', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-monit", :ref => branch_name +mod 'CiscoSystems/naginator', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-naginator", :ref => branch_name +mod 'CiscoSystems/ntp', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-ntp", :ref => branch_name +mod 'CiscoSystems/pip', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-pip", :ref => branch_name +mod 'CiscoSystems/puppet', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-puppet", :ref => branch_name +mod 'CiscoSystems/rsync', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-rsync", :ref => branch_name +mod 'CiscoSystems/sysctl', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-sysctl", :ref => branch_name +mod 'CiscoSystems/vswitch', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-vswitch", :ref => branch_name +mod 'CiscoSystems/xinetd', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-xinetd", :ref => branch_name +mod 'CiscoSystems/network', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-network", :ref => branch_name +mod 'CiscoSystems/filemapper', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-filemapper", :ref => branch_name +mod 'CiscoSystems/boolean', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-boolean", :ref => branch_name +#mod 'CiscoSystems/ssh', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-ssh", :ref => branch_name + +# puppet utilities +mod 'CiscoSystems/concat', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-concat", :ref => branch_name +# need the latest changes here +mod 'CiscoSystems/inifile', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-inifile", :ref => branch_name +mod 'CiscoSystems/stdlib', :git => "#{git_protocol}://github.com/CiscoSystems/puppet-stdlib", :ref => branch_name diff --git a/README.md b/README.md new file mode 100644 index 0000000..4881d86 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +Grizzly-manifests +================ + +Project for building out OpenStack COE. + +## Installing dependencies + +This setup requires that a few additional dependencies are installed: + +* virtualbox +* vagrant + +## User instructions + + git clone https://github.com/CiscoSystems/grizzly-manifests + cp grizzly-manifests/* /etc/puppet/manifests + +## Developer instructions + +Developers should be started by installing the following simple utility: +(I will eventually just have it bundled as a gem) + + mkdir vendor + export GEM_HOME=`pwd`/vendor + gem install thor --no-ri --no-rdoc + git clone git://github.com/bodepd/librarian-puppet-simple vendor/librarian-puppet-simple + export PATH=`pwd`/vendor/librarian-puppet-simple/bin/:$PATH + +Once this library is installed, you can run the following command from this project's +root directory: + + librarian-puppet install --verbose + +Add the basebox + + vagrant box add blank blank.box + +This command will clone all required modules into the modules directory. + +## Spinning up virtual machines with vagrant + +Now that you have set up the puppet content, the next step is to build +out your multi-node environment using vagrant. + +First, deploy the apt-ng-cacher instance: + + vagrant up cache + +Next, bring up the build server: + + vagrant up build + +Now, bring up the blank boxes so that they can PXE boot against the master + + vagrant up control + + vagrant up compute + + +Now, you have created a fully functional openstack environment, now have a look at some services: + + * service dashboard: http://192.168.242.100/ + * horizon: http://192.168.242.10/ (username: admin, password: Cisco123) + +Log into your controller at: ssh localadmin@192.168.242.10 (password ubuntu) + +and run through the 'Deploy Your First VM' section of this document: + + http://docwiki.cisco.com/wiki/OpenStack:Folsom-Multinode#Creating_a_build_server diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..2897708 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,184 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Four networks: +# 0 - VM host NAT +# 1 - COE build/deploy +# 2 - COE openstack internal +# 3 - COE openstack external (public) + + +def parse_vagrant_config( + config_file=File.expand_path(File.join(File.dirname(__FILE__), 'config.yaml')) +) + require 'yaml' + config = { + 'gui_mode' => false, + 'operatingsystem' => 'ubuntu', + 'verbose' => false, + 'update_repos' => true + } + if File.exists?(config_file) + overrides = YAML.load_file(config_file) + config.merge!(overrides) + end + config +end + +Vagrant::Config.run do |config| + require 'fileutils' + + if !File.symlink?("templates") + File.symlink("./modules/manifests/templates", "./templates") + end + + if !File.symlink?("manifests") + File.symlink("./modules/manifests/manifests", "./manifests") + end + + if !File.file?("./manifests/site.pp") && File.file?("./manifests/site.pp.example") + FileUtils.mv("./manifests/site.pp.example", "./manifests/site.pp") + end + + v_config = parse_vagrant_config + + apt_cache_proxy = 'Acquire::http { Proxy \"http://%s:3142\"; };' % v_config['apt_cache'] + + config.vm.define :cache do |cache_config| + cache_config.vm.box = "precise64" + cache_config.vm.box_url = 'http://files.vagrantup.com/precise64.box' + cache_config.vm.network :hostonly, "192.168.242.99" + cache_config.vm.network :hostonly, "10.2.3.99" + cache_config.vm.network :hostonly, "10.3.3.99" + cache_config.vm.customize ['modifyvm', :id, '--name', 'cache'] + cache_config.vm.host_name = 'cache' + cache_config.vm.provision :shell do |shell| + shell.inline = "apt-get update; apt-get install apt-cacher-ng -y; cp /vagrant/01apt-cacher-ng-proxy /etc/apt/apt.conf.d; apt-get update;sysctl -w net.ipv4.ip_forward=1;"#iptables –A FORWARD –i eth0 –o eth2 –j ACCEPT;iptables –A FORWARD –i eth2 –o eth0 –j ACCEPT;iptables –t nat –A POSTROUTING –o eth0 –j MASQUERADE" + end + end + + # Cobbler based "build" server + config.vm.define :build do |build_config| + build_config.vm.box = "precise64" + build_config.vm.box_url = 'http://files.vagrantup.com/precise64.box' + build_config.vm.customize ["modifyvm", :id, "--name", 'build-server'] + build_config.vm.host_name = 'build-server' + build_config.vm.network :hostonly, "192.168.242.100" + build_config.vm.network :hostonly, "10.2.3.100" + build_config.vm.customize ["modifyvm", :id, "--nicpromisc3", "allow-all"] + build_config.vm.network :hostonly, "10.3.3.100" + + # Use user-provided sources.list if available + build_config.vm.provision :shell do |shell| + shell.inline = 'if [ -f /vagrant/sources.list ]; then cp /vagrant/sources.list /etc/apt; fi;' + end + + # configure apt and basic packages needed for install + build_config.vm.provision :shell do |shell| + shell.inline = "cp /vagrant/dhclient.conf /etc/dhcp;echo \"%s\" > /etc/apt/apt.conf.d/01apt-cacher-ng-proxy; apt-get update; dhclient -r eth0 && dhclient eth0; apt-get install -y git vim puppet curl; cp /vagrant/templates/* /etc/puppet/templates" % apt_cache_proxy + end + + # pre-import the ubuntu image from an appropriate mirror + build_config.vm.provision :shell do |shell| + shell.inline = "if [ -f /vagrant/sources.list ]; then apt-get install -y cobbler; cobbler-ubuntu-import -m $(cat /vagrant/sources.list | grep deb | cut -d ' ' -f 2 | grep http | grep -v security | head -1) precise-x86_64; fi;" + end + + # now run puppet to install the build server + build_config.vm.provision(:puppet, :pp_path => "/etc/puppet") do |puppet| + puppet.manifests_path = 'manifests' + puppet.manifest_file = "site.pp" + puppet.module_path = 'modules' + puppet.options = ['--verbose', '--trace', '--debug'] + end + + # Configure puppet + build_config.vm.provision :shell do |shell| + shell.inline = 'if [ ! -h /etc/puppet/modules ]; then rmdir /etc/puppet/modules;ln -s /etc/puppet/modules-0 /etc/puppet/modules; fi;puppet plugin download --server build-server.domain.name;service apache2 restart' + end + + # enable ip forwarding and NAT so that the build server can act + # as an external gateway for the quantum router. + build_config.vm.provision :shell do |shell| + shell.inline = "ip addr add 172.16.2.1/24 dev eth2; sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -o eth0 -i eth1 -s 172.16.2.0/24 -m conntrack --ctstate NEW -j ACCEPT; iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT; iptables -t nat -F POSTROUTING; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE" + end + end + + # Openstack control server + config.vm.define :control_pxe do |control_config| + control_config.vm.customize(['modifyvm', :id ,'--nicbootprio2','1']) + control_config.vm.box = 'blank' + control_config.vm.boot_mode = 'gui' + control_config.ssh.port = 2727 + control_config.vm.network :hostonly, "192.168.242.10", :mac => "001122334455" + control_config.vm.network :hostonly, "10.2.3.10" + control_config.vm.network :hostonly, "10.3.3.10" + end + + config.vm.define :control_basevm do |control_config| + node_name = "control-server-#{Time.now.strftime('%Y%m%d%m%s')}.domain.name" + control_config.vm.box = "precise64" + control_config.vm.box_url = 'http://files.vagrantup.com/precise64.box' + control_config.vm.customize ["modifyvm", :id, "--name", 'control-server'] + control_config.vm.customize ["modifyvm", :id, "--memory", 1024] + control_config.vm.host_name = node_name + # you cannot boot this at the same time as the control_pxe b/c they have the same ip address + control_config.vm.network :hostonly, "192.168.242.10" + control_config.vm.network :hostonly, "10.2.3.10" + control_config.vm.customize ["modifyvm", :id, "--nicpromisc3", "allow-all"] + control_config.vm.network :hostonly, "10.3.3.10" + + # Use user-provided sources.list if available + control_config.vm.provision :shell do |shell| + shell.inline = 'if [ -f /vagrant/sources.list ]; then cp /vagrant/sources.list /etc/apt; fi;' + end + + control_config.vm.provision :shell do |shell| + shell.inline = 'echo "192.168.242.100 build-server build-server.domain.name" >> /etc/hosts;echo \"%s\" > /etc/apt/apt.conf.d/01apt-cacher-ng-proxy; apt-get update;apt-get install ubuntu-cloud-keyring' % apt_cache_proxy + end + + control_config.vm.provision(:puppet_server) do |puppet| + puppet.puppet_server = 'build-server.domain.name' + puppet.options = ['-t', '--pluginsync', '--trace', "--certname #{node_name}"] + end + # TODO install from puppet + end + + # Openstack compute server + config.vm.define :compute_pxe do |compute_config| + compute_config.vm.customize(['modifyvm', :id ,'--nicbootprio2','1']) + compute_config.vm.box = 'blank' + compute_config.vm.boot_mode = 'gui' + compute_config.ssh.port = 2728 + compute_config.vm.network :hostonly, "192.168.242.21", :mac => "001122334466" + compute_config.vm.network :hostonly, "10.2.3.21" + compute_config.vm.network :hostonly, "10.3.3.21" + end + + config.vm.define :compute_basevm do |compute_config| + node_name = "compute-server02-#{Time.now.strftime('%Y%m%d%m%s')}.domain.name" + compute_config.vm.box = "precise64" + compute_config.vm.box_url = 'http://files.vagrantup.com/precise64.box' + compute_config.vm.customize ["modifyvm", :id, "--name", 'compute-server02'] + compute_config.vm.host_name = node_name + compute_config.vm.customize ["modifyvm", :id, "--memory", 2512] + compute_config.vm.network :hostonly, "192.168.242.21" + compute_config.vm.network :hostonly, "10.2.3.21" + compute_config.vm.network :hostonly, "10.3.3.21" + + # Use user-provided sources.list if available + compute_config.vm.provision :shell do |shell| + shell.inline = 'if [ -f /vagrant/sources.list ]; then cp /vagrant/sources.list /etc/apt; fi;' + end + + compute_config.vm.provision :shell do |shell| + shell.inline = 'echo "192.168.242.100 build-server build-server.domain.name" >> /etc/hosts;echo \"%s\" > /etc/apt/apt.conf.d/01apt-cacher-ng-proxy; apt-get update;apt-get install ubuntu-cloud-keyring' % apt_cache_proxy + end + + compute_config.vm.provision(:puppet_server) do |puppet| + puppet.puppet_server = 'build-server.domain.name' + puppet.options = ['-t', '--pluginsync', '--trace', "--certname #{node_name}"] + end + # TODO install from puppet + end + +end diff --git a/blank.box b/blank.box new file mode 100644 index 0000000..af71f8a Binary files /dev/null and b/blank.box differ diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..ccb4441 --- /dev/null +++ b/config.yaml @@ -0,0 +1,2 @@ +--- +apt_cache: '192.168.1.16' diff --git a/dhclient.conf b/dhclient.conf new file mode 100644 index 0000000..077e323 --- /dev/null +++ b/dhclient.conf @@ -0,0 +1,55 @@ +# Configuration file for /sbin/dhclient, which is included in Debian's +# dhcp3-client package. +# +# This is a sample configuration file for dhclient. See dhclient.conf's +# man page for more information about the syntax of this file +# and a more comprehensive list of the parameters understood by +# dhclient. +# +# Normally, if the DHCP server provides reasonable information and does +# not leave anything out (like the domain name, for example), then +# few changes must be made to this file, if any. +# + +option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; + +send host-name ""; +#send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; +#send dhcp-lease-time 3600; +supersede domain-name "domain.name"; +#prepend domain-name-servers 127.0.0.1; +request subnet-mask, broadcast-address, time-offset, routers, + domain-name-servers, host-name, + netbios-name-servers, netbios-scope, interface-mtu, + rfc3442-classless-static-routes, ntp-servers, + dhcp6.domain-search, dhcp6.fqdn, + dhcp6.name-servers, dhcp6.sntp-servers; +#require subnet-mask, domain-name-servers; +#timeout 60; +#retry 60; +#reboot 10; +#select-timeout 5; +#initial-interval 2; +#script "/etc/dhcp3/dhclient-script"; +#media "-link0 -link1 -link2", "link0 link1"; +#reject 192.33.137.209; + +#alias { +# interface "eth0"; +# fixed-address 192.5.5.213; +# option subnet-mask 255.255.255.255; +#} + +#lease { +# interface "eth0"; +# fixed-address 192.33.137.200; +# medium "link0 link1"; +# option host-name "andare.swiftmedia.com"; +# option subnet-mask 255.255.255.0; +# option broadcast-address 192.33.137.255; +# option routers 192.33.137.250; +# option domain-name-servers 127.0.0.1; +# renew 2 2000/1/12 00:00:01; +# rebind 2 2000/1/12 00:00:01; +# expire 2 2000/1/12 00:00:01; +#} diff --git a/install_os_puppet b/install_os_puppet new file mode 100755 index 0000000..5b6fdbd --- /dev/null +++ b/install_os_puppet @@ -0,0 +1,130 @@ +#!/bin/bash +# install_os_puppet by Cisco Systems, Inc. is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. +# +# This script runs the basic steps for preparing to auto-deploy OpenStack as per the Cisco Edition process +# The manual steps are documented at http://docwiki.cisco.com/wiki/OpenStack:Folsom +# +# This script: updates apt, and makes sure that the system is up to date with the current Ubuntu baseline +# It then downloads the current set of Cisco validated puppet modules and a set of baseline manifests from the +# Cisco github repository +# If a proxy is necessary in order to download files from the internet, then either a proxy target can be passed +# to the script, or the environmet variables can be pre-set before running the script locally. +# + + +set -o errexit + +usage() { +cat < "mislav") +* Don't write options for nil or false values. This allows, for example, turning color off when running specs. +* Exit with the status of the spec command to help CI stuff out some. + +## 0.9.4, released 2008-08-13 +* Try to add Windows compatibility. +* BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore +* Allow options at the beginning of the argument list as well as the end. +* Make options available with symbol keys in addition to string keys. +* Allow true to be passed to Thor#method_options to denote a boolean option. +* If loading a thor file fails, don't give up, just print a warning and keep going. +* Make sure that we re-raise errors if they happened further down the pipe than we care about. +* Only delete the old file on updating when the installation of the new one is a success +* Make it Ruby 1.8.5 compatible. +* Don't raise an error if a boolean switch is defined multiple times. +* Thor::Options now doesn't parse through things that look like options but aren't. +* Add URI detection to install task, and make sure we don't append ".thor" to URIs +* Add rake2thor to the gem binfiles. +* Make sure local Thorfiles override system-wide ones. diff --git a/vendor/gems/thor-0.18.1/LICENSE.md b/vendor/gems/thor-0.18.1/LICENSE.md new file mode 100644 index 0000000..ef80540 --- /dev/null +++ b/vendor/gems/thor-0.18.1/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2008 Yehuda Katz, Eric Hodel, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/gems/thor-0.18.1/README.md b/vendor/gems/thor-0.18.1/README.md new file mode 100644 index 0000000..1ad690a --- /dev/null +++ b/vendor/gems/thor-0.18.1/README.md @@ -0,0 +1,35 @@ +[![Gem Version](https://badge.fury.io/rb/thor.png)](https://rubygems.org/gems/thor) +[![Build Status](https://secure.travis-ci.org/wycats/thor.png?branch=master)](http://travis-ci.org/wycats/thor) +[![Dependency Status](https://gemnasium.com/wycats/thor.png?travis)](https://gemnasium.com/wycats/thor) +[![Code Climate](https://codeclimate.com/github/wycats/thor.png)](https://codeclimate.com/github/wycats/thor) +[![Coverage Status](https://coveralls.io/repos/wycats/thor/badge.png?branch=master)](https://coveralls.io/r/wycats/thor) + +Thor +==== + +Description +----------- +Thor is a simple and efficient tool for building self-documenting command line +utilities. It removes the pain of parsing command line options, writing +"USAGE:" banners, and can also be used as an alternative to the [Rake][rake] +build tool. The syntax is Rake-like, so it should be familiar to most Rake +users. + +[rake]: https://github.com/jimweirich/rake + +Installation +------------ + gem install thor + +Usage and documentation +----------------------- +Please see the [wiki][] for basic usage and other documentation on using Thor. You can also checkout the [official homepage][homepage]. + +[wiki]: https://github.com/wycats/thor/wiki +[homepage]: http://whatisthor.com/ + +License +------- +Released under the MIT License. See the [LICENSE][] file for further details. + +[license]: LICENSE.md diff --git a/vendor/gems/thor-0.18.1/Thorfile b/vendor/gems/thor-0.18.1/Thorfile new file mode 100644 index 0000000..9f62810 --- /dev/null +++ b/vendor/gems/thor-0.18.1/Thorfile @@ -0,0 +1,30 @@ +# encoding: utf-8 +$:.unshift File.expand_path("../lib", __FILE__) + +require 'bundler' +require 'thor/rake_compat' + +class Default < Thor + include Thor::RakeCompat + Bundler::GemHelper.install_tasks + + desc "build", "Build thor-#{Thor::VERSION}.gem into the pkg directory" + def build + Rake::Task["build"].execute + end + + desc "install", "Build and install thor-#{Thor::VERSION}.gem into system gems" + def install + Rake::Task["install"].execute + end + + desc "release", "Create tag v#{Thor::VERSION} and build and push thor-#{Thor::VERSION}.gem to Rubygems" + def release + Rake::Task["release"].execute + end + + desc "spec", "Run RSpec code examples" + def spec + exec "rspec --color --format=documentation spec" + end +end diff --git a/vendor/gems/thor-0.18.1/bin/thor b/vendor/gems/thor-0.18.1/bin/thor new file mode 100755 index 0000000..16323b1 --- /dev/null +++ b/vendor/gems/thor-0.18.1/bin/thor @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# -*- mode: ruby -*- + +require 'thor/runner' +$thor_runner = true +Thor::Runner.start diff --git a/vendor/gems/thor-0.18.1/lib/thor.rb b/vendor/gems/thor-0.18.1/lib/thor.rb new file mode 100644 index 0000000..d7a7461 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor.rb @@ -0,0 +1,473 @@ +require 'set' +require 'thor/base' + +class Thor + class << self + # Allows for custom "Command" package naming. + # + # === Parameters + # name + # options + # + def package_name(name, options={}) + @package_name = name.nil? || name == '' ? nil : name + end + + # Sets the default command when thor is executed without an explicit command to be called. + # + # ==== Parameters + # meth:: name of the default command + # + def default_command(meth=nil) + @default_command = case meth + when :none + 'help' + when nil + @default_command || from_superclass(:default_command, 'help') + else + meth.to_s + end + end + alias default_task default_command + + # Registers another Thor subclass as a command. + # + # ==== Parameters + # klass:: Thor subclass to register + # command:: Subcommand name to use + # usage:: Short usage for the subcommand + # description:: Description for the subcommand + def register(klass, subcommand_name, usage, description, options={}) + if klass <= Thor::Group + desc usage, description, options + define_method(subcommand_name) { |*args| invoke(klass, args) } + else + desc usage, description, options + subcommand subcommand_name, klass + end + end + + # Defines the usage and the description of the next command. + # + # ==== Parameters + # usage + # description + # options + # + def desc(usage, description, options={}) + if options[:for] + command = find_and_refresh_command(options[:for]) + command.usage = usage if usage + command.description = description if description + else + @usage, @desc, @hide = usage, description, options[:hide] || false + end + end + + # Defines the long description of the next command. + # + # ==== Parameters + # long description + # + def long_desc(long_description, options={}) + if options[:for] + command = find_and_refresh_command(options[:for]) + command.long_description = long_description if long_description + else + @long_desc = long_description + end + end + + # Maps an input to a command. If you define: + # + # map "-T" => "list" + # + # Running: + # + # thor -T + # + # Will invoke the list command. + # + # ==== Parameters + # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command. + # + def map(mappings=nil) + @map ||= from_superclass(:map, {}) + + if mappings + mappings.each do |key, value| + if key.respond_to?(:each) + key.each {|subkey| @map[subkey] = value} + else + @map[key] = value + end + end + end + + @map + end + + # Declares the options for the next command to be declared. + # + # ==== Parameters + # Hash[Symbol => Object]:: The hash key is the name of the option and the value + # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric + # or :required (string). If you give a value, the type of the value is used. + # + def method_options(options=nil) + @method_options ||= {} + build_options(options, @method_options) if options + @method_options + end + + alias options method_options + + # Adds an option to the set of method options. If :for is given as option, + # it allows you to change the options from a previous defined command. + # + # def previous_command + # # magic + # end + # + # method_option :foo => :bar, :for => :previous_command + # + # def next_command + # # magic + # end + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. It cannot be required and have default values. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner - String to show on usage notes. + # :hide - If you want to hide this option from the help. + # + def method_option(name, options={}) + scope = if options[:for] + find_and_refresh_command(options[:for]).options + else + method_options + end + + build_option(name, options, scope) + end + alias option method_option + + # Prints help information for the given command. + # + # ==== Parameters + # shell + # command_name + # + def command_help(shell, command_name) + meth = normalize_command_name(command_name) + command = all_commands[meth] + handle_no_command_error(meth) unless command + + shell.say "Usage:" + shell.say " #{banner(command)}" + shell.say + class_options_help(shell, nil => command.options.map { |_, o| o }) + if command.long_description + shell.say "Description:" + shell.print_wrapped(command.long_description, :indent => 2) + else + shell.say command.description + end + end + alias task_help command_help + + # Prints help information for this class. + # + # ==== Parameters + # shell + # + def help(shell, subcommand = false) + list = printable_commands(true, subcommand) + Thor::Util.thor_classes_in(self).each do |klass| + list += klass.printable_commands(false) + end + list.sort!{ |a,b| a[0] <=> b[0] } + + if @package_name + shell.say "#{@package_name} commands:" + else + shell.say "Commands:" + end + + shell.print_table(list, :indent => 2, :truncate => true) + shell.say + class_options_help(shell) + end + + # Returns commands ready to be printed. + def printable_commands(all = true, subcommand = false) + (all ? all_commands : commands).map do |_, command| + next if command.hidden? + item = [] + item << banner(command, false, subcommand) + item << (command.description ? "# #{command.description.gsub(/\s+/m,' ')}" : "") + item + end.compact + end + alias printable_tasks printable_commands + + def subcommands + @subcommands ||= from_superclass(:subcommands, []) + end + alias subtasks subcommands + + def subcommand(subcommand, subcommand_class) + self.subcommands << subcommand.to_s + subcommand_class.subcommand_help subcommand + + define_method(subcommand) do |*args| + args, opts = Thor::Arguments.split(args) + invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options + end + end + alias subtask subcommand + + # Extend check unknown options to accept a hash of conditions. + # + # === Parameters + # options: A hash containing :only and/or :except keys + def check_unknown_options!(options={}) + @check_unknown_options ||= Hash.new + options.each do |key, value| + if value + @check_unknown_options[key] = Array(value) + else + @check_unknown_options.delete(key) + end + end + @check_unknown_options + end + + # Overwrite check_unknown_options? to take subcommands and options into account. + def check_unknown_options?(config) #:nodoc: + options = check_unknown_options + return false unless options + + command = config[:current_command] + return true unless command + + name = command.name + + if subcommands.include?(name) + false + elsif options[:except] + !options[:except].include?(name.to_sym) + elsif options[:only] + options[:only].include?(name.to_sym) + else + true + end + end + + # Stop parsing of options as soon as an unknown option or a regular + # argument is encountered. All remaining arguments are passed to the command. + # This is useful if you have a command that can receive arbitrary additional + # options, and where those additional options should not be handled by + # Thor. + # + # ==== Example + # + # To better understand how this is useful, let's consider a command that calls + # an external command. A user may want to pass arbitrary options and + # arguments to that command. The command itself also accepts some options, + # which should be handled by Thor. + # + # class_option "verbose", :type => :boolean + # stop_on_unknown_option! :exec + # check_unknown_options! :except => :exec + # + # desc "exec", "Run a shell command" + # def exec(*args) + # puts "diagnostic output" if options[:verbose] + # Kernel.exec(*args) + # end + # + # Here +exec+ can be called with +--verbose+ to get diagnostic output, + # e.g.: + # + # $ thor exec --verbose echo foo + # diagnostic output + # foo + # + # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead: + # + # $ thor exec echo --verbose foo + # --verbose foo + # + # ==== Parameters + # Symbol ...:: A list of commands that should be affected. + def stop_on_unknown_option!(*command_names) + @stop_on_unknown_option ||= Set.new + @stop_on_unknown_option.merge(command_names) + end + + def stop_on_unknown_option?(command) #:nodoc: + !!@stop_on_unknown_option && @stop_on_unknown_option.include?(command.name.to_sym) + end + + protected + + # The method responsible for dispatching given the args. + def dispatch(meth, given_args, given_opts, config) #:nodoc: + # There is an edge case when dispatching from a subcommand. + # A problem occurs invoking the default command. This case occurs + # when arguments are passed and a default command is defined, and + # the first given_args does not match the default command. + # Thor use "help" by default so we skip that case. + # Note the call to retrieve_command_name. It's called with + # given_args.dup since that method calls args.shift. Then lookup + # the command normally. If the first item in given_args is not + # a command then use the default command. The given_args will be + # intact later since dup was used. + if config[:invoked_via_subcommand] && given_args.size >= 1 && default_command != "help" && given_args.first != default_command + meth ||= retrieve_command_name(given_args.dup) + command = all_commands[normalize_command_name(meth)] + command ||= all_commands[normalize_command_name(default_command)] + else + meth ||= retrieve_command_name(given_args) + command = all_commands[normalize_command_name(meth)] + end + + if command + args, opts = Thor::Options.split(given_args) + if stop_on_unknown_option?(command) && !args.empty? + # given_args starts with a non-option, so we treat everything as + # ordinary arguments + args.concat opts + opts.clear + end + else + args, opts = given_args, nil + command = Thor::DynamicCommand.new(meth) + end + + opts = given_opts || opts || [] + config.merge!(:current_command => command, :command_options => command.options) + + instance = new(args, opts, config) + yield instance if block_given? + args = instance.args + trailing = args[Range.new(arguments.size, -1)] + instance.invoke_command(command, trailing || []) + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Thor::Runner. It receives + # the command that is going to be invoked and a boolean which indicates if + # the namespace should be displayed as arguments. + # + def banner(command, namespace = nil, subcommand = false) + "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}" + end + + def baseclass #:nodoc: + Thor + end + + def create_command(meth) #:nodoc: + if @usage && @desc + base_class = @hide ? Thor::HiddenCommand : Thor::Command + commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options) + @usage, @desc, @long_desc, @method_options, @hide = nil + true + elsif self.all_commands[meth] || meth == "method_missing" + true + else + puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " << + "Call desc if you want this method to be available as command or declare it inside a " << + "no_commands{} block. Invoked from #{caller[1].inspect}." + false + end + end + alias create_task create_command + + def initialize_added #:nodoc: + class_options.merge!(method_options) + @method_options = nil + end + + # Retrieve the command name from given args. + def retrieve_command_name(args) #:nodoc: + meth = args.first.to_s unless args.empty? + if meth && (map[meth] || meth !~ /^\-/) + args.shift + else + nil + end + end + alias retrieve_task_name retrieve_command_name + + # receives a (possibly nil) command name and returns a name that is in + # the commands hash. In addition to normalizing aliases, this logic + # will determine if a shortened command is an unambiguous substring of + # a command or alias. + # + # +normalize_command_name+ also converts names like +animal-prison+ + # into +animal_prison+. + def normalize_command_name(meth) #:nodoc: + return default_command.to_s.gsub('-', '_') unless meth + + possibilities = find_command_possibilities(meth) + if possibilities.size > 1 + raise ArgumentError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" + elsif possibilities.size < 1 + meth = meth || default_command + elsif map[meth] + meth = map[meth] + else + meth = possibilities.first + end + + meth.to_s.gsub('-','_') # treat foo-bar as foo_bar + end + alias normalize_task_name normalize_command_name + + # this is the logic that takes the command name passed in by the user + # and determines whether it is an unambiguous substrings of a command or + # alias name. + def find_command_possibilities(meth) + len = meth.to_s.length + possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort + unique_possibilities = possibilities.map { |k| map[k] || k }.uniq + + if possibilities.include?(meth) + [meth] + elsif unique_possibilities.size == 1 + unique_possibilities + else + possibilities + end + end + alias find_task_possibilities find_command_possibilities + + def subcommand_help(cmd) + desc "help [COMMAND]", "Describe subcommands or one specific subcommand" + class_eval <<-RUBY + def help(command = nil, subcommand = true); super; end + RUBY + end + alias subtask_help subcommand_help + + end + + include Thor::Base + + map HELP_MAPPINGS => :help + + desc "help [COMMAND]", "Describe available commands or one specific command" + def help(command = nil, subcommand = false) + command ? self.class.command_help(shell, command) : self.class.help(shell, subcommand) + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions.rb b/vendor/gems/thor-0.18.1/lib/thor/actions.rb new file mode 100644 index 0000000..d55b77b --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions.rb @@ -0,0 +1,318 @@ +require 'fileutils' +require 'uri' +require 'thor/core_ext/io_binary_read' +require 'thor/actions/create_file' +require 'thor/actions/create_link' +require 'thor/actions/directory' +require 'thor/actions/empty_directory' +require 'thor/actions/file_manipulation' +require 'thor/actions/inject_into_file' + +class Thor + module Actions + attr_accessor :behavior + + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # Hold source paths for one Thor instance. source_paths_for_search is the + # method responsible to gather source_paths from this current class, + # inherited paths and the source root. + # + def source_paths + @_source_paths ||= [] + end + + # Stores and return the source root for this class + def source_root(path=nil) + @_source_root = path if path + @_source_root + end + + # Returns the source paths in the following order: + # + # 1) This class source paths + # 2) Source root + # 3) Parents source paths + # + def source_paths_for_search + paths = [] + paths += self.source_paths + paths << self.source_root if self.source_root + paths += from_superclass(:source_paths, []) + paths + end + + # Add runtime options that help actions execution. + # + def add_runtime_options! + class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, + :desc => "Overwrite files that already exist" + + class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, + :desc => "Run but do not make any changes" + + class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, + :desc => "Suppress status output" + + class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, + :desc => "Skip files that already exist" + end + end + + # Extends initializer to add more configuration options. + # + # ==== Configuration + # behavior:: The actions default behavior. Can be :invoke or :revoke. + # It also accepts :force, :skip and :pretend to set the behavior + # and the respective option. + # + # destination_root:: The root directory needed for some actions. + # + def initialize(args=[], options={}, config={}) + self.behavior = case config[:behavior].to_s + when "force", "skip" + _cleanup_options_and_set(options, config[:behavior]) + :invoke + when "revoke" + :revoke + else + :invoke + end + + super + self.destination_root = config[:destination_root] + end + + # Wraps an action object and call it accordingly to the thor class behavior. + # + def action(instance) #:nodoc: + if behavior == :revoke + instance.revoke! + else + instance.invoke! + end + end + + # Returns the root for this thor class (also aliased as destination root). + # + def destination_root + @destination_stack.last + end + + # Sets the root for this thor class. Relatives path are added to the + # directory where the script was invoked and expanded. + # + def destination_root=(root) + @destination_stack ||= [] + @destination_stack[0] = File.expand_path(root || '') + end + + # Returns the given path relative to the absolute root (ie, root where + # the script started). + # + def relative_to_original_destination_root(path, remove_dot=true) + path = path.dup + if path.gsub!(@destination_stack[0], '.') + remove_dot ? (path[2..-1] || '') : path + else + path + end + end + + # Holds source paths in instance so they can be manipulated. + # + def source_paths + @source_paths ||= self.class.source_paths_for_search + end + + # Receives a file or directory and search for it in the source paths. + # + def find_in_source_paths(file) + relative_root = relative_to_original_destination_root(destination_root, false) + + source_paths.each do |source| + source_file = File.expand_path(file, File.join(source, relative_root)) + return source_file if File.exists?(source_file) + end + + message = "Could not find #{file.inspect} in any of your source paths. " + + unless self.class.source_root + message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. " + end + + if source_paths.empty? + message << "Currently you have no source paths." + else + message << "Your current source paths are: \n#{source_paths.join("\n")}" + end + + raise Error, message + end + + # Do something in the root or on a provided subfolder. If a relative path + # is given it's referenced from the current root. The full path is yielded + # to the block you provide. The path is set back to the previous path when + # the method exits. + # + # ==== Parameters + # dir:: the directory to move to. + # config:: give :verbose => true to log and use padding. + # + def inside(dir='', config={}, &block) + verbose = config.fetch(:verbose, false) + pretend = options[:pretend] + + say_status :inside, dir, verbose + shell.padding += 1 if verbose + @destination_stack.push File.expand_path(dir, destination_root) + + # If the directory doesnt exist and we're not pretending + if !File.exist?(destination_root) && !pretend + FileUtils.mkdir_p(destination_root) + end + + if pretend + # In pretend mode, just yield down to the block + block.arity == 1 ? yield(destination_root) : yield + else + FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + end + + @destination_stack.pop + shell.padding -= 1 if verbose + end + + # Goes to the root and execute the given block. + # + def in_root + inside(@destination_stack.first) { yield } + end + + # Loads an external file and execute it in the instance binding. + # + # ==== Parameters + # path:: The path to the file to execute. Can be a web address or + # a relative path from the source root. + # + # ==== Examples + # + # apply "http://gist.github.com/103208" + # + # apply "recipes/jquery.rb" + # + def apply(path, config={}) + verbose = config.fetch(:verbose, true) + is_uri = path =~ /^https?\:\/\// + path = find_in_source_paths(path) unless is_uri + + say_status :apply, path, verbose + shell.padding += 1 if verbose + + if is_uri + contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read } + else + contents = open(path) {|io| io.read } + end + + instance_eval(contents, path) + shell.padding -= 1 if verbose + end + + # Executes a command returning the contents of the command. + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with + # to append an executable to command executation. + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + def run(command, config={}) + return unless behavior == :invoke + + destination = relative_to_original_destination_root(destination_root, false) + desc = "#{command} from #{destination.inspect}" + + if config[:with] + desc = "#{File.basename(config[:with].to_s)} #{desc}" + command = "#{config[:with]} #{command}" + end + + say_status :run, desc, config.fetch(:verbose, true) + + unless options[:pretend] + config[:capture] ? `#{command}` : system("#{command}") + end + end + + # Executes a ruby script (taking into account WIN32 platform quirks). + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status. + # + def run_ruby_script(command, config={}) + return unless behavior == :invoke + run command, config.merge(:with => Thor::Util.ruby_command) + end + + # Run a thor command. A hash of options can be given and it's converted to + # switches. + # + # ==== Parameters + # command:: the command to be invoked + # args:: arguments to the command + # config:: give :verbose => false to not log the status, :capture => true to hide to output. + # Other options are given as parameter to Thor. + # + # + # ==== Examples + # + # thor :install, "http://gist.github.com/103208" + # #=> thor install http://gist.github.com/103208 + # + # thor :list, :all => true, :substring => 'rails' + # #=> thor list --all --substring=rails + # + def thor(command, *args) + config = args.last.is_a?(Hash) ? args.pop : {} + verbose = config.key?(:verbose) ? config.delete(:verbose) : true + pretend = config.key?(:pretend) ? config.delete(:pretend) : false + capture = config.key?(:capture) ? config.delete(:capture) : false + + args.unshift(command) + args.push Thor::Options.to_switches(config) + command = args.join(' ').strip + + run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture + end + + protected + + # Allow current root to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:destination_root => self.destination_root) + end + + def _cleanup_options_and_set(options, key) #:nodoc: + case options + when Array + %w(--force -f --skip -s).each { |i| options.delete(i) } + options << "--#{key}" + when Hash + [:force, :skip, "force", "skip"].each { |i| options.delete(i) } + options.merge!(key => true) + end + end + + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions/create_file.rb b/vendor/gems/thor-0.18.1/lib/thor/actions/create_file.rb new file mode 100644 index 0000000..ed5973a --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions/create_file.rb @@ -0,0 +1,105 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Create a new file relative to the destination root with the given data, + # which is the return value of a block or a data string. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # data:: the data to append to the file. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # create_file "lib/fun_party.rb" do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # create_file "config/apache.conf", "your apache config" + # + def create_file(destination, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + data = args.first + action CreateFile.new(self, destination, block || data.to_s, config) + end + alias :add_file :create_file + + # CreateFile is a subset of Template, which instead of rendering a file with + # ERB, it gets the content from the user. + # + class CreateFile < EmptyDirectory #:nodoc: + attr_reader :data + + def initialize(base, destination, data, config={}) + @data = data + super(base, destination, config) + end + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.binread(destination) == render + end + + # Holds the content to be added to the file. + # + def render + @render ||= if data.is_a?(Proc) + data.call + else + data + end + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + File.open(destination, 'wb') { |f| f.write render } + end + given_destination + end + + protected + + # Now on conflict we check if the file is identical or not. + # + def on_conflict_behavior(&block) + if identical? + say_status :identical, :blue + else + options = base.options.merge(config) + force_or_skip_or_conflict(options[:force], options[:skip], &block) + end + end + + # If force is true, run the action, otherwise check if it's not being + # skipped. If both are false, show the file_collision menu, if the menu + # returns true, force it, otherwise skip. + # + def force_or_skip_or_conflict(force, skip, &block) + if force + say_status :force, :yellow + block.call unless pretend? + elsif skip + say_status :skip, :yellow + else + say_status :conflict, :red + force_or_skip_or_conflict(force_on_collision?, true, &block) + end + end + + # Shows the file collision menu to the user and gets the result. + # + def force_on_collision? + base.shell.file_collision(destination){ render } + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions/create_link.rb b/vendor/gems/thor-0.18.1/lib/thor/actions/create_link.rb new file mode 100644 index 0000000..fba3915 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions/create_link.rb @@ -0,0 +1,60 @@ +require 'thor/actions/create_file' + +class Thor + module Actions + + # Create a new file relative to the destination root from the given source. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # source:: the relative path to the source root. + # config:: give :verbose => false to not log the status. + # :: give :symbolic => false for hard link. + # + # ==== Examples + # + # create_link "config/apache.conf", "/etc/apache.conf" + # + def create_link(destination, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + source = args.first + action CreateLink.new(self, destination, source, config) + end + alias :add_link :create_link + + # CreateLink is a subset of CreateFile, which instead of taking a block of + # data, just takes a source string from the user. + # + class CreateLink < CreateFile #:nodoc: + attr_reader :data + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.identical?(render, destination) + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + # Create a symlink by default + config[:symbolic] = true if config[:symbolic].nil? + File.unlink(destination) if exists? + if config[:symbolic] + File.symlink(render, destination) + else + File.link(render, destination) + end + end + given_destination + end + + def exists? + super || File.symlink?(destination) + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions/directory.rb b/vendor/gems/thor-0.18.1/lib/thor/actions/directory.rb new file mode 100644 index 0000000..7f8fd97 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions/directory.rb @@ -0,0 +1,119 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + # Copies recursively the files from source directory to root directory. + # If any of the files finishes with .tt, it's considered to be a template + # and is placed in the destination without the extension .tt. If any + # empty directory is found, it's copied and all .empty_directory files are + # ignored. If any file name is wrapped within % signs, the text within + # the % signs will be executed as a method and replaced with the returned + # value. Let's suppose a doc directory with the following files: + # + # doc/ + # components/.empty_directory + # README + # rdoc.rb.tt + # %app_name%.rb + # + # When invoked as: + # + # directory "doc" + # + # It will create a doc directory in the destination with the following + # files (assuming that the `app_name` method returns the value "blog"): + # + # doc/ + # components/ + # README + # rdoc.rb + # blog.rb + # + # Encoded path note: Since Thor internals use Object#respond_to? to check if it can + # expand %something%, this `something` should be a public method in the class calling + # #directory. If a method is private, Thor stack raises PrivateMethodEncodedError. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # If :recursive => false, does not look for paths recursively. + # If :mode => :preserve, preserve the file mode from the source. + # If :exclude_pattern => /regexp/, prevents copying files that match that regexp. + # + # ==== Examples + # + # directory "doc" + # directory "doc", "docs", :recursive => false + # + def directory(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + action Directory.new(self, source, destination || source, config, &block) + end + + class Directory < EmptyDirectory #:nodoc: + attr_reader :source + + def initialize(base, source, destination=nil, config={}, &block) + @source = File.expand_path(base.find_in_source_paths(source.to_s)) + @block = block + super(base, destination, { :recursive => true }.merge(config)) + end + + def invoke! + base.empty_directory given_destination, config + execute! + end + + def revoke! + execute! + end + + protected + + def execute! + lookup = Util.escape_globs(source) + lookup = config[:recursive] ? File.join(lookup, '**') : lookup + lookup = file_level_lookup(lookup) + + files(lookup).sort.each do |file_source| + next if File.directory?(file_source) + next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern]) + file_destination = File.join(given_destination, file_source.gsub(source, '.')) + file_destination.gsub!('/./', '/') + + case file_source + when /\.empty_directory$/ + dirname = File.dirname(file_destination).gsub(/\/\.$/, '') + next if dirname == given_destination + base.empty_directory(dirname, config) + when /\.tt$/ + destination = base.template(file_source, file_destination[0..-4], config, &@block) + else + destination = base.copy_file(file_source, file_destination, config, &@block) + end + end + end + + if RUBY_VERSION < '2.0' + def file_level_lookup(previous_lookup) + File.join(previous_lookup, '{*,.[a-z]*}') + end + + def files(lookup) + Dir[lookup] + end + else + def file_level_lookup(previous_lookup) + File.join(previous_lookup, '*') + end + + def files(lookup) + Dir.glob(lookup, File::FNM_DOTMATCH) + end + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions/empty_directory.rb b/vendor/gems/thor-0.18.1/lib/thor/actions/empty_directory.rb new file mode 100644 index 0000000..d9970ab --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions/empty_directory.rb @@ -0,0 +1,137 @@ +class Thor + module Actions + + # Creates an empty directory. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # empty_directory "doc" + # + def empty_directory(destination, config={}) + action EmptyDirectory.new(self, destination, config) + end + + # Class which holds create directory logic. This is the base class for + # other actions like create_file and directory. + # + # This implementation is based in Templater actions, created by Jonas Nicklas + # and Michael S. Klishin under MIT LICENSE. + # + class EmptyDirectory #:nodoc: + attr_reader :base, :destination, :given_destination, :relative_destination, :config + + # Initializes given the source and destination. + # + # ==== Parameters + # base:: A Thor::Base instance + # source:: Relative path to the source of this file + # destination:: Relative path to the destination of this file + # config:: give :verbose => false to not log the status. + # + def initialize(base, destination, config={}) + @base, @config = base, { :verbose => true }.merge(config) + self.destination = destination + end + + # Checks if the destination file already exists. + # + # ==== Returns + # Boolean:: true if the file exists, false otherwise. + # + def exists? + ::File.exists?(destination) + end + + def invoke! + invoke_with_conflict_check do + ::FileUtils.mkdir_p(destination) + end + end + + def revoke! + say_status :remove, :red + ::FileUtils.rm_rf(destination) if !pretend? && exists? + given_destination + end + + protected + + # Shortcut for pretend. + # + def pretend? + base.options[:pretend] + end + + # Sets the absolute destination value from a relative destination value. + # It also stores the given and relative destination. Let's suppose our + # script is being executed on "dest", it sets the destination root to + # "dest". The destination, given_destination and relative_destination + # are related in the following way: + # + # inside "bar" do + # empty_directory "baz" + # end + # + # destination #=> dest/bar/baz + # relative_destination #=> bar/baz + # given_destination #=> baz + # + def destination=(destination) + if destination + @given_destination = convert_encoded_instructions(destination.to_s) + @destination = ::File.expand_path(@given_destination, base.destination_root) + @relative_destination = base.relative_to_original_destination_root(@destination) + end + end + + # Filenames in the encoded form are converted. If you have a file: + # + # %file_name%.rb + # + # It calls #file_name from the base and replaces %-string with the + # return value (should be String) of #file_name: + # + # user.rb + # + # The method referenced can be either public or private. + # + def convert_encoded_instructions(filename) + filename.gsub(/%(.*?)%/) do |initial_string| + method = $1.strip + base.respond_to?(method, true) ? base.send(method) : initial_string + end + end + + # Receives a hash of options and just execute the block if some + # conditions are met. + # + def invoke_with_conflict_check(&block) + if exists? + on_conflict_behavior(&block) + else + say_status :create, :green + block.call unless pretend? + end + + destination + end + + # What to do when the destination file already exists. + # + def on_conflict_behavior(&block) + say_status :exist, :blue + end + + # Shortcut to say_status shell method. + # + def say_status(status, color) + base.shell.say_status status, relative_destination, color if config[:verbose] + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions/file_manipulation.rb b/vendor/gems/thor-0.18.1/lib/thor/actions/file_manipulation.rb new file mode 100644 index 0000000..a652244 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions/file_manipulation.rb @@ -0,0 +1,314 @@ +require 'erb' +require 'open-uri' + +class Thor + module Actions + + # Copies the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status, and + # :mode => :preserve, to preserve the file mode from the source. + + # + # ==== Examples + # + # copy_file "README", "doc/README" + # + # copy_file "doc/README" + # + def copy_file(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_file destination, nil, config do + content = File.binread(source) + content = block.call(content) if block + content + end + if config[:mode] == :preserve + mode = File.stat(source).mode + chmod(destination, mode, config) + end + end + + # Links the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # link_file "README", "doc/README" + # + # link_file "doc/README" + # + def link_file(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_link destination, source, config + end + + # Gets the content at the given address and places it at the given relative + # destination. If a block is given instead of destination, the content of + # the url is yielded and used as location. + # + # ==== Parameters + # source:: the address of the given content. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # get "http://gist.github.com/103208", "doc/README" + # + # get "http://gist.github.com/103208" do |content| + # content.split("\n").first + # end + # + def get(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first + + source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^https?\:\/\// + render = open(source) {|input| input.binmode.read } + + destination ||= if block_given? + block.arity == 1 ? block.call(render) : block.call + else + File.basename(source) + end + + create_file destination, render, config + end + + # Gets an ERB template at the relative source, executes it and makes a copy + # at the relative destination. If the destination is not given it's assumed + # to be equal to the source removing .tt from the filename. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # template "README", "doc/README" + # + # template "doc/README" + # + def template(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source.sub(/\.tt$/, '') + + source = File.expand_path(find_in_source_paths(source.to_s)) + context = instance_eval('binding') + + create_file destination, nil, config do + content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context) + content = block.call(content) if block + content + end + end + + # Changes the mode of the given file or directory. + # + # ==== Parameters + # mode:: the file mode + # path:: the name of the file to change mode + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # chmod "script/server", 0755 + # + def chmod(path, mode, config={}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) + FileUtils.chmod_R(mode, path) unless options[:pretend] + end + + # Prepend text to a file. Since it depends on insert_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # data:: the data to prepend to the file, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"' + # + # prepend_to_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def prepend_to_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:after => /\A/) + insert_into_file(path, *(args << config), &block) + end + alias_method :prepend_file, :prepend_to_file + + # Append text to a file. Since it depends on insert_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # data:: the data to append to the file, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # append_to_file 'config/environments/test.rb', 'config.gem "rspec"' + # + # append_to_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def append_to_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:before => /\z/) + insert_into_file(path, *(args << config), &block) + end + alias_method :append_file, :append_to_file + + # Injects text right after the class definition. Since it depends on + # insert_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # klass:: the class to be manipulated + # data:: the data to append to the class, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n" + # + # inject_into_class "app/controllers/application_controller.rb", ApplicationController do + # " filter_parameter :password\n" + # end + # + def inject_into_class(path, klass, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) + insert_into_file(path, *(args << config), &block) + end + + # Run a regular expression replacement on a file. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string to be replaced + # replacement:: the replacement, can be also given as a block + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + # gsub_file 'README', /rake/, :green do |match| + # match << " no more. Use thor!" + # end + # + def gsub_file(path, flag, *args, &block) + return unless behavior == :invoke + config = args.last.is_a?(Hash) ? args.pop : {} + + path = File.expand_path(path, destination_root) + say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) + + unless options[:pretend] + content = File.binread(path) + content.gsub!(flag, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + end + + # Uncomment all lines matching a given regex. It will leave the space + # which existed before the comment hash in tact but will remove any spacing + # between the comment hash and the beginning of the line. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string used to decide which lines to uncomment + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # uncomment_lines 'config/initializers/session_store.rb', /active_record/ + # + def uncomment_lines(path, flag, *args) + flag = flag.respond_to?(:source) ? flag.source : flag + + gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args) + end + + # Comment all lines matching a given regex. It will leave the space + # which existed before the beginning of the line in tact and will insert + # a single space after the comment hash. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string used to decide which lines to comment + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # comment_lines 'config/initializers/session_store.rb', /cookie_store/ + # + def comment_lines(path, flag, *args) + flag = flag.respond_to?(:source) ? flag.source : flag + + gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args) + end + + # Removes a file at the given location. + # + # ==== Parameters + # path:: path of the file to be changed + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # remove_file 'README' + # remove_file 'app/controllers/application_controller.rb' + # + def remove_file(path, config={}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + + say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true) + ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path) + end + alias :remove_dir :remove_file + + private + attr_accessor :output_buffer + def concat(string) + @output_buffer.concat(string) + end + + def capture(*args, &block) + with_output_buffer { block.call(*args) } + end + + def with_output_buffer(buf = '') #:nodoc: + self.output_buffer, old_buffer = buf, output_buffer + yield + output_buffer + ensure + self.output_buffer = old_buffer + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/actions/inject_into_file.rb b/vendor/gems/thor-0.18.1/lib/thor/actions/inject_into_file.rb new file mode 100644 index 0000000..c48cfab --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/actions/inject_into_file.rb @@ -0,0 +1,109 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Injects the given content into a file. Different from gsub_file, this + # method is reversible. + # + # ==== Parameters + # destination:: Relative path to the destination root + # data:: Data to add to the file. Can be given as a block. + # config:: give :verbose => false to not log the status and the flag + # for injection (:after or :before) or :force => true for + # insert two or more times the same content. + # + # ==== Examples + # + # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" + # + # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # gems = ask "Which gems would you like to add?" + # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") + # end + # + def insert_into_file(destination, *args, &block) + if block_given? + data, config = block, args.shift + else + data, config = args.shift, args.shift + end + action InjectIntoFile.new(self, destination, data, config) + end + alias_method :inject_into_file, :insert_into_file + + class InjectIntoFile < EmptyDirectory #:nodoc: + attr_reader :replacement, :flag, :behavior + + def initialize(base, destination, data, config) + super(base, destination, { :verbose => true }.merge(config)) + + @behavior, @flag = if @config.key?(:after) + [:after, @config.delete(:after)] + else + [:before, @config.delete(:before)] + end + + @replacement = data.is_a?(Proc) ? data.call : data + @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) + end + + def invoke! + say_status :invoke + + content = if @behavior == :after + '\0' + replacement + else + replacement + '\0' + end + + replace!(/#{flag}/, content, config[:force]) + end + + def revoke! + say_status :revoke + + regexp = if @behavior == :after + content = '\1\2' + /(#{flag})(.*)(#{Regexp.escape(replacement)})/m + else + content = '\2\3' + /(#{Regexp.escape(replacement)})(.*)(#{flag})/m + end + + replace!(regexp, content, true) + end + + protected + + def say_status(behavior) + status = if behavior == :invoke + if flag == /\A/ + :prepend + elsif flag == /\z/ + :append + else + :insert + end + else + :subtract + end + + super(status, config[:verbose]) + end + + # Adds the content to the file. + # + def replace!(regexp, string, force) + unless base.options[:pretend] + content = File.binread(destination) + if force || !content.include?(replacement) + content.gsub!(regexp, string) + File.open(destination, 'wb') { |file| file.write(content) } + end + end + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/base.rb b/vendor/gems/thor-0.18.1/lib/thor/base.rb new file mode 100644 index 0000000..4b5d5bd --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/base.rb @@ -0,0 +1,652 @@ +require 'thor/command' +require 'thor/core_ext/hash_with_indifferent_access' +require 'thor/core_ext/ordered_hash' +require 'thor/error' +require 'thor/invocation' +require 'thor/parser' +require 'thor/shell' +require 'thor/util' + +class Thor + autoload :Actions, 'thor/actions' + autoload :RakeCompat, 'thor/rake_compat' + autoload :Group, 'thor/group' + + # Shortcuts for help. + HELP_MAPPINGS = %w(-h -? --help -D) + + # Thor methods that should not be overwritten by the user. + THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root + action add_file create_file in_root inside run run_ruby_script) + + module Base + attr_accessor :options, :parent_options, :args + + # It receives arguments in an Array and two hashes, one for options and + # other for configuration. + # + # Notice that it does not check if all required arguments were supplied. + # It should be done by the parser. + # + # ==== Parameters + # args:: An array of objects. The objects are applied to their + # respective accessors declared with argument. + # + # options:: An options hash that will be available as self.options. + # The hash given is converted to a hash with indifferent + # access, magic predicates (options.skip?) and then frozen. + # + # config:: Configuration for this Thor class. + # + def initialize(args=[], options={}, config={}) + parse_options = self.class.class_options + + # The start method splits inbound arguments at the first argument + # that looks like an option (starts with - or --). It then calls + # new, passing in the two halves of the arguments Array as the + # first two parameters. + + if options.is_a?(Array) + command_options = config.delete(:command_options) # hook for start + parse_options = parse_options.merge(command_options) if command_options + array_options, hash_options = options, {} + else + # Handle the case where the class was explicitly instantiated + # with pre-parsed options. + array_options, hash_options = [], options + end + + # Let Thor::Options parse the options first, so it can remove + # declared options from the array. This will leave us with + # a list of arguments that weren't declared. + stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command] + opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown) + self.options = opts.parse(array_options) + self.options = config[:class_options].merge(self.options) if config[:class_options] + + # If unknown options are disallowed, make sure that none of the + # remaining arguments looks like an option. + opts.check_unknown! if self.class.check_unknown_options?(config) + + # Add the remaining arguments from the options parser to the + # arguments passed in to initialize. Then remove any positional + # arguments declared using #argument (this is primarily used + # by Thor::Group). Tis will leave us with the remaining + # positional arguments. + to_parse = args + to_parse += opts.remaining unless self.class.strict_args_position?(config) + + thor_args = Thor::Arguments.new(self.class.arguments) + thor_args.parse(to_parse).each { |k,v| __send__("#{k}=", v) } + @args = thor_args.remaining + end + + class << self + def included(base) #:nodoc: + base.send :extend, ClassMethods + base.send :include, Invocation + base.send :include, Shell + end + + # Returns the classes that inherits from Thor or Thor::Group. + # + # ==== Returns + # Array[Class] + # + def subclasses + @subclasses ||= [] + end + + # Returns the files where the subclasses are kept. + # + # ==== Returns + # Hash[path => Class] + # + def subclass_files + @subclass_files ||= Hash.new{ |h,k| h[k] = [] } + end + + # Whenever a class inherits from Thor or Thor::Group, we should track the + # class and the file on Thor::Base. This is the method responsable for it. + # + def register_klass_file(klass) #:nodoc: + file = caller[1].match(/(.*):\d+/)[1] + Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass) + + file_subclasses = Thor::Base.subclass_files[File.expand_path(file)] + file_subclasses << klass unless file_subclasses.include?(klass) + end + end + + module ClassMethods + def attr_reader(*) #:nodoc: + no_commands { super } + end + + def attr_writer(*) #:nodoc: + no_commands { super } + end + + def attr_accessor(*) #:nodoc: + no_commands { super } + end + + # If you want to raise an error for unknown options, call check_unknown_options! + # This is disabled by default to allow dynamic invocations. + def check_unknown_options! + @check_unknown_options = true + end + + def check_unknown_options #:nodoc: + @check_unknown_options ||= from_superclass(:check_unknown_options, false) + end + + def check_unknown_options?(config) #:nodoc: + !!check_unknown_options + end + + # If true, option parsing is suspended as soon as an unknown option or a + # regular argument is encountered. All remaining arguments are passed to + # the command as regular arguments. + def stop_on_unknown_option?(command_name) #:nodoc: + false + end + + # If you want only strict string args (useful when cascading thor classes), + # call strict_args_position! This is disabled by default to allow dynamic + # invocations. + def strict_args_position! + @strict_args_position = true + end + + def strict_args_position #:nodoc: + @strict_args_position ||= from_superclass(:strict_args_position, false) + end + + def strict_args_position?(config) #:nodoc: + !!strict_args_position + end + + # Adds an argument to the class and creates an attr_accessor for it. + # + # Arguments are different from options in several aspects. The first one + # is how they are parsed from the command line, arguments are retrieved + # from position: + # + # thor command NAME + # + # Instead of: + # + # thor command --name=NAME + # + # Besides, arguments are used inside your code as an accessor (self.argument), + # while options are all kept in a hash (self.options). + # + # Finally, arguments cannot have type :default or :boolean but can be + # optional (supplying :optional => :true or :required => false), although + # you cannot have a required argument after a non-required argument. If you + # try it, an error is raised. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :optional - If the argument is optional or not. + # :type - The type of the argument, can be :string, :hash, :array, :numeric. + # :default - Default value for this argument. It cannot be required and have default values. + # :banner - String to show on usage notes. + # + # ==== Errors + # ArgumentError:: Raised if you supply a required argument after a non required one. + # + def argument(name, options={}) + is_thor_reserved_word?(name, :argument) + no_commands { attr_accessor name } + + required = if options.key?(:optional) + !options[:optional] + elsif options.key?(:required) + options[:required] + else + options[:default].nil? + end + + remove_argument name + + arguments.each do |argument| + next if argument.required? + raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " << + "the non-required argument #{argument.human_name.inspect}." + end if required + + options[:required] = required + + arguments << Thor::Argument.new(name, options) + end + + # Returns this class arguments, looking up in the ancestors chain. + # + # ==== Returns + # Array[Thor::Argument] + # + def arguments + @arguments ||= from_superclass(:arguments, []) + end + + # Adds a bunch of options to the set of class options. + # + # class_options :foo => false, :bar => :required, :baz => :string + # + # If you prefer more detailed declaration, check class_option. + # + # ==== Parameters + # Hash[Symbol => Object] + # + def class_options(options=nil) + @class_options ||= from_superclass(:class_options, {}) + build_options(options, @class_options) if options + @class_options + end + + # Adds an option to the set of class options + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc:: -- Description for the argument. + # :required:: -- If the argument is required or not. + # :default:: -- Default value for this argument. + # :group:: -- The group for this options. Use by class options to output options in different levels. + # :aliases:: -- Aliases for this option. Note: Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead. + # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner:: -- String to show on usage notes. + # :hide:: -- If you want to hide this option from the help. + # + def class_option(name, options={}) + build_option(name, options, class_options) + end + + # Removes a previous defined argument. If :undefine is given, undefine + # accessors as well. + # + # ==== Parameters + # names:: Arguments to be removed + # + # ==== Examples + # + # remove_argument :foo + # remove_argument :foo, :bar, :baz, :undefine => true + # + def remove_argument(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + arguments.delete_if { |a| a.name == name.to_s } + undef_method name, "#{name}=" if options[:undefine] + end + end + + # Removes a previous defined class option. + # + # ==== Parameters + # names:: Class options to be removed + # + # ==== Examples + # + # remove_class_option :foo + # remove_class_option :foo, :bar, :baz + # + def remove_class_option(*names) + names.each do |name| + class_options.delete(name) + end + end + + # Defines the group. This is used when thor list is invoked so you can specify + # that only commands from a pre-defined group will be shown. Defaults to standard. + # + # ==== Parameters + # name + # + def group(name=nil) + @group = case name + when nil + @group || from_superclass(:group, 'standard') + else + name.to_s + end + end + + # Returns the commands for this Thor class. + # + # ==== Returns + # OrderedHash:: An ordered hash with commands names as keys and Thor::Command + # objects as values. + # + def commands + @commands ||= Thor::CoreExt::OrderedHash.new + end + alias tasks commands + + # Returns the commands for this Thor class and all subclasses. + # + # ==== Returns + # OrderedHash:: An ordered hash with commands names as keys and Thor::Command + # objects as values. + # + def all_commands + @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new) + @all_commands.merge(commands) + end + alias all_tasks all_commands + + # Removes a given command from this Thor class. This is usually done if you + # are inheriting from another class and don't want it to be available + # anymore. + # + # By default it only remove the mapping to the command. But you can supply + # :undefine => true to undefine the method from the class as well. + # + # ==== Parameters + # name:: The name of the command to be removed + # options:: You can give :undefine => true if you want commands the method + # to be undefined from the class as well. + # + def remove_command(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + commands.delete(name.to_s) + all_commands.delete(name.to_s) + undef_method name if options[:undefine] + end + end + alias remove_task remove_command + + # All methods defined inside the given block are not added as commands. + # + # So you can do: + # + # class MyScript < Thor + # no_commands do + # def this_is_not_a_command + # end + # end + # end + # + # You can also add the method and remove it from the command list: + # + # class MyScript < Thor + # def this_is_not_a_command + # end + # remove_command :this_is_not_a_command + # end + # + def no_commands + @no_commands = true + yield + ensure + @no_commands = false + end + alias no_tasks no_commands + + # Sets the namespace for the Thor or Thor::Group class. By default the + # namespace is retrieved from the class name. If your Thor class is named + # Scripts::MyScript, the help method, for example, will be called as: + # + # thor scripts:my_script -h + # + # If you change the namespace: + # + # namespace :my_scripts + # + # You change how your commands are invoked: + # + # thor my_scripts -h + # + # Finally, if you change your namespace to default: + # + # namespace :default + # + # Your commands can be invoked with a shortcut. Instead of: + # + # thor :my_command + # + def namespace(name=nil) + @namespace = case name + when nil + @namespace || Thor::Util.namespace_from_thor_class(self) + else + @namespace = name.to_s + end + end + + # Parses the command and options from the given args, instantiate the class + # and invoke the command. This method is used when the arguments must be parsed + # from an array. If you are inside Ruby and want to use a Thor class, you + # can simply initialize it: + # + # script = MyScript.new(args, options, config) + # script.invoke(:command, first_arg, second_arg, third_arg) + # + def start(given_args=ARGV, config={}) + config[:shell] ||= Thor::Base.shell.new + dispatch(nil, given_args.dup, nil, config) + rescue Thor::Error => e + ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) + exit(1) if exit_on_failure? + rescue Errno::EPIPE + # This happens if a thor command is piped to something like `head`, + # which closes the pipe when it's done reading. This will also + # mean that if the pipe is closed, further unnecessary + # computation will not occur. + exit(0) + end + + # Allows to use private methods from parent in child classes as commands. + # + # ==== Parameters + # names:: Method names to be used as commands + # + # ==== Examples + # + # public_command :foo + # public_command :foo, :bar, :baz + # + def public_command(*names) + names.each do |name| + class_eval "def #{name}(*); super end" + end + end + alias public_task public_command + + def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: + if has_namespace + raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." + else + raise UndefinedCommandError, "Could not find command #{command.inspect}." + end + end + alias handle_no_task_error handle_no_command_error + + def handle_argument_error(command, error, args, arity) #:nodoc: + msg = "ERROR: #{basename} #{command.name} was called with " + msg << 'no arguments' if args.empty? + msg << 'arguments ' << args.inspect if !args.empty? + msg << "\nUsage: #{self.banner(command).inspect}." + raise InvocationError, msg + end + + protected + + # Prints the class options per group. If an option does not belong to + # any group, it's printed as Class option. + # + def class_options_help(shell, groups={}) #:nodoc: + # Group options by group + class_options.each do |_, value| + groups[value.group] ||= [] + groups[value.group] << value + end + + # Deal with default group + global_options = groups.delete(nil) || [] + print_options(shell, global_options) + + # Print all others + groups.each do |group_name, options| + print_options(shell, options, group_name) + end + end + + # Receives a set of options and print them. + def print_options(shell, options, group_name=nil) + return if options.empty? + + list = [] + padding = options.collect{ |o| o.aliases.size }.max.to_i * 4 + + options.each do |option| + unless option.hide + item = [ option.usage(padding) ] + item.push(option.description ? "# #{option.description}" : "") + + list << item + list << [ "", "# Default: #{option.default}" ] if option.show_default? + list << [ "", "# Possible values: #{option.enum.join(', ')}" ] if option.enum + end + end + + shell.say(group_name ? "#{group_name} options:" : "Options:") + shell.print_table(list, :indent => 2) + shell.say "" + end + + # Raises an error if the word given is a Thor reserved word. + def is_thor_reserved_word?(word, type) #:nodoc: + return false unless THOR_RESERVED_WORDS.include?(word.to_s) + raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}" + end + + # Build an option and adds it to the given scope. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described in both class_option and method_option. + # scope:: Options hash that is being built up + def build_option(name, options, scope) #:nodoc: + scope[name] = Thor::Option.new(name, options) + end + + # Receives a hash of options, parse them and add to the scope. This is a + # fast way to set a bunch of options: + # + # build_options :foo => true, :bar => :required, :baz => :string + # + # ==== Parameters + # Hash[Symbol => Object] + def build_options(options, scope) #:nodoc: + options.each do |key, value| + scope[key] = Thor::Option.parse(key, value) + end + end + + # Finds a command with the given name. If the command belongs to the current + # class, just return it, otherwise dup it and add the fresh copy to the + # current command hash. + def find_and_refresh_command(name) #:nodoc: + command = if command = commands[name.to_s] + command + elsif command = all_commands[name.to_s] + commands[name.to_s] = command.clone + else + raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found." + end + end + alias find_and_refresh_task find_and_refresh_command + + # Everytime someone inherits from a Thor class, register the klass + # and file into baseclass. + def inherited(klass) + Thor::Base.register_klass_file(klass) + klass.instance_variable_set(:@no_commands, false) + end + + # Fire this callback whenever a method is added. Added methods are + # tracked as commands by invoking the create_command method. + def method_added(meth) + meth = meth.to_s + + if meth == "initialize" + initialize_added + return + end + + # Return if it's not a public instance method + return unless public_method_defined?(meth.to_sym) + + return if @no_commands || !create_command(meth) + + is_thor_reserved_word?(meth, :command) + Thor::Base.register_klass_file(self) + end + + # Retrieves a value from superclass. If it reaches the baseclass, + # returns default. + def from_superclass(method, default=nil) + if self == baseclass || !superclass.respond_to?(method, true) + default + else + value = superclass.send(method) + + if value + if value.is_a?(TrueClass) || value.is_a?(Symbol) + value + else + value.dup + end + end + end + end + + # A flag that makes the process exit with status 1 if any error happens. + def exit_on_failure? + false + end + + # + # The basename of the program invoking the thor class. + # + def basename + File.basename($0).split(' ').first + end + + # SIGNATURE: Sets the baseclass. This is where the superclass lookup + # finishes. + def baseclass #:nodoc: + end + + # SIGNATURE: Creates a new command if valid_command? is true. This method is + # called when a new method is added to the class. + def create_command(meth) #:nodoc: + end + alias create_task create_command + + # SIGNATURE: Defines behavior when the initialize method is added to the + # class. + def initialize_added #:nodoc: + end + + # SIGNATURE: The hook invoked by start. + def dispatch(command, given_args, given_opts, config) #:nodoc: + raise NotImplementedError + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/command.rb b/vendor/gems/thor-0.18.1/lib/thor/command.rb new file mode 100644 index 0000000..e56bd44 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/command.rb @@ -0,0 +1,136 @@ +class Thor + class Command < Struct.new(:name, :description, :long_description, :usage, :options) + FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/ + + def initialize(name, description, long_description, usage, options=nil) + super(name.to_s, description, long_description, usage, options || {}) + end + + def initialize_copy(other) #:nodoc: + super(other) + self.options = other.options.dup if other.options + end + + def hidden? + false + end + + # By default, a command invokes a method in the thor class. You can change this + # implementation to create custom commands. + def run(instance, args=[]) + arity = nil + + if private_method?(instance) + instance.class.handle_no_command_error(name) + elsif public_method?(instance) + arity = instance.method(name).arity + instance.__send__(name, *args) + elsif local_method?(instance, :method_missing) + instance.__send__(:method_missing, name.to_sym, *args) + else + instance.class.handle_no_command_error(name) + end + rescue ArgumentError => e + handle_argument_error?(instance, e, caller) ? + instance.class.handle_argument_error(self, e, args, arity) : (raise e) + rescue NoMethodError => e + handle_no_method_error?(instance, e, caller) ? + instance.class.handle_no_command_error(name) : (raise e) + end + + # Returns the formatted usage by injecting given required arguments + # and required options into the given usage. + def formatted_usage(klass, namespace = true, subcommand = false) + if namespace + namespace = klass.namespace + formatted = "#{namespace.gsub(/^(default)/,'')}:" + end + formatted = "#{klass.namespace.split(':').last} " if subcommand + + formatted ||= "" + + # Add usage with required arguments + formatted << if klass && !klass.arguments.empty? + usage.to_s.gsub(/^#{name}/) do |match| + match << " " << klass.arguments.map{ |a| a.usage }.compact.join(' ') + end + else + usage.to_s + end + + # Add required options + formatted << " #{required_options}" + + # Strip and go! + formatted.strip + end + + protected + + def not_debugging?(instance) + !(instance.class.respond_to?(:debugging) && instance.class.debugging) + end + + def required_options + @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ") + end + + # Given a target, checks if this class name is a public method. + def public_method?(instance) #:nodoc: + !(instance.public_methods & [name.to_s, name.to_sym]).empty? + end + + def private_method?(instance) + !(instance.private_methods & [name.to_s, name.to_sym]).empty? + end + + def local_method?(instance, name) + methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false) + !(methods & [name.to_s, name.to_sym]).empty? + end + + def sans_backtrace(backtrace, caller) #:nodoc: + saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) } + saned -= caller + end + + def handle_argument_error?(instance, error, caller) + not_debugging?(instance) && error.message =~ /wrong number of arguments/ && begin + saned = sans_backtrace(error.backtrace, caller) + # Ruby 1.9 always include the called method in the backtrace + saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9") + end + end + + def handle_no_method_error?(instance, error, caller) + not_debugging?(instance) && + error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ + end + end + Task = Command + + # A command that is hidden in help messages but still invocable. + class HiddenCommand < Command + def hidden? + true + end + end + HiddenTask = HiddenCommand + + # A dynamic command that handles method missing scenarios. + class DynamicCommand < Command + def initialize(name, options=nil) + super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options) + end + + def run(instance, args=[]) + if (instance.methods & [name.to_s, name.to_sym]).empty? + super + else + instance.class.handle_no_command_error(name) + end + end + end + DynamicTask = DynamicCommand + +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/core_ext/hash_with_indifferent_access.rb b/vendor/gems/thor-0.18.1/lib/thor/core_ext/hash_with_indifferent_access.rb new file mode 100644 index 0000000..0a583e6 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/core_ext/hash_with_indifferent_access.rb @@ -0,0 +1,80 @@ +class Thor + module CoreExt #:nodoc: + + # A hash with indifferent access and magic predicates. + # + # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true + # + # hash[:foo] #=> 'bar' + # hash['foo'] #=> 'bar' + # hash.foo? #=> true + # + class HashWithIndifferentAccess < ::Hash #:nodoc: + + def initialize(hash={}) + super() + hash.each do |key, value| + self[convert_key(key)] = value + end + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def values_at(*indices) + indices.collect { |key| self[convert_key(key)] } + end + + def merge(other) + dup.merge!(other) + end + + def merge!(other) + other.each do |key, value| + self[convert_key(key)] = value + end + self + end + + # Convert to a Hash with String keys. + def to_hash + Hash.new(default).merge!(self) + end + + protected + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + # Magic predicates. For instance: + # + # options.force? # => !!options['force'] + # options.shebang # => "/usr/lib/local/ruby" + # options.test_framework?(:rspec) # => options[:test_framework] == :rspec + # + def method_missing(method, *args, &block) + method = method.to_s + if method =~ /^(\w+)\?$/ + if args.empty? + !!self[$1] + else + self[$1] == args.first + end + else + self[method] + end + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/core_ext/io_binary_read.rb b/vendor/gems/thor-0.18.1/lib/thor/core_ext/io_binary_read.rb new file mode 100644 index 0000000..a824f1b --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/core_ext/io_binary_read.rb @@ -0,0 +1,12 @@ +class IO #:nodoc: + class << self + + def binread(file, *args) + raise ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3 + File.open(file, 'rb') do |f| + f.read(*args) + end + end unless method_defined? :binread + + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/core_ext/ordered_hash.rb b/vendor/gems/thor-0.18.1/lib/thor/core_ext/ordered_hash.rb new file mode 100644 index 0000000..27fea5b --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/core_ext/ordered_hash.rb @@ -0,0 +1,100 @@ +class Thor + module CoreExt #:nodoc: + + if RUBY_VERSION >= '1.9' + class OrderedHash < ::Hash + end + else + # This class is based on the Ruby 1.9 ordered hashes. + # + # It keeps the semantics and most of the efficiency of normal hashes + # while also keeping track of the order in which elements were set. + # + class OrderedHash #:nodoc: + include Enumerable + + Node = Struct.new(:key, :value, :next, :prev) + + def initialize + @hash = {} + end + + def [](key) + @hash[key] && @hash[key].value + end + + def []=(key, value) + if node = @hash[key] + node.value = value + else + node = Node.new(key, value) + + if @first.nil? + @first = @last = node + else + node.prev = @last + @last.next = node + @last = node + end + end + + @hash[key] = node + value + end + + def delete(key) + if node = @hash[key] + prev_node = node.prev + next_node = node.next + + next_node.prev = prev_node if next_node + prev_node.next = next_node if prev_node + + @first = next_node if @first == node + @last = prev_node if @last == node + + value = node.value + end + + @hash.delete(key) + value + end + + def keys + self.map { |k, v| k } + end + + def values + self.map { |k, v| v } + end + + def each + return unless @first + yield [@first.key, @first.value] + node = @first + yield [node.key, node.value] while node = node.next + self + end + + def merge(other) + hash = self.class.new + + self.each do |key, value| + hash[key] = value + end + + other.each do |key, value| + hash[key] = value + end + + hash + end + + def empty? + @hash.empty? + end + end + end + + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/error.rb b/vendor/gems/thor-0.18.1/lib/thor/error.rb new file mode 100644 index 0000000..31e0c4b --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/error.rb @@ -0,0 +1,28 @@ +class Thor + # Thor::Error is raised when it's caused by wrong usage of thor classes. Those + # errors have their backtrace suppressed and are nicely shown to the user. + # + # Errors that are caused by the developer, like declaring a method which + # overwrites a thor keyword, it SHOULD NOT raise a Thor::Error. This way, we + # ensure that developer errors are shown with full backtrace. + class Error < StandardError + end + + # Raised when a command was not found. + class UndefinedCommandError < Error + end + UndefinedTaskError = UndefinedCommandError + + # Raised when a command was found, but not invoked properly. + class InvocationError < Error + end + + class UnknownArgumentError < Error + end + + class RequiredArgumentMissingError < InvocationError + end + + class MalformattedArgumentError < InvocationError + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/group.rb b/vendor/gems/thor-0.18.1/lib/thor/group.rb new file mode 100644 index 0000000..2aaee73 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/group.rb @@ -0,0 +1,282 @@ +require 'thor/base' + +# Thor has a special class called Thor::Group. The main difference to Thor class +# is that it invokes all commands at once. It also include some methods that allows +# invocations to be done at the class method, which are not available to Thor +# commands. +class Thor::Group + class << self + # The description for this Thor::Group. If none is provided, but a source root + # exists, tries to find the USAGE one folder above it, otherwise searches + # in the superclass. + # + # ==== Parameters + # description:: The description for this Thor::Group. + # + def desc(description=nil) + @desc = case description + when nil + @desc || from_superclass(:desc, nil) + else + description + end + end + + # Prints help information. + # + # ==== Options + # short:: When true, shows only usage. + # + def help(shell) + shell.say "Usage:" + shell.say " #{banner}\n" + shell.say + class_options_help(shell) + shell.say self.desc if self.desc + end + + # Stores invocations for this class merging with superclass values. + # + def invocations #:nodoc: + @invocations ||= from_superclass(:invocations, {}) + end + + # Stores invocation blocks used on invoke_from_option. + # + def invocation_blocks #:nodoc: + @invocation_blocks ||= from_superclass(:invocation_blocks, {}) + end + + # Invoke the given namespace or class given. It adds an instance + # method that will invoke the klass and command. You can give a block to + # configure how it will be invoked. + # + # The namespace/class given will have its options showed on the help + # usage. Check invoke_from_option for more information. + # + def invoke(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, true) + + names.each do |name| + invocations[name] = false + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_#{name.to_s.gsub(/\W/, '_')} + klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) + + if klass + say_status :invoke, #{name.inspect}, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, command, &block + else + say_status :error, %(#{name.inspect} [not found]), :red + end + end + METHOD + end + end + + # Invoke a thor class based on the value supplied by the user to the + # given option named "name". A class option must be created before this + # method is invoked for each name given. + # + # ==== Examples + # + # class GemGenerator < Thor::Group + # class_option :test_framework, :type => :string + # invoke_from_option :test_framework + # end + # + # ==== Boolean options + # + # In some cases, you want to invoke a thor class if some option is true or + # false. This is automatically handled by invoke_from_option. Then the + # option name is used to invoke the generator. + # + # ==== Preparing for invocation + # + # In some cases you want to customize how a specified hook is going to be + # invoked. You can do that by overwriting the class method + # prepare_for_invocation. The class method must necessarily return a klass + # and an optional command. + # + # ==== Custom invocations + # + # You can also supply a block to customize how the option is going to be + # invoked. The block receives two parameters, an instance of the current + # class and the klass to be invoked. + # + def invoke_from_option(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + unless class_options.key?(name) + raise ArgumentError, "You have to define the option #{name.inspect} " << + "before setting invoke_from_option." + end + + invocations[name] = true + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} + return unless options[#{name.inspect}] + + value = options[#{name.inspect}] + value = #{name.inspect} if TrueClass === value + klass, command = self.class.prepare_for_invocation(#{name.inspect}, value) + + if klass + say_status :invoke, value, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, command, &block + else + say_status :error, %(\#{value} [not found]), :red + end + end + METHOD + end + end + + # Remove a previously added invocation. + # + # ==== Examples + # + # remove_invocation :test_framework + # + def remove_invocation(*names) + names.each do |name| + remove_command(name) + remove_class_option(name) + invocations.delete(name) + invocation_blocks.delete(name) + end + end + + # Overwrite class options help to allow invoked generators options to be + # shown recursively when invoking a generator. + # + def class_options_help(shell, groups={}) #:nodoc: + get_options_from_invocations(groups, class_options) do |klass| + klass.send(:get_options_from_invocations, groups, class_options) + end + super(shell, groups) + end + + # Get invocations array and merge options from invocations. Those + # options are added to group_options hash. Options that already exists + # in base_options are not added twice. + # + def get_options_from_invocations(group_options, base_options) #:nodoc: + invocations.each do |name, from_option| + value = if from_option + option = class_options[name] + option.type == :boolean ? name : option.default + else + name + end + next unless value + + klass, _ = prepare_for_invocation(name, value) + next unless klass && klass.respond_to?(:class_options) + + value = value.to_s + human_name = value.respond_to?(:classify) ? value.classify : value + + group_options[human_name] ||= [] + group_options[human_name] += klass.class_options.values.select do |class_option| + base_options[class_option.name.to_sym].nil? && class_option.group.nil? && + !group_options.values.flatten.any? { |i| i.name == class_option.name } + end + + yield klass if block_given? + end + end + + # Returns commands ready to be printed. + def printable_commands(*) + item = [] + item << banner + item << (desc ? "# #{desc.gsub(/\s+/m,' ')}" : "") + [item] + end + alias printable_tasks printable_commands + + def handle_argument_error(command, error, args, arity) #:nodoc: + msg = "#{basename} #{command.name} takes #{arity} argument" + msg << "s" if arity > 1 + msg << ", but it should not." + raise error, msg + end + + protected + + # The method responsible for dispatching given the args. + def dispatch(command, given_args, given_opts, config) #:nodoc: + if Thor::HELP_MAPPINGS.include?(given_args.first) + help(config[:shell]) + return + end + + args, opts = Thor::Options.split(given_args) + opts = given_opts || opts + + instance = new(args, opts, config) + yield instance if block_given? + + if command + instance.invoke_command(all_commands[command]) + else + instance.invoke_all + end + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Thor::Runner. + def banner + "#{basename} #{self_command.formatted_usage(self, false)}" + end + + # Represents the whole class as a command. + def self_command #:nodoc: + Thor::DynamicCommand.new(self.namespace, class_options) + end + alias self_task self_command + + def baseclass #:nodoc: + Thor::Group + end + + def create_command(meth) #:nodoc: + commands[meth.to_s] = Thor::Command.new(meth, nil, nil, nil, nil) + true + end + alias create_task create_command + end + + include Thor::Base + + protected + + # Shortcut to invoke with padding and block handling. Use internally by + # invoke and invoke_from_option class methods. + def _invoke_for_class_method(klass, command=nil, *args, &block) #:nodoc: + with_padding do + if block + case block.arity + when 3 + block.call(self, klass, command) + when 2 + block.call(self, klass) + when 1 + instance_exec(klass, &block) + end + else + invoke klass, command, *args + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/invocation.rb b/vendor/gems/thor-0.18.1/lib/thor/invocation.rb new file mode 100644 index 0000000..a9adeb5 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/invocation.rb @@ -0,0 +1,172 @@ +class Thor + module Invocation + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # This method is responsible for receiving a name and find the proper + # class and command for it. The key is an optional parameter which is + # available only in class methods invocations (i.e. in Thor::Group). + def prepare_for_invocation(key, name) #:nodoc: + case name + when Symbol, String + Thor::Util.find_class_and_command_by_namespace(name.to_s, !key) + else + name + end + end + end + + # Make initializer aware of invocations and the initialization args. + def initialize(args=[], options={}, config={}, &block) #:nodoc: + @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] } + @_initializer = [ args, options, config ] + super + end + + # Receives a name and invokes it. The name can be a string (either "command" or + # "namespace:command"), a Thor::Command, a Class or a Thor instance. If the + # command cannot be guessed by name, it can also be supplied as second argument. + # + # You can also supply the arguments, options and configuration values for + # the command to be invoked, if none is given, the same values used to + # initialize the invoker are used to initialize the invoked. + # + # When no name is given, it will invoke the default command of the current class. + # + # ==== Examples + # + # class A < Thor + # def foo + # invoke :bar + # invoke "b:hello", ["José"] + # end + # + # def bar + # invoke "b:hello", ["José"] + # end + # end + # + # class B < Thor + # def hello(name) + # puts "hello #{name}" + # end + # end + # + # You can notice that the method "foo" above invokes two commands: "bar", + # which belongs to the same class and "hello" which belongs to the class B. + # + # By using an invocation system you ensure that a command is invoked only once. + # In the example above, invoking "foo" will invoke "b:hello" just once, even + # if it's invoked later by "bar" method. + # + # When class A invokes class B, all arguments used on A initialization are + # supplied to B. This allows lazy parse of options. Let's suppose you have + # some rspec commands: + # + # class Rspec < Thor::Group + # class_option :mock_framework, :type => :string, :default => :rr + # + # def invoke_mock_framework + # invoke "rspec:#{options[:mock_framework]}" + # end + # end + # + # As you noticed, it invokes the given mock framework, which might have its + # own options: + # + # class Rspec::RR < Thor::Group + # class_option :style, :type => :string, :default => :mock + # end + # + # Since it's not rspec concern to parse mock framework options, when RR + # is invoked all options are parsed again, so RR can extract only the options + # that it's going to use. + # + # If you want Rspec::RR to be initialized with its own set of options, you + # have to do that explicitly: + # + # invoke "rspec:rr", [], :style => :foo + # + # Besides giving an instance, you can also give a class to invoke: + # + # invoke Rspec::RR, [], :style => :foo + # + def invoke(name=nil, *args) + if name.nil? + warn "[Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}" + return invoke_all + end + + args.unshift(nil) if Array === args.first || NilClass === args.first + command, args, opts, config = args + + klass, command = _retrieve_class_and_command(name, command) + raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base + + args, opts, config = _parse_initialization_options(args, opts, config) + klass.send(:dispatch, command, args, opts, config) do |instance| + instance.parent_options = options + end + end + + # Invoke the given command if the given args. + def invoke_command(command, *args) #:nodoc: + current = @_invocations[self.class] + + unless current.include?(command.name) + current << command.name + command.run(self, *args) + end + end + alias invoke_task invoke_command + + # Invoke all commands for the current instance. + def invoke_all #:nodoc: + self.class.all_commands.map { |_, command| invoke_command(command) } + end + + # Invokes using shell padding. + def invoke_with_padding(*args) + with_padding { invoke(*args) } + end + + protected + + # Configuration values that are shared between invocations. + def _shared_configuration #:nodoc: + { :invocations => @_invocations } + end + + # This method simply retrieves the class and command to be invoked. + # If the name is nil or the given name is a command in the current class, + # use the given name and return self as class. Otherwise, call + # prepare_for_invocation in the current class. + def _retrieve_class_and_command(name, sent_command=nil) #:nodoc: + case + when name.nil? + [self.class, nil] + when self.class.all_commands[name.to_s] + [self.class, name.to_s] + else + klass, command = self.class.prepare_for_invocation(nil, name) + [klass, command || sent_command] + end + end + alias _retrieve_class_and_task _retrieve_class_and_command + + # Initialize klass using values stored in the @_initializer. + def _parse_initialization_options(args, opts, config) #:nodoc: + stored_args, stored_opts, stored_config = @_initializer + + args ||= stored_args.dup + opts ||= stored_opts.dup + + config ||= {} + config = stored_config.merge(_shared_configuration).merge!(config) + + [ args, opts, config ] + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/parser.rb b/vendor/gems/thor-0.18.1/lib/thor/parser.rb new file mode 100644 index 0000000..57a3f6e --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/parser.rb @@ -0,0 +1,4 @@ +require 'thor/parser/argument' +require 'thor/parser/arguments' +require 'thor/parser/option' +require 'thor/parser/options' diff --git a/vendor/gems/thor-0.18.1/lib/thor/parser/argument.rb b/vendor/gems/thor-0.18.1/lib/thor/parser/argument.rb new file mode 100644 index 0000000..39ef9f2 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/parser/argument.rb @@ -0,0 +1,74 @@ +class Thor + class Argument #:nodoc: + VALID_TYPES = [ :numeric, :hash, :array, :string ] + + attr_reader :name, :description, :enum, :required, :type, :default, :banner + alias :human_name :name + + def initialize(name, options={}) + class_name = self.class.name.split("::").last + + type = options[:type] + + raise ArgumentError, "#{class_name} name can't be nil." if name.nil? + raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) + + @name = name.to_s + @description = options[:desc] + @required = options.key?(:required) ? options[:required] : true + @type = (type || :string).to_sym + @default = options[:default] + @banner = options[:banner] || default_banner + @enum = options[:enum] + + validate! # Trigger specific validations + end + + def usage + required? ? banner : "[#{banner}]" + end + + def required? + required + end + + def show_default? + case default + when Array, String, Hash + !default.empty? + else + default + end + end + + protected + + def validate! + if required? && !default.nil? + raise ArgumentError, "An argument cannot be required and have default value." + elsif @enum && !@enum.is_a?(Array) + raise ArgumentError, "An argument cannot have an enum other than an array." + end + end + + def valid_type?(type) + self.class::VALID_TYPES.include?(type.to_sym) + end + + def default_banner + case type + when :boolean + nil + when :string, :default + human_name.upcase + when :numeric + "N" + when :hash + "key:value" + when :array + "one two three" + end + end + + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/parser/arguments.rb b/vendor/gems/thor-0.18.1/lib/thor/parser/arguments.rb new file mode 100644 index 0000000..f86166d --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/parser/arguments.rb @@ -0,0 +1,171 @@ +class Thor + class Arguments #:nodoc: + NUMERIC = /(\d*\.\d+|\d+)/ + + # Receives an array of args and returns two arrays, one with arguments + # and one with switches. + # + def self.split(args) + arguments = [] + + args.each do |item| + break if item =~ /^-/ + arguments << item + end + + return arguments, args[Range.new(arguments.size, -1)] + end + + def self.parse(*args) + to_parse = args.pop + new(*args).parse(to_parse) + end + + # Takes an array of Thor::Argument objects. + # + def initialize(arguments=[]) + @assigns, @non_assigned_required = {}, [] + @switches = arguments + + arguments.each do |argument| + if argument.default != nil + @assigns[argument.human_name] = argument.default + elsif argument.required? + @non_assigned_required << argument + end + end + end + + def parse(args) + @pile = args.dup + + @switches.each do |argument| + break unless peek + @non_assigned_required.delete(argument) + @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) + end + + check_requirement! + @assigns + end + + def remaining + @pile + end + + private + + def no_or_skip?(arg) + arg =~ /^--(no|skip)-([-\w]+)$/ + $2 + end + + def last? + @pile.empty? + end + + def peek + @pile.first + end + + def shift + @pile.shift + end + + def unshift(arg) + unless arg.kind_of?(Array) + @pile.unshift(arg) + else + @pile = arg + @pile + end + end + + def current_is_value? + peek && peek.to_s !~ /^-/ + end + + # Runs through the argument array getting strings that contains ":" and + # mark it as a hash: + # + # [ "name:string", "age:integer" ] + # + # Becomes: + # + # { "name" => "string", "age" => "integer" } + # + def parse_hash(name) + return shift if peek.is_a?(Hash) + hash = {} + + while current_is_value? && peek.include?(?:) + key, value = shift.split(':',2) + hash[key] = value + end + hash + end + + # Runs through the argument array getting all strings until no string is + # found or a switch is found. + # + # ["a", "b", "c"] + # + # And returns it as an array: + # + # ["a", "b", "c"] + # + def parse_array(name) + return shift if peek.is_a?(Array) + array = [] + + while current_is_value? + array << shift + end + array + end + + # Check if the peek is numeric format and return a Float or Integer. + # Otherwise raises an error. + # + def parse_numeric(name) + return shift if peek.is_a?(Numeric) + + unless peek =~ NUMERIC && $& == peek + raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}" + end + + $&.index('.') ? shift.to_f : shift.to_i + end + + # Parse string: + # for --string-arg, just return the current value in the pile + # for --no-string-arg, nil + # + def parse_string(name) + if no_or_skip?(name) + nil + else + value = shift + if @switches.is_a?(Hash) && switch = @switches[name] + if switch.enum && !switch.enum.include?(value) + raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" + end + end + value + end + end + + # Raises an error if @non_assigned_required array is not empty. + # + def check_requirement! + unless @non_assigned_required.empty? + names = @non_assigned_required.map do |o| + o.respond_to?(:switch_name) ? o.switch_name : o.human_name + end.join("', '") + + class_name = self.class.name.split('::').last.downcase + raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'" + end + end + + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/parser/option.rb b/vendor/gems/thor-0.18.1/lib/thor/parser/option.rb new file mode 100644 index 0000000..4781069 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/parser/option.rb @@ -0,0 +1,121 @@ +class Thor + class Option < Argument #:nodoc: + attr_reader :aliases, :group, :lazy_default, :hide + + VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] + + def initialize(name, options={}) + options[:required] = false unless options.key?(:required) + super + @lazy_default = options[:lazy_default] + @group = options[:group].to_s.capitalize if options[:group] + @aliases = Array(options[:aliases]) + @hide = options[:hide] + end + + # This parse quick options given as method_options. It makes several + # assumptions, but you can be more specific using the option method. + # + # parse :foo => "bar" + # #=> Option foo with default value bar + # + # parse [:foo, :baz] => "bar" + # #=> Option foo with default value bar and alias :baz + # + # parse :foo => :required + # #=> Required option foo without default value + # + # parse :foo => 2 + # #=> Option foo with default value 2 and type numeric + # + # parse :foo => :numeric + # #=> Option foo without default value and type numeric + # + # parse :foo => true + # #=> Option foo with default value true and type boolean + # + # The valid types are :boolean, :numeric, :hash, :array and :string. If none + # is given a default type is assumed. This default type accepts arguments as + # string (--foo=value) or booleans (just --foo). + # + # By default all options are optional, unless :required is given. + # + def self.parse(key, value) + if key.is_a?(Array) + name, *aliases = key + else + name, aliases = key, [] + end + + name = name.to_s + default = value + + type = case value + when Symbol + default = nil + if VALID_TYPES.include?(value) + value + elsif required = (value == :required) + :string + end + when TrueClass, FalseClass + :boolean + when Numeric + :numeric + when Hash, Array, String + value.class.name.downcase.to_sym + end + self.new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases) + end + + def switch_name + @switch_name ||= dasherized? ? name : dasherize(name) + end + + def human_name + @human_name ||= dasherized? ? undasherize(name) : name + end + + def usage(padding=0) + sample = if banner && !banner.to_s.empty? + "#{switch_name}=#{banner}" + else + switch_name + end + + sample = "[#{sample}]" unless required? + + if aliases.empty? + (" " * padding) << sample + else + "#{aliases.join(', ')}, #{sample}" + end + end + + VALID_TYPES.each do |type| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{type}? + self.type == #{type.inspect} + end + RUBY + end + + protected + + def validate! + raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? + end + + def dasherized? + name.index('-') == 0 + end + + def undasherize(str) + str.sub(/^-{1,2}/, '') + end + + def dasherize(str) + (str.length > 1 ? "--" : "-") + str.gsub('_', '-') + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/parser/options.rb b/vendor/gems/thor-0.18.1/lib/thor/parser/options.rb new file mode 100644 index 0000000..9542e81 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/parser/options.rb @@ -0,0 +1,218 @@ +class Thor + class Options < Arguments #:nodoc: + LONG_RE = /^(--\w+(?:-\w+)*)$/ + SHORT_RE = /^(-[a-z])$/i + EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i + SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args + SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i + OPTS_END = '--'.freeze + + # Receives a hash and makes it switches. + def self.to_switches(options) + options.map do |key, value| + case value + when true + "--#{key}" + when Array + "--#{key} #{value.map{ |v| v.inspect }.join(' ')}" + when Hash + "--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}" + when nil, false + "" + else + "--#{key} #{value.inspect}" + end + end.join(" ") + end + + # Takes a hash of Thor::Option and a hash with defaults. + # + # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters + # an unknown option or a regular argument. + def initialize(hash_options={}, defaults={}, stop_on_unknown=false) + @stop_on_unknown = stop_on_unknown + options = hash_options.values + super(options) + + # Add defaults + defaults.each do |key, value| + @assigns[key.to_s] = value + @non_assigned_required.delete(hash_options[key]) + end + + @shorts, @switches, @extra = {}, {}, [] + + options.each do |option| + @switches[option.switch_name] = option + + option.aliases.each do |short| + name = short.to_s.sub(/^(?!\-)/, '-') + @shorts[name] ||= option.switch_name + end + end + end + + def remaining + @extra + end + + def peek + return super unless @parsing_options + + result = super + if result == OPTS_END + shift + @parsing_options = false + super + else + result + end + end + + def parse(args) + @pile = args.dup + @parsing_options = true + + while peek + if parsing_options? + match, is_switch = current_is_switch? + shifted = shift + + if is_switch + case shifted + when SHORT_SQ_RE + unshift($1.split('').map { |f| "-#{f}" }) + next + when EQ_RE, SHORT_NUM + unshift($2) + switch = $1 + when LONG_RE, SHORT_RE + switch = $1 + end + + switch = normalize_switch(switch) + option = switch_option(switch) + @assigns[option.human_name] = parse_peek(switch, option) + elsif @stop_on_unknown + @parsing_options = false + @extra << shifted + @extra << shift while peek + break + elsif match + @extra << shifted + @extra << shift while peek && peek !~ /^-/ + else + @extra << shifted + end + else + @extra << shift + end + end + + check_requirement! + + assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns) + assigns.freeze + assigns + end + + def check_unknown! + # an unknown option starts with - or -- and has no more --'s afterward. + unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } + raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? + end + + protected + + # Check if the current value in peek is a registered switch. + # + # Two booleans are returned. The first is true if the current value + # starts with a hyphen; the second is true if it is a registered switch. + def current_is_switch? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM + [true, switch?($1)] + when SHORT_SQ_RE + [true, $1.split('').any? { |f| switch?("-#{f}") }] + else + [false, false] + end + end + + def current_is_switch_formatted? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE + true + else + false + end + end + + def current_is_value? + peek && (!parsing_options? || super) + end + + def switch?(arg) + switch_option(normalize_switch(arg)) + end + + def switch_option(arg) + if match = no_or_skip?(arg) + @switches[arg] || @switches["--#{match}"] + else + @switches[arg] + end + end + + # Check if the given argument is actually a shortcut. + # + def normalize_switch(arg) + (@shorts[arg] || arg).tr('_', '-') + end + + def parsing_options? + peek + @parsing_options + end + + # Parse boolean values which can be given as --foo=true, --foo or --no-foo. + # + def parse_boolean(switch) + if current_is_value? + if ["true", "TRUE", "t", "T", true].include?(peek) + shift + true + elsif ["false", "FALSE", "f", "F", false].include?(peek) + shift + false + else + true + end + else + @switches.key?(switch) || !no_or_skip?(switch) + end + end + + # Parse the value at the peek analyzing if it requires an input or not. + # + def parse_peek(switch, option) + if parsing_options? && (current_is_switch_formatted? || last?) + if option.boolean? + # No problem for boolean types + elsif no_or_skip?(switch) + return nil # User set value to nil + elsif option.string? && !option.required? + # Return the default if there is one, else the human name + return option.lazy_default || option.default || option.human_name + elsif option.lazy_default + return option.lazy_default + else + raise MalformattedArgumentError, "No value provided for option '#{switch}'" + end + end + + @non_assigned_required.delete(option) + send(:"parse_#{option.type}", switch) + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/rake_compat.rb b/vendor/gems/thor-0.18.1/lib/thor/rake_compat.rb new file mode 100644 index 0000000..fcb3b24 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/rake_compat.rb @@ -0,0 +1,72 @@ +require 'rake' +require 'rake/dsl_definition' + +class Thor + # Adds a compatibility layer to your Thor classes which allows you to use + # rake package tasks. For example, to use rspec rake tasks, one can do: + # + # require 'thor/rake_compat' + # require 'rspec/core/rake_task' + # + # class Default < Thor + # include Thor::RakeCompat + # + # RSpec::Core::RakeTask.new(:spec) do |t| + # t.spec_opts = ['--options', "./.rspec"] + # t.spec_files = FileList['spec/**/*_spec.rb'] + # end + # end + # + module RakeCompat + include Rake::DSL if defined?(Rake::DSL) + + def self.rake_classes + @rake_classes ||= [] + end + + def self.included(base) + # Hack. Make rakefile point to invoker, so rdoc task is generated properly. + rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) + Rake.application.instance_variable_set(:@rakefile, rakefile) + self.rake_classes << base + end + end +end + +# override task on (main), for compatibility with Rake 0.9 +self.instance_eval do + alias rake_namespace namespace + + def task(*) + task = super + + if klass = Thor::RakeCompat.rake_classes.last + non_namespaced_name = task.name.split(':').last + + description = non_namespaced_name + description << task.arg_names.map{ |n| n.to_s.upcase }.join(' ') + description.strip! + + klass.desc description, Rake.application.last_description || non_namespaced_name + Rake.application.last_description = nil + klass.send :define_method, non_namespaced_name do |*args| + Rake::Task[task.name.to_sym].invoke(*args) + end + end + + task + end + + def namespace(name) + if klass = Thor::RakeCompat.rake_classes.last + const_name = Thor::Util.camel_case(name.to_s).to_sym + klass.const_set(const_name, Class.new(Thor)) + new_klass = klass.const_get(const_name) + Thor::RakeCompat.rake_classes << new_klass + end + + super + Thor::RakeCompat.rake_classes.pop + end +end + diff --git a/vendor/gems/thor-0.18.1/lib/thor/runner.rb b/vendor/gems/thor-0.18.1/lib/thor/runner.rb new file mode 100644 index 0000000..e51ceb5 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/runner.rb @@ -0,0 +1,322 @@ +require 'thor' +require 'thor/group' +require 'thor/core_ext/io_binary_read' + +require 'fileutils' +require 'open-uri' +require 'yaml' +require 'digest/md5' +require 'pathname' + +class Thor::Runner < Thor #:nodoc: + map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version + + # Override Thor#help so it can give information about any class and any method. + # + def help(meth = nil) + if meth && !self.respond_to?(meth) + initialize_thorfiles(meth) + klass, command = Thor::Util.find_class_and_command_by_namespace(meth) + self.class.handle_no_command_error(command, false) if klass.nil? + klass.start(["-h", command].compact, :shell => self.shell) + else + super + end + end + + # If a command is not found on Thor::Runner, method missing is invoked and + # Thor::Runner is then responsible for finding the command in all classes. + # + def method_missing(meth, *args) + meth = meth.to_s + initialize_thorfiles(meth) + klass, command = Thor::Util.find_class_and_command_by_namespace(meth) + self.class.handle_no_command_error(command, false) if klass.nil? + args.unshift(command) if command + klass.start(args, :shell => self.shell) + end + + desc "install NAME", "Install an optionally named Thor file into your system commands" + method_options :as => :string, :relative => :boolean, :force => :boolean + def install(name) + initialize_thorfiles + + # If a directory name is provided as the argument, look for a 'main.thor' + # command in said directory. + begin + if File.directory?(File.expand_path(name)) + base, package = File.join(name, "main.thor"), :directory + contents = open(base) {|input| input.read } + else + base, package = name, :file + contents = open(name) {|input| input.read } + end + rescue OpenURI::HTTPError + raise Error, "Error opening URI '#{name}'" + rescue Errno::ENOENT + raise Error, "Error opening file '#{name}'" + end + + say "Your Thorfile contains:" + say contents + + unless options["force"] + return false if no?("Do you wish to continue [y/N]?") + end + + as = options["as"] || begin + first_line = contents.split("\n")[0] + (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil + end + + unless as + basename = File.basename(name) + as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") + as = basename if as.empty? + end + + location = if options[:relative] || name =~ /^https?:\/\// + name + else + File.expand_path(name) + end + + thor_yaml[as] = { + :filename => Digest::MD5.hexdigest(name + as), + :location => location, + :namespaces => Thor::Util.namespaces_in_content(contents, base) + } + + save_yaml(thor_yaml) + say "Storing thor file in your system repository" + destination = File.join(thor_root, thor_yaml[as][:filename]) + + if package == :file + File.open(destination, "w") { |f| f.puts contents } + else + FileUtils.cp_r(name, destination) + end + + thor_yaml[as][:filename] # Indicate success + end + + desc "version", "Show Thor version" + def version + require 'thor/version' + say "Thor #{Thor::VERSION}" + end + + desc "uninstall NAME", "Uninstall a named Thor module" + def uninstall(name) + raise Error, "Can't find module '#{name}'" unless thor_yaml[name] + say "Uninstalling #{name}." + FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) + + thor_yaml.delete(name) + save_yaml(thor_yaml) + + puts "Done." + end + + desc "update NAME", "Update a Thor file from its original location" + def update(name) + raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] + + say "Updating '#{name}' from #{thor_yaml[name][:location]}" + + old_filename = thor_yaml[name][:filename] + self.options = self.options.merge("as" => name) + + if File.directory? File.expand_path(name) + FileUtils.rm_rf(File.join(thor_root, old_filename)) + + thor_yaml.delete(old_filename) + save_yaml(thor_yaml) + + filename = install(name) + else + filename = install(thor_yaml[name][:location]) + end + + unless filename == old_filename + File.delete(File.join(thor_root, old_filename)) + end + end + + desc "installed", "List the installed Thor modules and commands" + method_options :internal => :boolean + def installed + initialize_thorfiles(nil, true) + display_klasses(true, options["internal"]) + end + + desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)" + method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean + def list(search="") + initialize_thorfiles + + search = ".*#{search}" if options["substring"] + search = /^#{search}.*/i + group = options[:group] || "standard" + + klasses = Thor::Base.subclasses.select do |k| + (options[:all] || k.group == group) && k.namespace =~ search + end + + display_klasses(false, false, klasses) + end + + private + + def self.banner(command, all = false, subcommand = false) + "thor " + command.formatted_usage(self, all, subcommand) + end + + def thor_root + Thor::Util.thor_root + end + + def thor_yaml + @thor_yaml ||= begin + yaml_file = File.join(thor_root, "thor.yml") + yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file) + yaml || {} + end + end + + # Save the yaml file. If none exists in thor root, creates one. + # + def save_yaml(yaml) + yaml_file = File.join(thor_root, "thor.yml") + + unless File.exists?(yaml_file) + FileUtils.mkdir_p(thor_root) + yaml_file = File.join(thor_root, "thor.yml") + FileUtils.touch(yaml_file) + end + + File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } + end + + def self.exit_on_failure? + true + end + + # Load the Thorfiles. If relevant_to is supplied, looks for specific files + # in the thor_root instead of loading them all. + # + # By default, it also traverses the current path until find Thor files, as + # described in thorfiles. This look up can be skipped by suppliying + # skip_lookup true. + # + def initialize_thorfiles(relevant_to=nil, skip_lookup=false) + thorfiles(relevant_to, skip_lookup).each do |f| + Thor::Util.load_thorfile(f, nil, options[:debug]) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f)) + end + end + + # Finds Thorfiles by traversing from your current directory down to the root + # directory of your system. If at any time we find a Thor file, we stop. + # + # We also ensure that system-wide Thorfiles are loaded first, so local + # Thorfiles can override them. + # + # ==== Example + # + # If we start at /Users/wycats/dev/thor ... + # + # 1. /Users/wycats/dev/thor + # 2. /Users/wycats/dev + # 3. /Users/wycats <-- we find a Thorfile here, so we stop + # + # Suppose we start at c:\Documents and Settings\james\dev\thor ... + # + # 1. c:\Documents and Settings\james\dev\thor + # 2. c:\Documents and Settings\james\dev + # 3. c:\Documents and Settings\james + # 4. c:\Documents and Settings + # 5. c:\ <-- no Thorfiles found! + # + def thorfiles(relevant_to=nil, skip_lookup=false) + thorfiles = [] + + unless skip_lookup + Pathname.pwd.ascend do |path| + thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten + break unless thorfiles.empty? + end + end + + files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob) + files += thorfiles + files -= ["#{thor_root}/thor.yml"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Load Thorfiles relevant to the given method. If you provide "foo:bar" it + # will load all thor files in the thor.yaml that has "foo" e "foo:bar" + # namespaces registered. + # + def thorfiles_relevant_to(meth) + lookup = [ meth, meth.split(":")[0...-1].join(":") ] + + files = thor_yaml.select do |k, v| + v[:namespaces] && !(v[:namespaces] & lookup).empty? + end + + files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } + end + + # Display information about the given klasses. If with_module is given, + # it shows a table with information extracted from the yaml file. + # + def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses) + klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal + + raise Error, "No Thor commands available" if klasses.empty? + show_modules if with_modules && !thor_yaml.empty? + + list = Hash.new { |h,k| h[k] = [] } + groups = klasses.select { |k| k.ancestors.include?(Thor::Group) } + + # Get classes which inherit from Thor + (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) } + + # Get classes which inherit from Thor::Base + groups.map! { |k| k.printable_commands(false).first } + list["root"] = groups + + # Order namespaces with default coming first + list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') } + list.each { |n, commands| display_commands(n, commands) unless commands.empty? } + end + + def display_commands(namespace, list) #:nodoc: + list.sort!{ |a,b| a[0] <=> b[0] } + + say shell.set_color(namespace, :blue, true) + say "-" * namespace.size + + print_table(list, :truncate => true) + say + end + alias display_tasks display_commands + + def show_modules #:nodoc: + info = [] + labels = ["Modules", "Namespaces"] + + info << labels + info << [ "-" * labels[0].size, "-" * labels[1].size ] + + thor_yaml.each do |name, hash| + info << [ name, hash[:namespaces].join(", ") ] + end + + print_table info + say "" + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/shell.rb b/vendor/gems/thor-0.18.1/lib/thor/shell.rb new file mode 100644 index 0000000..a718c53 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/shell.rb @@ -0,0 +1,88 @@ +require 'rbconfig' + +class Thor + module Base + # Returns the shell used in all Thor classes. If you are in a Unix platform + # it will use a colored log, otherwise it will use a basic one without color. + # + def self.shell + @shell ||= if ENV['THOR_SHELL'] && ENV['THOR_SHELL'].size > 0 + Thor::Shell.const_get(ENV['THOR_SHELL']) + elsif ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) && !(ENV['ANSICON'])) + Thor::Shell::Basic + else + Thor::Shell::Color + end + end + + # Sets the shell used in all Thor classes. + # + def self.shell=(klass) + @shell = klass + end + end + + module Shell + SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] + + autoload :Basic, 'thor/shell/basic' + autoload :Color, 'thor/shell/color' + autoload :HTML, 'thor/shell/html' + + # Add shell to initialize config values. + # + # ==== Configuration + # shell:: An instance of the shell to be used. + # + # ==== Examples + # + # class MyScript < Thor + # argument :first, :type => :numeric + # end + # + # MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new + # + def initialize(args=[], options={}, config={}) + super + self.shell = config[:shell] + self.shell.base ||= self if self.shell.respond_to?(:base) + end + + # Holds the shell for the given Thor instance. If no shell is given, + # it gets a default shell from Thor::Base.shell. + def shell + @shell ||= Thor::Base.shell.new + end + + # Sets the shell for this thor class. + def shell=(shell) + @shell = shell + end + + # Common methods that are delegated to the shell. + SHELL_DELEGATED_METHODS.each do |method| + module_eval <<-METHOD, __FILE__, __LINE__ + def #{method}(*args,&block) + shell.#{method}(*args,&block) + end + METHOD + end + + # Yields the given block with padding. + def with_padding + shell.padding += 1 + yield + ensure + shell.padding -= 1 + end + + protected + + # Allow shell to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:shell => self.shell) + end + + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/shell/basic.rb b/vendor/gems/thor-0.18.1/lib/thor/shell/basic.rb new file mode 100644 index 0000000..1e1e9be --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/shell/basic.rb @@ -0,0 +1,393 @@ +require 'tempfile' + +class Thor + module Shell + class Basic + attr_accessor :base + attr_reader :padding + + # Initialize base, mute and padding to nil. + # + def initialize #:nodoc: + @base, @mute, @padding = nil, false, 0 + end + + # Mute everything that's inside given block + # + def mute + @mute = true + yield + ensure + @mute = false + end + + # Check if base is muted + # + def mute? + @mute + end + + # Sets the output padding, not allowing less than zero values. + # + def padding=(value) + @padding = [0, value].max + end + + # Asks something to the user and receives a response. + # + # If asked to limit the correct responses, you can pass in an + # array of acceptable answers. If one of those is not supplied, + # they will be shown a message stating that one of those answers + # must be given and re-asked the question. + # + # ==== Example + # ask("What is your name?") + # + # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) + # + def ask(statement, *args) + options = args.last.is_a?(Hash) ? args.pop : {} + + options[:limited_to] ? ask_filtered(statement, options[:limited_to], *args) : ask_simply(statement, *args) + end + + # Say (print) something to the user. If the sentence ends with a whitespace + # or tab character, a new line is not appended (print + flush). Otherwise + # are passed straight to puts (behavior got from Highline). + # + # ==== Example + # say("I know you knew that.") + # + def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)\Z/)) + message = message.to_s + + message = set_color(message, *color) if color && can_display_colors? + + spaces = " " * padding + + if force_new_line + stdout.puts(spaces + message) + else + stdout.print(spaces + message) + end + stdout.flush + end + + # Say a status with the given color and appends the message. Since this + # method is used frequently by actions, it allows nil or false to be given + # in log_status, avoiding the message from being shown. If a Symbol is + # given in log_status, it's used as the color. + # + def say_status(status, message, log_status=true) + return if quiet? || log_status == false + spaces = " " * (padding + 1) + color = log_status.is_a?(Symbol) ? log_status : :green + + status = status.to_s.rjust(12) + status = set_color status, color, true if color + + stdout.puts "#{status}#{spaces}#{message}" + stdout.flush + end + + # Make a question the to user and returns true if the user replies "y" or + # "yes". + # + def yes?(statement, color=nil) + !!(ask(statement, color) =~ is?(:yes)) + end + + # Make a question the to user and returns true if the user replies "n" or + # "no". + # + def no?(statement, color=nil) + !yes?(statement, color) + end + + # Prints values in columns + # + # ==== Parameters + # Array[String, String, ...] + # + def print_in_columns(array) + return if array.empty? + colwidth = (array.map{|el| el.to_s.size}.max || 0) + 2 + array.each_with_index do |value, index| + # Don't output trailing spaces when printing the last column + if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length + stdout.puts value + else + stdout.printf("%-#{colwidth}s", value) + end + end + end + + # Prints a table. + # + # ==== Parameters + # Array[Array[String, String, ...]] + # + # ==== Options + # indent:: Indent the first column by indent value. + # colwidth:: Force the first column to colwidth spaces wide. + # + def print_table(array, options={}) + return if array.empty? + + formats, indent, colwidth = [], options[:indent].to_i, options[:colwidth] + options[:truncate] = terminal_width if options[:truncate] == true + + formats << "%-#{colwidth + 2}s" if colwidth + start = colwidth ? 1 : 0 + + colcount = array.max{|a,b| a.size <=> b.size }.size + + maximas = [] + + start.upto(colcount - 1) do |index| + maxima = array.map {|row| row[index] ? row[index].to_s.size : 0 }.max + maximas << maxima + if index == colcount - 1 + # Don't output 2 trailing spaces when printing the last column + formats << "%-s" + else + formats << "%-#{maxima + 2}s" + end + end + + formats[0] = formats[0].insert(0, " " * indent) + formats << "%s" + + array.each do |row| + sentence = "" + + row.each_with_index do |column, index| + maxima = maximas[index] + + if column.is_a?(Numeric) + if index == row.size - 1 + # Don't output 2 trailing spaces when printing the last column + f = "%#{maxima}s" + else + f = "%#{maxima}s " + end + else + f = formats[index] + end + sentence << f % column.to_s + end + + sentence = truncate(sentence, options[:truncate]) if options[:truncate] + stdout.puts sentence + end + end + + # Prints a long string, word-wrapping the text to the current width of the + # terminal display. Ideal for printing heredocs. + # + # ==== Parameters + # String + # + # ==== Options + # indent:: Indent each line of the printed paragraph by indent value. + # + def print_wrapped(message, options={}) + indent = options[:indent] || 0 + width = terminal_width - indent + paras = message.split("\n\n") + + paras.map! do |unwrapped| + unwrapped.strip.gsub(/\n/, " ").squeeze(" "). + gsub(/.{1,#{width}}(?:\s|\Z)/){($& + 5.chr). + gsub(/\n\005/,"\n").gsub(/\005/,"\n")} + end + + paras.each do |para| + para.split("\n").each do |line| + stdout.puts line.insert(0, " " * indent) + end + stdout.puts unless para == paras.last + end + end + + # Deals with file collision and returns true if the file should be + # overwritten and false otherwise. If a block is given, it uses the block + # response as the content for the diff. + # + # ==== Parameters + # destination:: the destination file to solve conflicts + # block:: an optional block that returns the value to be used in diff + # + def file_collision(destination) + return true if @always_force + options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + + while true + answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}] + + case answer + when is?(:yes), is?(:force), "" + return true + when is?(:no), is?(:skip) + return false + when is?(:always) + return @always_force = true + when is?(:quit) + say 'Aborting...' + raise SystemExit + when is?(:diff) + show_diff(destination, yield) if block_given? + say 'Retrying...' + else + say file_collision_help + end + end + end + + # This code was copied from Rake, available under MIT-LICENSE + # Copyright (c) 2003, 2004 Jim Weirich + def terminal_width + if ENV['THOR_COLUMNS'] + result = ENV['THOR_COLUMNS'].to_i + else + result = unix? ? dynamic_width : 80 + end + (result < 10) ? 80 : result + rescue + 80 + end + + # Called if something goes wrong during the execution. This is used by Thor + # internally and should not be used inside your scripts. If something went + # wrong, you can always raise an exception. If you raise a Thor::Error, it + # will be rescued and wrapped in the method below. + # + def error(statement) + stderr.puts statement + end + + # Apply color to the given string with optional bold. Disabled in the + # Thor::Shell::Basic class. + # + def set_color(string, *args) #:nodoc: + string + end + + protected + + def can_display_colors? + false + end + + def lookup_color(color) + return color unless color.is_a?(Symbol) + self.class.const_get(color.to_s.upcase) + end + + def stdout + $stdout + end + + def stdin + $stdin + end + + def stderr + $stderr + end + + def is?(value) #:nodoc: + value = value.to_s + + if value.size == 1 + /\A#{value}\z/i + else + /\A(#{value}|#{value[0,1]})\z/i + end + end + + def file_collision_help #:nodoc: +</dev/null}.split[1].to_i + end + + def dynamic_width_tput + %x{tput cols 2>/dev/null}.to_i + end + + def unix? + RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i + end + + def truncate(string, width) + as_unicode do + chars = string.chars.to_a + if chars.length <= width + chars.join + else + ( chars[0, width-3].join ) + "..." + end + end + end + + if "".respond_to?(:encode) + def as_unicode + yield + end + else + def as_unicode + old, $KCODE = $KCODE, "U" + yield + ensure + $KCODE = old + end + end + + def ask_simply(statement, color=nil) + say("#{statement} ", color) + stdin.gets.tap{|text| text.strip! if text} + end + + def ask_filtered(statement, answer_set, *args) + correct_answer = nil + until correct_answer + answer = ask_simply("#{statement} #{answer_set.inspect}", *args) + correct_answer = answer_set.include?(answer) ? answer : nil + answers = answer_set.map(&:inspect).join(", ") + say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer + end + correct_answer + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/shell/color.rb b/vendor/gems/thor-0.18.1/lib/thor/shell/color.rb new file mode 100644 index 0000000..fcf9c25 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/shell/color.rb @@ -0,0 +1,148 @@ +require 'thor/shell/basic' + +class Thor + module Shell + # Inherit from Thor::Shell::Basic and add set_color behavior. Check + # Thor::Shell::Basic to see all available methods. + # + class Color < Basic + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + # The start of an ANSI bold sequence. + BOLD = "\e[1m" + + # Set the terminal's foreground ANSI color to black. + BLACK = "\e[30m" + # Set the terminal's foreground ANSI color to red. + RED = "\e[31m" + # Set the terminal's foreground ANSI color to green. + GREEN = "\e[32m" + # Set the terminal's foreground ANSI color to yellow. + YELLOW = "\e[33m" + # Set the terminal's foreground ANSI color to blue. + BLUE = "\e[34m" + # Set the terminal's foreground ANSI color to magenta. + MAGENTA = "\e[35m" + # Set the terminal's foreground ANSI color to cyan. + CYAN = "\e[36m" + # Set the terminal's foreground ANSI color to white. + WHITE = "\e[37m" + + # Set the terminal's background ANSI color to black. + ON_BLACK = "\e[40m" + # Set the terminal's background ANSI color to red. + ON_RED = "\e[41m" + # Set the terminal's background ANSI color to green. + ON_GREEN = "\e[42m" + # Set the terminal's background ANSI color to yellow. + ON_YELLOW = "\e[43m" + # Set the terminal's background ANSI color to blue. + ON_BLUE = "\e[44m" + # Set the terminal's background ANSI color to magenta. + ON_MAGENTA = "\e[45m" + # Set the terminal's background ANSI color to cyan. + ON_CYAN = "\e[46m" + # Set the terminal's background ANSI color to white. + ON_WHITE = "\e[47m" + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + # Pass foreground, background and bold options to this method as + # symbols. + # + # Example: + # + # set_color "Hi!", :red, :on_white, :bold + # + # The available colors are: + # + # :bold + # :black + # :red + # :green + # :yellow + # :blue + # :magenta + # :cyan + # :white + # :on_black + # :on_red + # :on_green + # :on_yellow + # :on_blue + # :on_magenta + # :on_cyan + # :on_white + def set_color(string, *colors) + if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } + ansi_colors = colors.map { |color| lookup_color(color) } + "#{ansi_colors.join}#{string}#{CLEAR}" + else + # The old API was `set_color(color, bold=boolean)`. We + # continue to support the old API because you should never + # break old APIs unnecessarily :P + foreground, bold = colors + foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol) + + bold = bold ? BOLD : "" + "#{bold}#{foreground}#{string}#{CLEAR}" + end + end + + protected + + def can_display_colors? + stdout.tty? + end + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) #:nodoc: + if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil? + actual = File.binread(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) #:nodoc: + case diff.action + when '-' + say "- #{diff.old_element.chomp}", :red, true + when '+' + say "+ #{diff.new_element.chomp}", :green, true + when '!' + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? #:nodoc: + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require 'diff/lcs' + true + rescue LoadError + false + end + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/shell/html.rb b/vendor/gems/thor-0.18.1/lib/thor/shell/html.rb new file mode 100644 index 0000000..2a1bb38 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/shell/html.rb @@ -0,0 +1,127 @@ +require 'thor/shell/basic' + +class Thor + module Shell + # Inherit from Thor::Shell::Basic and add set_color behavior. Check + # Thor::Shell::Basic to see all available methods. + # + class HTML < Basic + # The start of an HTML bold sequence. + BOLD = "font-weight: bold" + + # Set the terminal's foreground HTML color to black. + BLACK = 'color: black' + # Set the terminal's foreground HTML color to red. + RED = 'color: red' + # Set the terminal's foreground HTML color to green. + GREEN = 'color: green' + # Set the terminal's foreground HTML color to yellow. + YELLOW = 'color: yellow' + # Set the terminal's foreground HTML color to blue. + BLUE = 'color: blue' + # Set the terminal's foreground HTML color to magenta. + MAGENTA = 'color: magenta' + # Set the terminal's foreground HTML color to cyan. + CYAN = 'color: cyan' + # Set the terminal's foreground HTML color to white. + WHITE = 'color: white' + + # Set the terminal's background HTML color to black. + ON_BLACK = 'background-color: black' + # Set the terminal's background HTML color to red. + ON_RED = 'background-color: red' + # Set the terminal's background HTML color to green. + ON_GREEN = 'background-color: green' + # Set the terminal's background HTML color to yellow. + ON_YELLOW = 'background-color: yellow' + # Set the terminal's background HTML color to blue. + ON_BLUE = 'background-color: blue' + # Set the terminal's background HTML color to magenta. + ON_MAGENTA = 'background-color: magenta' + # Set the terminal's background HTML color to cyan. + ON_CYAN = 'background-color: cyan' + # Set the terminal's background HTML color to white. + ON_WHITE = 'background-color: white' + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + def set_color(string, *colors) + if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } + html_colors = colors.map { |color| lookup_color(color) } + "#{string}" + else + color, bold = colors + html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) + styles = [html_color] + styles << BOLD if bold + "#{string}" + end + end + + # Ask something to the user and receives a response. + # + # ==== Example + # ask("What is your name?") + # + # TODO: Implement #ask for Thor::Shell::HTML + def ask(statement, color=nil) + raise NotImplementedError, "Implement #ask for Thor::Shell::HTML" + end + + protected + + def can_display_colors? + true + end + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) #:nodoc: + if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil? + actual = File.binread(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) #:nodoc: + case diff.action + when '-' + say "- #{diff.old_element.chomp}", :red, true + when '+' + say "+ #{diff.new_element.chomp}", :green, true + when '!' + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? #:nodoc: + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require 'diff/lcs' + true + rescue LoadError + false + end + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/util.rb b/vendor/gems/thor-0.18.1/lib/thor/util.rb new file mode 100644 index 0000000..2510630 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/util.rb @@ -0,0 +1,270 @@ +require 'rbconfig' + +class Thor + module Sandbox #:nodoc: + end + + # This module holds several utilities: + # + # 1) Methods to convert thor namespaces to constants and vice-versa. + # + # Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" + # + # 2) Loading thor files and sandboxing: + # + # Thor::Util.load_thorfile("~/.thor/foo") + # + module Util + + class << self + + # Receives a namespace and search for it in the Thor::Base subclasses. + # + # ==== Parameters + # namespace:: The namespace to search for. + # + def find_by_namespace(namespace) + namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ + Thor::Base.subclasses.find { |klass| klass.namespace == namespace } + end + + # Receives a constant and converts it to a Thor namespace. Since Thor + # commands can be added to a sandbox, this method is also responsable for + # removing the sandbox namespace. + # + # This method should not be used in general because it's used to deal with + # older versions of Thor. On current versions, if you need to get the + # namespace from a class, just call namespace on it. + # + # ==== Parameters + # constant:: The constant to be converted to the thor path. + # + # ==== Returns + # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" + # + def namespace_from_thor_class(constant) + constant = constant.to_s.gsub(/^Thor::Sandbox::/, "") + constant = snake_case(constant).squeeze(":") + constant + end + + # Given the contents, evaluate it inside the sandbox and returns the + # namespaces defined in the sandbox. + # + # ==== Parameters + # contents + # + # ==== Returns + # Array[Object] + # + def namespaces_in_content(contents, file=__FILE__) + old_constants = Thor::Base.subclasses.dup + Thor::Base.subclasses.clear + + load_thorfile(file, contents) + + new_constants = Thor::Base.subclasses.dup + Thor::Base.subclasses.replace(old_constants) + + new_constants.map!{ |c| c.namespace } + new_constants.compact! + new_constants + end + + # Returns the thor classes declared inside the given class. + # + def thor_classes_in(klass) + stringfied_constants = klass.constants.map { |c| c.to_s } + Thor::Base.subclasses.select do |subclass| + next unless subclass.name + stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", '')) + end + end + + # Receives a string and convert it to snake case. SnakeCase returns snake_case. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def snake_case(str) + return str.downcase if str =~ /^[A-Z_]+$/ + str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/ + return $+.downcase + end + + # Receives a string and convert it to camel case. camel_case returns CamelCase. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def camel_case(str) + return str if str !~ /_/ && str =~ /[A-Z]+.*/ + str.split('_').map { |i| i.capitalize }.join + end + + # Receives a namespace and tries to retrieve a Thor or Thor::Group class + # from it. It first searches for a class using the all the given namespace, + # if it's not found, removes the highest entry and searches for the class + # again. If found, returns the highest entry as the class name. + # + # ==== Examples + # + # class Foo::Bar < Thor + # def baz + # end + # end + # + # class Baz::Foo < Thor::Group + # end + # + # Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command + # Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil + # Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" + # + # ==== Parameters + # namespace + # + def find_class_and_command_by_namespace(namespace, fallback = true) + if namespace.include?(?:) # look for a namespaced command + pieces = namespace.split(":") + command = pieces.pop + klass = Thor::Util.find_by_namespace(pieces.join(":")) + end + unless klass # look for a Thor::Group with the right name + klass, command = Thor::Util.find_by_namespace(namespace), nil + end + if !klass && fallback # try a command in the default namespace + command = namespace + klass = Thor::Util.find_by_namespace('') + end + return klass, command + end + alias find_class_and_task_by_namespace find_class_and_command_by_namespace + + # Receives a path and load the thor file in the path. The file is evaluated + # inside the sandbox to avoid namespacing conflicts. + # + def load_thorfile(path, content=nil, debug=false) + content ||= File.binread(path) + + begin + Thor::Sandbox.class_eval(content, path) + rescue Exception => e + $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}") + if debug + $stderr.puts(*e.backtrace) + else + $stderr.puts(e.backtrace.first) + end + end + end + + def user_home + @@user_home ||= if ENV["HOME"] + ENV["HOME"] + elsif ENV["USERPROFILE"] + ENV["USERPROFILE"] + elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] + File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) + elsif ENV["APPDATA"] + ENV["APPDATA"] + else + begin + File.expand_path("~") + rescue + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + end + + # Returns the root where thor files are located, depending on the OS. + # + def thor_root + File.join(user_home, ".thor").gsub(/\\/, '/') + end + + # Returns the files in the thor root. On Windows thor_root will be something + # like this: + # + # C:\Documents and Settings\james\.thor + # + # If we don't #gsub the \ character, Dir.glob will fail. + # + def thor_root_glob + files = Dir["#{escape_globs(thor_root)}/*"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Where to look for Thor files. + # + def globs_for(path) + path = escape_globs(path) + ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] + end + + # Return the path to the ruby interpreter taking into account multiple + # installations and windows extensions. + # + def ruby_command + @ruby_command ||= begin + ruby_name = RbConfig::CONFIG['ruby_install_name'] + ruby = File.join(RbConfig::CONFIG['bindir'], ruby_name) + ruby << RbConfig::CONFIG['EXEEXT'] + + # avoid using different name than ruby (on platforms supporting links) + if ruby_name != 'ruby' && File.respond_to?(:readlink) + begin + alternate_ruby = File.join(RbConfig::CONFIG['bindir'], 'ruby') + alternate_ruby << RbConfig::CONFIG['EXEEXT'] + + # ruby is a symlink + if File.symlink? alternate_ruby + linked_ruby = File.readlink alternate_ruby + + # symlink points to 'ruby_install_name' + ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby + end + rescue NotImplementedError + # just ignore on windows + end + end + + # escape string in case path to ruby executable contain spaces. + ruby.sub!(/.*\s.*/m, '"\&"') + ruby + end + end + + # Returns a string that has had any glob characters escaped. + # The glob characters are `* ? { } [ ]`. + # + # ==== Examples + # + # Thor::Util.escape_globs('[apps]') # => '\[apps\]' + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def escape_globs(path) + path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') + end + + end + end +end diff --git a/vendor/gems/thor-0.18.1/lib/thor/version.rb b/vendor/gems/thor-0.18.1/lib/thor/version.rb new file mode 100644 index 0000000..646cd37 --- /dev/null +++ b/vendor/gems/thor-0.18.1/lib/thor/version.rb @@ -0,0 +1,3 @@ +class Thor + VERSION = "0.18.1" +end diff --git a/vendor/gems/thor-0.18.1/spec/actions/create_file_spec.rb b/vendor/gems/thor-0.18.1/spec/actions/create_file_spec.rb new file mode 100644 index 0000000..2586704 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions/create_file_spec.rb @@ -0,0 +1,170 @@ +require 'helper' +require 'thor/actions' + +describe Thor::Actions::CreateFile do + before do + ::FileUtils.rm_rf(destination_root) + end + + def create_file(destination=nil, config={}, options={}) + @base = MyCounter.new([1, 2], options, { :destination_root => destination_root }) + @base.stub!(:file_name).and_return('rdoc') + + @action = Thor::Actions::CreateFile.new(@base, destination, "CONFIGURATION", + { :verbose => !@silence }.merge(config)) + end + + def invoke! + capture(:stdout) { @action.invoke! } + end + + def revoke! + capture(:stdout) { @action.revoke! } + end + + def silence! + @silence = true + end + + describe "#invoke!" do + it "creates a file" do + create_file("doc/config.rb") + invoke! + expect(File.exists?(File.join(destination_root, "doc/config.rb"))).to be_true + end + + it "does not create a file if pretending" do + create_file("doc/config.rb", {}, :pretend => true) + invoke! + expect(File.exists?(File.join(destination_root, "doc/config.rb"))).to be_false + end + + it "shows created status to the user" do + create_file("doc/config.rb") + expect(invoke!).to eq(" create doc/config.rb\n") + end + + it "does not show any information if log status is false" do + silence! + create_file("doc/config.rb") + expect(invoke!).to be_empty + end + + it "returns the given destination" do + capture(:stdout) do + expect(create_file("doc/config.rb").invoke!).to eq("doc/config.rb") + end + end + + it "converts encoded instructions" do + create_file("doc/%file_name%.rb.tt") + invoke! + expect(File.exists?(File.join(destination_root, "doc/rdoc.rb.tt"))).to be_true + end + + describe "when file exists" do + before do + create_file("doc/config.rb") + invoke! + end + + describe "and is identical" do + it "shows identical status" do + create_file("doc/config.rb") + invoke! + expect(invoke!).to eq(" identical doc/config.rb\n") + end + end + + describe "and is not identical" do + before do + File.open(File.join(destination_root, 'doc/config.rb'), 'w'){ |f| f.write("FOO = 3") } + end + + it "shows forced status to the user if force is given" do + expect(create_file("doc/config.rb", {}, :force => true)).not_to be_identical + expect(invoke!).to eq(" force doc/config.rb\n") + end + + it "shows skipped status to the user if skip is given" do + expect(create_file("doc/config.rb", {}, :skip => true)).not_to be_identical + expect(invoke!).to eq(" skip doc/config.rb\n") + end + + it "shows forced status to the user if force is configured" do + expect(create_file("doc/config.rb", :force => true)).not_to be_identical + expect(invoke!).to eq(" force doc/config.rb\n") + end + + it "shows skipped status to the user if skip is configured" do + expect(create_file("doc/config.rb", :skip => true)).not_to be_identical + expect(invoke!).to eq(" skip doc/config.rb\n") + end + + it "shows conflict status to ther user" do + expect(create_file("doc/config.rb")).not_to be_identical + $stdin.should_receive(:gets).and_return('s') + file = File.join(destination_root, 'doc/config.rb') + + content = invoke! + expect(content).to match(/conflict doc\/config\.rb/) + expect(content).to match(/Overwrite #{file}\? \(enter "h" for help\) \[Ynaqdh\]/) + expect(content).to match(/skip doc\/config\.rb/) + end + + it "creates the file if the file collision menu returns true" do + create_file("doc/config.rb") + $stdin.should_receive(:gets).and_return('y') + expect(invoke!).to match(/force doc\/config\.rb/) + end + + it "skips the file if the file collision menu returns false" do + create_file("doc/config.rb") + $stdin.should_receive(:gets).and_return('n') + expect(invoke!).to match(/skip doc\/config\.rb/) + end + + it "executes the block given to show file content" do + create_file("doc/config.rb") + $stdin.should_receive(:gets).and_return('d') + $stdin.should_receive(:gets).and_return('n') + @base.shell.should_receive(:system).with(/diff -u/) + invoke! + end + end + end + end + + describe "#revoke!" do + it "removes the destination file" do + create_file("doc/config.rb") + invoke! + revoke! + expect(File.exists?(@action.destination)).to be_false + end + + it "does not raise an error if the file does not exist" do + create_file("doc/config.rb") + revoke! + expect(File.exists?(@action.destination)).to be_false + end + end + + describe "#exists?" do + it "returns true if the destination file exists" do + create_file("doc/config.rb") + expect(@action.exists?).to be_false + invoke! + expect(@action.exists?).to be_true + end + end + + describe "#identical?" do + it "returns true if the destination file and is identical" do + create_file("doc/config.rb") + expect(@action.identical?).to be_false + invoke! + expect(@action.identical?).to be_true + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/actions/create_link_spec.rb b/vendor/gems/thor-0.18.1/spec/actions/create_link_spec.rb new file mode 100644 index 0000000..95f25d0 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions/create_link_spec.rb @@ -0,0 +1,95 @@ +require 'helper' +require 'thor/actions' +require 'tempfile' + +describe Thor::Actions::CreateLink do + before do + @hardlink_to = File.join(Dir.tmpdir, 'linkdest.rb') + ::FileUtils.rm_rf(destination_root) + ::FileUtils.rm_rf(@hardlink_to) + end + + def create_link(destination=nil, config={}, options={}) + @base = MyCounter.new([1,2], options, { :destination_root => destination_root }) + @base.stub!(:file_name).and_return('rdoc') + + @tempfile = Tempfile.new("config.rb") + + @action = Thor::Actions::CreateLink.new(@base, destination, @tempfile.path, + { :verbose => !@silence }.merge(config)) + end + + def invoke! + capture(:stdout) { @action.invoke! } + end + + def revoke! + capture(:stdout) { @action.revoke! } + end + + def silence! + @silence = true + end + + describe "#invoke!" do + it "creates a symbolic link for :symbolic => true" do + create_link("doc/config.rb", :symbolic => true) + invoke! + destination_path = File.join(destination_root, "doc/config.rb") + expect(File.exists?(destination_path)).to be_true + expect(File.symlink?(destination_path)).to be_true + end + + it "creates a hard link for :symbolic => false" do + create_link(@hardlink_to, :symbolic => false) + invoke! + destination_path = @hardlink_to + expect(File.exists?(destination_path)).to be_true + expect(File.symlink?(destination_path)).to be_false + end + + it "creates a symbolic link by default" do + create_link("doc/config.rb") + invoke! + destination_path = File.join(destination_root, "doc/config.rb") + expect(File.exists?(destination_path)).to be_true + expect(File.symlink?(destination_path)).to be_true + end + + it "does not create a link if pretending" do + create_link("doc/config.rb", {}, :pretend => true) + invoke! + expect(File.exists?(File.join(destination_root, "doc/config.rb"))).to be_false + end + + it "shows created status to the user" do + create_link("doc/config.rb") + expect(invoke!).to eq(" create doc/config.rb\n") + end + + it "does not show any information if log status is false" do + silence! + create_link("doc/config.rb") + expect(invoke!).to be_empty + end + end + + describe "#identical?" do + it "returns true if the destination link exists and is identical" do + create_link("doc/config.rb") + expect(@action.identical?).to be_false + invoke! + expect(@action.identical?).to be_true + end + end + + describe "#revoke!" do + it "removes the symbolic link of non-existent destination" do + create_link("doc/config.rb") + invoke! + File.delete(@tempfile.path) + revoke! + expect(File.symlink?(@action.destination)).to be_false + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/actions/directory_spec.rb b/vendor/gems/thor-0.18.1/spec/actions/directory_spec.rb new file mode 100644 index 0000000..b7e70f2 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions/directory_spec.rb @@ -0,0 +1,169 @@ +require 'helper' +require 'thor/actions' + +describe Thor::Actions::Directory do + before do + ::FileUtils.rm_rf(destination_root) + invoker.stub!(:file_name).and_return("rdoc") + end + + def invoker + @invoker ||= WhinyGenerator.new([1,2], {}, { :destination_root => destination_root }) + end + + def revoker + @revoker ||= WhinyGenerator.new([1,2], {}, { :destination_root => destination_root, :behavior => :revoke }) + end + + def invoke!(*args, &block) + capture(:stdout){ invoker.directory(*args, &block) } + end + + def revoke!(*args, &block) + capture(:stdout){ revoker.directory(*args, &block) } + end + + def exists_and_identical?(source_path, destination_path) + %w(config.rb README).each do |file| + source = File.join(source_root, source_path, file) + destination = File.join(destination_root, destination_path, file) + + expect(File.exists?(destination)).to be_true + expect(FileUtils.identical?(source, destination)).to be_true + end + end + + describe "#invoke!" do + it "raises an error if the source does not exist" do + expect { + invoke! "unknown" + }.to raise_error(Thor::Error, /Could not find "unknown" in any of your source paths/) + end + + it "does not create a directory in pretend mode" do + invoke! "doc", "ghost", :pretend => true + expect(File.exists?("ghost")).to be_false + end + + it "copies the whole directory recursively to the default destination" do + invoke! "doc" + exists_and_identical?("doc", "doc") + end + + it "copies the whole directory recursively to the specified destination" do + invoke! "doc", "docs" + exists_and_identical?("doc", "docs") + end + + it "copies only the first level files if recursive" do + invoke! ".", "commands", :recursive => false + + file = File.join(destination_root, "commands", "group.thor") + expect(File.exists?(file)).to be_true + + file = File.join(destination_root, "commands", "doc") + expect(File.exists?(file)).to be_false + + file = File.join(destination_root, "commands", "doc", "README") + expect(File.exists?(file)).to be_false + end + + it "ignores files within excluding/ directories when exclude_pattern is provided" do + invoke! "doc", "docs", :exclude_pattern => /excluding\// + file = File.join(destination_root, "docs", "excluding", "rdoc.rb") + expect(File.exists?(file)).to be_false + end + + it "copies and evalutes files within excluding/ directory when no exclude_pattern is present" do + invoke! "doc", "docs" + file = File.join(destination_root, "docs", "excluding", "rdoc.rb") + expect(File.exists?(file)).to be_true + expect(File.read(file)).to eq("BAR = BAR\n") + end + + it "copies files from the source relative to the current path" do + invoker.inside "doc" do + invoke! "." + end + exists_and_identical?("doc", "doc") + end + + it "copies and evaluates templates" do + invoke! "doc", "docs" + file = File.join(destination_root, "docs", "rdoc.rb") + expect(File.exists?(file)).to be_true + expect(File.read(file)).to eq("FOO = FOO\n") + end + + it "copies directories and preserved file mode" do + invoke! "preserve", "preserved", :mode => :preserve + original = File.join(source_root, "preserve", "script.sh") + copy = File.join(destination_root, "preserved", "script.sh") + expect(File.stat(original).mode).to eq(File.stat(copy).mode) + end + + it "copies directories" do + invoke! "doc", "docs" + file = File.join(destination_root, "docs", "components") + expect(File.exists?(file)).to be_true + expect(File.directory?(file)).to be_true + end + + it "does not copy .empty_directory files" do + invoke! "doc", "docs" + file = File.join(destination_root, "docs", "components", ".empty_directory") + expect(File.exists?(file)).to be_false + end + + it "copies directories even if they are empty" do + invoke! "doc/components", "docs/components" + file = File.join(destination_root, "docs", "components") + expect(File.exists?(file)).to be_true + end + + it "does not copy empty directories twice" do + content = invoke!("doc/components", "docs/components") + expect(content).not_to match(/exist/) + end + + it "logs status" do + content = invoke!("doc") + expect(content).to match(/create doc\/README/) + expect(content).to match(/create doc\/config\.rb/) + expect(content).to match(/create doc\/rdoc\.rb/) + expect(content).to match(/create doc\/components/) + end + + it "yields a block" do + checked = false + invoke!("doc") do |content| + checked ||= !!(content =~ /FOO/) + end + expect(checked).to be_true + end + + it "works with glob characters in the path" do + content = invoke!("app{1}") + expect(content).to match(/create app\{1\}\/README/) + end + end + + describe "#revoke!" do + it "removes the destination file" do + invoke! "doc" + revoke! "doc" + + expect(File.exists?(File.join(destination_root, "doc", "README"))).to be_false + expect(File.exists?(File.join(destination_root, "doc", "config.rb"))).to be_false + expect(File.exists?(File.join(destination_root, "doc", "components"))).to be_false + end + + it "works with glob characters in the path" do + invoke! "app{1}" + expect(File.exists?(File.join(destination_root, "app{1}", "README"))).to be_true + + revoke! "app{1}" + expect(File.exists?(File.join(destination_root, "app{1}", "README"))).to be_false + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/actions/empty_directory_spec.rb b/vendor/gems/thor-0.18.1/spec/actions/empty_directory_spec.rb new file mode 100644 index 0000000..49a1846 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions/empty_directory_spec.rb @@ -0,0 +1,129 @@ +require 'helper' +require 'thor/actions' + +describe Thor::Actions::EmptyDirectory do + before do + ::FileUtils.rm_rf(destination_root) + end + + def empty_directory(destination, options={}) + @action = Thor::Actions::EmptyDirectory.new(base, destination) + end + + def invoke! + capture(:stdout) { @action.invoke! } + end + + def revoke! + capture(:stdout) { @action.revoke! } + end + + def base + @base ||= MyCounter.new([1,2], {}, { :destination_root => destination_root }) + end + + describe "#destination" do + it "returns the full destination with the destination_root" do + expect(empty_directory('doc').destination).to eq(File.join(destination_root, 'doc')) + end + + it "takes relative root into account" do + base.inside('doc') do + expect(empty_directory('contents').destination).to eq(File.join(destination_root, 'doc', 'contents')) + end + end + end + + describe "#relative_destination" do + it "returns the relative destination to the original destination root" do + base.inside('doc') do + expect(empty_directory('contents').relative_destination).to eq('doc/contents') + end + end + end + + describe "#given_destination" do + it "returns the destination supplied by the user" do + base.inside('doc') do + expect(empty_directory('contents').given_destination).to eq('contents') + end + end + end + + describe "#invoke!" do + it "copies the file to the specified destination" do + empty_directory("doc") + invoke! + expect(File.exists?(File.join(destination_root, "doc"))).to be_true + end + + it "shows created status to the user" do + empty_directory("doc") + expect(invoke!).to eq(" create doc\n") + end + + it "does not create a directory if pretending" do + base.inside("foo", :pretend => true) do + empty_directory("ghost") + end + expect(File.exists?(File.join(base.destination_root, "ghost"))).to be_false + end + + describe "when directory exists" do + it "shows exist status" do + empty_directory("doc") + invoke! + expect(invoke!).to eq(" exist doc\n") + end + end + end + + describe "#revoke!" do + it "removes the destination file" do + empty_directory("doc") + invoke! + revoke! + expect(File.exists?(@action.destination)).to be_false + end + end + + describe "#exists?" do + it "returns true if the destination file exists" do + empty_directory("doc") + expect(@action.exists?).to be_false + invoke! + expect(@action.exists?).to be_true + end + end + + context "protected methods" do + describe "#convert_encoded_instructions" do + before do + empty_directory("test_dir") + @action.base.stub!(:file_name).and_return("expected") + end + + it "accepts and executes a 'legal' %\w+% encoded instruction" do + expect(@action.send(:convert_encoded_instructions, "%file_name%.txt")).to eq("expected.txt") + end + + it "accepts and executes a private %\w+% encoded instruction" do + @action.base.extend Module.new { + private + def private_file_name + "expected" + end + } + expect(@action.send(:convert_encoded_instructions, "%private_file_name%.txt")).to eq("expected.txt") + end + + it "ignores an 'illegal' %\w+% encoded instruction" do + expect(@action.send(:convert_encoded_instructions, "%some_name%.txt")).to eq("%some_name%.txt") + end + + it "ignores incorrectly encoded instruction" do + expect(@action.send(:convert_encoded_instructions, "%some.name%.txt")).to eq("%some.name%.txt") + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/actions/file_manipulation_spec.rb b/vendor/gems/thor-0.18.1/spec/actions/file_manipulation_spec.rb new file mode 100644 index 0000000..c4d571c --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions/file_manipulation_spec.rb @@ -0,0 +1,382 @@ +require 'helper' + +class Application; end + +describe Thor::Actions do + def runner(options={}) + @runner ||= MyCounter.new([1], options, { :destination_root => destination_root }) + end + + def action(*args, &block) + capture(:stdout) { runner.send(*args, &block) } + end + + def exists_and_identical?(source, destination) + destination = File.join(destination_root, destination) + expect(File.exists?(destination)).to be_true + + source = File.join(source_root, source) + expect(FileUtils).to be_identical(source, destination) + end + + def file + File.join(destination_root, "foo") + end + + before do + ::FileUtils.rm_rf(destination_root) + end + + describe "#chmod" do + it "executes the command given" do + FileUtils.should_receive(:chmod_R).with(0755, file) + action :chmod, "foo", 0755 + end + + it "does not execute the command if pretending given" do + FileUtils.should_not_receive(:chmod_R) + runner(:pretend => true) + action :chmod, "foo", 0755 + end + + it "logs status" do + FileUtils.should_receive(:chmod_R).with(0755, file) + expect(action(:chmod, "foo", 0755)).to eq(" chmod foo\n") + end + + it "does not log status if required" do + FileUtils.should_receive(:chmod_R).with(0755, file) + expect(action(:chmod, "foo", 0755, :verbose => false)).to be_empty + end + end + + describe "#copy_file" do + it "copies file from source to default destination" do + action :copy_file, "command.thor" + exists_and_identical?("command.thor", "command.thor") + end + + it "copies file from source to the specified destination" do + action :copy_file, "command.thor", "foo.thor" + exists_and_identical?("command.thor", "foo.thor") + end + + it "copies file from the source relative to the current path" do + runner.inside("doc") do + action :copy_file, "README" + end + exists_and_identical?("doc/README", "doc/README") + end + + it "copies file from source to default destination and preserves file mode" do + action :copy_file, "preserve/script.sh", :mode => :preserve + original = File.join(source_root, "preserve/script.sh") + copy = File.join(destination_root, "preserve/script.sh") + expect(File.stat(original).mode).to eq(File.stat(copy).mode) + end + + it "logs status" do + expect(action(:copy_file, "command.thor")).to eq(" create command.thor\n") + end + + it "accepts a block to change output" do + action :copy_file, "command.thor" do |content| + "OMG" + content + end + expect(File.read(File.join(destination_root, "command.thor"))).to match(/^OMG/) + end + end + + describe "#link_file" do + it "links file from source to default destination" do + action :link_file, "command.thor" + exists_and_identical?("command.thor", "command.thor") + end + + it "links file from source to the specified destination" do + action :link_file, "command.thor", "foo.thor" + exists_and_identical?("command.thor", "foo.thor") + end + + it "links file from the source relative to the current path" do + runner.inside("doc") do + action :link_file, "README" + end + exists_and_identical?("doc/README", "doc/README") + end + + it "logs status" do + expect(action(:link_file, "command.thor")).to eq(" create command.thor\n") + end + end + + describe "#get" do + it "copies file from source to the specified destination" do + action :get, "doc/README", "docs/README" + exists_and_identical?("doc/README", "docs/README") + end + + it "uses just the source basename as destination if none is specified" do + action :get, "doc/README" + exists_and_identical?("doc/README", "README") + end + + it "allows the destination to be set as a block result" do + action(:get, "doc/README"){ |c| "docs/README" } + exists_and_identical?("doc/README", "docs/README") + end + + it "yields file content to a block" do + action :get, "doc/README" do |content| + expect(content).to eq("__start__\nREADME\n__end__\n") + end + end + + it "logs status" do + expect(action(:get, "doc/README", "docs/README")).to eq(" create docs/README\n") + end + + it "accepts http remote sources" do + body = "__start__\nHTTPFILE\n__end__\n" + FakeWeb.register_uri(:get, 'http://example.com/file.txt', :body => body) + action :get, "http://example.com/file.txt" do |content| + expect(content).to eq(body) + end + FakeWeb.clean_registry + end + + it "accepts https remote sources" do + body = "__start__\nHTTPSFILE\n__end__\n" + FakeWeb.register_uri(:get, 'https://example.com/file.txt', :body => body) + action :get, "https://example.com/file.txt" do |content| + expect(content).to eq(body) + end + FakeWeb.clean_registry + end + end + + describe "#template" do + it "allows using block helpers in the template" do + action :template, "doc/block_helper.rb" + + file = File.join(destination_root, "doc/block_helper.rb") + expect(File.read(file)).to eq("Hello world!") + end + + it "evaluates the template given as source" do + runner.instance_variable_set("@klass", "Config") + action :template, "doc/config.rb" + + file = File.join(destination_root, "doc/config.rb") + expect(File.read(file)).to eq("class Config; end\n") + end + + it "copies the template to the specified destination" do + action :template, "doc/config.rb", "doc/configuration.rb" + file = File.join(destination_root, "doc/configuration.rb") + expect(File.exists?(file)).to be_true + end + + it "converts enconded instructions" do + runner.should_receive(:file_name).and_return("rdoc") + action :template, "doc/%file_name%.rb.tt" + file = File.join(destination_root, "doc/rdoc.rb") + expect(File.exists?(file)).to be_true + end + + it "logs status" do + expect(capture(:stdout) { runner.template("doc/config.rb") }).to eq(" create doc/config.rb\n") + end + + it "accepts a block to change output" do + action :template, "doc/config.rb" do |content| + "OMG" + content + end + expect(File.read(File.join(destination_root, "doc/config.rb"))).to match(/^OMG/) + end + + it "guesses the destination name when given only a source" do + action :template, "doc/config.yaml.tt" + + file = File.join(destination_root, "doc/config.yaml") + expect(File.exists?(file)).to be_true + end + end + + describe "when changing existent files" do + before do + ::FileUtils.cp_r(source_root, destination_root) + end + + def file + File.join(destination_root, "doc", "README") + end + + describe "#remove_file" do + it "removes the file given" do + action :remove_file, "doc/README" + expect(File.exists?(file)).to be_false + end + + it "removes directories too" do + action :remove_dir, "doc" + expect(File.exists?(File.join(destination_root, "doc"))).to be_false + end + + it "does not remove if pretending" do + runner(:pretend => true) + action :remove_file, "doc/README" + expect(File.exists?(file)).to be_true + end + + it "logs status" do + expect(action(:remove_file, "doc/README")).to eq(" remove doc/README\n") + end + + it "does not log status if required" do + expect(action(:remove_file, "doc/README", :verbose => false)).to be_empty + end + end + + describe "#gsub_file" do + it "replaces the content in the file" do + action :gsub_file, "doc/README", "__start__", "START" + expect(File.binread(file)).to eq("START\nREADME\n__end__\n") + end + + it "does not replace if pretending" do + runner(:pretend => true) + action :gsub_file, "doc/README", "__start__", "START" + expect(File.binread(file)).to eq("__start__\nREADME\n__end__\n") + end + + it "accepts a block" do + action(:gsub_file, "doc/README", "__start__"){ |match| match.gsub('__', '').upcase } + expect(File.binread(file)).to eq("START\nREADME\n__end__\n") + end + + it "logs status" do + expect(action(:gsub_file, "doc/README", "__start__", "START")).to eq(" gsub doc/README\n") + end + + it "does not log status if required" do + expect(action(:gsub_file, file, "__", :verbose => false){ |match| match * 2 }).to be_empty + end + end + + describe "#append_to_file" do + it "appends content to the file" do + action :append_to_file, "doc/README", "END\n" + expect(File.binread(file)).to eq("__start__\nREADME\n__end__\nEND\n") + end + + it "accepts a block" do + action(:append_to_file, "doc/README"){ "END\n" } + expect(File.binread(file)).to eq("__start__\nREADME\n__end__\nEND\n") + end + + it "logs status" do + expect(action(:append_to_file, "doc/README", "END")).to eq(" append doc/README\n") + end + end + + describe "#prepend_to_file" do + it "prepends content to the file" do + action :prepend_to_file, "doc/README", "START\n" + expect(File.binread(file)).to eq("START\n__start__\nREADME\n__end__\n") + end + + it "accepts a block" do + action(:prepend_to_file, "doc/README"){ "START\n" } + expect(File.binread(file)).to eq("START\n__start__\nREADME\n__end__\n") + end + + it "logs status" do + expect(action(:prepend_to_file, "doc/README", "START")).to eq(" prepend doc/README\n") + end + end + + describe "#inject_into_class" do + def file + File.join(destination_root, "application.rb") + end + + it "appends content to a class" do + action :inject_into_class, "application.rb", Application, " filter_parameters :password\n" + expect(File.binread(file)).to eq("class Application < Base\n filter_parameters :password\nend\n") + end + + it "accepts a block" do + action(:inject_into_class, "application.rb", Application){ " filter_parameters :password\n" } + expect(File.binread(file)).to eq("class Application < Base\n filter_parameters :password\nend\n") + end + + it "logs status" do + expect(action(:inject_into_class, "application.rb", Application, " filter_parameters :password\n")).to eq(" insert application.rb\n") + end + + it "does not append if class name does not match" do + action :inject_into_class, "application.rb", "App", " filter_parameters :password\n" + expect(File.binread(file)).to eq("class Application < Base\nend\n") + end + end + end + + describe "when adjusting comments" do + before do + ::FileUtils.cp_r(source_root, destination_root) + end + + def file + File.join(destination_root, "doc", "COMMENTER") + end + + unmodified_comments_file = /__start__\n # greenblue\n#\n# yellowblue\n#yellowred\n #greenred\norange\n purple\n ind#igo\n # ind#igo\n__end__/ + + describe "#uncomment_lines" do + it "uncomments all matching lines in the file" do + action :uncomment_lines, "doc/COMMENTER", "green" + expect(File.binread(file)).to match(/__start__\n greenblue\n#\n# yellowblue\n#yellowred\n greenred\norange\n purple\n ind#igo\n # ind#igo\n__end__/) + + action :uncomment_lines, "doc/COMMENTER", "red" + expect(File.binread(file)).to match(/__start__\n greenblue\n#\n# yellowblue\nyellowred\n greenred\norange\n purple\n ind#igo\n # ind#igo\n__end__/) + end + + it "correctly uncomments lines with hashes in them" do + action :uncomment_lines, "doc/COMMENTER", "ind#igo" + expect(File.binread(file)).to match(/__start__\n # greenblue\n#\n# yellowblue\n#yellowred\n #greenred\norange\n purple\n ind#igo\n ind#igo\n__end__/) + end + + it "does not modify already uncommented lines in the file" do + action :uncomment_lines, "doc/COMMENTER", "orange" + action :uncomment_lines, "doc/COMMENTER", "purple" + expect(File.binread(file)).to match(unmodified_comments_file) + end + + it "does not uncomment the wrong line when uncommenting lines preceded by blank commented line" do + action :uncomment_lines, "doc/COMMENTER", "yellow" + expect(File.binread(file)).to match(/__start__\n # greenblue\n#\nyellowblue\nyellowred\n #greenred\norange\n purple\n ind#igo\n # ind#igo\n__end__/) + end + end + + describe "#comment_lines" do + it "comments lines which are not commented" do + action :comment_lines, "doc/COMMENTER", "orange" + expect(File.binread(file)).to match(/__start__\n # greenblue\n#\n# yellowblue\n#yellowred\n #greenred\n# orange\n purple\n ind#igo\n # ind#igo\n__end__/) + + action :comment_lines, "doc/COMMENTER", "purple" + expect(File.binread(file)).to match(/__start__\n # greenblue\n#\n# yellowblue\n#yellowred\n #greenred\n# orange\n # purple\n ind#igo\n # ind#igo\n__end__/) + end + + it "correctly comments lines with hashes in them" do + action :comment_lines, "doc/COMMENTER", "ind#igo" + expect(File.binread(file)).to match(/__start__\n # greenblue\n#\n# yellowblue\n#yellowred\n #greenred\norange\n purple\n # ind#igo\n # ind#igo\n__end__/) + end + + it "does not modify already commented lines" do + action :comment_lines, "doc/COMMENTER", "green" + expect(File.binread(file)).to match(unmodified_comments_file) + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/actions/inject_into_file_spec.rb b/vendor/gems/thor-0.18.1/spec/actions/inject_into_file_spec.rb new file mode 100644 index 0000000..cf577de --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions/inject_into_file_spec.rb @@ -0,0 +1,135 @@ +require 'helper' +require 'thor/actions' + +describe Thor::Actions::InjectIntoFile do + before do + ::FileUtils.rm_rf(destination_root) + ::FileUtils.cp_r(source_root, destination_root) + end + + def invoker(options={}) + @invoker ||= MyCounter.new([1,2], options, { :destination_root => destination_root }) + end + + def revoker + @revoker ||= MyCounter.new([1,2], {}, { :destination_root => destination_root, :behavior => :revoke }) + end + + def invoke!(*args, &block) + capture(:stdout) { invoker.insert_into_file(*args, &block) } + end + + def revoke!(*args, &block) + capture(:stdout) { revoker.insert_into_file(*args, &block) } + end + + def file + File.join(destination_root, "doc/README") + end + + describe "#invoke!" do + it "changes the file adding content after the flag" do + invoke! "doc/README", "\nmore content", :after => "__start__" + expect(File.read(file)).to eq("__start__\nmore content\nREADME\n__end__\n") + end + + it "changes the file adding content before the flag" do + invoke! "doc/README", "more content\n", :before => "__end__" + expect(File.read(file)).to eq("__start__\nREADME\nmore content\n__end__\n") + end + + it "accepts data as a block" do + invoke! "doc/README", :before => "__end__" do + "more content\n" + end + + expect(File.read(file)).to eq("__start__\nREADME\nmore content\n__end__\n") + end + + it "logs status" do + expect(invoke!("doc/README", "\nmore content", :after => "__start__")).to eq(" insert doc/README\n") + end + + it "does not change the file if pretending" do + invoker :pretend => true + invoke! "doc/README", "\nmore content", :after => "__start__" + expect(File.read(file)).to eq("__start__\nREADME\n__end__\n") + end + + it "does not change the file if already include content" do + invoke! "doc/README", :before => "__end__" do + "more content\n" + end + + expect(File.read(file)).to eq("__start__\nREADME\nmore content\n__end__\n") + + invoke! "doc/README", :before => "__end__" do + "more content\n" + end + + expect(File.read(file)).to eq("__start__\nREADME\nmore content\n__end__\n") + end + + it "does change the file if already include content and :force == true" do + invoke! "doc/README", :before => "__end__" do + "more content\n" + end + + expect(File.read(file)).to eq("__start__\nREADME\nmore content\n__end__\n") + + invoke! "doc/README", :before => "__end__", :force => true do + "more content\n" + end + + expect(File.read(file)).to eq("__start__\nREADME\nmore content\nmore content\n__end__\n") + end + + end + + describe "#revoke!" do + it "substracts the destination file after injection" do + invoke! "doc/README", "\nmore content", :after => "__start__" + revoke! "doc/README", "\nmore content", :after => "__start__" + expect(File.read(file)).to eq("__start__\nREADME\n__end__\n") + end + + it "substracts the destination file before injection" do + invoke! "doc/README", "more content\n", :before => "__start__" + revoke! "doc/README", "more content\n", :before => "__start__" + expect(File.read(file)).to eq("__start__\nREADME\n__end__\n") + end + + it "substracts even with double after injection" do + invoke! "doc/README", "\nmore content", :after => "__start__" + invoke! "doc/README", "\nanother stuff", :after => "__start__" + revoke! "doc/README", "\nmore content", :after => "__start__" + expect(File.read(file)).to eq("__start__\nanother stuff\nREADME\n__end__\n") + end + + it "substracts even with double before injection" do + invoke! "doc/README", "more content\n", :before => "__start__" + invoke! "doc/README", "another stuff\n", :before => "__start__" + revoke! "doc/README", "more content\n", :before => "__start__" + expect(File.read(file)).to eq("another stuff\n__start__\nREADME\n__end__\n") + end + + it "substracts when prepending" do + invoke! "doc/README", "more content\n", :after => /\A/ + invoke! "doc/README", "another stuff\n", :after => /\A/ + revoke! "doc/README", "more content\n", :after => /\A/ + expect(File.read(file)).to eq("another stuff\n__start__\nREADME\n__end__\n") + end + + it "substracts when appending" do + invoke! "doc/README", "more content\n", :before => /\z/ + invoke! "doc/README", "another stuff\n", :before => /\z/ + revoke! "doc/README", "more content\n", :before => /\z/ + expect(File.read(file)).to eq("__start__\nREADME\n__end__\nanother stuff\n") + end + + it "shows progress information to the user" do + invoke!("doc/README", "\nmore content", :after => "__start__") + expect(revoke!("doc/README", "\nmore content", :after => "__start__")).to eq(" subtract doc/README\n") + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/actions_spec.rb b/vendor/gems/thor-0.18.1/spec/actions_spec.rb new file mode 100644 index 0000000..fc452c6 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/actions_spec.rb @@ -0,0 +1,331 @@ +require 'helper' + +describe Thor::Actions do + def runner(options={}) + @runner ||= MyCounter.new([1], options, { :destination_root => destination_root }) + end + + def action(*args, &block) + capture(:stdout) { runner.send(*args, &block) } + end + + def file + File.join(destination_root, "foo") + end + + describe "on include" do + it "adds runtime options to the base class" do + expect(MyCounter.class_options.keys).to include(:pretend) + expect(MyCounter.class_options.keys).to include(:force) + expect(MyCounter.class_options.keys).to include(:quiet) + expect(MyCounter.class_options.keys).to include(:skip) + end + end + + describe "#initialize" do + it "has default behavior invoke" do + expect(runner.behavior).to eq(:invoke) + end + + it "can have behavior revoke" do + expect(MyCounter.new([1], {}, :behavior => :revoke).behavior).to eq(:revoke) + end + + it "when behavior is set to force, overwrite options" do + runner = MyCounter.new([1], { :force => false, :skip => true }, :behavior => :force) + expect(runner.behavior).to eq(:invoke) + expect(runner.options.force).to be_true + expect(runner.options.skip).not_to be_true + end + + it "when behavior is set to skip, overwrite options" do + runner = MyCounter.new([1], ["--force"], :behavior => :skip) + expect(runner.behavior).to eq(:invoke) + expect(runner.options.force).not_to be_true + expect(runner.options.skip).to be_true + end + end + + describe "accessors" do + describe "#destination_root=" do + it "gets the current directory and expands the path to set the root" do + base = MyCounter.new([1]) + base.destination_root = "here" + expect(base.destination_root).to eq(File.expand_path(File.join(File.dirname(__FILE__), "..", "here"))) + end + + it "does not use the current directory if one is given" do + root = File.expand_path("/") + base = MyCounter.new([1]) + base.destination_root = root + expect(base.destination_root).to eq(root) + end + + it "uses the current directory if none is given" do + base = MyCounter.new([1]) + expect(base.destination_root).to eq(File.expand_path(File.join(File.dirname(__FILE__), ".."))) + end + end + + describe "#relative_to_original_destination_root" do + it "returns the path relative to the absolute root" do + expect(runner.relative_to_original_destination_root(file)).to eq("foo") + end + + it "does not remove dot if required" do + expect(runner.relative_to_original_destination_root(file, false)).to eq("./foo") + end + + it "always use the absolute root" do + runner.inside("foo") do + expect(runner.relative_to_original_destination_root(file)).to eq("foo") + end + end + + it "creates proper relative paths for absolute file location" do + expect(runner.relative_to_original_destination_root('/test/file')).to eq("/test/file") + end + + it "does not fail with files constaining regexp characters" do + runner = MyCounter.new([1], {}, { :destination_root => File.join(destination_root, "fo[o-b]ar") }) + expect(runner.relative_to_original_destination_root("bar")).to eq("bar") + end + + describe "#source_paths_for_search" do + it "add source_root to source_paths_for_search" do + expect(MyCounter.source_paths_for_search).to include(File.expand_path("fixtures", File.dirname(__FILE__))) + end + + it "keeps only current source root in source paths" do + expect(ClearCounter.source_paths_for_search).to include(File.expand_path("fixtures/bundle", File.dirname(__FILE__))) + expect(ClearCounter.source_paths_for_search).not_to include(File.expand_path("fixtures", File.dirname(__FILE__))) + end + + it "customized source paths should be before source roots" do + expect(ClearCounter.source_paths_for_search[0]).to eq(File.expand_path("fixtures/doc", File.dirname(__FILE__))) + expect(ClearCounter.source_paths_for_search[1]).to eq(File.expand_path("fixtures/bundle", File.dirname(__FILE__))) + end + + it "keeps inherited source paths at the end" do + expect(ClearCounter.source_paths_for_search.last).to eq(File.expand_path("fixtures/broken", File.dirname(__FILE__))) + end + end + end + + describe "#find_in_source_paths" do + it "raises an error if source path is empty" do + expect { + A.new.find_in_source_paths("foo") + }.to raise_error(Thor::Error, /Currently you have no source paths/) + end + + it "finds a template inside the source path" do + expect(runner.find_in_source_paths("doc")).to eq(File.expand_path("doc", source_root)) + expect{ runner.find_in_source_paths("README") }.to raise_error + + new_path = File.join(source_root, "doc") + runner.instance_variable_set(:@source_paths, nil) + runner.source_paths.unshift(new_path) + expect(runner.find_in_source_paths("README")).to eq(File.expand_path("README", new_path)) + end + end + end + + describe "#inside" do + it "executes the block inside the given folder" do + runner.inside("foo") do + expect(Dir.pwd).to eq(file) + end + end + + it "changes the base root" do + runner.inside("foo") do + expect(runner.destination_root).to eq(file) + end + end + + it "creates the directory if it does not exist" do + runner.inside("foo") do + expect(File.exists?(file)).to be_true + end + end + + describe "when pretending" do + it "no directories should be created" do + runner.inside("bar", :pretend => true) {} + expect(File.exists?("bar")).to be_false + end + end + + describe "when verbose" do + it "logs status" do + expect(capture(:stdout) { + runner.inside("foo", :verbose => true) {} + }).to match(/inside foo/) + end + + it "uses padding in next status" do + expect(capture(:stdout) { + runner.inside("foo", :verbose => true) do + runner.say_status :cool, :padding + end + }).to match(/cool padding/) + end + + it "removes padding after block" do + expect(capture(:stdout) { + runner.inside("foo", :verbose => true) {} + runner.say_status :no, :padding + }).to match(/no padding/) + end + end + end + + describe "#in_root" do + it "executes the block in the root folder" do + runner.inside("foo") do + runner.in_root { expect(Dir.pwd).to eq(destination_root) } + end + end + + it "changes the base root" do + runner.inside("foo") do + runner.in_root { expect(runner.destination_root).to eq(destination_root) } + end + end + + it "returns to the previous state" do + runner.inside("foo") do + runner.in_root { } + expect(runner.destination_root).to eq(file) + end + end + end + + describe "#apply" do + before do + @template = <<-TEMPLATE + @foo = "FOO" + say_status :cool, :padding + TEMPLATE + @template.stub(:read).and_return(@template) + + @file = '/' + runner.stub(:open).and_return(@template) + end + + it "accepts a URL as the path" do + @file = "http://gist.github.com/103208.txt" + runner.should_receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template) + action(:apply, @file) + end + + it "accepts a secure URL as the path" do + @file = "https://gist.github.com/103208.txt" + runner.should_receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template) + action(:apply, @file) + end + + it "accepts a local file path with spaces" do + @file = File.expand_path("fixtures/path with spaces", File.dirname(__FILE__)) + runner.should_receive(:open).with(@file).and_return(@template) + action(:apply, @file) + end + + it "opens a file and executes its content in the instance binding" do + action :apply, @file + expect(runner.instance_variable_get("@foo")).to eq("FOO") + end + + it "applies padding to the content inside the file" do + expect(action(:apply, @file)).to match(/cool padding/) + end + + it "logs its status" do + expect(action(:apply, @file)).to match(/ apply #{@file}\n/) + end + + it "does not log status" do + content = action(:apply, @file, :verbose => false) + expect(content).to match(/cool padding/) + expect(content).not_to match(/apply http/) + end + end + + describe "#run" do + before do + runner.should_receive(:system).with("ls") + end + + it "executes the command given" do + action :run, "ls" + end + + it "logs status" do + expect(action(:run, "ls")).to eq(" run ls from \".\"\n") + end + + it "does not log status if required" do + expect(action(:run, "ls", :verbose => false)).to be_empty + end + + it "accepts a color as status" do + runner.shell.should_receive(:say_status).with(:run, 'ls from "."', :yellow) + action :run, "ls", :verbose => :yellow + end + end + + describe "#run_ruby_script" do + before do + Thor::Util.stub!(:ruby_command).and_return("/opt/jruby") + runner.should_receive(:system).with("/opt/jruby script.rb") + end + + it "executes the ruby script" do + action :run_ruby_script, "script.rb" + end + + it "logs status" do + expect(action(:run_ruby_script, "script.rb")).to eq(" run jruby script.rb from \".\"\n") + end + + it "does not log status if required" do + expect(action(:run_ruby_script, "script.rb", :verbose => false)).to be_empty + end + end + + describe "#thor" do + it "executes the thor command" do + runner.should_receive(:system).with("thor list") + action :thor, :list, :verbose => true + end + + it "converts extra arguments to command arguments" do + runner.should_receive(:system).with("thor list foo bar") + action :thor, :list, "foo", "bar" + end + + it "converts options hash to switches" do + runner.should_receive(:system).with("thor list foo bar --foo") + action :thor, :list, "foo", "bar", :foo => true + + runner.should_receive(:system).with("thor list --foo 1 2 3") + action :thor, :list, :foo => [1,2,3] + end + + it "logs status" do + runner.should_receive(:system).with("thor list") + expect(action(:thor, :list)).to eq(" run thor list from \".\"\n") + end + + it "does not log status if required" do + runner.should_receive(:system).with("thor list --foo 1 2 3") + expect(action(:thor, :list, :foo => [1,2,3], :verbose => false)).to be_empty + end + + it "captures the output when :capture is given" do + runner.should_receive(:`).with("thor foo bar") + action(:thor, "foo", "bar", :capture => true) + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/base_spec.rb b/vendor/gems/thor-0.18.1/spec/base_spec.rb new file mode 100644 index 0000000..12143fd --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/base_spec.rb @@ -0,0 +1,291 @@ +require 'helper' +require 'thor/base' + +class Amazing + desc "hello", "say hello" + def hello + puts "Hello" + end +end + +describe Thor::Base do + describe "#initialize" do + it "sets arguments array" do + base = MyCounter.new [1, 2] + expect(base.first).to eq(1) + expect(base.second).to eq(2) + end + + it "sets arguments default values" do + base = MyCounter.new [1] + expect(base.second).to eq(2) + end + + it "sets options default values" do + base = MyCounter.new [1, 2] + expect(base.options[:third]).to eq(3) + end + + it "allows options to be given as symbols or strings" do + base = MyCounter.new [1, 2], :third => 4 + expect(base.options[:third]).to eq(4) + + base = MyCounter.new [1, 2], "third" => 4 + expect(base.options[:third]).to eq(4) + end + + it "creates options with indifferent access" do + base = MyCounter.new [1, 2], :third => 3 + expect(base.options['third']).to eq(3) + end + + it "creates options with magic predicates" do + base = MyCounter.new [1, 2], :third => 3 + expect(base.options.third).to eq(3) + end + end + + describe "#no_commands" do + it "avoids methods being added as commands" do + expect(MyScript.commands.keys).to include("animal") + expect(MyScript.commands.keys).not_to include("this_is_not_a_command") + end + end + + describe "#argument" do + it "sets a value as required and creates an accessor for it" do + expect(MyCounter.start(["1", "2", "--third", "3"])[0]).to eq(1) + expect(Scripts::MyScript.start(["zoo", "my_special_param", "--param=normal_param"])).to eq("my_special_param") + end + + it "does not set a value in the options hash" do + expect(BrokenCounter.start(["1", "2", "--third", "3"])[0]).to be_nil + end + end + + describe "#arguments" do + it "returns the arguments for the class" do + expect(MyCounter.arguments).to have(2).items + end + end + + describe ":aliases" do + it "supports string aliases without a dash prefix" do + expect(MyCounter.start(["1", "2", "-z", "3"])[4]).to eq(3) + end + + it "supports symbol aliases" do + expect(MyCounter.start(["1", "2", "-y", "3"])[5]).to eq(3) + expect(MyCounter.start(["1", "2", "-r", "3"])[5]).to eq(3) + end + end + + describe "#class_option" do + it "sets options class wise" do + expect(MyCounter.start(["1", "2", "--third", "3"])[2]).to eq(3) + end + + it "does not create an accessor for it" do + expect(BrokenCounter.start(["1", "2", "--third", "3"])[3]).to be_false + end + end + + describe "#class_options" do + it "sets default options overwriting superclass definitions" do + options = Scripts::MyScript.class_options + expect(options[:force]).not_to be_required + end + end + + describe "#remove_argument" do + it "removes previous defined arguments from class" do + expect(ClearCounter.arguments).to be_empty + end + + it "undefine accessors if required" do + expect(ClearCounter.new).not_to respond_to(:first) + expect(ClearCounter.new).not_to respond_to(:second) + end + end + + describe "#remove_class_option" do + it "removes previous defined class option" do + expect(ClearCounter.class_options[:third]).to be_nil + end + end + + describe "#class_options_help" do + before do + @content = capture(:stdout) { MyCounter.help(Thor::Base.shell.new) } + end + + it "shows options description" do + expect(@content).to match(/# The third argument/) + end + + it "shows usage with banner content" do + expect(@content).to match(/\[\-\-third=THREE\]/) + end + + it "shows default values below description" do + expect(@content).to match(/# Default: 3/) + end + + it "shows options in different groups" do + expect(@content).to match(/Options\:/) + expect(@content).to match(/Runtime options\:/) + expect(@content).to match(/\-p, \[\-\-pretend\]/) + end + + it "use padding in options that does not have aliases" do + expect(@content).to match(/^ -t, \[--third/) + expect(@content).to match(/^ \[--fourth/) + end + + it "allows extra options to be given" do + hash = { "Foo" => B.class_options.values } + + content = capture(:stdout) { MyCounter.send(:class_options_help, Thor::Base.shell.new, hash) } + expect(content).to match(/Foo options\:/) + expect(content).to match(/--last-name=LAST_NAME/) + end + + it "displays choices for enums" do + content = capture(:stdout) { Enum.help(Thor::Base.shell.new) } + expect(content).to match(/Possible values\: apple, banana/) + end + end + + describe "#namespace" do + it "returns the default class namespace" do + expect(Scripts::MyScript.namespace).to eq("scripts:my_script") + end + + it "sets a namespace to the class" do + expect(Scripts::MyDefaults.namespace).to eq("default") + end + end + + describe "#group" do + it "sets a group" do + expect(MyScript.group).to eq("script") + end + + it "inherits the group from parent" do + expect(MyChildScript.group).to eq("script") + end + + it "defaults to standard if no group is given" do + expect(Amazing.group).to eq("standard") + end + end + + describe "#subclasses" do + it "tracks its subclasses in an Array" do + expect(Thor::Base.subclasses).to include(MyScript) + expect(Thor::Base.subclasses).to include(MyChildScript) + expect(Thor::Base.subclasses).to include(Scripts::MyScript) + end + end + + describe "#subclass_files" do + it "returns tracked subclasses, grouped by the files they come from" do + thorfile = File.join(File.dirname(__FILE__), "fixtures", "script.thor") + expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to eq([ + MyScript, MyScript::AnotherScript, MyChildScript, Barn, + PackageNameScript, Scripts::MyScript, Scripts::MyDefaults, + Scripts::ChildDefault, Scripts::Arities + ]) + end + + it "tracks a single subclass across multiple files" do + thorfile = File.join(File.dirname(__FILE__), "fixtures", "command.thor") + expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to include(Amazing) + expect(Thor::Base.subclass_files[File.expand_path(__FILE__)]).to include(Amazing) + end + end + + describe "#commands" do + it "returns a list with all commands defined in this class" do + expect(MyChildScript.new).to respond_to("animal") + expect(MyChildScript.commands.keys).to include("animal") + end + + it "raises an error if a command with reserved word is defined" do + expect { + klass = Class.new(Thor::Group) + klass.class_eval "def shell; end" + }.to raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as command/) + end + end + + describe "#all_commands" do + it "returns a list with all commands defined in this class plus superclasses" do + expect(MyChildScript.new).to respond_to("foo") + expect(MyChildScript.all_commands.keys).to include("foo") + end + end + + describe "#remove_command" do + it "removes the command from its commands hash" do + expect(MyChildScript.commands.keys).not_to include("bar") + expect(MyChildScript.commands.keys).not_to include("boom") + end + + it "undefines the method if desired" do + expect(MyChildScript.new).not_to respond_to("boom") + end + end + + describe "#from_superclass" do + it "does not send a method to the superclass if the superclass does not respond to it" do + expect(MyCounter.get_from_super).to eq(13) + end + end + + describe "#start" do + it "raises an error instead of rescueing if THOR_DEBUG=1 is given" do + begin + ENV["THOR_DEBUG"] = 1 + expect { + MyScript.start ["what", "--debug"] + }.to raise_error(Thor::UndefinedcommandError, 'Could not find command "what" in "my_script" namespace.') + rescue + ENV["THOR_DEBUG"] = nil + end + end + + it "does not steal args" do + args = ["foo", "bar", "--force", "true"] + MyScript.start(args) + expect(args).to eq(["foo", "bar", "--force", "true"]) + end + + it "checks unknown options" do + expect(capture(:stderr) { + MyScript.start(["foo", "bar", "--force", "true", "--unknown", "baz"]) + }.strip).to eq("Unknown switches '--unknown'") + end + + it "checks unknown options except specified" do + expect(capture(:stderr) { + expect(MyScript.start(["with_optional", "NAME", "--omg", "--invalid"])).to eq(["NAME", {}, ["--omg", "--invalid"]]) + }.strip).to be_empty + end + end + + describe "attr_*" do + it "does not add attr_reader as a command" do + expect(capture(:stderr){ MyScript.start(["another_attribute"]) }).to match(/Could not find/) + end + + it "does not add attr_writer as a command" do + expect(capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }).to match(/Could not find/) + end + + it "does not add attr_accessor as a command" do + expect(capture(:stderr){ MyScript.start(["some_attribute"]) }).to match(/Could not find/) + expect(capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }).to match(/Could not find/) + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/command_spec.rb b/vendor/gems/thor-0.18.1/spec/command_spec.rb new file mode 100644 index 0000000..2f8dac0 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/command_spec.rb @@ -0,0 +1,80 @@ +require 'helper' + +describe Thor::Command do + def command(options={}) + options.each do |key, value| + options[key] = Thor::Option.parse(key, value) + end + + @command ||= Thor::Command.new(:can_has, "I can has cheezburger", "I can has cheezburger\nLots and lots of it", "can_has", options) + end + + describe "#formatted_usage" do + it "includes namespace within usage" do + object = Struct.new(:namespace, :arguments).new("foo", []) + expect(command(:bar => :required).formatted_usage(object)).to eq("foo:can_has --bar=BAR") + end + + it "includes subcommand name within subcommand usage" do + object = Struct.new(:namespace, :arguments).new("main:foo", []) + expect(command(:bar => :required).formatted_usage(object, false, true)).to eq("foo can_has --bar=BAR") + end + + it "removes default from namespace" do + object = Struct.new(:namespace, :arguments).new("default:foo", []) + expect(command(:bar => :required).formatted_usage(object)).to eq(":foo:can_has --bar=BAR") + end + + it "injects arguments into usage" do + options = {:required => true, :type => :string} + object = Struct.new(:namespace, :arguments).new("foo", [Thor::Argument.new(:bar, options)]) + expect(command(:foo => :required).formatted_usage(object)).to eq("foo:can_has BAR --foo=FOO") + end + end + + describe "#dynamic" do + it "creates a dynamic command with the given name" do + expect(Thor::DynamicCommand.new('command').name).to eq('command') + expect(Thor::DynamicCommand.new('command').description).to eq('A dynamically-generated command') + expect(Thor::DynamicCommand.new('command').usage).to eq('command') + expect(Thor::DynamicCommand.new('command').options).to eq({}) + end + + it "does not invoke an existing method" do + mock = mock() + mock.class.should_receive(:handle_no_command_error).with("to_s") + Thor::DynamicCommand.new('to_s').run(mock) + end + end + + describe "#dup" do + it "dup options hash" do + command = Thor::Command.new("can_has", nil, nil, nil, :foo => true, :bar => :required) + command.dup.options.delete(:foo) + expect(command.options[:foo]).to be + end + end + + describe "#run" do + it "runs a command by calling a method in the given instance" do + mock = mock() + mock.should_receive(:can_has).and_return {|*args| args } + expect(command.run(mock, [1, 2, 3])).to eq([1, 2, 3]) + end + + it "raises an error if the method to be invoked is private" do + klass = Class.new do + def self.handle_no_command_error(name) + name + end + + private + def can_has + "fail" + end + end + + expect(command.run(klass.new)).to eq("can_has") + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/core_ext/hash_with_indifferent_access_spec.rb b/vendor/gems/thor-0.18.1/spec/core_ext/hash_with_indifferent_access_spec.rb new file mode 100644 index 0000000..5d66951 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/core_ext/hash_with_indifferent_access_spec.rb @@ -0,0 +1,48 @@ +require 'helper' +require 'thor/core_ext/hash_with_indifferent_access' + +describe Thor::CoreExt::HashWithIndifferentAccess do + before do + @hash = Thor::CoreExt::HashWithIndifferentAccess.new :foo => 'bar', 'baz' => 'bee', :force => true + end + + it "has values accessible by either strings or symbols" do + expect(@hash['foo']).to eq('bar') + expect(@hash[:foo]).to eq('bar') + + expect(@hash.values_at(:foo, :baz)).to eq(['bar', 'bee']) + expect(@hash.delete(:foo)).to eq('bar') + end + + it "handles magic boolean predicates" do + expect(@hash.force?).to be_true + expect(@hash.foo?).to be_true + expect(@hash.nothing?).to be_false + end + + it "handles magic comparisions" do + expect(@hash.foo?('bar')).to be_true + expect(@hash.foo?('bee')).to be_false + end + + it "maps methods to keys" do + expect(@hash.foo).to eq(@hash['foo']) + end + + it "merges keys independent if they are symbols or strings" do + @hash.merge!('force' => false, :baz => "boom") + expect(@hash[:force]).to eq(false) + expect(@hash[:baz]).to eq("boom") + end + + it "creates a new hash by merging keys independent if they are symbols or strings" do + other = @hash.merge('force' => false, :baz => "boom") + expect(other[:force]).to eq(false) + expect(other[:baz]).to eq("boom") + end + + it "converts to a traditional hash" do + expect(@hash.to_hash.class).to eq(Hash) + expect(@hash).to eq({ 'foo' => 'bar', 'baz' => 'bee', 'force' => true }) + end +end diff --git a/vendor/gems/thor-0.18.1/spec/core_ext/ordered_hash_spec.rb b/vendor/gems/thor-0.18.1/spec/core_ext/ordered_hash_spec.rb new file mode 100644 index 0000000..aba5226 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/core_ext/ordered_hash_spec.rb @@ -0,0 +1,115 @@ +require 'helper' +require 'thor/core_ext/ordered_hash' + +describe Thor::CoreExt::OrderedHash do + before do + @hash = Thor::CoreExt::OrderedHash.new + end + + describe "without any items" do + it "returns nil for an undefined key" do + expect(@hash["foo"]).to be_nil + end + + it "doesn't iterate through any items" do + @hash.each { fail } + end + + it "has an empty key and values list" do + expect(@hash.keys).to be_empty + expect(@hash.values).to be_empty + end + + it "must be empty" do + expect(@hash).to be_empty + end + end + + describe "with several items" do + before do + @hash[:foo] = "Foo!" + @hash[:bar] = "Bar!" + @hash[:baz] = "Baz!" + @hash[:bop] = "Bop!" + @hash[:bat] = "Bat!" + end + + it "returns nil for an undefined key" do + expect(@hash[:boom]).to be_nil + end + + it "returns the value for each key" do + expect(@hash[:foo]).to eq("Foo!") + expect(@hash[:bar]).to eq("Bar!") + expect(@hash[:baz]).to eq("Baz!") + expect(@hash[:bop]).to eq("Bop!") + expect(@hash[:bat]).to eq("Bat!") + end + + it "iterates through the keys and values in order of assignment" do + arr = [] + @hash.each do |key, value| + arr << [key, value] + end + expect(arr).to eq([[:foo, "Foo!"], [:bar, "Bar!"], [:baz, "Baz!"], + [:bop, "Bop!"], [:bat, "Bat!"]]) + end + + it "returns the keys in order of insertion" do + expect(@hash.keys).to eq([:foo, :bar, :baz, :bop, :bat]) + end + + it "returns the values in order of insertion" do + expect(@hash.values).to eq(["Foo!", "Bar!", "Baz!", "Bop!", "Bat!"]) + end + + it "does not move an overwritten node to the end of the ordering" do + @hash[:baz] = "Bip!" + expect(@hash.values).to eq(["Foo!", "Bar!", "Bip!", "Bop!", "Bat!"]) + + @hash[:foo] = "Bip!" + expect(@hash.values).to eq(["Bip!", "Bar!", "Bip!", "Bop!", "Bat!"]) + + @hash[:bat] = "Bip!" + expect(@hash.values).to eq(["Bip!", "Bar!", "Bip!", "Bop!", "Bip!"]) + end + + it "appends another ordered hash while preserving ordering" do + other_hash = Thor::CoreExt::OrderedHash.new + other_hash[1] = "one" + other_hash[2] = "two" + other_hash[3] = "three" + expect(@hash.merge(other_hash).values).to eq(["Foo!", "Bar!", "Baz!", "Bop!", "Bat!", "one", "two", "three"]) + end + + it "overwrites hash keys with matching appended keys" do + other_hash = Thor::CoreExt::OrderedHash.new + other_hash[:bar] = "bar" + expect(@hash.merge(other_hash)[:bar]).to eq("bar") + expect(@hash[:bar]).to eq("Bar!") + end + + it "converts to an array" do + expect(@hash.to_a).to eq([[:foo, "Foo!"], [:bar, "Bar!"], [:baz, "Baz!"], [:bop, "Bop!"], [:bat, "Bat!"]]) + end + + it "must not be empty" do + expect(@hash).not_to be_empty + end + + it "deletes values from hash" do + expect(@hash.delete(:baz)).to eq("Baz!") + expect(@hash.values).to eq(["Foo!", "Bar!", "Bop!", "Bat!"]) + + expect(@hash.delete(:foo)).to eq("Foo!") + expect(@hash.values).to eq(["Bar!", "Bop!", "Bat!"]) + + expect(@hash.delete(:bat)).to eq("Bat!") + expect(@hash.values).to eq(["Bar!", "Bop!"]) + end + + it "returns nil if the value to be deleted can't be found" do + expect(@hash.delete(:nothing)).to be_nil + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/exit_condition_spec.rb b/vendor/gems/thor-0.18.1/spec/exit_condition_spec.rb new file mode 100644 index 0000000..a7571ce --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/exit_condition_spec.rb @@ -0,0 +1,19 @@ +require 'helper' +require 'thor/base' + +describe "Exit conditions" do + it "exits 0, not bubble up EPIPE, if EPIPE is raised" do + epiped = false + + command = Class.new(Thor) do + desc "my_action", "testing EPIPE" + define_method :my_action do + epiped = true + raise Errno::EPIPE + end + end + + expect{ command.start(["my_action"]) }.to raise_error(SystemExit) + expect(epiped).to eq(true) + end +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/application.rb b/vendor/gems/thor-0.18.1/spec/fixtures/application.rb new file mode 100644 index 0000000..50d2fae --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/application.rb @@ -0,0 +1,2 @@ +class Application < Base +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/app{1}/README b/vendor/gems/thor-0.18.1/spec/fixtures/app{1}/README new file mode 100644 index 0000000..16374df --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/app{1}/README @@ -0,0 +1,3 @@ +__start__ +README +__end__ diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/bundle/execute.rb b/vendor/gems/thor-0.18.1/spec/fixtures/bundle/execute.rb new file mode 100644 index 0000000..0530d87 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/bundle/execute.rb @@ -0,0 +1,6 @@ +class Execute < Thor + desc "ls", "Execute ls" + def ls + system "ls" + end +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/bundle/main.thor b/vendor/gems/thor-0.18.1/spec/fixtures/bundle/main.thor new file mode 100644 index 0000000..38bdfbc --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/bundle/main.thor @@ -0,0 +1 @@ +require File.join(File.dirname(__FILE__), 'execute') diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/command.thor b/vendor/gems/thor-0.18.1/spec/fixtures/command.thor new file mode 100644 index 0000000..26a0268 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/command.thor @@ -0,0 +1,10 @@ +# module: random + +class Amazing < Thor + desc "describe NAME", "say that someone is amazing" + method_options :forcefully => :boolean + def describe(name, opts) + ret = "#{name} is amazing" + puts opts["forcefully"] ? ret.upcase : ret + end +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/%file_name%.rb.tt b/vendor/gems/thor-0.18.1/spec/fixtures/doc/%file_name%.rb.tt new file mode 100644 index 0000000..4c4c6c0 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/%file_name%.rb.tt @@ -0,0 +1 @@ +FOO = <%= "FOO" %> diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/COMMENTER b/vendor/gems/thor-0.18.1/spec/fixtures/doc/COMMENTER new file mode 100644 index 0000000..384cb3a --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/COMMENTER @@ -0,0 +1,11 @@ +__start__ + # greenblue +# +# yellowblue +#yellowred + #greenred +orange + purple + ind#igo + # ind#igo +__end__ diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/README b/vendor/gems/thor-0.18.1/spec/fixtures/doc/README new file mode 100644 index 0000000..16374df --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/README @@ -0,0 +1,3 @@ +__start__ +README +__end__ diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/block_helper.rb b/vendor/gems/thor-0.18.1/spec/fixtures/doc/block_helper.rb new file mode 100644 index 0000000..df59211 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/block_helper.rb @@ -0,0 +1,3 @@ +<% world do -%> +Hello +<% end -%> diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/config.rb b/vendor/gems/thor-0.18.1/spec/fixtures/doc/config.rb new file mode 100644 index 0000000..6211739 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/config.rb @@ -0,0 +1 @@ +class <%= @klass %>; end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/config.yaml.tt b/vendor/gems/thor-0.18.1/spec/fixtures/doc/config.yaml.tt new file mode 100644 index 0000000..e75615c --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/config.yaml.tt @@ -0,0 +1 @@ +--- Hi from yaml diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/doc/excluding/%file_name%.rb.tt b/vendor/gems/thor-0.18.1/spec/fixtures/doc/excluding/%file_name%.rb.tt new file mode 100644 index 0000000..6296c46 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/doc/excluding/%file_name%.rb.tt @@ -0,0 +1 @@ +BAR = <%= "BAR" %> diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/enum.thor b/vendor/gems/thor-0.18.1/spec/fixtures/enum.thor new file mode 100644 index 0000000..b5a7ded --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/enum.thor @@ -0,0 +1,10 @@ +class Enum < Thor::Group + include Thor::Actions + + desc "snack" + class_option "fruit", :aliases => "-f", :type => :string, :enum => %w(apple banana) + def snack + puts options['fruit'] + end + +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/group.thor b/vendor/gems/thor-0.18.1/spec/fixtures/group.thor new file mode 100644 index 0000000..bc7e102 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/group.thor @@ -0,0 +1,128 @@ +class MyCounter < Thor::Group + include Thor::Actions + add_runtime_options! + + def self.get_from_super + from_superclass(:get_from_super, 13) + end + + source_root File.expand_path(File.dirname(__FILE__)) + source_paths << File.expand_path("broken", File.dirname(__FILE__)) + + argument :first, :type => :numeric + argument :second, :type => :numeric, :default => 2 + + class_option :third, :type => :numeric, :desc => "The third argument", :default => 3, + :banner => "THREE", :aliases => "-t" + class_option :fourth, :type => :numeric, :desc => "The fourth argument" + class_option :simple, :type => :numeric, :aliases => 'z' + class_option :symbolic, :type => :numeric, :aliases => [:y, :r] + + desc <<-FOO +Description: + This generator runs three commands: one, two and three. +FOO + + def one + first + end + + def two + second + end + + def three + options[:third] + end + + def four + options[:fourth] + end + + def five + options[:simple] + end + + def six + options[:symbolic] + end + + def self.inherited(base) + super + base.source_paths.unshift(File.expand_path(File.join(File.dirname(__FILE__), "doc"))) + end + + no_commands do + def world(&block) + result = capture(&block) + concat(result.strip + " world!") + end + end +end + +class ClearCounter < MyCounter + remove_argument :first, :second, :undefine => true + remove_class_option :third + + def self.source_root + File.expand_path(File.join(File.dirname(__FILE__), "bundle")) + end +end + +class BrokenCounter < MyCounter + namespace "app:broken:counter" + class_option :fail, :type => :boolean, :default => false + + class << self + undef_method :source_root + end + + def one + options[:first] + end + + def four + respond_to?(:fail) + end + + def five + options[:fail] ? this_method_does_not_exist : 5 + end +end + +class WhinyGenerator < Thor::Group + include Thor::Actions + + def self.source_root + File.expand_path(File.dirname(__FILE__)) + end + + def wrong_arity(required) + end +end + +class CommandConflict < Thor::Group + desc "A group with the same name as a default command" + def group + puts "group" + end +end + +class ParentGroup < Thor::Group +private + def foo + "foo" + end + + def baz(name = 'baz') + name + end +end + +class ChildGroup < ParentGroup + def bar + "bar" + end + + public_command :foo, :baz +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/invoke.thor b/vendor/gems/thor-0.18.1/spec/fixtures/invoke.thor new file mode 100644 index 0000000..75bd51b --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/invoke.thor @@ -0,0 +1,112 @@ +class A < Thor + include Thor::Actions + + desc "one", "invoke one" + def one + p 1 + invoke :two + invoke :three + end + + desc "two", "invoke two" + def two + p 2 + invoke :three + end + + desc "three", "invoke three" + def three + p 3 + end + + desc "four", "invoke four" + def four + p 4 + invoke "defined:five" + end + + desc "five N", "check if number is equal 5" + def five(number) + number == 5 + end + + desc "invoker", "invoke a b command" + def invoker(*args) + invoke :b, :one, ["Jose"] + end +end + +class B < Thor + class_option :last_name, :type => :string + + desc "one FIRST_NAME", "invoke one" + def one(first_name) + "#{options.last_name}, #{first_name}" + end + + desc "two", "invoke two" + def two + options + end + + desc "three", "invoke three" + def three + self + end +end + +class C < Thor::Group + include Thor::Actions + + def one + p 1 + end + + def two + p 2 + end + + def three + p 3 + end +end + +class Defined < Thor::Group + class_option :unused, :type => :boolean, :desc => "This option has no use" + + def one + p 1 + invoke "a:two" + invoke "a:three" + invoke "a:four" + invoke "defined:five" + end + + def five + p 5 + end + + def print_status + say_status :finished, :counting + end +end + +class E < Thor::Group + invoke Defined +end + +class F < Thor::Group + invoke "b:one" do |instance, klass, command| + instance.invoke klass, command, [ "Jose" ], :last_name => "Valim" + end +end + +class G < Thor::Group + class_option :invoked, :type => :string, :default => "defined" + invoke_from_option :invoked +end + +class H < Thor::Group + class_option :defined, :type => :boolean, :default => true + invoke_from_option :defined +end diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/path with spaces b/vendor/gems/thor-0.18.1/spec/fixtures/path with spaces new file mode 100644 index 0000000..e69de29 diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/preserve/script.sh b/vendor/gems/thor-0.18.1/spec/fixtures/preserve/script.sh new file mode 100755 index 0000000..c52d3c2 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/preserve/script.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/script.thor b/vendor/gems/thor-0.18.1/spec/fixtures/script.thor new file mode 100644 index 0000000..3875133 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/script.thor @@ -0,0 +1,220 @@ +class MyScript < Thor + check_unknown_options! :except => :with_optional + + attr_accessor :some_attribute + attr_writer :another_attribute + attr_reader :another_attribute + + private + attr_reader :private_attribute + + public + group :script + default_command :example_default_command + + map "-T" => :animal, ["-f", "--foo"] => :foo + + map "animal_prison" => "zoo" + + desc "zoo", "zoo around" + def zoo + true + end + + desc "animal TYPE", "horse around" + + no_commands do + def this_is_not_a_command + end + end + + def animal(type) + [type] + end + + map "hid" => "hidden" + + desc "hidden TYPE", "this is hidden", :hide => true + def hidden(type) + [type] + end + + map "fu" => "zoo" + + desc "foo BAR", < :boolean, :desc => "Force to do some fooing" + def foo(bar) + [bar, options] + end + + desc "example_default_command", "example!" + method_options :with => :string + def example_default_command + options.empty? ? "default command" : options + end + + desc "call_myself_with_wrong_arity", "get the right error" + def call_myself_with_wrong_arity + call_myself_with_wrong_arity(4) + end + + desc "call_unexistent_method", "Call unexistent method inside a command" + def call_unexistent_method + boom! + end + + desc "long_description", "a" * 80 + long_desc <<-D + This is a really really really long description. + Here you go. So very long. + + It even has two paragraphs. + D + def long_description + end + + desc "name-with-dashes", "Ensure normalization of command names" + def name_with_dashes + end + + method_options :all => :boolean + method_option :lazy, :lazy_default => "yes" + method_option :lazy_numeric, :type => :numeric, :lazy_default => 42 + method_option :lazy_array, :type => :array, :lazy_default => %w[eat at joes] + method_option :lazy_hash, :type => :hash, :lazy_default => {'swedish' => 'meatballs'} + desc "with_optional NAME", "invoke with optional name" + def with_optional(name=nil, *args) + [ name, options, args ] + end + + class AnotherScript < Thor + desc "baz", "do some bazing" + def baz + end + end + + desc "send", "send as a command name" + def send + true + end + + private + + def method_missing(meth, *args) + if meth == :boom! + super + else + [meth, args] + end + end + + desc "what", "what" + def what + end +end + +class MyChildScript < MyScript + remove_command :bar + + method_options :force => :boolean, :param => :numeric + def initialize(*args) + super + end + + desc "zoo", "zoo around" + method_options :param => :required + def zoo + options + end + + desc "animal TYPE", "horse around" + def animal(type) + [type, options] + end + method_option :other, :type => :string, :default => "method default", :for => :animal + desc "animal KIND", "fish around", :for => :animal + + desc "boom", "explodes everything" + def boom + end + + remove_command :boom, :undefine => true +end + +class Barn < Thor + desc "open [ITEM]", "open the barn door" + def open(item = nil) + if item == "shotgun" + puts "That's going to leave a mark." + else + puts "Open sesame!" + end + end + + desc "paint [COLOR]", "paint the barn" + method_option :coats, :type => :numeric, :default => 2, :desc => 'how many coats of paint' + def paint(color='red') + puts "#{options[:coats]} coats of #{color} paint" + end +end + +class PackageNameScript < Thor + package_name "Baboon" +end + +module Scripts + class MyScript < MyChildScript + argument :accessor, :type => :string + class_options :force => :boolean + method_option :new_option, :type => :string, :for => :example_default_command + + def zoo + self.accessor + end + end + + class MyDefaults < Thor + check_unknown_options! + + namespace :default + desc "cow", "prints 'moo'" + def cow + puts "moo" + end + + desc "command_conflict", "only gets called when prepended with a colon" + def command_conflict + puts "command" + end + + desc "barn", "commands to manage the barn" + subcommand "barn", Barn + end + + class ChildDefault < Thor + namespace "default:child" + end + + class Arities < Thor + desc "zero_args", "takes zero args" + def zero_args + end + + desc "one_arg ARG", "takes one arg" + def one_arg(arg) + end + + desc "two_args ARG1 ARG2", "takes two args" + def two_args(arg1, arg2) + end + + desc "optional_arg [ARG]", "takes an optional arg" + def optional_arg(arg='default') + end + end +end + diff --git a/vendor/gems/thor-0.18.1/spec/fixtures/subcommand.thor b/vendor/gems/thor-0.18.1/spec/fixtures/subcommand.thor new file mode 100644 index 0000000..35d0b57 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/fixtures/subcommand.thor @@ -0,0 +1,17 @@ +module TestSubcommands + + class Subcommand < Thor + desc "print_opt", "My method" + def print_opt + print options["opt"] + end + end + + class Parent < Thor + class_option "opt" + + desc "sub", "My subcommand" + subcommand "sub", Subcommand + end + +end diff --git a/vendor/gems/thor-0.18.1/spec/group_spec.rb b/vendor/gems/thor-0.18.1/spec/group_spec.rb new file mode 100644 index 0000000..0fc88fe --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/group_spec.rb @@ -0,0 +1,216 @@ +require 'helper' + +describe Thor::Group do + describe "command" do + it "allows to use private methods from parent class as commands" do + expect(ChildGroup.start).to eq(["bar", "foo", "baz"]) + expect(ChildGroup.new.baz("bar")).to eq("bar") + end + end + + describe "#start" do + it "invokes all the commands under the Thor group" do + expect(MyCounter.start(["1", "2", "--third", "3"])).to eq([ 1, 2, 3, nil, nil, nil ]) + end + + it "uses argument default value" do + expect(MyCounter.start(["1", "--third", "3"])).to eq([ 1, 2, 3, nil, nil, nil ]) + end + + it "invokes all the commands in the Thor group and its parents" do + expect(BrokenCounter.start(["1", "2", "--third", "3"])).to eq([ nil, 2, 3, false, 5, nil ]) + end + + it "raises an error if a required argument is added after a non-required" do + expect { + MyCounter.argument(:foo, :type => :string) + }.to raise_error(ArgumentError, 'You cannot have "foo" as required argument after the non-required argument "second".') + end + + it "raises when an exception happens within the command call" do + expect{ BrokenCounter.start(["1", "2", "--fail"]) }.to raise_error + end + + it "raises an error when a Thor group command expects arguments" do + expect{ WhinyGenerator.start }.to raise_error(ArgumentError, /thor wrong_arity takes 1 argument, but it should not/) + end + + it "invokes help message if any of the shortcuts is given" do + MyCounter.should_receive(:help) + MyCounter.start(["-h"]) + end + end + + describe "#desc" do + it "sets the description for a given class" do + expect(MyCounter.desc).to eq("Description:\n This generator runs three commands: one, two and three.\n") + end + + it "can be inherited" do + expect(BrokenCounter.desc).to eq("Description:\n This generator runs three commands: one, two and three.\n") + end + + it "can be nil" do + expect(WhinyGenerator.desc).to be_nil + end + end + + describe "#help" do + before do + @content = capture(:stdout) { MyCounter.help(Thor::Base.shell.new) } + end + + it "provides usage information" do + expect(@content).to match(/my_counter N \[N\]/) + end + + it "shows description" do + expect(@content).to match(/Description:/) + expect(@content).to match(/This generator runs three commands: one, two and three./) + end + + it "shows options information" do + expect(@content).to match(/Options/) + expect(@content).to match(/\[\-\-third=THREE\]/) + end + end + + describe "#invoke" do + before do + @content = capture(:stdout) { E.start } + end + + it "allows to invoke a class from the class binding" do + expect(@content).to match(/1\n2\n3\n4\n5\n/) + end + + it "shows invocation information to the user" do + expect(@content).to match(/invoke Defined/) + end + + it "uses padding on status generated by the invoked class" do + expect(@content).to match(/finished counting/) + end + + it "allows invocation to be configured with blocks" do + capture(:stdout) do + expect(F.start).to eq(["Valim, Jose"]) + end + end + + it "shows invoked options on help" do + content = capture(:stdout) { E.help(Thor::Base.shell.new) } + expect(content).to match(/Defined options:/) + expect(content).to match(/\[--unused\]/) + expect(content).to match(/# This option has no use/) + end + end + + describe "#invoke_from_option" do + describe "with default type" do + before do + @content = capture(:stdout) { G.start } + end + + it "allows to invoke a class from the class binding by a default option" do + expect(@content).to match(/1\n2\n3\n4\n5\n/) + end + + it "does not invoke if the option is nil" do + expect(capture(:stdout) { G.start(["--skip-invoked"]) }).not_to match(/invoke/) + end + + it "prints a message if invocation cannot be found" do + content = capture(:stdout) { G.start(["--invoked", "unknown"]) } + expect(content).to match(/error unknown \[not found\]/) + end + + it "allows to invoke a class from the class binding by the given option" do + content = capture(:stdout) { G.start(["--invoked", "e"]) } + expect(content).to match(/invoke e/) + end + + it "shows invocation information to the user" do + expect(@content).to match(/invoke defined/) + end + + it "uses padding on status generated by the invoked class" do + expect(@content).to match(/finished counting/) + end + + it "shows invoked options on help" do + content = capture(:stdout) { G.help(Thor::Base.shell.new) } + expect(content).to match(/defined options:/) + expect(content).to match(/\[--unused\]/) + expect(content).to match(/# This option has no use/) + end + end + + describe "with boolean type" do + before do + @content = capture(:stdout) { H.start } + end + + it "allows to invoke a class from the class binding by a default option" do + expect(@content).to match(/1\n2\n3\n4\n5\n/) + end + + it "does not invoke if the option is false" do + expect(capture(:stdout) { H.start(["--no-defined"]) }).not_to match(/invoke/) + end + + it "shows invocation information to the user" do + expect(@content).to match(/invoke defined/) + end + + it "uses padding on status generated by the invoked class" do + expect(@content).to match(/finished counting/) + end + + it "shows invoked options on help" do + content = capture(:stdout) { H.help(Thor::Base.shell.new) } + expect(content).to match(/defined options:/) + expect(content).to match(/\[--unused\]/) + expect(content).to match(/# This option has no use/) + end + end + end + + describe "edge-cases" do + it "can handle boolean options followed by arguments" do + klass = Class.new(Thor::Group) do + desc "say hi to name" + argument :name, :type => :string + class_option :loud, :type => :boolean + + def hi + name.upcase! if options[:loud] + "Hi #{name}" + end + end + + expect(klass.start(["jose"])).to eq(["Hi jose"]) + expect(klass.start(["jose", "--loud"])).to eq(["Hi JOSE"]) + expect(klass.start(["--loud", "jose"])).to eq(["Hi JOSE"]) + end + + it "provides extra args as `args`" do + klass = Class.new(Thor::Group) do + desc "say hi to name" + argument :name, :type => :string + class_option :loud, :type => :boolean + + def hi + name.upcase! if options[:loud] + out = "Hi #{name}" + out << ": " << args.join(", ") unless args.empty? + out + end + end + + expect(klass.start(["jose"])).to eq(["Hi jose"]) + expect(klass.start(["jose", "--loud"])).to eq(["Hi JOSE"]) + expect(klass.start(["--loud", "jose"])).to eq(["Hi JOSE"]) + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/helper.rb b/vendor/gems/thor-0.18.1/spec/helper.rb new file mode 100644 index 0000000..6372759 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/helper.rb @@ -0,0 +1,67 @@ +$TESTING=true + +require 'simplecov' +require 'coveralls' + +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ + SimpleCov::Formatter::HTMLFormatter, + Coveralls::SimpleCov::Formatter +] +SimpleCov.start + +$:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) +require 'thor' +require 'thor/group' +require 'stringio' + +require 'rdoc' +require 'rspec' +require 'diff/lcs' # You need diff/lcs installed to run specs (but not to run Thor). +require 'fakeweb' # You need fakeweb installed to run specs (but not to run Thor). + +# Set shell to basic +$0 = "thor" +$thor_runner = true +ARGV.clear +Thor::Base.shell = Thor::Shell::Basic + +# Load fixtures +load File.join(File.dirname(__FILE__), "fixtures", "enum.thor") +load File.join(File.dirname(__FILE__), "fixtures", "group.thor") +load File.join(File.dirname(__FILE__), "fixtures", "invoke.thor") +load File.join(File.dirname(__FILE__), "fixtures", "script.thor") +load File.join(File.dirname(__FILE__), "fixtures", "subcommand.thor") +load File.join(File.dirname(__FILE__), "fixtures", "command.thor") + +RSpec.configure do |config| + config.before do + ARGV.replace [] + end + + config.expect_with :rspec do |c| + c.syntax = :expect + end + + def capture(stream) + begin + stream = stream.to_s + eval "$#{stream} = StringIO.new" + yield + result = eval("$#{stream}").string + ensure + eval("$#{stream} = #{stream.upcase}") + end + + result + end + + def source_root + File.join(File.dirname(__FILE__), 'fixtures') + end + + def destination_root + File.join(File.dirname(__FILE__), 'sandbox') + end + + alias :silence :capture +end diff --git a/vendor/gems/thor-0.18.1/spec/invocation_spec.rb b/vendor/gems/thor-0.18.1/spec/invocation_spec.rb new file mode 100644 index 0000000..2c0db51 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/invocation_spec.rb @@ -0,0 +1,100 @@ +require 'helper' +require 'thor/base' + +describe Thor::Invocation do + describe "#invoke" do + it "invokes a command inside another command" do + expect(capture(:stdout) { A.new.invoke(:two) }).to eq("2\n3\n") + end + + it "invokes a command just once" do + expect(capture(:stdout) { A.new.invoke(:one) }).to eq("1\n2\n3\n") + end + + it "invokes a command just once even if they belongs to different classes" do + expect(capture(:stdout) { Defined.new.invoke(:one) }).to eq("1\n2\n3\n4\n5\n") + end + + it "invokes a command with arguments" do + expect(A.new.invoke(:five, [5])).to be_true + expect(A.new.invoke(:five, [7])).to be_false + end + + it "invokes the default command if none is given to a Thor class" do + content = capture(:stdout) { A.new.invoke("b") } + expect(content).to match(/Commands/) + expect(content).to match(/LAST_NAME/) + end + + it "accepts a class as argument without a command to invoke" do + content = capture(:stdout) { A.new.invoke(B) } + expect(content).to match(/Commands/) + expect(content).to match(/LAST_NAME/) + end + + it "accepts a class as argument with a command to invoke" do + base = A.new([], :last_name => "Valim") + expect(base.invoke(B, :one, ["Jose"])).to eq("Valim, Jose") + end + + it "allows customized options to be given" do + base = A.new([], :last_name => "Wrong") + expect(base.invoke(B, :one, ["Jose"], :last_name => "Valim")).to eq("Valim, Jose") + end + + it "reparses options in the new class" do + expect(A.start(["invoker", "--last-name", "Valim"])).to eq("Valim, Jose") + end + + it "shares initialize options with invoked class" do + expect(A.new([], :foo => :bar).invoke("b:two")).to eq({ "foo" => :bar }) + end + + it "dump configuration values to be used in the invoked class" do + base = A.new + expect(base.invoke("b:three").shell).to eq(base.shell) + end + + it "allow extra configuration values to be given" do + base, shell = A.new, Thor::Base.shell.new + expect(base.invoke("b:three", [], {}, :shell => shell).shell).to eq(shell) + end + + it "invokes a Thor::Group and all of its commands" do + expect(capture(:stdout) { A.new.invoke(:c) }).to eq("1\n2\n3\n") + end + + it "does not invoke a Thor::Group twice" do + base = A.new + silence(:stdout){ base.invoke(:c) } + expect(capture(:stdout) { base.invoke(:c) }).to be_empty + end + + it "does not invoke any of Thor::Group commands twice" do + base = A.new + silence(:stdout){ base.invoke(:c) } + expect(capture(:stdout) { base.invoke("c:one") }).to be_empty + end + + it "raises Thor::UndefinedcommandError if the command can't be found" do + expect { + A.new.invoke("foo:bar") + }.to raise_error(Thor::UndefinedCommandError) + end + + it "raises Thor::UndefinedcommandError if the command can't be found even if all commands were already executed" do + base = C.new + silence(:stdout){ base.invoke_all } + + expect { + base.invoke("foo:bar") + }.to raise_error(Thor::UndefinedCommandError) + end + + it "raises an error if a non Thor class is given" do + expect { + A.new.invoke(Object) + }.to raise_error(RuntimeError, "Expected Thor class, got Object") + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/parser/argument_spec.rb b/vendor/gems/thor-0.18.1/spec/parser/argument_spec.rb new file mode 100644 index 0000000..6c6b0e8 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/parser/argument_spec.rb @@ -0,0 +1,53 @@ +require 'helper' +require 'thor/parser' + +describe Thor::Argument do + + def argument(name, options={}) + @argument ||= Thor::Argument.new(name, options) + end + + describe "errors" do + it "raises an error if name is not supplied" do + expect { + argument(nil) + }.to raise_error(ArgumentError, "Argument name can't be nil.") + end + + it "raises an error if type is unknown" do + expect { + argument(:command, :type => :unknown) + }.to raise_error(ArgumentError, "Type :unknown is not valid for arguments.") + end + + it "raises an error if argument is required and have default values" do + expect { + argument(:command, :type => :string, :default => "bar", :required => true) + }.to raise_error(ArgumentError, "An argument cannot be required and have default value.") + end + + it "raises an error if enum isn't an array" do + expect { + argument(:command, :type => :string, :enum => "bar") + }.to raise_error(ArgumentError, "An argument cannot have an enum other than an array.") + end + end + + describe "#usage" do + it "returns usage for string types" do + expect(argument(:foo, :type => :string).usage).to eq("FOO") + end + + it "returns usage for numeric types" do + expect(argument(:foo, :type => :numeric).usage).to eq("N") + end + + it "returns usage for array types" do + expect(argument(:foo, :type => :array).usage).to eq("one two three") + end + + it "returns usage for hash types" do + expect(argument(:foo, :type => :hash).usage).to eq("key:value") + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/parser/arguments_spec.rb b/vendor/gems/thor-0.18.1/spec/parser/arguments_spec.rb new file mode 100644 index 0000000..19588d9 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/parser/arguments_spec.rb @@ -0,0 +1,66 @@ +require 'helper' +require 'thor/parser' + +describe Thor::Arguments do + def create(opts={}) + arguments = opts.map do |type, default| + options = {:required => default.nil?, :type => type, :default => default} + Thor::Argument.new(type.to_s, options) + end + + arguments.sort!{ |a,b| b.name <=> a.name } + @opt = Thor::Arguments.new(arguments) + end + + def parse(*args) + @opt.parse(args) + end + + describe "#parse" do + it "parses arguments in the given order" do + create :string => nil, :numeric => nil + expect(parse("name", "13")["string"]).to eq("name") + expect(parse("name", "13")["numeric"]).to eq(13) + end + + it "accepts hashes" do + create :string => nil, :hash => nil + expect(parse("product", "title:string", "age:integer")["string"]).to eq("product") + expect(parse("product", "title:string", "age:integer")["hash"]).to eq({ "title" => "string", "age" => "integer"}) + expect(parse("product", "url:http://www.amazon.com/gp/product/123")["hash"]).to eq({ "url" => "http://www.amazon.com/gp/product/123" }) + end + + it "accepts arrays" do + create :string => nil, :array => nil + expect(parse("product", "title", "age")["string"]).to eq("product") + expect(parse("product", "title", "age")["array"]).to eq(%w(title age)) + end + + describe "with no inputs" do + it "and no arguments returns an empty hash" do + create + expect(parse).to eq({}) + end + + it "and required arguments raises an error" do + create :string => nil, :numeric => nil + expect{ parse }.to raise_error(Thor::RequiredArgumentMissingError, "No value provided for required arguments 'string', 'numeric'") + end + + it "and default arguments returns default values" do + create :string => "name", :numeric => 13 + expect(parse).to eq({ "string" => "name", "numeric" => 13 }) + end + end + + it "returns the input if it's already parsed" do + create :string => nil, :hash => nil, :array => nil, :numeric => nil + expect(parse("", 0, {}, [])).to eq({ "string" => "", "numeric" => 0, "hash" => {}, "array" => [] }) + end + + it "returns the default value if none is provided" do + create :string => "foo", :numeric => 3.0 + expect(parse("bar")).to eq({ "string" => "bar", "numeric" => 3.0 }) + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/parser/option_spec.rb b/vendor/gems/thor-0.18.1/spec/parser/option_spec.rb new file mode 100644 index 0000000..d573976 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/parser/option_spec.rb @@ -0,0 +1,202 @@ +require 'helper' +require 'thor/parser' + +describe Thor::Option do + def parse(key, value) + Thor::Option.parse(key, value) + end + + def option(name, options={}) + @option ||= Thor::Option.new(name, options) + end + + describe "#parse" do + + describe "with value as a symbol" do + describe "and symbol is a valid type" do + it "has type equals to the symbol" do + expect(parse(:foo, :string).type).to eq(:string) + expect(parse(:foo, :numeric).type).to eq(:numeric) + end + + it "has not default value" do + expect(parse(:foo, :string).default).to be_nil + expect(parse(:foo, :numeric).default).to be_nil + end + end + + describe "equals to :required" do + it "has type equals to :string" do + expect(parse(:foo, :required).type).to eq(:string) + end + + it "has no default value" do + expect(parse(:foo, :required).default).to be_nil + end + end + + describe "and symbol is not a reserved key" do + it "has type equals to :string" do + expect(parse(:foo, :bar).type).to eq(:string) + end + + it "has no default value" do + expect(parse(:foo, :bar).default).to be_nil + end + end + end + + describe "with value as hash" do + it "has default type :hash" do + expect(parse(:foo, :a => :b).type).to eq(:hash) + end + + it "has default value equals to the hash" do + expect(parse(:foo, :a => :b).default).to eq({ :a => :b }) + end + end + + describe "with value as array" do + it "has default type :array" do + expect(parse(:foo, [:a, :b]).type).to eq(:array) + end + + it "has default value equals to the array" do + expect(parse(:foo, [:a, :b]).default).to eq([:a, :b]) + end + end + + describe "with value as string" do + it "has default type :string" do + expect(parse(:foo, "bar").type).to eq(:string) + end + + it "has default value equals to the string" do + expect(parse(:foo, "bar").default).to eq("bar") + end + end + + describe "with value as numeric" do + it "has default type :numeric" do + expect(parse(:foo, 2.0).type).to eq(:numeric) + end + + it "has default value equals to the numeric" do + expect(parse(:foo, 2.0).default).to eq(2.0) + end + end + + describe "with value as boolean" do + it "has default type :boolean" do + expect(parse(:foo, true).type).to eq(:boolean) + expect(parse(:foo, false).type).to eq(:boolean) + end + + it "has default value equals to the boolean" do + expect(parse(:foo, true).default).to eq(true) + expect(parse(:foo, false).default).to eq(false) + end + end + + describe "with key as a symbol" do + it "sets the name equals to the key" do + expect(parse(:foo, true).name).to eq("foo") + end + end + + describe "with key as an array" do + it "sets the first items in the array to the name" do + expect(parse([:foo, :bar, :baz], true).name).to eq("foo") + end + + it "sets all other items as aliases" do + expect(parse([:foo, :bar, :baz], true).aliases).to eq([:bar, :baz]) + end + end + end + + it "returns the switch name" do + expect(option("foo").switch_name).to eq("--foo") + expect(option("--foo").switch_name).to eq("--foo") + end + + it "returns the human name" do + expect(option("foo").human_name).to eq("foo") + expect(option("--foo").human_name).to eq("foo") + end + + it "converts underscores to dashes" do + expect(option("foo_bar").switch_name).to eq("--foo-bar") + end + + it "can be required and have default values" do + option = option("foo", :required => true, :type => :string, :default => "bar") + expect(option.default).to eq("bar") + expect(option).to be_required + end + + it "cannot be required and have type boolean" do + expect { + option("foo", :required => true, :type => :boolean) + }.to raise_error(ArgumentError, "An option cannot be boolean and required.") + end + + it "allows type predicates" do + expect(parse(:foo, :string)).to be_string + expect(parse(:foo, :boolean)).to be_boolean + expect(parse(:foo, :numeric)).to be_numeric + end + + it "raises an error on method missing" do + expect { + parse(:foo, :string).unknown? + }.to raise_error(NoMethodError) + end + + describe "#usage" do + + it "returns usage for string types" do + expect(parse(:foo, :string).usage).to eq("[--foo=FOO]") + end + + it "returns usage for numeric types" do + expect(parse(:foo, :numeric).usage).to eq("[--foo=N]") + end + + it "returns usage for array types" do + expect(parse(:foo, :array).usage).to eq("[--foo=one two three]") + end + + it "returns usage for hash types" do + expect(parse(:foo, :hash).usage).to eq("[--foo=key:value]") + end + + it "returns usage for boolean types" do + expect(parse(:foo, :boolean).usage).to eq("[--foo]") + end + + it "uses padding when no aliases is given" do + expect(parse(:foo, :boolean).usage(4)).to eq(" [--foo]") + end + + it "uses banner when supplied" do + expect(option(:foo, :required => false, :type => :string, :banner => "BAR").usage).to eq("[--foo=BAR]") + end + + it "checkes when banner is an empty string" do + expect(option(:foo, :required => false, :type => :string, :banner => "").usage).to eq("[--foo]") + end + + describe "with required values" do + it "does not show the usage between brackets" do + expect(parse(:foo, :required).usage).to eq("--foo=FOO") + end + end + + describe "with aliases" do + it "does not show the usage between brackets" do + expect(parse([:foo, "-f", "-b"], :required).usage).to eq("-f, -b, --foo=FOO") + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/parser/options_spec.rb b/vendor/gems/thor-0.18.1/spec/parser/options_spec.rb new file mode 100644 index 0000000..1505b38 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/parser/options_spec.rb @@ -0,0 +1,400 @@ +require 'helper' +require 'thor/parser' + +describe Thor::Options do + def create(opts, defaults={}, stop_on_unknown=false) + opts.each do |key, value| + opts[key] = Thor::Option.parse(key, value) unless value.is_a?(Thor::Option) + end + + @opt = Thor::Options.new(opts, defaults, stop_on_unknown) + end + + def parse(*args) + @opt.parse(args.flatten) + end + + def check_unknown! + @opt.check_unknown! + end + + def remaining + @opt.remaining + end + + describe "#to_switches" do + it "turns true values into a flag" do + expect(Thor::Options.to_switches(:color => true)).to eq("--color") + end + + it "ignores nil" do + expect(Thor::Options.to_switches(:color => nil)).to eq("") + end + + it "ignores false" do + expect(Thor::Options.to_switches(:color => false)).to eq("") + end + + it "writes --name value for anything else" do + expect(Thor::Options.to_switches(:format => "specdoc")).to eq('--format "specdoc"') + end + + it "joins several values" do + switches = Thor::Options.to_switches(:color => true, :foo => "bar").split(' ').sort + expect(switches).to eq(['"bar"', "--color", "--foo"]) + end + + it "accepts arrays" do + expect(Thor::Options.to_switches(:count => [1,2,3])).to eq("--count 1 2 3") + end + + it "accepts hashes" do + expect(Thor::Options.to_switches(:count => {:a => :b})).to eq("--count a:b") + end + + it "accepts underscored options" do + expect(Thor::Options.to_switches(:under_score_option => "foo bar")).to eq('--under_score_option "foo bar"') + end + + end + + describe "#parse" do + it "allows multiple aliases for a given switch" do + create ["--foo", "--bar", "--baz"] => :string + expect(parse("--foo", "12")["foo"]).to eq("12") + expect(parse("--bar", "12")["foo"]).to eq("12") + expect(parse("--baz", "12")["foo"]).to eq("12") + end + + it "allows custom short names" do + create "-f" => :string + expect(parse("-f", "12")).to eq({"f" => "12"}) + end + + it "allows custom short-name aliases" do + create ["--bar", "-f"] => :string + expect(parse("-f", "12")).to eq({"bar" => "12"}) + end + + it "accepts conjoined short switches" do + create ["--foo", "-f"] => true, ["--bar", "-b"] => true, ["--app", "-a"] => true + opts = parse("-fba") + expect(opts["foo"]).to be_true + expect(opts["bar"]).to be_true + expect(opts["app"]).to be_true + end + + it "accepts conjoined short switches with input" do + create ["--foo", "-f"] => true, ["--bar", "-b"] => true, ["--app", "-a"] => :required + opts = parse "-fba", "12" + expect(opts["foo"]).to be_true + expect(opts["bar"]).to be_true + expect(opts["app"]).to eq("12") + end + + it "returns the default value if none is provided" do + create :foo => "baz", :bar => :required + expect(parse("--bar", "boom")["foo"]).to eq("baz") + end + + it "returns the default value from defaults hash to required arguments" do + create Hash[:bar => :required], Hash[:bar => "baz"] + expect(parse["bar"]).to eq("baz") + end + + it "gives higher priority to defaults given in the hash" do + create Hash[:bar => true], Hash[:bar => false] + expect(parse["bar"]).to eq(false) + end + + it "raises an error for unknown switches" do + create :foo => "baz", :bar => :required + parse("--bar", "baz", "--baz", "unknown") + expect{ check_unknown! }.to raise_error(Thor::UnknownArgumentError, "Unknown switches '--baz'") + end + + it "skips leading non-switches" do + create(:foo => "baz") + + expect(parse("asdf", "--foo", "bar")).to eq({"foo" => "bar"}) + end + + it "correctly recognizes things that look kind of like options, but aren't, as not options" do + create(:foo => "baz") + expect(parse("--asdf---asdf", "baz", "--foo", "--asdf---dsf--asdf")).to eq({"foo" => "--asdf---dsf--asdf"}) + check_unknown! + end + + it "accepts underscores in commandline args hash for boolean" do + create :foo_bar => :boolean + expect(parse("--foo_bar")["foo_bar"]).to eq(true) + expect(parse("--no_foo_bar")["foo_bar"]).to eq(false) + end + + it "accepts underscores in commandline args hash for strings" do + create :foo_bar => :string, :baz_foo => :string + expect(parse("--foo_bar", "baz")["foo_bar"]).to eq("baz") + expect(parse("--baz_foo", "foo bar")["baz_foo"]).to eq("foo bar") + end + + it "interprets everything after -- as args instead of options" do + create(:foo => :string, :bar => :required) + expect(parse(%w[--bar abc moo -- --foo def -a])).to eq({"bar" => "abc"}) + expect(remaining).to eq(%w[moo --foo def -a]) + end + + it "ignores -- when looking for single option values" do + create(:foo => :string, :bar => :required) + expect(parse(%w[--bar -- --foo def -a])).to eq({"bar" => "--foo"}) + expect(remaining).to eq(%w[def -a]) + end + + it "ignores -- when looking for array option values" do + create(:foo => :array) + expect(parse(%w[--foo a b -- c d -e])).to eq({"foo" => %w[a b c d -e]}) + expect(remaining).to eq([]) + end + + it "ignores -- when looking for hash option values" do + create(:foo => :hash) + expect(parse(%w[--foo a:b -- c:d -e])).to eq({"foo" => {'a' => 'b', 'c' => 'd'}}) + expect(remaining).to eq(%w[-e]) + end + + it "ignores trailing --" do + create(:foo => :string) + expect(parse(%w[--foo --])).to eq({"foo" => nil}) + expect(remaining).to eq([]) + end + + describe "with no input" do + it "and no switches returns an empty hash" do + create({}) + expect(parse).to eq({}) + end + + it "and several switches returns an empty hash" do + create "--foo" => :boolean, "--bar" => :string + expect(parse).to eq({}) + end + + it "and a required switch raises an error" do + create "--foo" => :required + expect{ parse }.to raise_error(Thor::RequiredArgumentMissingError, "No value provided for required options '--foo'") + end + end + + describe "with one required and one optional switch" do + before do + create "--foo" => :required, "--bar" => :boolean + end + + it "raises an error if the required switch has no argument" do + expect{ parse("--foo") }.to raise_error(Thor::MalformattedArgumentError) + end + + it "raises an error if the required switch isn't given" do + expect{ parse("--bar") }.to raise_error(Thor::RequiredArgumentMissingError) + end + + it "raises an error if the required switch is set to nil" do + expect{ parse("--no-foo") }.to raise_error(Thor::RequiredArgumentMissingError) + end + + it "does not raises an error if the required option has a default value" do + options = {:required => true, :type => :string, :default => "baz"} + create :foo => Thor::Option.new("foo", options), :bar => :boolean + expect{ parse("--bar") }.not_to raise_error + end + end + + context "when stop_on_unknown is true" do + before do + create({:foo => :string, :verbose => :boolean}, {}, true) + end + + it "stops parsing on first non-option" do + expect(parse(%w[foo --verbose])).to eq({}) + expect(remaining).to eq(["foo", "--verbose"]) + end + + it "stops parsing on unknown option" do + expect(parse(%w[--bar --verbose])).to eq({}) + expect(remaining).to eq(["--bar", "--verbose"]) + end + + it "retains -- after it has stopped parsing" do + expect(parse(%w[--bar -- whatever])).to eq({}) + expect(remaining).to eq(["--bar", "--", "whatever"]) + end + + it "still accepts options that are given before non-options" do + expect(parse(%w[--verbose foo])).to eq({"verbose" => true}) + expect(remaining).to eq(["foo"]) + end + + it "still accepts options that require a value" do + expect(parse(%w[--foo bar baz])).to eq({"foo" => "bar"}) + expect(remaining).to eq(["baz"]) + end + + it "still interprets everything after -- as args instead of options" do + expect(parse(%w[-- --verbose])).to eq({}) + expect(remaining).to eq(["--verbose"]) + end + end + + describe "with :string type" do + before do + create ["--foo", "-f"] => :required + end + + it "accepts a switch assignment" do + expect(parse("--foo", "12")["foo"]).to eq("12") + end + + it "accepts a switch= assignment" do + expect(parse("-f=12")["foo"]).to eq("12") + expect(parse("--foo=12")["foo"]).to eq("12") + expect(parse("--foo=bar=baz")["foo"]).to eq("bar=baz") + end + + it "must accept underscores switch=value assignment" do + create :foo_bar => :required + expect(parse("--foo_bar=http://example.com/under_score/")["foo_bar"]).to eq("http://example.com/under_score/") + end + + it "accepts a --no-switch format" do + create "--foo" => "bar" + expect(parse("--no-foo")["foo"]).to be_nil + end + + it "does not consume an argument for --no-switch format" do + create "--cheese" => :string + expect(parse('burger', '--no-cheese', 'fries')["cheese"]).to be_nil + end + + it "accepts a --switch format on non required types" do + create "--foo" => :string + expect(parse("--foo")["foo"]).to eq("foo") + end + + it "accepts a --switch format on non required types with default values" do + create "--baz" => :string, "--foo" => "bar" + expect(parse("--baz", "bang", "--foo")["foo"]).to eq("bar") + end + + it "overwrites earlier values with later values" do + expect(parse("--foo=bar", "--foo", "12")["foo"]).to eq("12") + expect(parse("--foo", "12", "--foo", "13")["foo"]).to eq("13") + end + end + + describe "with :boolean type" do + before do + create "--foo" => false + end + + it "accepts --opt assignment" do + expect(parse("--foo")["foo"]).to eq(true) + expect(parse("--foo", "--bar")["foo"]).to eq(true) + end + + it "uses the default value if no switch is given" do + expect(parse("")["foo"]).to eq(false) + end + + it "accepts --opt=value assignment" do + expect(parse("--foo=true")["foo"]).to eq(true) + expect(parse("--foo=false")["foo"]).to eq(false) + end + + it "accepts --[no-]opt variant, setting false for value" do + expect(parse("--no-foo")["foo"]).to eq(false) + end + + it "accepts --[skip-]opt variant, setting false for value" do + expect(parse("--skip-foo")["foo"]).to eq(false) + end + + it "will prefer 'no-opt' variant over inverting 'opt' if explicitly set" do + create "--no-foo" => true + expect(parse("--no-foo")["no-foo"]).to eq(true) + end + + it "will prefer 'skip-opt' variant over inverting 'opt' if explicitly set" do + create "--skip-foo" => true + expect(parse("--skip-foo")["skip-foo"]).to eq(true) + end + + it "accepts inputs in the human name format" do + create :foo_bar => :boolean + expect(parse("--foo-bar")["foo_bar"]).to eq(true) + expect(parse("--no-foo-bar")["foo_bar"]).to eq(false) + expect(parse("--skip-foo-bar")["foo_bar"]).to eq(false) + end + + it "doesn't eat the next part of the param" do + create :foo => :boolean + expect(parse("--foo", "bar")).to eq({"foo" => true}) + expect(@opt.remaining).to eq(["bar"]) + end + end + + describe "with :hash type" do + before do + create "--attributes" => :hash + end + + it "accepts a switch= assignment" do + expect(parse("--attributes=name:string", "age:integer")["attributes"]).to eq({"name" => "string", "age" => "integer"}) + end + + it "accepts a switch assignment" do + expect(parse("--attributes", "name:string", "age:integer")["attributes"]).to eq({"name" => "string", "age" => "integer"}) + end + + it "must not mix values with other switches" do + expect(parse("--attributes", "name:string", "age:integer", "--baz", "cool")["attributes"]).to eq({"name" => "string", "age" => "integer"}) + end + end + + describe "with :array type" do + before do + create "--attributes" => :array + end + + it "accepts a switch= assignment" do + expect(parse("--attributes=a", "b", "c")["attributes"]).to eq(["a", "b", "c"]) + end + + it "accepts a switch assignment" do + expect(parse("--attributes", "a", "b", "c")["attributes"]).to eq(["a", "b", "c"]) + end + + it "must not mix values with other switches" do + expect(parse("--attributes", "a", "b", "c", "--baz", "cool")["attributes"]).to eq(["a", "b", "c"]) + end + end + + describe "with :numeric type" do + before do + create "n" => :numeric, "m" => 5 + end + + it "accepts a -nXY assignment" do + expect(parse("-n12")["n"]).to eq(12) + end + + it "converts values to numeric types" do + expect(parse("-n", "3", "-m", ".5")).to eq({"n" => 3, "m" => 0.5}) + end + + it "raises error when value isn't numeric" do + expect{ parse("-n", "foo") }.to raise_error(Thor::MalformattedArgumentError, + "Expected numeric value for '-n'; got \"foo\"") + end + end + + end +end diff --git a/vendor/gems/thor-0.18.1/spec/rake_compat_spec.rb b/vendor/gems/thor-0.18.1/spec/rake_compat_spec.rb new file mode 100644 index 0000000..4009661 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/rake_compat_spec.rb @@ -0,0 +1,72 @@ +require 'helper' +require 'thor/rake_compat' +require 'rake/tasklib' + +$main = self + +class RakeTask < Rake::TaskLib + def initialize + define + end + + def define + $main.instance_eval do + desc "Say it's cool" + task :cool do + puts "COOL" + end + + namespace :hiper_mega do + task :super do + puts "HIPER MEGA SUPER" + end + end + end + end +end + +class ThorTask < Thor + include Thor::RakeCompat + RakeTask.new +end + +describe Thor::RakeCompat do + it "sets the rakefile application" do + expect(["rake_compat_spec.rb", "Thorfile"]).to include(Rake.application.rakefile) + end + + it "adds rake tasks to thor classes too" do + task = ThorTask.tasks["cool"] + expect(task).to be + end + + it "uses rake tasks descriptions on thor" do + expect(ThorTask.tasks["cool"].description).to eq("Say it's cool") + end + + it "gets usage from rake tasks name" do + expect(ThorTask.tasks["cool"].usage).to eq("cool") + end + + it "uses non namespaced name as description if non is available" do + expect(ThorTask::HiperMega.tasks["super"].description).to eq("super") + end + + it "converts namespaces to classes" do + expect(ThorTask.const_get(:HiperMega)).to eq(ThorTask::HiperMega) + end + + it "does not add tasks from higher namespaces in lowers namespaces" do + expect(ThorTask.tasks["super"]).not_to be + end + + it "invoking the thor task invokes the rake task" do + expect(capture(:stdout) { + ThorTask.start ["cool"] + }).to eq("COOL\n") + + expect(capture(:stdout) { + ThorTask::HiperMega.start ["super"] + }).to eq("HIPER MEGA SUPER\n") + end +end diff --git a/vendor/gems/thor-0.18.1/spec/register_spec.rb b/vendor/gems/thor-0.18.1/spec/register_spec.rb new file mode 100644 index 0000000..3810831 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/register_spec.rb @@ -0,0 +1,197 @@ +require 'helper' + +class BoringVendorProvidedCLI < Thor + desc "boring", "do boring stuff" + def boring + puts "bored. " + end +end + +class ExcitingPluginCLI < Thor + desc "hooray", "say hooray!" + def hooray + puts "hooray!" + end + + desc "fireworks", "exciting fireworks!" + def fireworks + puts "kaboom!" + end +end + +class SuperSecretPlugin < Thor + default_command :squirrel + + desc "squirrel", "All of secret squirrel's secrets" + def squirrel + puts "I love nuts" + end +end + +class GroupPlugin < Thor::Group + desc "part one" + def part_one + puts "part one" + end + + desc "part two" + def part_two + puts "part two" + end +end + +class ClassOptionGroupPlugin < Thor::Group + class_option :who, + :type => :string, + :aliases => "-w", + :default => "zebra" +end + +class CompatibleWith19Plugin < ClassOptionGroupPlugin + desc "animal" + def animal + p options[:who] + end +end + +class PluginWithDefault < Thor + desc "say MSG", "print MSG" + def say(msg) + puts msg + end + + default_command :say +end + +class PluginWithDefaultMultipleArguments < Thor + desc "say MSG [MSG]", "print multiple messages" + def say(*args) + puts args + end + + default_command :say +end + +class PluginWithDefaultcommandAndDeclaredArgument < Thor + desc "say MSG [MSG]", "print multiple messages" + argument :msg + def say + puts msg + end + + default_command :say +end + +BoringVendorProvidedCLI.register( + ExcitingPluginCLI, + "exciting", + "do exciting things", + "Various non-boring actions") + +BoringVendorProvidedCLI.register( + SuperSecretPlugin, + "secret", + "secret stuff", + "Nothing to see here. Move along.", + :hide => true) + +BoringVendorProvidedCLI.register( + GroupPlugin, + 'groupwork', + "Do a bunch of things in a row", + "purple monkey dishwasher") + +BoringVendorProvidedCLI.register( + CompatibleWith19Plugin, + 'zoo', + "zoo [-w animal]", + "Shows a provided animal or just zebra") + +BoringVendorProvidedCLI.register( + PluginWithDefault, + 'say', + 'say message', + 'subcommands ftw') + +BoringVendorProvidedCLI.register( + PluginWithDefaultMultipleArguments, + 'say_multiple', + 'say message', + 'subcommands ftw') + +BoringVendorProvidedCLI.register( + PluginWithDefaultcommandAndDeclaredArgument, + 'say_argument', + 'say message', + 'subcommands ftw') + +describe ".register-ing a Thor subclass" do + it "registers the plugin as a subcommand" do + fireworks_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[exciting fireworks]) } + expect(fireworks_output).to eq("kaboom!\n") + end + + it "includes the plugin's usage in the help" do + help_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[help]) } + expect(help_output).to include('do exciting things') + end + + it "invokes the default command correctly" do + output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say hello]) } + expect(output).to include("hello") + end + + it "invokes the default command correctly with multiple args" do + output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say_multiple hello adam]) } + expect(output).to include("hello") + expect(output).to include("adam") + end + + it "invokes the default command correctly with a declared argument" do + output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say_argument hello]) } + expect(output).to include("hello") + end + + context "when $thor_runner is false" do + it "includes the plugin's subcommand name in subcommand's help" do + begin + $thor_runner = false + help_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[exciting]) } + expect(help_output).to include('thor exciting_plugin_c_l_i fireworks') + ensure + $thor_runner = true + end + end + end + + context "when hidden" do + it "omits the hidden plugin's usage from the help" do + help_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[help]) } + expect(help_output).not_to include('secret stuff') + end + + it "registers the plugin as a subcommand" do + secret_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[secret squirrel]) } + expect(secret_output).to eq("I love nuts\n") + end + end +end + +describe ".register-ing a Thor::Group subclass" do + it "registers the group as a single command" do + group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[groupwork]) } + expect(group_output).to eq("part one\npart two\n") + end +end + +describe "1.8 and 1.9 syntax compatibility" do + it "is compatible with both 1.8 and 1.9 syntax w/o command options" do + group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[zoo]) } + expect(group_output).to match(/zebra/) + end + + it "is compatible with both 1.8 and 1.9 syntax w/command options" do + group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[zoo -w lion]) } + expect(group_output).to match(/lion/) + end +end diff --git a/vendor/gems/thor-0.18.1/spec/runner_spec.rb b/vendor/gems/thor-0.18.1/spec/runner_spec.rb new file mode 100644 index 0000000..17fd29f --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/runner_spec.rb @@ -0,0 +1,241 @@ +require 'helper' +require 'thor/runner' + +describe Thor::Runner do + def when_no_thorfiles_exist + old_dir = Dir.pwd + Dir.chdir '..' + delete = Thor::Base.subclasses.select {|e| e.namespace == 'default' } + delete.each {|e| Thor::Base.subclasses.delete e } + yield + Thor::Base.subclasses.concat delete + Dir.chdir old_dir + end + + describe "#help" do + it "shows information about Thor::Runner itself" do + expect(capture(:stdout) { Thor::Runner.start(["help"]) }).to match(/List the available thor commands/) + end + + it "shows information about a specific Thor::Runner command" do + content = capture(:stdout) { Thor::Runner.start(["help", "list"]) } + expect(content).to match(/List the available thor commands/) + expect(content).not_to match(/help \[COMMAND\]/) + end + + it "shows information about a specific Thor class" do + content = capture(:stdout) { Thor::Runner.start(["help", "my_script"]) } + expect(content).to match(/zoo\s+# zoo around/m) + end + + it "shows information about an specific command from an specific Thor class" do + content = capture(:stdout) { Thor::Runner.start(["help", "my_script:zoo"]) } + expect(content).to match(/zoo around/) + expect(content).not_to match(/help \[COMMAND\]/) + end + + it "shows information about a specific Thor group class" do + content = capture(:stdout) { Thor::Runner.start(["help", "my_counter"]) } + expect(content).to match(/my_counter N/) + end + + it "raises error if a class/command cannot be found" do + content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) } + expect(content.strip).to eq('Could not find command "unknown" in "default" namespace.') + end + + it "raises error if a class/command cannot be found for a setup without thorfiles" do + when_no_thorfiles_exist do + Thor::Runner.should_receive :exit + content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) } + expect(content.strip).to eq('Could not find command "unknown".') + end + end + end + + describe "#start" do + it "invokes a command from Thor::Runner" do + ARGV.replace ["list"] + expect(capture(:stdout) { Thor::Runner.start }).to match(/my_counter N/) + end + + it "invokes a command from a specific Thor class" do + ARGV.replace ["my_script:zoo"] + expect(Thor::Runner.start).to be_true + end + + it "invokes the default command from a specific Thor class if none is specified" do + ARGV.replace ["my_script"] + expect(Thor::Runner.start).to eq("default command") + end + + it "forwads arguments to the invoked command" do + ARGV.replace ["my_script:animal", "horse"] + expect(Thor::Runner.start).to eq(["horse"]) + end + + it "invokes commands through shortcuts" do + ARGV.replace ["my_script", "-T", "horse"] + expect(Thor::Runner.start).to eq(["horse"]) + end + + it "invokes a Thor::Group" do + ARGV.replace ["my_counter", "1", "2", "--third", "3"] + expect(Thor::Runner.start).to eq([1, 2, 3, nil, nil, nil]) + end + + it "raises an error if class/command can't be found" do + ARGV.replace ["unknown"] + content = capture(:stderr){ Thor::Runner.start } + expect(content.strip).to eq('Could not find command "unknown" in "default" namespace.') + end + + it "raises an error if class/command can't be found in a setup without thorfiles" do + when_no_thorfiles_exist do + ARGV.replace ["unknown"] + Thor::Runner.should_receive :exit + content = capture(:stderr){ Thor::Runner.start } + expect(content.strip).to eq('Could not find command "unknown".') + end + end + + it "does not swallow NoMethodErrors that occur inside the called method" do + ARGV.replace ["my_script:call_unexistent_method"] + expect{ Thor::Runner.start }.to raise_error(NoMethodError) + end + + it "does not swallow Thor::Group InvocationError" do + ARGV.replace ["whiny_generator"] + expect{ Thor::Runner.start }.to raise_error(ArgumentError, /thor wrong_arity takes 1 argument, but it should not/) + end + + it "does not swallow Thor InvocationError" do + ARGV.replace ["my_script:animal"] + content = capture(:stderr) { Thor::Runner.start } + expect(content.strip).to eq(%Q'ERROR: thor animal was called with no arguments\nUsage: "thor my_script:animal TYPE".') + end + end + + describe "commands" do + before do + @location = "#{File.dirname(__FILE__)}/fixtures/command.thor" + @original_yaml = { + "random" => { + :location => @location, + :filename => "4a33b894ffce85d7b412fc1b36f88fe0", + :namespaces => ["amazing"] + } + } + + root_file = File.join(Thor::Util.thor_root, "thor.yml") + + # Stub load and save to avoid thor.yaml from being overwritten + YAML.stub!(:load_file).and_return(@original_yaml) + File.stub!(:exists?).with(root_file).and_return(true) + File.stub!(:open).with(root_file, "w") + end + + describe "list" do + it "gives a list of the available commands" do + ARGV.replace ["list"] + content = capture(:stdout) { Thor::Runner.start } + expect(content).to match(/amazing:describe NAME\s+# say that someone is amazing/m) + end + + it "gives a list of the available Thor::Group classes" do + ARGV.replace ["list"] + expect(capture(:stdout) { Thor::Runner.start }).to match(/my_counter N/) + end + + it "can filter a list of the available commands by --group" do + ARGV.replace ["list", "--group", "standard"] + expect(capture(:stdout) { Thor::Runner.start }).to match(/amazing:describe NAME/) + ARGV.replace [] + expect(capture(:stdout) { Thor::Runner.start }).not_to match(/my_script:animal TYPE/) + ARGV.replace ["list", "--group", "script"] + expect(capture(:stdout) { Thor::Runner.start }).to match(/my_script:animal TYPE/) + end + + it "can skip all filters to show all commands using --all" do + ARGV.replace ["list", "--all"] + content = capture(:stdout) { Thor::Runner.start } + expect(content).to match(/amazing:describe NAME/) + expect(content).to match(/my_script:animal TYPE/) + end + + it "doesn't list superclass commands in the subclass" do + ARGV.replace ["list"] + expect(capture(:stdout) { Thor::Runner.start }).not_to match(/amazing:help/) + end + + it "presents commands in the default namespace with an empty namespace" do + ARGV.replace ["list"] + expect(capture(:stdout) { Thor::Runner.start }).to match(/^thor :cow\s+# prints 'moo'/m) + end + + it "runs commands with an empty namespace from the default namespace" do + ARGV.replace [":command_conflict"] + expect(capture(:stdout) { Thor::Runner.start }).to eq("command\n") + end + + it "runs groups even when there is a command with the same name" do + ARGV.replace ["command_conflict"] + expect(capture(:stdout) { Thor::Runner.start }).to eq("group\n") + end + + it "runs commands with no colon in the default namespace" do + ARGV.replace ["cow"] + expect(capture(:stdout) { Thor::Runner.start }).to eq("moo\n") + end + end + + describe "uninstall" do + before do + path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename]) + FileUtils.should_receive(:rm_rf).with(path) + end + + it "uninstalls existing thor modules" do + silence(:stdout) { Thor::Runner.start(["uninstall", "random"]) } + end + end + + describe "installed" do + before do + Dir.should_receive(:[]).and_return([]) + end + + it "displays the modules installed in a pretty way" do + stdout = capture(:stdout) { Thor::Runner.start(["installed"]) } + expect(stdout).to match(/random\s*amazing/) + expect(stdout).to match(/amazing:describe NAME\s+# say that someone is amazing/m) + end + end + + describe "install/update" do + before do + FileUtils.stub!(:mkdir_p) + FileUtils.stub!(:touch) + $stdin.stub!(:gets).and_return("Y") + + path = File.join(Thor::Util.thor_root, Digest::MD5.hexdigest(@location + "random")) + File.should_receive(:open).with(path, "w") + end + + it "updates existing thor files" do + path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename]) + if File.directory? path + FileUtils.should_receive(:rm_rf).with(path) + else + File.should_receive(:delete).with(path) + end + silence(:stdout) { Thor::Runner.start(["update", "random"]) } + end + + it "installs thor files" do + ARGV.replace ["install", @location] + silence(:stdout) { Thor::Runner.start } + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/shell/basic_spec.rb b/vendor/gems/thor-0.18.1/spec/shell/basic_spec.rb new file mode 100644 index 0000000..c1a4173 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/shell/basic_spec.rb @@ -0,0 +1,311 @@ +# coding: UTF-8 +require 'helper' + +describe Thor::Shell::Basic do + def shell + @shell ||= Thor::Shell::Basic.new + end + + describe "#padding" do + it "cannot be set to below zero" do + shell.padding = 10 + expect(shell.padding).to eq(10) + + shell.padding = -1 + expect(shell.padding).to eq(0) + end + end + + describe "#ask" do + it "prints a message to the user and gets the response" do + $stdout.should_receive(:print).with("Should I overwrite it? ") + $stdin.should_receive(:gets).and_return('Sure') + expect(shell.ask("Should I overwrite it?")).to eq("Sure") + end + + it "prints a message and returns nil if EOF is sent to stdin" do + $stdout.should_receive(:print).with(" ") + $stdin.should_receive(:gets).and_return(nil) + expect(shell.ask("")).to eq(nil) + end + + + it "prints a message to the user with the available options and determines the correctness of the answer" do + $stdout.should_receive(:print).with('What\'s your favorite Neopolitan flavor? ["strawberry", "chocolate", "vanilla"] ') + $stdin.should_receive(:gets).and_return('chocolate') + expect(shell.ask("What's your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])).to eq("chocolate") + end + + it "prints a message to the user with the available options and reasks the question after an incorrect repsonse" do + $stdout.should_receive(:print).with('What\'s your favorite Neopolitan flavor? ["strawberry", "chocolate", "vanilla"] ').twice + $stdout.should_receive(:puts).with('Your response must be one of: ["strawberry", "chocolate", "vanilla"]. Please try again.') + $stdin.should_receive(:gets).and_return('moose tracks', 'chocolate') + expect(shell.ask("What's your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])).to eq("chocolate") + end + end + + describe "#yes?" do + it "asks the user and returns true if the user replies yes" do + $stdout.should_receive(:print).with("Should I overwrite it? ") + $stdin.should_receive(:gets).and_return('y') + expect(shell.yes?("Should I overwrite it?")).to be_true + + $stdout.should_receive(:print).with("Should I overwrite it? ") + $stdin.should_receive(:gets).and_return('n') + expect(shell.yes?("Should I overwrite it?")).not_to be_true + end + end + + describe "#no?" do + it "asks the user and returns true if the user replies no" do + $stdout.should_receive(:print).with("Should I overwrite it? ") + $stdin.should_receive(:gets).and_return('n') + expect(shell.no?("Should I overwrite it?")).to be_true + + $stdout.should_receive(:print).with("Should I overwrite it? ") + $stdin.should_receive(:gets).and_return('Yes') + expect(shell.no?("Should I overwrite it?")).to be_false + end + end + + describe "#say" do + it "prints a message to the user" do + $stdout.should_receive(:puts).with("Running...") + shell.say("Running...") + end + + it "prints a message to the user without new line if it ends with a whitespace" do + $stdout.should_receive(:print).with("Running... ") + shell.say("Running... ") + end + + it "does not use a new line with whitespace+newline embedded" do + $stdout.should_receive(:puts).with("It's \nRunning...") + shell.say("It's \nRunning...") + end + + it "prints a message to the user without new line" do + $stdout.should_receive(:print).with("Running...") + shell.say("Running...", nil, false) + end + end + + describe "#say_status" do + it "prints a message to the user with status" do + $stdout.should_receive(:puts).with(" create ~/.thor/command.thor") + shell.say_status(:create, "~/.thor/command.thor") + end + + it "always use new line" do + $stdout.should_receive(:puts).with(" create ") + shell.say_status(:create, "") + end + + it "does not print a message if base is muted" do + shell.should_receive(:mute?).and_return(true) + $stdout.should_not_receive(:puts) + + shell.mute do + shell.say_status(:created, "~/.thor/command.thor") + end + end + + it "does not print a message if base is set to quiet" do + base = MyCounter.new [1,2] + base.should_receive(:options).and_return(:quiet => true) + + $stdout.should_not_receive(:puts) + shell.base = base + shell.say_status(:created, "~/.thor/command.thor") + end + + it "does not print a message if log status is set to false" do + $stdout.should_not_receive(:puts) + shell.say_status(:created, "~/.thor/command.thor", false) + end + + it "uses padding to set messages left margin" do + shell.padding = 2 + $stdout.should_receive(:puts).with(" create ~/.thor/command.thor") + shell.say_status(:create, "~/.thor/command.thor") + end + end + + describe "#print_in_columns" do + before do + @array = [1234567890] + @array += ('a'..'e').to_a + end + + it "prints in columns" do + content = capture(:stdout) { shell.print_in_columns(@array) } + expect(content.rstrip).to eq("1234567890 a b c d e") + end + end + + describe "#print_table" do + before do + @table = [] + @table << ["abc", "#123", "first three"] + @table << ["", "#0", "empty"] + @table << ["xyz", "#786", "last three"] + end + + it "prints a table" do + content = capture(:stdout) { shell.print_table(@table) } + expect(content).to eq(<<-TABLE) +abc #123 first three + #0 empty +xyz #786 last three +TABLE + end + + it "prints a table with indentation" do + content = capture(:stdout) { shell.print_table(@table, :indent => 2) } + expect(content).to eq(<<-TABLE) + abc #123 first three + #0 empty + xyz #786 last three +TABLE + end + + it "uses maximum terminal width" do + @table << ["def", "#456", "Lançam foo bar"] + @table << ["ghi", "#789", "بالله عليكم"] + shell.should_receive(:terminal_width).and_return(20) + content = capture(:stdout) { shell.print_table(@table, :indent => 2, :truncate => true) } + expect(content).to eq(<<-TABLE) + abc #123 firs... + #0 empty + xyz #786 last... + def #456 Lanç... + ghi #789 بالل... +TABLE + end + + it "honors the colwidth option" do + content = capture(:stdout) { shell.print_table(@table, :colwidth => 10)} + expect(content).to eq(<<-TABLE) +abc #123 first three + #0 empty +xyz #786 last three +TABLE + end + + it "prints tables with implicit columns" do + 2.times { @table.first.pop } + content = capture(:stdout) { shell.print_table(@table) } + expect(content).to eq(<<-TABLE) +abc + #0 empty +xyz #786 last three +TABLE + end + + it "prints a table with small numbers and right-aligns them" do + table = [ + ["Name", "Number", "Color"], + ["Erik", 1, "green"] + ] + content = capture(:stdout) { shell.print_table(table) } + expect(content).to eq(<<-TABLE) +Name Number Color +Erik 1 green +TABLE + end + + it "doesn't output extra spaces for right-aligned columns in the last column" do + table = [ + ["Name", "Number"], + ["Erik", 1] + ] + content = capture(:stdout) { shell.print_table(table) } + expect(content).to eq(<<-TABLE) +Name Number +Erik 1 +TABLE + end + + it "prints a table with big numbers" do + table = [ + ["Name", "Number", "Color"], + ["Erik", 1234567890123, "green"] + ] + content = capture(:stdout) { shell.print_table(table) } + expect(content).to eq(<<-TABLE) +Name Number Color +Erik 1234567890123 green +TABLE + end + end + + describe "#file_collision" do + it "shows a menu with options" do + $stdout.should_receive(:print).with('Overwrite foo? (enter "h" for help) [Ynaqh] ') + $stdin.should_receive(:gets).and_return('n') + shell.file_collision('foo') + end + + it "returns true if the user choose default option" do + $stdout.stub!(:print) + $stdin.should_receive(:gets).and_return('') + expect(shell.file_collision('foo')).to be_true + end + + it "returns false if the user choose no" do + $stdout.stub!(:print) + $stdin.should_receive(:gets).and_return('n') + expect(shell.file_collision('foo')).to be_false + end + + it "returns true if the user choose yes" do + $stdout.stub!(:print) + $stdin.should_receive(:gets).and_return('y') + expect(shell.file_collision('foo')).to be_true + end + + it "shows help usage if the user choose help" do + $stdout.stub!(:print) + $stdin.should_receive(:gets).and_return('h') + $stdin.should_receive(:gets).and_return('n') + help = capture(:stdout) { shell.file_collision('foo') } + expect(help).to match(/h \- help, show this help/) + end + + it "quits if the user choose quit" do + $stdout.stub!(:print) + $stdout.should_receive(:puts).with('Aborting...') + $stdin.should_receive(:gets).and_return('q') + + expect { + shell.file_collision('foo') + }.to raise_error(SystemExit) + end + + it "always returns true if the user choose always" do + $stdout.should_receive(:print).with('Overwrite foo? (enter "h" for help) [Ynaqh] ') + $stdin.should_receive(:gets).and_return('a') + + expect(shell.file_collision('foo')).to be_true + + $stdout.should_not_receive(:print) + expect(shell.file_collision('foo')).to be_true + end + + describe "when a block is given" do + it "displays diff options to the user" do + $stdout.should_receive(:print).with('Overwrite foo? (enter "h" for help) [Ynaqdh] ') + $stdin.should_receive(:gets).and_return('s') + shell.file_collision('foo'){ } + end + + it "invokes the diff command" do + $stdout.stub!(:print) + $stdin.should_receive(:gets).and_return('d') + $stdin.should_receive(:gets).and_return('n') + shell.should_receive(:system).with(/diff -u/) + capture(:stdout) { shell.file_collision('foo'){ } } + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/shell/color_spec.rb b/vendor/gems/thor-0.18.1/spec/shell/color_spec.rb new file mode 100644 index 0000000..7822fab --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/shell/color_spec.rb @@ -0,0 +1,95 @@ +require 'helper' + +describe Thor::Shell::Color do + def shell + @shell ||= Thor::Shell::Color.new + end + + before do + StringIO.any_instance.stub(:tty?).and_return(true) + end + + describe "#say" do + it "set the color if specified and tty?" do + out = capture(:stdout) do + shell.say "Wow! Now we have colors!", :green + end + + expect(out.chomp).to eq("\e[32mWow! Now we have colors!\e[0m") + end + + it "does not set the color if output is not a tty" do + out = capture(:stdout) do + $stdout.should_receive(:tty?).and_return(false) + shell.say "Wow! Now we have colors!", :green + end + + expect(out.chomp).to eq("Wow! Now we have colors!") + end + + it "does not use a new line even with colors" do + out = capture(:stdout) do + shell.say "Wow! Now we have colors! ", :green + end + + expect(out.chomp).to eq("\e[32mWow! Now we have colors! \e[0m") + end + + it "handles an Array of colors" do + out = capture(:stdout) do + shell.say "Wow! Now we have colors *and* background colors", [:green, :on_red, :bold] + end + + expect(out.chomp).to eq("\e[32m\e[41m\e[1mWow! Now we have colors *and* background colors\e[0m") + end + end + + describe "#say_status" do + it "uses color to say status" do + out = capture(:stdout) do + shell.say_status :conflict, "README", :red + end + + expect(out.chomp).to eq("\e[1m\e[31m conflict\e[0m README") + end + end + + describe "#set_color" do + it "colors a string with a foreground color" do + red = shell.set_color "hi!", :red + expect(red).to eq("\e[31mhi!\e[0m") + end + + it "colors a string with a background color" do + on_red = shell.set_color "hi!", :white, :on_red + expect(on_red).to eq("\e[37m\e[41mhi!\e[0m") + end + + it "colors a string with a bold color" do + bold = shell.set_color "hi!", :white, true + expect(bold).to eq("\e[1m\e[37mhi!\e[0m") + + bold = shell.set_color "hi!", :white, :bold + expect(bold).to eq("\e[37m\e[1mhi!\e[0m") + + bold = shell.set_color "hi!", :white, :on_red, :bold + expect(bold).to eq("\e[37m\e[41m\e[1mhi!\e[0m") + end + end + + describe "#file_collision" do + describe "when a block is given" do + it "invokes the diff command" do + $stdout.stub!(:print) + $stdout.stub!(:tty?).and_return(true) + $stdin.should_receive(:gets).and_return('d') + $stdin.should_receive(:gets).and_return('n') + + output = capture(:stdout) { shell.file_collision('spec/fixtures/doc/README'){ "README\nEND\n" } } + expect(output).to match(/\e\[31m\- __start__\e\[0m/) + expect(output).to match(/^ README/) + expect(output).to match(/\e\[32m\+ END\e\[0m/) + end + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/shell/html_spec.rb b/vendor/gems/thor-0.18.1/spec/shell/html_spec.rb new file mode 100644 index 0000000..b32f72c --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/shell/html_spec.rb @@ -0,0 +1,32 @@ +require 'helper' + +describe Thor::Shell::HTML do + def shell + @shell ||= Thor::Shell::HTML.new + end + + describe "#say" do + it "set the color if specified" do + out = capture(:stdout) { shell.say "Wow! Now we have colors!", :green } + expect(out.chomp).to eq('Wow! Now we have colors!') + end + + it "sets bold if specified" do + out = capture(:stdout) { shell.say "Wow! Now we have colors *and* bold!", [:green, :bold] } + expect(out.chomp).to eq('Wow! Now we have colors *and* bold!') + end + + it "does not use a new line even with colors" do + out = capture(:stdout) { shell.say "Wow! Now we have colors! ", :green } + expect(out.chomp).to eq('Wow! Now we have colors! ') + end + end + + describe "#say_status" do + it "uses color to say status" do + $stdout.should_receive(:puts).with(' conflict README') + shell.say_status :conflict, "README", :red + end + end + +end diff --git a/vendor/gems/thor-0.18.1/spec/shell_spec.rb b/vendor/gems/thor-0.18.1/spec/shell_spec.rb new file mode 100644 index 0000000..7ee9c47 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/shell_spec.rb @@ -0,0 +1,47 @@ +require 'helper' + +describe Thor::Shell do + def shell + @shell ||= Thor::Base.shell.new + end + + describe "#initialize" do + it "sets shell value" do + base = MyCounter.new [1, 2], { }, :shell => shell + expect(base.shell).to eq(shell) + end + + it "sets the base value on the shell if an accessor is available" do + base = MyCounter.new [1, 2], { }, :shell => shell + expect(shell.base).to eq(base) + end + end + + describe "#shell" do + it "returns the shell in use" do + expect(MyCounter.new([1,2]).shell).to be_kind_of(Thor::Base.shell) + end + + it "uses $THOR_SHELL" do + class Thor::Shell::TestShell < Thor::Shell::Basic; end + + expect(Thor::Base.shell).to eq(shell.class) + ENV['THOR_SHELL'] = 'TestShell' + Thor::Base.shell = nil + expect(Thor::Base.shell).to eq(Thor::Shell::TestShell) + ENV['THOR_SHELL'] = '' + Thor::Base.shell = shell.class + expect(Thor::Base.shell).to eq(shell.class) + end + end + + describe "with_padding" do + it "uses padding for inside block outputs" do + base = MyCounter.new([1,2]) + base.with_padding do + expect(capture(:stdout) { base.say_status :padding, "cool" }.strip).to eq("padding cool") + end + end + end + +end diff --git a/vendor/gems/thor-0.18.1/spec/subcommand_spec.rb b/vendor/gems/thor-0.18.1/spec/subcommand_spec.rb new file mode 100644 index 0000000..721fedb --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/subcommand_spec.rb @@ -0,0 +1,30 @@ +require 'helper' + +describe Thor do + + describe "#subcommand" do + + it "maps a given subcommand to another Thor subclass" do + barn_help = capture(:stdout) { Scripts::MyDefaults.start(%w[barn]) } + expect(barn_help).to include("barn help [COMMAND] # Describe subcommands or one specific subcommand") + end + + it "passes commands to subcommand classes" do + expect(capture(:stdout) { Scripts::MyDefaults.start(%w[barn open]) }.strip).to eq("Open sesame!") + end + + it "passes arguments to subcommand classes" do + expect(capture(:stdout) { Scripts::MyDefaults.start(%w[barn open shotgun]) }.strip).to eq("That's going to leave a mark.") + end + + it "ignores unknown options (the subcommand class will handle them)" do + expect(capture(:stdout) { Scripts::MyDefaults.start(%w[barn paint blue --coats 4])}.strip).to eq("4 coats of blue paint") + end + + it "passes parsed options to subcommands" do + output = capture(:stdout) { TestSubcommands::Parent.start(%w[sub print_opt --opt output]) } + expect(output).to eq("output") + end + end + +end diff --git a/vendor/gems/thor-0.18.1/spec/thor_spec.rb b/vendor/gems/thor-0.18.1/spec/thor_spec.rb new file mode 100644 index 0000000..1bc484e --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/thor_spec.rb @@ -0,0 +1,491 @@ +require 'helper' + +describe Thor do + describe "#method_option" do + it "sets options to the next method to be invoked" do + args = ["foo", "bar", "--force"] + arg, options = MyScript.start(args) + expect(options).to eq({ "force" => true }) + end + + describe ":lazy_default" do + it "is absent when option is not specified" do + arg, options = MyScript.start(["with_optional"]) + expect(options).to eq({}) + end + + it "sets a default that can be overridden for strings" do + arg, options = MyScript.start(["with_optional", "--lazy"]) + expect(options).to eq({ "lazy" => "yes" }) + + arg, options = MyScript.start(["with_optional", "--lazy", "yesyes!"]) + expect(options).to eq({ "lazy" => "yesyes!" }) + end + + it "sets a default that can be overridden for numerics" do + arg, options = MyScript.start(["with_optional", "--lazy-numeric"]) + expect(options).to eq({ "lazy_numeric" => 42 }) + + arg, options = MyScript.start(["with_optional", "--lazy-numeric", 20000]) + expect(options).to eq({ "lazy_numeric" => 20000 }) + end + + it "sets a default that can be overridden for arrays" do + arg, options = MyScript.start(["with_optional", "--lazy-array"]) + expect(options).to eq({ "lazy_array" => %w[eat at joes] }) + + arg, options = MyScript.start(["with_optional", "--lazy-array", "hello", "there"]) + expect(options).to eq({ "lazy_array" => %w[hello there] }) + end + + it "sets a default that can be overridden for hashes" do + arg, options = MyScript.start(["with_optional", "--lazy-hash"]) + expect(options).to eq({ "lazy_hash" => {'swedish' => 'meatballs'} }) + + arg, options = MyScript.start(["with_optional", "--lazy-hash", "polish:sausage"]) + expect(options).to eq({ "lazy_hash" => {'polish' => 'sausage'} }) + end + end + + describe "when :for is supplied" do + it "updates an already defined command" do + args, options = MyChildScript.start(["animal", "horse", "--other=fish"]) + expect(options[:other]).to eq("fish") + end + + describe "and the target is on the parent class" do + it "updates an already defined command" do + args = ["example_default_command", "my_param", "--new-option=verified"] + options = Scripts::MyScript.start(args) + expect(options[:new_option]).to eq("verified") + end + + it "adds a command to the command list if the updated command is on the parent class" do + expect(Scripts::MyScript.commands["example_default_command"]).to be + end + + it "clones the parent command" do + expect(Scripts::MyScript.commands["example_default_command"]).not_to eq(MyChildScript.commands["example_default_command"]) + end + end + end + end + + describe "#default_command" do + it "sets a default command" do + expect(MyScript.default_command).to eq("example_default_command") + end + + it "invokes the default command if no command is specified" do + expect(MyScript.start([])).to eq("default command") + end + + it "invokes the default command if no command is specified even if switches are given" do + expect(MyScript.start(["--with", "option"])).to eq({"with"=>"option"}) + end + + it "inherits the default command from parent" do + expect(MyChildScript.default_command).to eq("example_default_command") + end + end + + describe "#stop_on_unknown_option!" do + my_script = Class.new(Thor) do + class_option "verbose", :type => :boolean + class_option "mode", :type => :string + + stop_on_unknown_option! :exec + + desc "exec", "Run a command" + def exec(*args) + return options, args + end + + desc "boring", "An ordinary command" + def boring(*args) + return options, args + end + end + + it "passes remaining args to command when it encounters a non-option" do + expect(my_script.start(%w[exec command --verbose])).to eq [{}, ["command", "--verbose"]] + end + + it "passes remaining args to command when it encounters an unknown option" do + expect(my_script.start(%w[exec --foo command --bar])).to eq [{}, ["--foo", "command", "--bar"]] + end + + it "still accepts options that are given before non-options" do + expect(my_script.start(%w[exec --verbose command --foo])).to eq [{"verbose" => true}, ["command", "--foo"]] + end + + it "still accepts options that require a value" do + expect(my_script.start(%w[exec --mode rashly command])).to eq [{"mode" => "rashly"}, ["command"]] + end + + it "still passes everything after -- to command" do + expect(my_script.start(%w[exec -- --verbose])).to eq [{}, ["--verbose"]] + end + + it "does not affect ordinary commands" do + expect(my_script.start(%w[boring command --verbose])).to eq [{"verbose" => true}, ["command"]] + end + + context "when provided with multiple command names" do + klass = Class.new(Thor) do + stop_on_unknown_option! :foo, :bar + end + it "affects all specified commands" do + expect(klass.stop_on_unknown_option?(mock :name => "foo")).to be_true + expect(klass.stop_on_unknown_option?(mock :name => "bar")).to be_true + expect(klass.stop_on_unknown_option?(mock :name => "baz")).to be_false + end + end + + context "when invoked several times" do + klass = Class.new(Thor) do + stop_on_unknown_option! :foo + stop_on_unknown_option! :bar + end + it "affects all specified commands" do + expect(klass.stop_on_unknown_option?(mock :name => "foo")).to be_true + expect(klass.stop_on_unknown_option?(mock :name => "bar")).to be_true + expect(klass.stop_on_unknown_option?(mock :name => "baz")).to be_false + end + end + end + + describe "#map" do + it "calls the alias of a method if one is provided" do + expect(MyScript.start(["-T", "fish"])).to eq(["fish"]) + end + + it "calls the alias of a method if several are provided via .map" do + expect(MyScript.start(["-f", "fish"])).to eq(["fish", {}]) + expect(MyScript.start(["--foo", "fish"])).to eq(["fish", {}]) + end + + it "inherits all mappings from parent" do + expect(MyChildScript.default_command).to eq("example_default_command") + end + end + + describe "#package_name" do + it "provides a proper description for a command when the package_name is assigned" do + content = capture(:stdout) { PackageNameScript.start(["help"]) } + expect(content).to match(/Baboon commands:/m) + end + + # TODO: remove this, might be redundant, just wanted to prove full coverage + it "provides a proper description for a command when the package_name is NOT assigned" do + content = capture(:stdout) { MyScript.start(["help"]) } + expect(content).to match(/Commands:/m) + end + end + + describe "#desc" do + it "provides description for a command" do + content = capture(:stdout) { MyScript.start(["help"]) } + expect(content).to match(/thor my_script:zoo\s+# zoo around/m) + end + + it "provides no namespace if $thor_runner is false" do + begin + $thor_runner = false + content = capture(:stdout) { MyScript.start(["help"]) } + expect(content).to match(/thor zoo\s+# zoo around/m) + ensure + $thor_runner = true + end + end + + describe "when :for is supplied" do + it "overwrites a previous defined command" do + expect(capture(:stdout) { MyChildScript.start(["help"]) }).to match(/animal KIND \s+# fish around/m) + end + end + + describe "when :hide is supplied" do + it "does not show the command in help" do + expect(capture(:stdout) { MyScript.start(["help"]) }).not_to match(/this is hidden/m) + end + + it "but the command is still invokcable not show the command in help" do + expect(MyScript.start(["hidden", "yesyes"])).to eq(["yesyes"]) + end + end + end + + describe "#method_options" do + it "sets default options if called before an initializer" do + options = MyChildScript.class_options + expect(options[:force].type).to eq(:boolean) + expect(options[:param].type).to eq(:numeric) + end + + it "overwrites default options if called on the method scope" do + args = ["zoo", "--force", "--param", "feathers"] + options = MyChildScript.start(args) + expect(options).to eq({ "force" => true, "param" => "feathers" }) + end + + it "allows default options to be merged with method options" do + args = ["animal", "bird", "--force", "--param", "1.0", "--other", "tweets"] + arg, options = MyChildScript.start(args) + expect(arg).to eq('bird') + expect(options).to eq({ "force"=>true, "param"=>1.0, "other"=>"tweets" }) + end + end + + describe "#start" do + it "calls a no-param method when no params are passed" do + expect(MyScript.start(["zoo"])).to eq(true) + end + + it "calls a single-param method when a single param is passed" do + expect(MyScript.start(["animal", "fish"])).to eq(["fish"]) + end + + it "does not set options in attributes" do + expect(MyScript.start(["with_optional", "--all"])).to eq([nil, { "all" => true }, []]) + end + + it "raises an error if the wrong number of params are provided" do + arity_asserter = lambda do |args, msg| + stderr = capture(:stderr) { Scripts::Arities.start(args) } + expect(stderr.strip).to eq(msg) + end + arity_asserter.call ["zero_args", "one" ], %Q'ERROR: thor zero_args was called with arguments ["one"]\nUsage: "thor scripts:arities:zero_args".' + arity_asserter.call ["one_arg" ], %Q'ERROR: thor one_arg was called with no arguments\nUsage: "thor scripts:arities:one_arg ARG".' + arity_asserter.call ["one_arg", "one", "two" ], %Q'ERROR: thor one_arg was called with arguments ["one", "two"]\nUsage: "thor scripts:arities:one_arg ARG".' + arity_asserter.call ["one_arg", "one", "two" ], %Q'ERROR: thor one_arg was called with arguments ["one", "two"]\nUsage: "thor scripts:arities:one_arg ARG".' + arity_asserter.call ["two_args", "one" ], %Q'ERROR: thor two_args was called with arguments ["one"]\nUsage: "thor scripts:arities:two_args ARG1 ARG2".' + arity_asserter.call ["optional_arg", "one", "two" ], %Q'ERROR: thor optional_arg was called with arguments ["one", "two"]\nUsage: "thor scripts:arities:optional_arg [ARG]".' + end + + it "raises an error if the invoked command does not exist" do + expect(capture(:stderr) { Amazing.start(["animal"]) }.strip).to eq('Could not find command "animal" in "amazing" namespace.') + end + + it "calls method_missing if an unknown method is passed in" do + expect(MyScript.start(["unk", "hello"])).to eq([:unk, ["hello"]]) + end + + it "does not call a private method no matter what" do + expect(capture(:stderr) { MyScript.start(["what"]) }.strip).to eq('Could not find command "what" in "my_script" namespace.') + end + + it "uses command default options" do + options = MyChildScript.start(["animal", "fish"]).last + expect(options).to eq({ "other" => "method default" }) + end + + it "raises when an exception happens within the command call" do + expect{ MyScript.start(["call_myself_with_wrong_arity"]) }.to raise_error(ArgumentError) + end + + context "when the user enters an unambiguous substring of a command" do + it "invokes a command" do + expect(MyScript.start(["z"])).to eq(MyScript.start(["zoo"])) + end + + it "invokes a command, even when there's an alias it resolves to the same command" do + expect(MyScript.start(["hi", "arg"])).to eq(MyScript.start(["hidden", "arg"])) + end + + it "invokes an alias" do + expect(MyScript.start(["animal_pri"])).to eq(MyScript.start(["zoo"])) + end + end + + context "when the user enters an ambiguous substring of a command" do + it "raises an exception that explains the ambiguity" do + expect{ MyScript.start(["call"]) }.to raise_error(ArgumentError, 'Ambiguous command call matches [call_myself_with_wrong_arity, call_unexistent_method]') + end + + it "raises an exception when there is an alias" do + expect{ MyScript.start(["f"]) }.to raise_error(ArgumentError, 'Ambiguous command f matches [foo, fu]') + end + end + + end + + describe "#help" do + def shell + @shell ||= Thor::Base.shell.new + end + + describe "on general" do + before do + @content = capture(:stdout) { MyScript.help(shell) } + end + + it "provides useful help info for the help method itself" do + expect(@content).to match(/help \[COMMAND\]\s+# Describe available commands/) + end + + it "provides useful help info for a method with params" do + expect(@content).to match(/animal TYPE\s+# horse around/) + end + + it "uses the maximum terminal size to show commands" do + @shell.should_receive(:terminal_width).and_return(80) + content = capture(:stdout) { MyScript.help(shell) } + expect(content).to match(/aaa\.\.\.$/) + end + + it "provides description for commands from classes in the same namespace" do + expect(@content).to match(/baz\s+# do some bazing/) + end + + it "shows superclass commands" do + content = capture(:stdout) { MyChildScript.help(shell) } + expect(content).to match(/foo BAR \s+# do some fooing/) + end + + it "shows class options information" do + content = capture(:stdout) { MyChildScript.help(shell) } + expect(content).to match(/Options\:/) + expect(content).to match(/\[\-\-param=N\]/) + end + + it "injects class arguments into default usage" do + content = capture(:stdout) { Scripts::MyScript.help(shell) } + expect(content).to match(/zoo ACCESSOR \-\-param\=PARAM/) + end + end + + describe "for a specific command" do + it "provides full help info when talking about a specific command" do + expect(capture(:stdout) { MyScript.command_help(shell, "foo") }).to eq(<<-END) +Usage: + thor my_script:foo BAR + +Options: + [--force] # Force to do some fooing + +do some fooing + This is more info! + Everyone likes more info! +END + end + + it "raises an error if the command can't be found" do + expect { + MyScript.command_help(shell, "unknown") + }.to raise_error(Thor::UndefinedCommandError, 'Could not find command "unknown" in "my_script" namespace.') + end + + it "normalizes names before claiming they don't exist" do + expect(capture(:stdout) { MyScript.command_help(shell, "name-with-dashes") }).to match(/thor my_script:name-with-dashes/) + end + + it "uses the long description if it exists" do + expect(capture(:stdout) { MyScript.command_help(shell, "long_description") }).to eq(<<-HELP) +Usage: + thor my_script:long_description + +Description: + This is a really really really long description. Here you go. So very long. + + It even has two paragraphs. +HELP + end + + it "doesn't assign the long description to the next command without one" do + expect(capture(:stdout) { + MyScript.command_help(shell, "name_with_dashes") + }).not_to match(/so very long/i) + end + end + + describe "instance method" do + it "calls the class method" do + expect(capture(:stdout) { MyScript.start(["help"]) }).to match(/Commands:/) + end + + it "calls the class method" do + expect(capture(:stdout) { MyScript.start(["help", "foo"]) }).to match(/Usage:/) + end + end + end + + describe "when creating commands" do + it "prints a warning if a public method is created without description or usage" do + expect(capture(:stdout) { + klass = Class.new(Thor) + klass.class_eval "def hello_from_thor; end" + }).to match(/\[WARNING\] Attempted to create command "hello_from_thor" without usage or description/) + end + + it "does not print if overwriting a previous command" do + expect(capture(:stdout) { + klass = Class.new(Thor) + klass.class_eval "def help; end" + }).to be_empty + end + end + + describe "edge-cases" do + it "can handle boolean options followed by arguments" do + klass = Class.new(Thor) do + method_option :loud, :type => :boolean + desc "hi NAME", "say hi to name" + def hi(name) + name.upcase! if options[:loud] + "Hi #{name}" + end + end + + expect(klass.start(["hi", "jose"])).to eq("Hi jose") + expect(klass.start(["hi", "jose", "--loud"])).to eq("Hi JOSE") + expect(klass.start(["hi", "--loud", "jose"])).to eq("Hi JOSE") + end + + it "passes through unknown options" do + klass = Class.new(Thor) do + desc "unknown", "passing unknown options" + def unknown(*args) + args + end + end + + expect(klass.start(["unknown", "foo", "--bar", "baz", "bat", "--bam"])).to eq(["foo", "--bar", "baz", "bat", "--bam"]) + expect(klass.start(["unknown", "--bar", "baz"])).to eq(["--bar", "baz"]) + end + + it "does not pass through unknown options with strict args" do + klass = Class.new(Thor) do + strict_args_position! + + desc "unknown", "passing unknown options" + def unknown(*args) + args + end + end + + expect(klass.start(["unknown", "--bar", "baz"])).to eq([]) + expect(klass.start(["unknown", "foo", "--bar", "baz"])).to eq(["foo"]) + end + + it "strict args works in the inheritance chain" do + parent = Class.new(Thor) do + strict_args_position! + end + + klass = Class.new(parent) do + desc "unknown", "passing unknown options" + def unknown(*args) + args + end + end + + expect(klass.start(["unknown", "--bar", "baz"])).to eq([]) + expect(klass.start(["unknown", "foo", "--bar", "baz"])).to eq(["foo"]) + end + + it "send as a command name" do + expect(MyScript.start(["send"])).to eq(true) + end + end +end diff --git a/vendor/gems/thor-0.18.1/spec/util_spec.rb b/vendor/gems/thor-0.18.1/spec/util_spec.rb new file mode 100644 index 0000000..f451b53 --- /dev/null +++ b/vendor/gems/thor-0.18.1/spec/util_spec.rb @@ -0,0 +1,196 @@ +require 'helper' + +module Thor::Util + def self.clear_user_home! + @@user_home = nil + end +end + +describe Thor::Util do + describe "#find_by_namespace" do + it "returns 'default' if no namespace is given" do + expect(Thor::Util.find_by_namespace('')).to eq(Scripts::MyDefaults) + end + + it "adds 'default' if namespace starts with :" do + expect(Thor::Util.find_by_namespace(':child')).to eq(Scripts::ChildDefault) + end + + it "returns nil if the namespace can't be found" do + expect(Thor::Util.find_by_namespace('thor:core_ext:ordered_hash')).to be_nil + end + + it "returns a class if it matches the namespace" do + expect(Thor::Util.find_by_namespace('app:broken:counter')).to eq(BrokenCounter) + end + + it "matches classes default namespace" do + expect(Thor::Util.find_by_namespace('scripts:my_script')).to eq(Scripts::MyScript) + end + end + + describe "#namespace_from_thor_class" do + it "replaces constant nesting with command namespacing" do + expect(Thor::Util.namespace_from_thor_class("Foo::Bar::Baz")).to eq("foo:bar:baz") + end + + it "snake-cases component strings" do + expect(Thor::Util.namespace_from_thor_class("FooBar::BarBaz::BazBoom")).to eq("foo_bar:bar_baz:baz_boom") + end + + it "accepts class and module objects" do + expect(Thor::Util.namespace_from_thor_class(Thor::CoreExt::OrderedHash)).to eq("thor:core_ext:ordered_hash") + expect(Thor::Util.namespace_from_thor_class(Thor::Util)).to eq("thor:util") + end + + it "removes Thor::Sandbox namespace" do + expect(Thor::Util.namespace_from_thor_class("Thor::Sandbox::Package")).to eq("package") + end + end + + describe "#namespaces_in_content" do + it "returns an array of names of constants defined in the string" do + list = Thor::Util.namespaces_in_content("class Foo; class Bar < Thor; end; end; class Baz; class Bat; end; end") + expect(list).to include("foo:bar") + expect(list).not_to include("bar:bat") + end + + it "doesn't put the newly-defined constants in the enclosing namespace" do + Thor::Util.namespaces_in_content("class Blat; end") + expect(defined?(Blat)).not_to be + expect(defined?(Thor::Sandbox::Blat)).to be + end + end + + describe "#snake_case" do + it "preserves no-cap strings" do + expect(Thor::Util.snake_case("foo")).to eq("foo") + expect(Thor::Util.snake_case("foo_bar")).to eq("foo_bar") + end + + it "downcases all-caps strings" do + expect(Thor::Util.snake_case("FOO")).to eq("foo") + expect(Thor::Util.snake_case("FOO_BAR")).to eq("foo_bar") + end + + it "downcases initial-cap strings" do + expect(Thor::Util.snake_case("Foo")).to eq("foo") + end + + it "replaces camel-casing with underscores" do + expect(Thor::Util.snake_case("FooBarBaz")).to eq("foo_bar_baz") + expect(Thor::Util.snake_case("Foo_BarBaz")).to eq("foo_bar_baz") + end + + it "places underscores between multiple capitals" do + expect(Thor::Util.snake_case("ABClass")).to eq("a_b_class") + end + end + + describe "#find_class_and_command_by_namespace" do + it "returns a Thor::Group class if full namespace matches" do + expect(Thor::Util.find_class_and_command_by_namespace("my_counter")).to eq([MyCounter, nil]) + end + + it "returns a Thor class if full namespace matches" do + expect(Thor::Util.find_class_and_command_by_namespace("thor")).to eq([Thor, nil]) + end + + it "returns a Thor class and the command name" do + expect(Thor::Util.find_class_and_command_by_namespace("thor:help")).to eq([Thor, "help"]) + end + + it "falls back in the namespace:command look up even if a full namespace does not match" do + Thor.const_set(:Help, Module.new) + expect(Thor::Util.find_class_and_command_by_namespace("thor:help")).to eq([Thor, "help"]) + Thor.send :remove_const, :Help + end + + it "falls back on the default namespace class if nothing else matches" do + expect(Thor::Util.find_class_and_command_by_namespace("test")).to eq([Scripts::MyDefaults, "test"]) + end + end + + describe "#thor_classes_in" do + it "returns thor classes inside the given class" do + expect(Thor::Util.thor_classes_in(MyScript)).to eq([MyScript::AnotherScript]) + expect(Thor::Util.thor_classes_in(MyScript::AnotherScript)).to be_empty + end + end + + describe "#user_home" do + before do + ENV.stub!(:[]) + Thor::Util.clear_user_home! + end + + it "returns the user path if none variable is set on the environment" do + expect(Thor::Util.user_home).to eq(File.expand_path("~")) + end + + it "returns the *unix system path if file cannot be expanded and separator does not exist" do + File.should_receive(:expand_path).with("~").and_raise(RuntimeError) + previous_value = File::ALT_SEPARATOR + capture(:stderr){ File.const_set(:ALT_SEPARATOR, false) } + expect(Thor::Util.user_home).to eq("/") + capture(:stderr){ File.const_set(:ALT_SEPARATOR, previous_value) } + end + + it "returns the windows system path if file cannot be expanded and a separator exists" do + File.should_receive(:expand_path).with("~").and_raise(RuntimeError) + previous_value = File::ALT_SEPARATOR + capture(:stderr){ File.const_set(:ALT_SEPARATOR, true) } + expect(Thor::Util.user_home).to eq("C:/") + capture(:stderr){ File.const_set(:ALT_SEPARATOR, previous_value) } + end + + it "returns HOME/.thor if set" do + ENV.stub!(:[]).with("HOME").and_return("/home/user/") + expect(Thor::Util.user_home).to eq("/home/user/") + end + + it "returns path with HOMEDRIVE and HOMEPATH if set" do + ENV.stub!(:[]).with("HOMEDRIVE").and_return("D:/") + ENV.stub!(:[]).with("HOMEPATH").and_return("Documents and Settings/James") + expect(Thor::Util.user_home).to eq("D:/Documents and Settings/James") + end + + it "returns APPDATA/.thor if set" do + ENV.stub!(:[]).with("APPDATA").and_return("/home/user/") + expect(Thor::Util.user_home).to eq("/home/user/") + end + end + + describe "#thor_root_glob" do + before do + ENV.stub!(:[]) + Thor::Util.clear_user_home! + end + + it "escapes globs in path" do + ENV.stub!(:[]).with("HOME").and_return("/home/user{1}/") + Dir.should_receive(:[]).with("/home/user\\{1\\}/.thor/*").and_return([]) + expect(Thor::Util.thor_root_glob).to eq([]) + end + end + + describe "#globs_for" do + it "escapes globs in path" do + expect(Thor::Util.globs_for("/home/apps{1}")).to eq([ + "/home/apps\\{1\\}/Thorfile", + "/home/apps\\{1\\}/*.thor", + "/home/apps\\{1\\}/tasks/*.thor", + "/home/apps\\{1\\}/lib/tasks/*.thor" + ]) + end + end + + describe "#escape_globs" do + it "escapes ? * { } [ ] glob characters" do + expect(Thor::Util.escape_globs("apps?")).to eq("apps\\?") + expect(Thor::Util.escape_globs("apps*")).to eq("apps\\*") + expect(Thor::Util.escape_globs("apps {1}")).to eq("apps \\{1\\}") + expect(Thor::Util.escape_globs("apps [1]")).to eq("apps \\[1\\]") + end + end +end diff --git a/vendor/gems/thor-0.18.1/thor.gemspec b/vendor/gems/thor-0.18.1/thor.gemspec new file mode 100644 index 0000000..3124644 --- /dev/null +++ b/vendor/gems/thor-0.18.1/thor.gemspec @@ -0,0 +1,24 @@ +# coding: utf-8 +lib = File.expand_path('../lib/', __FILE__) +$:.unshift lib unless $:.include?(lib) +require 'thor/version' + +Gem::Specification.new do |spec| + spec.add_development_dependency 'bundler', '~> 1.0' + spec.authors = ['Yehuda Katz', 'José Valim'] + spec.description = %q{A scripting framework that replaces rake, sake and rubigen} + spec.email = 'ruby-thor@googlegroups.com' + spec.executables = %w(thor) + spec.files = %w(.document CHANGELOG.md LICENSE.md README.md Thorfile thor.gemspec) + spec.files += Dir.glob("bin/**/*") + spec.files += Dir.glob("lib/**/*.rb") + spec.files += Dir.glob("spec/**/*") + spec.homepage = 'http://whatisthor.com/' + spec.licenses = ['MIT'] + spec.name = 'thor' + spec.require_paths = ['lib'] + spec.required_rubygems_version = '>= 1.3.6' + spec.summary = spec.description + spec.test_files = Dir.glob("spec/**/*") + spec.version = Thor::VERSION +end diff --git a/vendor/librarian-puppet-simple b/vendor/librarian-puppet-simple new file mode 160000 index 0000000..574f86d --- /dev/null +++ b/vendor/librarian-puppet-simple @@ -0,0 +1 @@ +Subproject commit 574f86da8faabf8f6c66ef7b28f0c9732b93db78 diff --git a/vendor/specifications/thor-0.18.1.gemspec b/vendor/specifications/thor-0.18.1.gemspec new file mode 100644 index 0000000..a471ba4 --- /dev/null +++ b/vendor/specifications/thor-0.18.1.gemspec @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{thor} + s.version = "0.18.1" + + s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version= + s.authors = ["Yehuda Katz", "Jos\303\251 Valim"] + s.date = %q{2013-03-30} + s.default_executable = %q{thor} + s.description = %q{A scripting framework that replaces rake, sake and rubigen} + s.email = %q{ruby-thor@googlegroups.com} + s.executables = ["thor"] + s.files = [".document", "CHANGELOG.md", "LICENSE.md", "README.md", "Thorfile", "thor.gemspec", "bin/thor", "lib/thor/actions/create_file.rb", "lib/thor/actions/create_link.rb", "lib/thor/actions/directory.rb", "lib/thor/actions/empty_directory.rb", "lib/thor/actions/file_manipulation.rb", "lib/thor/actions/inject_into_file.rb", "lib/thor/actions.rb", "lib/thor/base.rb", "lib/thor/command.rb", "lib/thor/core_ext/hash_with_indifferent_access.rb", "lib/thor/core_ext/io_binary_read.rb", "lib/thor/core_ext/ordered_hash.rb", "lib/thor/error.rb", "lib/thor/group.rb", "lib/thor/invocation.rb", "lib/thor/parser/argument.rb", "lib/thor/parser/arguments.rb", "lib/thor/parser/option.rb", "lib/thor/parser/options.rb", "lib/thor/parser.rb", "lib/thor/rake_compat.rb", "lib/thor/runner.rb", "lib/thor/shell/basic.rb", "lib/thor/shell/color.rb", "lib/thor/shell/html.rb", "lib/thor/shell.rb", "lib/thor/util.rb", "lib/thor/version.rb", "lib/thor.rb", "spec/actions/create_file_spec.rb", "spec/actions/create_link_spec.rb", "spec/actions/directory_spec.rb", "spec/actions/empty_directory_spec.rb", "spec/actions/file_manipulation_spec.rb", "spec/actions/inject_into_file_spec.rb", "spec/actions_spec.rb", "spec/base_spec.rb", "spec/command_spec.rb", "spec/core_ext/hash_with_indifferent_access_spec.rb", "spec/core_ext/ordered_hash_spec.rb", "spec/exit_condition_spec.rb", "spec/fixtures/application.rb", "spec/fixtures/app{1}/README", "spec/fixtures/bundle/execute.rb", "spec/fixtures/bundle/main.thor", "spec/fixtures/command.thor", "spec/fixtures/doc/%file_name%.rb.tt", "spec/fixtures/doc/block_helper.rb", "spec/fixtures/doc/COMMENTER", "spec/fixtures/doc/config.rb", "spec/fixtures/doc/config.yaml.tt", "spec/fixtures/doc/excluding/%file_name%.rb.tt", "spec/fixtures/doc/README", "spec/fixtures/enum.thor", "spec/fixtures/group.thor", "spec/fixtures/invoke.thor", "spec/fixtures/path with spaces", "spec/fixtures/preserve/script.sh", "spec/fixtures/script.thor", "spec/fixtures/subcommand.thor", "spec/group_spec.rb", "spec/helper.rb", "spec/invocation_spec.rb", "spec/parser/argument_spec.rb", "spec/parser/arguments_spec.rb", "spec/parser/option_spec.rb", "spec/parser/options_spec.rb", "spec/rake_compat_spec.rb", "spec/register_spec.rb", "spec/runner_spec.rb", "spec/shell/basic_spec.rb", "spec/shell/color_spec.rb", "spec/shell/html_spec.rb", "spec/shell_spec.rb", "spec/subcommand_spec.rb", "spec/thor_spec.rb", "spec/util_spec.rb"] + s.homepage = %q{http://whatisthor.com/} + s.licenses = ["MIT"] + s.require_paths = ["lib"] + s.rubygems_version = %q{1.3.6} + s.summary = %q{A scripting framework that replaces rake, sake and rubigen} + s.test_files = ["spec/actions/create_file_spec.rb", "spec/actions/create_link_spec.rb", "spec/actions/directory_spec.rb", "spec/actions/empty_directory_spec.rb", "spec/actions/file_manipulation_spec.rb", "spec/actions/inject_into_file_spec.rb", "spec/actions_spec.rb", "spec/base_spec.rb", "spec/command_spec.rb", "spec/core_ext/hash_with_indifferent_access_spec.rb", "spec/core_ext/ordered_hash_spec.rb", "spec/exit_condition_spec.rb", "spec/fixtures/application.rb", "spec/fixtures/app{1}/README", "spec/fixtures/bundle/execute.rb", "spec/fixtures/bundle/main.thor", "spec/fixtures/command.thor", "spec/fixtures/doc/%file_name%.rb.tt", "spec/fixtures/doc/block_helper.rb", "spec/fixtures/doc/COMMENTER", "spec/fixtures/doc/config.rb", "spec/fixtures/doc/config.yaml.tt", "spec/fixtures/doc/excluding/%file_name%.rb.tt", "spec/fixtures/doc/README", "spec/fixtures/enum.thor", "spec/fixtures/group.thor", "spec/fixtures/invoke.thor", "spec/fixtures/path with spaces", "spec/fixtures/preserve/script.sh", "spec/fixtures/script.thor", "spec/fixtures/subcommand.thor", "spec/group_spec.rb", "spec/helper.rb", "spec/invocation_spec.rb", "spec/parser/argument_spec.rb", "spec/parser/arguments_spec.rb", "spec/parser/option_spec.rb", "spec/parser/options_spec.rb", "spec/rake_compat_spec.rb", "spec/register_spec.rb", "spec/runner_spec.rb", "spec/shell/basic_spec.rb", "spec/shell/color_spec.rb", "spec/shell/html_spec.rb", "spec/shell_spec.rb", "spec/subcommand_spec.rb", "spec/thor_spec.rb", "spec/util_spec.rb"] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + s.add_development_dependency(%q, ["~> 1.0"]) + else + s.add_dependency(%q, ["~> 1.0"]) + end + else + s.add_dependency(%q, ["~> 1.0"]) + end +end