23

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 タスクに複数の名前空間を持つことは可能ですか? もしそうなら、数百行を必要とする 1 つのモノリシックなクラスを持たないようにするにはどうすればよいでしょうか?

4

6 に答える 6

14

うまくいかない理由:クラスdesc内でを使用するThorと、実際にはクラス メソッドが呼び出されますThor.desc。モジュールでそれを行うと、YourModule.desc明らかに存在しないものを呼び出します。

これを修正するために私が提案できる方法は2つあります。

修正 1: Module.included を使用する

それらのタスクを複数の Thor クラスで再利用したかったですか?

includeRuby でモジュールを として使用すると、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

修正 2: Thor クラスを複数のファイルに分割する

別のファイルでタスクを個別に定義したかっただけですか?

その場合は、別のファイルで App クラスを再度開いてください。Thorfileはのようになります。

# Thorfile
Dir['./lib/thor/**/*.rb'].sort.each { |f| load f }

次に、別のファイルに同じクラスのいくつかのlib/thor/app.rbタスクが含まれている間に、 のいくつかのタスクが含まれます。Applib/thor/app-grouping.rbApp

于 2011-08-07T20:46:15.327 に答える
14

Fooすべてのサブモジュールとサブクラスを定義する包括的なモジュール、たとえばを使用します。

foo.thorこのモジュールの定義は、すべての Thor タスクを実行するディレクトリにある単一のファイルで開始します。Fooこの のモジュールの先頭で、次の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これにより、それらのディレクトリ内のすべてのファイルが再帰的に含まれます。メイン モジュール内にモジュールをネストFooして、タスクに名前を付けます。上記の方法ですべてのトール関連のディレクトリを含める限り、ファイルがどこにあるのか、その時点で何と呼ばれているのかは問題ではありません。

于 2011-09-30T21:25:17.840 に答える
5

私はこれと同じ問題を抱えていて、ほとんどあきらめていましたが、それから私は考えを持っていました:

Thorfileタスクをrubyクラスとしてではなくsに書き込む場合はrequire、Thorサブクラスを含むRubyファイルに入れるだけで、実行時に使用可能なタスクのリストに表示されますthor -T

これはすべてThor::Runnerクラスによって管理されます。これを調べると、現在の作業ディレクトリの下にある#thorfiles名前のファイルを探すためのメソッドが表示されます。Thorfile

私がしなければならなかったのは、a)Thorタスクを複数のファイルに分割する一方で、b)単一Thorfileのサブクラスを作成する必要はなく、そのメソッドを、アプリ固有のThorタスクファイルのリストを返すものでThor::Runner上書きしてから呼び出すことでした。#thorfileその#start方法とそれはすべてうまくいきました:

class MyApp::Runner < ::Thor::Runner
  private
  def thorfiles(*args)
    Dir['thortasks/**/*.rb']
  end
end

MyApp::Runner.start

thortasksしたがって、たとえば、Thorタスクを定義するRubyクラスをいくつでも持つことができます。

class MyApp::MyThorNamespace < ::Thor
  namespace :mynamespace

  # Unless you include the namespace in the task name the -T task list
  # will list everything under the top-level namespace
  # (which I think is a bug in Thor)
  desc "#{namespace}:task", "Does something"
  def task
    # do something
  end
end

これを理解するまではThorをあきらめていましたが、ジェネレーターの作成や名前空間化されたタスクの構築を処理するライブラリーはあまりないので、解決策を見つけてよかったです。

于 2011-08-01T12:57:56.467 に答える
3

Thorのドキュメントは本当に改善する必要があります。以下は、コード、仕様、問題、およびgoogle-fuを何時間も読んだことから収集されたものです。これが想定どおりに機能するとは言えませんが、このように設定すると確実に機能します。

クラスがThorから継承する場合、いくつかの重要なClassメソッドを取得します。

  1. 登録。これにより、新しいサブコマンドをタスクとして登録できます
  2. class_options。これにより、すべてのクラスオプションのハッシュが得られます。
  3. タスク。これにより、定義されたすべてのタスクのハッシュが得られます。

これらを使用して、多くのクラスのタスクを1つのランナーに含めることができます。

動作しているトールアプリ全体を表示できるように、いくつかの追加ファイルを含めました。確かにそれはあまり効果がありません...

#############################################################
#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
于 2011-08-07T11:32:49.610 に答える
-4

desc はクラス メソッドであるため、include の代わりに extend を使用する必要があります。説明については、こちらをご覧ください。

于 2011-05-19T19:00:23.973 に答える