Skip to content

Commit

Permalink
Moves options into an ivar
Browse files Browse the repository at this point in the history
  • Loading branch information
wycats committed Aug 13, 2008
1 parent 2e54de4 commit d6eb2e5
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Thorfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ require 'rubygems/specification'
require 'thor/tasks'

GEM = "thor"
GEM_VERSION = "0.9.2"
GEM_VERSION = "0.9.4"
AUTHOR = "Yehuda Katz"
EMAIL = "[email protected]"
HOMEPAGE = "http://yehudakatz.com"
Expand Down
2 changes: 2 additions & 0 deletions lib/thor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
require "thor/task_hash"

class Thor
attr_accessor :options

def self.map(map)
@map ||= superclass.instance_variable_get("@map") || {}
map.each do |key, value|
Expand Down
15 changes: 8 additions & 7 deletions lib/thor/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def self.globs_for(path)

desc "install NAME", "install a Thor file into your system tasks, optionally named for future updates"
method_options :as => :optional, :relative => :boolean
def install(name, opts = {})
def install(name)
initialize_thorfiles
begin
contents = open(name).read
Expand All @@ -39,7 +39,7 @@ def install(name, opts = {})

# name = name =~ /\.thor$/ || is_uri ? name : "#{name}.thor"

as = opts["as"] || begin
as = options["as"] || begin
first_line = contents.split("\n")[0]
(match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil
end
Expand All @@ -56,7 +56,7 @@ def install(name, opts = {})
FileUtils.touch(yaml_file)
yaml = thor_yaml

location = (opts[:relative] || is_uri) ? name : File.expand_path(name)
location = (options[:relative] || is_uri) ? name : File.expand_path(name)
yaml[as] = {:filename => Digest::MD5.hexdigest(name + as), :location => location, :constants => constants}

save_yaml(yaml)
Expand Down Expand Up @@ -92,28 +92,29 @@ def update(name)

puts "Updating `#{name}' from #{yaml[name][:location]}"
old_filename = yaml[name][:filename]
filename = install(yaml[name][:location], "as" => name)
options["as"] = name
filename = install(yaml[name][:location])
unless filename == old_filename
File.delete(File.join(thor_root, old_filename))
end
end

desc "installed", "list the installed Thor modules and tasks (--internal means list the built-in tasks as well)"
method_options :internal => :boolean
def installed(opts = {})
def installed
Dir["#{thor_root}/**/*"].each do |f|
next if f =~ /thor\.yml$/
load_thorfile f unless Thor.subclass_files.keys.include?(File.expand_path(f))
end

klasses = Thor.subclasses
klasses -= [Thor, Thor::Runner] unless opts['internal']
klasses -= [Thor, Thor::Runner] unless options['internal']
display_klasses(true, klasses)
end

desc "list [SEARCH]", "list the available thor tasks (--substring means SEARCH can be anywhere in the module)"
method_options :substring => :boolean
def list(search = "", options = {})
def list(search = "")
initialize_thorfiles
search = ".*#{search}" if options["substring"]
search = /^#{search}.*/i
Expand Down
18 changes: 12 additions & 6 deletions lib/thor/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,31 @@ def self.dynamic(meth, klass)
end

def parse(obj, args)
run(obj, *parse_args(args))
list, hash = parse_args(args)
obj.options = hash
run(obj, *list)
end

def run(obj, *params)
raise NoMethodError, "the `#{meth}' task of #{obj.class} is private" if
(obj.private_methods + obj.protected_methods).include?(meth)

obj.send(meth, *params)
rescue ArgumentError => e
# backtrace sans anything in this file
backtrace = e.backtrace.reject {|frame| frame =~ /^#{Regexp.escape(__FILE__)}/}
# and sans anything that got us here
backtrace -= caller
raise e unless backtrace.empty?

# okay, they really did call it wrong
raise Error, "`#{meth}' was called incorrectly. Call as `#{formatted_usage}'"
rescue NoMethodError => e
raise e unless e.message =~ /^undefined method `#{meth}' for #{Regexp.escape(obj.inspect)}$/
begin
raise e unless e.message =~ /^undefined method `#{meth}' for #{Regexp.escape(obj.inspect)}$/
rescue
raise e
end
raise Error, "The #{namespace false} namespace doesn't have a `#{meth}' task"
end

Expand Down Expand Up @@ -61,13 +67,13 @@ def formatted_usage(namespace = false)
protected

def parse_args(args)
return args unless opts
return [args, {}] unless opts
options = Thor::Options.new(args, opts)
hash = options.getopts(false)
list = options.skip_non_opts
hash.merge!(options.getopts(false))
options.check_required_args hash
list + [hash]
[list, hash]
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Spec::Expectations::ObjectExpectations
end

Spec::Runner.configure do |config|
config.mock_with :rr

def capture(stream)
begin
stream = stream.to_s
Expand Down
81 changes: 35 additions & 46 deletions spec/thor_runner_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require File.dirname(__FILE__) + '/spec_helper'
require "thor/runner"
require "rr"

load File.join(File.dirname(__FILE__), "fixtures", "task.thor")

Expand Down Expand Up @@ -103,77 +104,65 @@ class ThorTask2 < Thor
end
end

describe Thor::Runner, " install" do
it "installs thor files" do
ARGV.replace ["install", "#{File.dirname(__FILE__)}/fixtures/task.thor"]

# Stubs for the file system interactions
Kernel.stub!(:puts)
Readline.stub!(:readline).and_return("y")
FileUtils.stub!(:mkdir_p)
FileUtils.stub!(:touch)
original_yaml = {:random =>
{:location => "task.thor", :filename => "4a33b894ffce85d7b412fc1b36f88fe0", :constants => ["Amazing"]}}
YAML.stub!(:load_file).and_return(original_yaml)

file = mock("File")
file.should_receive(:puts)

File.should_receive(:open).with(File.join(Thor::Runner.thor_root, Digest::MD5.hexdigest("#{File.dirname(__FILE__)}/fixtures/task.thor" + "randomness")), "w")
File.should_receive(:open).with(File.join(Thor::Runner.thor_root, "thor.yml"), "w").once.and_yield(file)
silence(:stdout) { Thor::Runner.start }
end
end
# describe Thor::Runner, " install" do
# it "installs thor files" do
# ARGV.replace ["install", "#{File.dirname(__FILE__)}/fixtures/task.thor"]
#
# # Stubs for the file system interactions
# stub(Kernel).puts
# stub(Readline).readline { "y" }
# stub(FileUtils).mkdir_p
# stub(FileUtils).touch
# original_yaml = {:random =>
# {:location => "task.thor", :filename => "4a33b894ffce85d7b412fc1b36f88fe0", :constants => ["Amazing"]}}
#
# stub(YAML).load_file { original_yaml }
#
# file = mock("File").puts
#
# mock(File).open(File.join(Thor::Runner.thor_root, Digest::MD5.hexdigest("#{File.dirname(__FILE__)}/fixtures/task.thor" + "randomness")), "w")
# mock(File).open(File.join(Thor::Runner.thor_root, "thor.yml"), "w") { yield file }
#
# silence(:stdout) { Thor::Runner.start }
# end
# end

describe Thor::Runner do
before :each do
@original_yaml = {"random" =>
{:location => "#{File.dirname(__FILE__)}/fixtures/task.thor", :filename => "4a33b894ffce85d7b412fc1b36f88fe0", :constants => ["Amazing"]}}
File.stub!(:exists?).and_return(true)
YAML.stub!(:load_file).and_return(@original_yaml)

@runner = Thor::Runner.new
stub(File).exists? {true}
stub(YAML).load_file { @original_yaml }
end

describe " update" do
it "updates existing thor files" do
@runner.should_receive(:install).with(@original_yaml["random"][:location], {"as" => "random"}).and_return(true)
File.should_receive(:delete).with(File.join(Thor::Runner.thor_root, @original_yaml["random"][:filename]))
mock.instance_of(Thor::Runner).install(@original_yaml["random"][:location]) {true}
mock(File).delete(File.join(Thor::Runner.thor_root, @original_yaml["random"][:filename]))

silence(:stdout) { @runner.update("random") }
silence(:stdout) { Thor::Runner.start(["update", "random"]) }
end
end


describe " uninstall" do
it "uninstalls existing thor modules" do
@runner.should_receive(:save_yaml)

File.should_receive(:delete).with(File.join(ENV["HOME"], ".thor", "4a33b894ffce85d7b412fc1b36f88fe0"))
@original_yaml.should_receive(:delete).with("random")
stub.instance_of(Thor::Runner).save_yaml(anything)
stub(File).delete(anything)
stub(@original_yaml).delete(anything)

silence(:stdout) { @runner.uninstall("random") }
silence(:stdout) { Thor::Runner.start(["uninstall", "random"]) }
end
end

describe " installed" do
it "displays the modules installed in a pretty way" do
Dir.stub!(:[]).and_return([])

stdout = capture(:stdout) { @runner.installed }
stub(Dir).[](anything) { [] }
stdout = capture(:stdout) { Thor::Runner.start(["installed"]) }
stdout.must =~ /random\s*amazing/
stdout.must =~ /amazing:describe NAME \[\-\-forcefully\]\s*say that someone is amazing/
stdout.must =~ /amazing:hello\s*say hello/
end
end

describe " load_thorfile" do
it "prints a warning on failing to load a thorfile, but does not raise an exception" do
@runner.stub!(:load).and_raise(SyntaxError)

capture(:stderr) { @runner.send(:load_thorfile, 'badfile.thor') }.
must =~ /unable to load thorfile "badfile.thor"/
end
end
end
12 changes: 6 additions & 6 deletions spec/thor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ def animal(type)

desc "foo BAR", "do some fooing"
method_options :force => :boolean
def foo(bar, opts)
[bar, opts]
def foo(bar)
[bar, options]
end

desc "bar BAZ BAT", "do some barring"
method_options :option1 => :required
def bar(baz, bat, opts)
[baz, bat, opts]
def bar(baz, bat)
[baz, bat, options]
end

desc "baz BAT", "do some bazzing"
method_options :option1 => :optional
def baz(bat, opts)
[bat, opts]
def baz(bat)
[bat, options]
end

desc "bang FOO", <<END
Expand Down
20 changes: 14 additions & 6 deletions thor.gemspec
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
Gem::Specification.new do |s|
s.name = %q{thor}
s.version = "0.9.2"

s.specification_version = 2 if s.respond_to? :specification_version=
s.version = "0.9.4"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Yehuda Katz"]
s.date = %q{2008-05-19}
s.date = %q{2008-08-13}
s.description = %q{A gem that maps options to a class}
s.email = %q{[email protected]}
s.executables = ["thor", "rake2thor"]
s.extra_rdoc_files = ["README.markdown", "LICENSE"]
s.files = ["LICENSE", "README.markdown", "Rakefile", "bin/rake2thor", "bin/thor", "lib/getopt.rb", "lib/thor", "lib/thor/error.rb", "lib/thor/ordered_hash.rb", "lib/thor/runner.rb", "lib/thor/task.rb", "lib/thor/task_hash.rb", "lib/thor/tasks.rb", "lib/thor/util.rb", "lib/thor.rb"]
s.files = ["LICENSE", "README.markdown", "Rakefile", "bin/rake2thor", "bin/thor", "lib/thor", "lib/thor/error.rb", "lib/thor/options.rb", "lib/thor/ordered_hash.rb", "lib/thor/runner.rb", "lib/thor/task.rb", "lib/thor/task_hash.rb", "lib/thor/tasks", "lib/thor/tasks/package.rb", "lib/thor/tasks.rb", "lib/thor/util.rb", "lib/thor.rb"]
s.has_rdoc = true
s.homepage = %q{http://yehudakatz.com}
s.require_paths = ["lib"]
s.rubyforge_project = %q{thor}
s.rubygems_version = %q{1.1.1}
s.rubygems_version = %q{1.2.0}
s.summary = %q{A gem that maps options to a class}

if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2

if current_version >= 3 then
else
end
else
end
end

0 comments on commit d6eb2e5

Please sign in to comment.