Open adamakhtar opened 11 years ago
will add more tomorrow.
but on inspection of /bin/thor line 19 don't exist - Does anyone know why this happen???
It looks like that's the rvm wrapper, not thor/bin/thor. Note the path.
Well ive been busy working on a side project but going to try and plow through some more here.
resuming where I left off:
I actually missed something previously.
in the bin/thor
Thor::Runner.start is called
If you go to lib/thor/runner.rb
you will notice there is no start method, so where is it?
Thor::Runner inherits from Thor like so
class Thor::Runner < Thor:
A bit mind bending but ill ignore asking why for the moment.
So maybe the start method is in Thor's class definition in lib/thor.rb .
No such luck. But after doing a project wide search for "start" I found it in thor/base.rb
class Thor
...
module Base
...
def start
I spent ages thinking how Runner get its hands on it. Then in thor.rb I noticed at line 378 it includes Thor::Base
and Thor::Base has a hook for when it is included via
line 81
def included(base) #:nodoc:
base.send :extend, ClassMethods #=> BAM!! start is defined within Base::ClassMethods
base.send :include, Invocation
base.send :include, Shell
end
So it extends the Thor class with Base.start. Thor is then inherited by Runner which now has a shiny start method.
Ok with that over lets resume from this start method.
Actually I wanted to know why did the authors created this Base module. Why didnt they just write those methods into the Thor class definition in lib/thor.rb.
I believe the answer is because of the class Thor::Groups. I havent used thor groups before but you can learn more on the wiki about this functionality.
The main difference is explained from code comments
# Thor has a special class called Thor::Group. The main difference to Thor class
# is that it invokes all tasks at once. It also include some methods that allows
# invocations to be done at the class method, which are not available to Thor
# tasks.
class Thor::Group
...
...
...
include Thor::Base
end
The class Thor::Groups also requires some of the Base methods and includes Thor::Base just like the regular Thor class does.
So Runner, which inherits from Thor now has a number of class methods such as .start
and .dispatch
.
Lets look at Runners inherited start method
def start(given_args=ARGV, config={})
config[:shell] ||= Thor::Base.shell.new
dispatch(nil, given_args.dup, nil, config)
rescue Thor::Error => e
Ignoring the config[:shell] line for now, the next method dispatch(nil, given_args.dup, nil, config)
is situated in lib/thor.rb at line 257 ( you can see that from the stacktrace produced earlier)
This too is another class method that was originally in Base::ClassMethods but was extended into Thor and via inheritance available to Runner.
# The method responsible for dispatching given the args.
def dispatch(meth, given_args, given_opts, config) #:nodoc:
meth ||= retrieve_task_name(given_args)
task = all_tasks[normalize_task_name(meth)]
...
Since .start
called dispatch passing nil for the meth argument retrieve_task_name(given_args)
is called to extract the name of the task provided on the command line (thor app:boom).
#lib/thor.rb 312
# Retrieve the task name from given args.
def retrieve_task_name(args) #:nodoc:
meth = args.first.to_s unless args.empty?
if meth && (map[meth] || meth !~ /^\-/)
args.shift
else
nil
end
end
if we use pry to inspect what args (ARGV) looks like ala
def retrieve_task_name(args) #:nodoc:
require 'pry'; binding.pry #make sure you have installed the pry gem via => gem install pry
meth = args.first.to_s unless args.empty?
if meth && (map[meth] || meth !~ /^\-/)
we get ["app:boom"]
as expected.
This is set as the value for meth
and extracted via args.shift from the argument args
.
regarding (map[meth] || meth !~ /^\-/)
map
is a method defined on line 82 which stores any mappings - or command line shortcuts as I like to think of them - you may have set in your thor definition file. We havent so that part of the OR expression returns nil.
The second part of the OR expression is simply making sure no dashes are present in the args.
Im not sure what the significance of this but its probably to do with -- flags or something.
Moving on the next line in dispatch is task = all_tasks[normalize_task_name(meth)]
all_tasks is another class method that is defined in Base::ClassMethods and eventually extended into Thor just like the start method above.
This is being called in the context of Runner
# Returns the tasks for this Thor class and all subclasses.
#
# ==== Returns
# OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
# objects as values.
#
def all_tasks
@all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new)
@all_tasks.merge(tasks)
end
from_superclass, another inherited method, is defined further down in base.rb and looks like this
# 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
Ill save these for the next comment.
@codereading/readers
(Eventually this will get edited and placed on the readme. )
In a directory of your choice create this file
2 Run
thor app:boom
You should get a stacktrace roughly similiar to this
The stacktrace is ordered with the last method called being first. i.e. The top line is the last method called - the method with our raise call. The bottom line is the birthpoint of the program.
Look for the first lines mentioning the thor codebase
These two lines are the first to do so
but on inspection of /bin/thor line 19 don't exist - Does anyone know why this happen???
anyway just skip them and move up the stacktrace untill you find something relevant. Luckily
show us the entry point. Not surprisingly it's the exec file at thor/bin/thor
this is run whenever you run
thor blahblahblah
on the command line.Thor::Runner.start is easy to find in your editor, just look at the stacktrace, it tells us it's at
from /Users/adam/.rvm/gems/ruby-1.9.3-p194-Ruby@codereading/gems/thor-0.16.0/lib/thor.rb:275:in 'dispatch' from /Users/adam/.rvm/gems/ruby-1.9.3-p194-Ruby@codereading/gems/thor-0.16.0/ lib/thor/base.rb:425 :in 'start' from /Users/adam/.rvm/gems/ruby-1.9.3-p194-Ruby@codereading/gems/thor-0.16.0/bin/thor:6:in '<top (required)>'