我在Thor做这件事时遇到了一些麻烦,所以希望有人可以指出我做错了什么。
我有一个主要的课程class MyApp < Thor
,我希望将其划分为多个名称空间的单独文件,例如thor create:app_type
和thor update:app_type
。我找不到任何显示如何将Thor应用程序拆分成碎片的示例,而且我尝试过的东西似乎不起作用。
以此为例,我正试图从主要的Thor课程中脱颖而出:
module Things
module Grouping
desc "something", "Do something cool in this group"
def something
....
end
end
end
当我尝试在我的主要课程中包含或要求时:
class App < Thor
....
require 'grouping_file'
include Things::Grouping
....
end
我得到一个例外:'<module:Grouping>': undefined method 'desc' for Things::Grouping:Module (NoMethodError)
是否有可能为Thor任务设置多个名称空间,如果是,那么如何将其分解出来以便您没有一个需要数百行的单一类?
答案 0 :(得分:13)
使用覆盖模块,假设Foo
,在其中定义所有子模块和子类。
在单个foo.thor
文件中启动此模块的定义,该文件位于运行所有Thor任务的目录中。在此Foo
的{{1}}模块的顶部,定义此方法:
foo.thor
然后在主# Load all our thor files
module Foo
def self.load_thorfiles(dir)
Dir.chdir(dir) do
thor_files = Dir.glob('**/*.thor').delete_if { |x| not File.file?(x) }
thor_files.each do |f|
Thor::Util.load_thorfile(f)
end
end
end
end
文件的底部添加:
foo.thor
这将以递归方式包含这些目录中的所有Foo.load_thorfiles('directory_a')
Foo.load_thorfiles('directory_b')
个文件。在您的主*.thor
模块中嵌套模块以命名您的任务。只要您通过上述方法包含所有与Thor相关的目录,无论文件存在于何处或在此处调用它们都无关紧要。
答案 1 :(得分:12)
为什么不起作用:当您在desc
类中使用Thor
时,实际上是在调用类方法Thor.desc
。当你在模块中执行此操作时,它会调用显然不存在的YourModule.desc
。
我可以通过两种方式来解决这个问题。
您是否希望在多个Thor课程中重复使用这些任务?
当一个模块在Ruby中用作include
时,会调用included
类方法。 http://www.ruby-doc.org/core/classes/Module.html#M000458
module MyModule
def self.included(thor)
thor.class_eval do
desc "Something", "Something cool"
def something
# ...
end
end
end
end
您是否只想在另一个文件中单独定义任务?
如果是这样,只需在另一个文件中重新打开App类。你的 Thorfile 看起来像是:
# Thorfile
Dir['./lib/thor/**/*.rb'].sort.each { |f| load f }
然后,您的lib/thor/app.rb
将包含App
的一些任务,而另一个文件lib/thor/app-grouping.rb
将包含同一App
类的更多任务。
答案 2 :(得分:4)
我遇到了同样的问题,几乎放弃了,但后来我有了一个想法:
如果您将任务写入Thorfile
而不是ruby类,那么您可以简单地在包含Thor子类的Ruby文件中require
,并且当您运行时它们将出现在可用任务列表中thor -T
。
这全部由Thor::Runner
类管理。如果你看一下这个,你会看到一个#thorfiles
方法,它负责在当前工作目录下查找名为Thorfile
的文件。
所有我必须做的事情a)将我的Thor任务分成多个文件,而b)不必有一个Thorfile
就是创建一个Thor::Runner
的本地子类,覆盖它{{1}方法,其中一个返回我的应用程序特定的Thor任务文件列表,然后调用其#thorfile
方法,它都工作:
#start
所以我可以在class MyApp::Runner < ::Thor::Runner
private
def thorfiles(*args)
Dir['thortasks/**/*.rb']
end
end
MyApp::Runner.start
例如
thortasks
我几乎放弃了Thor,直到我弄明白这一点,但是没有很多库可以处理创建生成器以及构建命名空间任务,所以我很高兴找到了解决方案。
答案 3 :(得分:3)
Thor文档确实需要改进。以下是从阅读代码,规格,问题和谷歌的几个小时收集到的。我不能说这是它应该工作的方式,但它肯定会以这种方式设置。
当一个类继承自Thor时,它会获得一些重要的Class方法。
我们可以使用它们将许多类中的任务包含在单个运行中。
我添加了一些额外的文件,以便您可以看到整个正在运行的应用程序。 Grantesd它做的不多......
#############################################################
#my_app/bin/my_app #
# #
#This file is the executable that requires the MyApp module,#
#then starts the runner. #
#############################################################
#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include(File.dirname(__FILE__) + '/../lib')
require "rubygems" # ruby1.9 doesn't "require" it though
require "my_app"
MyApp::Runner.start
########################################################
#my_app/lib/my_app.rb #
# #
#This is the main module, used to control the requires #
#the my_app requires should be done last to make sure #
#everything else is defined first. #
########################################################
require 'thor'
require 'thor/group'
module MyApp
#include other helper apps here
require 'my_app/runner' #first so all subcommands can register
require 'my_app/more'
require 'my_app/config'
end
###################################################################
#my_app/lib/my_app/runner.rb #
# #
#This is the main runner class. #
#ALL class_methods should be defined here except for Thor::Groups #
###################################################################
class MyApp::Runner < ::Thor
class_option :config, :type => :string,
:desc => "configuration file. accepts ENV $MYAPP_CONFIG_FILE",
:default => ENV["MYAPP_CONFIG_FILE"] || "~/.my_apprc"
method_option :rf, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "foo","prints foo"
def foo
puts "foo" * options.rf
end
end
#######################################################################
#my_app/lib/my_app/more.rb #
# #
#A Thor Group example. #
#Class_options defined for a Thor Group become method_options when #
#used as a subcommand. #
#Since MyApp::Runner is already defined when this class is evaluated #
#It can automatcially register itself as a subcommand for the runner, #
#######################################################################
class Revamp::Init < ::Thor::Group
class_option :repeat, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "prints woot"
def woot
puts "woot! " * options.repeat
end
desc "prints toow"
def toow
puts "!toow" * options.repeat
end
#This line registers this group as a sub command of the runner
MyApp::Runner.register MyApp::More, :more, "more", "print more stuff"
#This line copies the class_options for this class to the method_options of the :more task
MyApp::Runner.tasks["more"].options = MyApp::More.class_options
end
#####################################################################
#my_app/lib/my_app/config.rb #
# #
#For normal Thor classes, each task must be registered individually #
#####################################################################
class MyApp::Config < Thor
method_option :dr, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "show_default", "show the default config"
def show_default
puts "default " * options.dr
end
MyApp::Runner.register MyApp::Config, :show_default, "show_default", "print default"
MyApp::Runner.tasks["show_default"].options = MyApp::Config.tasks["show_default"].options
method_option :cr, :type => :numeric,
:desc => "repeat greeting X times",
:default => 3
desc "show_config", "show the config"
def show_config
puts "config " * options.cr
end
MyApp::Runner.register MyApp::Config, :show_config, "show_config", "print config"
MyApp::Runner.tasks["show_config"].options = MyApp::Config.tasks["show_config"].options
end
答案 4 :(得分:2)
您可能会发现这有用:https://github.com/lastobelus/cleanthor
我想为gem创建一个基于thor的可执行文件,使用命名空间子命令,但是根据正常的ruby gem lib / mygem / * / .rb结构组织任务文件。
我还希望有一个根级Thorfile,以便在开发期间在项目目录中正常运行thor也显示所有gem任务。
解决方案包括以下步骤:
Thor::Runner
中继承Mygem::Thor::Runner
并覆盖其私有thorfiles
和method_missing
方法。在method_missing
中,我还从命令中删除了宝石名称。Mygem::Thor::Runner.start
Thor::Task
和Mygem::Thor::Task
中继承namespace
namespace
类方法。自定义Mygem::Thor::Tasks
方法删除了任务模块层次结构的thorfiles
部分。Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rb')]
方法以返回lib/mygem/thor/tasks/**/*.rb
Mygem::Thor::Task
中组织任务。它们都应该继承自lib/mygem/thor/tasks/**/*.rb
答案 5 :(得分:-4)
desc是一个类方法,需要使用extend而不是include。 请查看here以获取解释。