From 84189ac33a9dc57aa4da969c5b5a1910129748c6 Mon Sep 17 00:00:00 2001 From: Michael Chapman Date: Fri, 31 May 2013 16:11:04 +1000 Subject: [PATCH] Initial non-working --- .vagrant | 1 + 01apt-cacher-ng-proxy | 1 + Gemfile | 3 + Puppetfile | 105 +++ README.md | 69 ++ Vagrantfile | 184 +++++ blank.box | Bin 0 -> 96256 bytes config.yaml | 2 + dhclient.conf | 55 ++ install_os_puppet | 130 ++++ manifests | 1 + modules/apache | 1 + modules/apt | 1 + modules/apt-cacher-ng | 1 + modules/boolean | 1 + modules/cinder | 1 + modules/cobbler | 1 + modules/coe | 1 + modules/collectd | 1 + modules/concat | 1 + modules/corosync | 1 + modules/dnsmasq | 1 + modules/drbd | 1 + modules/filemapper | 1 + modules/glance | 1 + modules/graphite | 1 + modules/horizon | 1 + modules/inifile | 1 + modules/keystone | 1 + modules/manifests | 1 + modules/memcached | 1 + modules/monit | 1 + modules/mysql | 1 + modules/naginator | 1 + modules/network | 1 + modules/nova | 1 + modules/ntp | 1 + modules/openstack | 1 + modules/openstack_admin | 1 + modules/pip | 1 + modules/puppet | 1 + modules/quantum | 1 + modules/rabbitmq | 1 + modules/rsync | 1 + modules/stdlib | 1 + modules/swift | 1 + modules/sysctl | 1 + modules/vswitch | 1 + modules/xinetd | 1 + sources.list | 61 ++ templates | 1 + vendor/bin/thor | 19 + vendor/cache/thor-0.18.1.gem | Bin 0 -> 83456 bytes vendor/gems/thor-0.18.1/.document | 5 + vendor/gems/thor-0.18.1/CHANGELOG.md | 139 ++++ vendor/gems/thor-0.18.1/LICENSE.md | 20 + vendor/gems/thor-0.18.1/README.md | 35 + vendor/gems/thor-0.18.1/Thorfile | 30 + vendor/gems/thor-0.18.1/bin/thor | 6 + vendor/gems/thor-0.18.1/lib/thor.rb | 473 +++++++++++++ vendor/gems/thor-0.18.1/lib/thor/actions.rb | 318 +++++++++ .../lib/thor/actions/create_file.rb | 105 +++ .../lib/thor/actions/create_link.rb | 60 ++ .../thor-0.18.1/lib/thor/actions/directory.rb | 119 ++++ .../lib/thor/actions/empty_directory.rb | 137 ++++ .../lib/thor/actions/file_manipulation.rb | 314 +++++++++ .../lib/thor/actions/inject_into_file.rb | 109 +++ vendor/gems/thor-0.18.1/lib/thor/base.rb | 652 ++++++++++++++++++ vendor/gems/thor-0.18.1/lib/thor/command.rb | 136 ++++ .../core_ext/hash_with_indifferent_access.rb | 80 +++ .../lib/thor/core_ext/io_binary_read.rb | 12 + .../lib/thor/core_ext/ordered_hash.rb | 100 +++ vendor/gems/thor-0.18.1/lib/thor/error.rb | 28 + vendor/gems/thor-0.18.1/lib/thor/group.rb | 282 ++++++++ .../gems/thor-0.18.1/lib/thor/invocation.rb | 172 +++++ vendor/gems/thor-0.18.1/lib/thor/parser.rb | 4 + .../thor-0.18.1/lib/thor/parser/argument.rb | 74 ++ .../thor-0.18.1/lib/thor/parser/arguments.rb | 171 +++++ .../thor-0.18.1/lib/thor/parser/option.rb | 121 ++++ .../thor-0.18.1/lib/thor/parser/options.rb | 218 ++++++ .../gems/thor-0.18.1/lib/thor/rake_compat.rb | 72 ++ vendor/gems/thor-0.18.1/lib/thor/runner.rb | 322 +++++++++ vendor/gems/thor-0.18.1/lib/thor/shell.rb | 88 +++ .../gems/thor-0.18.1/lib/thor/shell/basic.rb | 393 +++++++++++ .../gems/thor-0.18.1/lib/thor/shell/color.rb | 148 ++++ .../gems/thor-0.18.1/lib/thor/shell/html.rb | 127 ++++ vendor/gems/thor-0.18.1/lib/thor/util.rb | 270 ++++++++ vendor/gems/thor-0.18.1/lib/thor/version.rb | 3 + .../spec/actions/create_file_spec.rb | 170 +++++ .../spec/actions/create_link_spec.rb | 95 +++ .../spec/actions/directory_spec.rb | 169 +++++ .../spec/actions/empty_directory_spec.rb | 129 ++++ .../spec/actions/file_manipulation_spec.rb | 382 ++++++++++ .../spec/actions/inject_into_file_spec.rb | 135 ++++ vendor/gems/thor-0.18.1/spec/actions_spec.rb | 331 +++++++++ vendor/gems/thor-0.18.1/spec/base_spec.rb | 291 ++++++++ vendor/gems/thor-0.18.1/spec/command_spec.rb | 80 +++ .../hash_with_indifferent_access_spec.rb | 48 ++ .../spec/core_ext/ordered_hash_spec.rb | 115 +++ .../thor-0.18.1/spec/exit_condition_spec.rb | 19 + .../thor-0.18.1/spec/fixtures/application.rb | 2 + .../thor-0.18.1/spec/fixtures/app{1}/README | 3 + .../spec/fixtures/bundle/execute.rb | 6 + .../spec/fixtures/bundle/main.thor | 1 + .../thor-0.18.1/spec/fixtures/command.thor | 10 + .../spec/fixtures/doc/%file_name%.rb.tt | 1 + .../thor-0.18.1/spec/fixtures/doc/COMMENTER | 11 + .../gems/thor-0.18.1/spec/fixtures/doc/README | 3 + .../spec/fixtures/doc/block_helper.rb | 3 + .../thor-0.18.1/spec/fixtures/doc/config.rb | 1 + .../spec/fixtures/doc/config.yaml.tt | 1 + .../fixtures/doc/excluding/%file_name%.rb.tt | 1 + .../gems/thor-0.18.1/spec/fixtures/enum.thor | 10 + .../gems/thor-0.18.1/spec/fixtures/group.thor | 128 ++++ .../thor-0.18.1/spec/fixtures/invoke.thor | 112 +++ .../spec/fixtures/path with spaces | 0 .../spec/fixtures/preserve/script.sh | 3 + .../thor-0.18.1/spec/fixtures/script.thor | 220 ++++++ .../thor-0.18.1/spec/fixtures/subcommand.thor | 17 + vendor/gems/thor-0.18.1/spec/group_spec.rb | 216 ++++++ vendor/gems/thor-0.18.1/spec/helper.rb | 67 ++ .../gems/thor-0.18.1/spec/invocation_spec.rb | 100 +++ .../thor-0.18.1/spec/parser/argument_spec.rb | 53 ++ .../thor-0.18.1/spec/parser/arguments_spec.rb | 66 ++ .../thor-0.18.1/spec/parser/option_spec.rb | 202 ++++++ .../thor-0.18.1/spec/parser/options_spec.rb | 400 +++++++++++ .../gems/thor-0.18.1/spec/rake_compat_spec.rb | 72 ++ vendor/gems/thor-0.18.1/spec/register_spec.rb | 197 ++++++ vendor/gems/thor-0.18.1/spec/runner_spec.rb | 241 +++++++ .../gems/thor-0.18.1/spec/shell/basic_spec.rb | 311 +++++++++ .../gems/thor-0.18.1/spec/shell/color_spec.rb | 95 +++ .../gems/thor-0.18.1/spec/shell/html_spec.rb | 32 + vendor/gems/thor-0.18.1/spec/shell_spec.rb | 47 ++ .../gems/thor-0.18.1/spec/subcommand_spec.rb | 30 + vendor/gems/thor-0.18.1/spec/thor_spec.rb | 491 +++++++++++++ vendor/gems/thor-0.18.1/spec/util_spec.rb | 196 ++++++ vendor/gems/thor-0.18.1/thor.gemspec | 24 + vendor/librarian-puppet-simple | 1 + vendor/specifications/thor-0.18.1.gemspec | 34 + 139 files changed, 11385 insertions(+) create mode 100644 .vagrant create mode 100644 01apt-cacher-ng-proxy create mode 100644 Gemfile create mode 100644 Puppetfile create mode 100644 README.md create mode 100644 Vagrantfile create mode 100644 blank.box create mode 100644 config.yaml create mode 100644 dhclient.conf create mode 100755 install_os_puppet create mode 120000 manifests create mode 160000 modules/apache create mode 160000 modules/apt create mode 160000 modules/apt-cacher-ng create mode 160000 modules/boolean create mode 160000 modules/cinder create mode 160000 modules/cobbler create mode 160000 modules/coe create mode 160000 modules/collectd create mode 160000 modules/concat create mode 160000 modules/corosync create mode 160000 modules/dnsmasq create mode 160000 modules/drbd create mode 160000 modules/filemapper create mode 160000 modules/glance create mode 160000 modules/graphite create mode 160000 modules/horizon create mode 160000 modules/inifile create mode 160000 modules/keystone create mode 160000 modules/manifests create mode 160000 modules/memcached create mode 160000 modules/monit create mode 160000 modules/mysql create mode 160000 modules/naginator create mode 160000 modules/network create mode 160000 modules/nova create mode 160000 modules/ntp create mode 160000 modules/openstack create mode 160000 modules/openstack_admin create mode 160000 modules/pip create mode 160000 modules/puppet create mode 160000 modules/quantum create mode 160000 modules/rabbitmq create mode 160000 modules/rsync create mode 160000 modules/stdlib create mode 160000 modules/swift create mode 160000 modules/sysctl create mode 160000 modules/vswitch create mode 160000 modules/xinetd create mode 100644 sources.list create mode 120000 templates create mode 100755 vendor/bin/thor create mode 100644 vendor/cache/thor-0.18.1.gem create mode 100644 vendor/gems/thor-0.18.1/.document create mode 100644 vendor/gems/thor-0.18.1/CHANGELOG.md create mode 100644 vendor/gems/thor-0.18.1/LICENSE.md create mode 100644 vendor/gems/thor-0.18.1/README.md create mode 100644 vendor/gems/thor-0.18.1/Thorfile create mode 100755 vendor/gems/thor-0.18.1/bin/thor create mode 100644 vendor/gems/thor-0.18.1/lib/thor.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions/create_file.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions/create_link.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions/directory.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions/empty_directory.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions/file_manipulation.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/actions/inject_into_file.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/base.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/command.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/core_ext/hash_with_indifferent_access.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/core_ext/io_binary_read.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/core_ext/ordered_hash.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/error.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/group.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/invocation.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/parser.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/parser/argument.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/parser/arguments.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/parser/option.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/parser/options.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/rake_compat.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/runner.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/shell.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/shell/basic.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/shell/color.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/shell/html.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/util.rb create mode 100644 vendor/gems/thor-0.18.1/lib/thor/version.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions/create_file_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions/create_link_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions/directory_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions/empty_directory_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions/file_manipulation_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions/inject_into_file_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/actions_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/base_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/command_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/core_ext/hash_with_indifferent_access_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/core_ext/ordered_hash_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/exit_condition_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/application.rb create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/app{1}/README create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/bundle/execute.rb create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/bundle/main.thor create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/command.thor create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/%file_name%.rb.tt create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/COMMENTER create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/README create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/block_helper.rb create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/config.rb create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/config.yaml.tt create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/doc/excluding/%file_name%.rb.tt create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/enum.thor create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/group.thor create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/invoke.thor create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/path with spaces create mode 100755 vendor/gems/thor-0.18.1/spec/fixtures/preserve/script.sh create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/script.thor create mode 100644 vendor/gems/thor-0.18.1/spec/fixtures/subcommand.thor create mode 100644 vendor/gems/thor-0.18.1/spec/group_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/helper.rb create mode 100644 vendor/gems/thor-0.18.1/spec/invocation_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/parser/argument_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/parser/arguments_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/parser/option_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/parser/options_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/rake_compat_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/register_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/runner_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/shell/basic_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/shell/color_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/shell/html_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/shell_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/subcommand_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/thor_spec.rb create mode 100644 vendor/gems/thor-0.18.1/spec/util_spec.rb create mode 100644 vendor/gems/thor-0.18.1/thor.gemspec create mode 160000 vendor/librarian-puppet-simple create mode 100644 vendor/specifications/thor-0.18.1.gemspec 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 0000000000000000000000000000000000000000..af71f8ae57f7f764474eb888cf9905c14efefda2 GIT binary patch literal 96256 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"; +#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 < 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 z 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 BqM!f( literal 0 HcmV?d00001 diff --git a/vendor/gems/thor-0.18.1/.document b/vendor/gems/thor-0.18.1/.document new file mode 100644 index 0000000..3ce5450 --- /dev/null +++ b/vendor/gems/thor-0.18.1/.document @@ -0,0 +1,5 @@ +lib/*.rb +lib/**/*.rb +- +CHANGELOG.rdoc +LICENSE.md diff --git a/vendor/gems/thor-0.18.1/CHANGELOG.md b/vendor/gems/thor-0.18.1/CHANGELOG.md new file mode 100644 index 0000000..38687db --- /dev/null +++ b/vendor/gems/thor-0.18.1/CHANGELOG.md @@ -0,0 +1,139 @@ +## 0.18.1, release 2013-03-30 +* Revert regressions found in 0.18.0 + +## 0.18.0, release 2013-03-26 +* Remove rake2thor +* Only display colors if output medium supports colors +* Pass parent_options to subcommands +* Fix non-dash-prefixed aliases +* Make error messages more helpful +* Rename "task" to "command" +* Add the method to allow for custom package name + +## 0.17.0, release 2013-01-24 +* Add better support for tasks that accept arbitrary additional arguments (e.g. things like `bundle exec`) +* Add #stop_on_unknown_option! +* Only strip from stdin.gets if it wasn't ended with EOF +* Allow "send" as a task name +* Allow passing options as arguments after "--" +* Autoload Thor::Group + +## 0.16.0, release 2012-08-14 +* Add enum to string arguments + +## 0.15.4, release 2012-06-29 +* Fix regression when destination root contains reserved regexp characters + +## 0.15.3, release 2012-06-18 +* Support strict_args_position! for backwards compatibility +* Escape Dir glob characters in paths + +## 0.15.2, released 2012-05-07 +* Added print_in_columns +* Exposed terminal_width as a public API + +## 0.15.1, release 2012-05-06 +* Fix Ruby 1.8 truncation bug with unicode chars +* Fix shell delegate methods to pass their block +* Don't output trailing spaces when printing the last column in a table + +## 0.15, released 2012-04-29 +* Alias method_options to options +* Refactor say to allow multiple colors +* Exposed error as a public API +* Exposed file_collision as a public API +* Exposed print_wrapped as a public API +* Exposed set_color as a public API +* Fix number-formatting bugs in print_table +* Fix "indent" typo in print_table +* Fix Errno::EPIPE when piping tasks to `head` +* More friendly error messages + +## 0.14, released 2010-07-25 +* Added CreateLink class and #link_file method +* Made Thor::Actions#run use system as default method for system calls +* Allow use of private methods from superclass as tasks +* Added mute(&block) method which allows to run block without any output +* Removed config[:pretend] +* Enabled underscores for command line switches +* Added Thor::Base.basename which is used by both Thor.banner and Thor::Group.banner +* Deprecated invoke() without arguments +* Added :only and :except to check_unknown_options + +## 0.13, released 2010-02-03 +* Added :lazy_default which is only triggered if a switch is given +* Added Thor::Shell::HTML +* Added subcommands +* Decoupled Thor::Group and Thor, so it's easier to vendor +* Added check_unknown_options! in case you want error messages to be raised in valid switches +* run(command) should return the results of command + +## 0.12, released 2010-01-02 +* Methods generated by attr_* are automatically not marked as tasks +* inject_into_file does not add the same content twice, unless :force is set +* Removed rr in favor to rspec mock framework +* Improved output for thor -T +* [#7] Do not force white color on status +* [#8] Yield a block with the filename on directory + +## 0.11, released 2009-07-01 +* Added a rake compatibility layer. It allows you to use spec and rdoc tasks on + Thor classes. +* BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore + since it may cause wrong behavior in the invocation system. +* thor help now show information about any class/task. All those calls are + possible: + + thor help describe + thor help describe:amazing + Or even with default namespaces: + + thor help :spec +* Thor::Runner now invokes the default task if none is supplied: + + thor describe # invokes the default task, usually help +* Thor::Runner now works with mappings: + + thor describe -h +* Added some documentation and code refactoring. + +## 0.9.8, released 2008-10-20 +* Fixed some tiny issues that were introduced lately. + +## 0.9.7, released 2008-10-13 +* Setting global method options on the initialize method works as expected: + All other tasks will accept these global options in addition to their own. +* Added 'group' notion to Thor task sets (class Thor); by default all tasks + are in the 'standard' group. Running 'thor -T' will only show the standard + tasks - adding --all will show all tasks. You can also filter on a specific + group using the --group option: thor -T --group advanced + +## 0.9.6, released 2008-09-13 +* Generic improvements + +## 0.9.5, released 2008-08-27 +* Improve Windows compatibility +* Update (incorrect) README and task.thor sample file +* Options hash is now frozen (once returned) +* Allow magic predicates on options object. For instance: `options.force?` +* Add support for :numeric type +* BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f]) +* Allow specifying optional args with default values: method_options(:user => "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