2011-12-05 6 views
4

私はかなりの共通構造を共有する簡単なコマンドラインのScalaアプリケーションを多数持っています。それらのすべてがscala.Appから継承しています。これは問題ありません。これらのコマンドラインアプリの共有構造を共通の特性にリファクタリングして、私の(もっと簡単な)コマンドラインアプリクラスを継承することができます。この問題は、共通構造の中にコマンドライン引数の解析が含まれていることが原因で発生します。Scalaの特質のコマンドライン引数の取得

object MyScript extends BaseScript with App{ 
    //small bits of business logic using components defined in BaseScript 
} 

trait BaseScript extends App{ 
    val configuration = loadConfiguration(args(0)) 
    //setup a bezillion components, usable from any of the scripts, based on the configuration 
} 

これはコンパイルが、それはargsデリファレンス、実際に時間を来るときApp形質がまだ初期化されていないと思われるので、NPEで失敗します。特性型の順序を変更し、BaseScriptのAppの継承を自己型宣言に変更すると、DelayedInitの実験と同様に何も起こりません。 BaseScriptでコンポーネントを「怠惰」と宣言すると、初期化中にこれらのコンポーネントを実際に使用する必要があります(たとえば、ログディレクトリの設定やコンフィグレーションに基づくJDBCドライバクラスのロードなど)。 BaseScriptの特性でコマンドライン引数を表示して初期化するためにできることはありますか?

+0

:https://github.com/jstrachan/scopt – wheaties

答えて

4

BaseScript特性を2つの理由で変更することをお勧めします。最初は、クラスと比較したであり、形質初期化は逆順で実行されますthis question on initialization behaviorを参照してください。第二に、BaseScriptは、意味的には追加動作よりもスーパークラスのほうが多い。私はあなたがこれが事を簡素化できることを見つけるだろうと思う。

MyScriptを実行すると、次のコードはBaseScriptクラスを最初に初期化します。 BaseScriptは順番にApp形質に依存しており、最初にそれを強制的に初期化します。 Appsourceを見ると

object MyScript extends BaseScript { 
    //small bits of business logic using components defined in BaseScript 
    println("running") 
    println("arg(0): " + configuration) 
} 

class BaseScript extends App { 
    val configuration = loadConfiguration(args) 
    //setup a bezillion components, usable from any of the scripts, based on the configuration 
    def loadConfiguration(args: Array[String]) = { 
    println("configuring") 
    if (args.length > 0) args(0) else null 
    } 
} 
+0

のために動作しません理由を説明するために、最初の質問を編集しますBaseScriptの初期化後にAppの初期化が行われるので、 "args"の呼び出しはNPEのままです。クルード –

+0

自分で動作するようなコードをいくつか用意しました。すべてのケースを見ることなく、私は何かを見逃しているかもしれませんが、BaseScriptを拡張するたびに「with App」ミックスを削除すると、問題が解決する可能性があります。 –

+0

それはそうしました。 MyScript上で「With App」を使用することは、何かをねじ込むことです。ありがとう。 –

3

怠惰なvalを使ってみましたか(Appの特性を拡張していませんか?)

trait BaseScript { self : App => 
    lazy val configuration = loadConfiguration(args(0)) 
    //setup a bezillion components, usable from any of the scripts 
    //based on the configuration 
} 
+0

は「それは悲しいことにまだdoesnのこと、私のいずれか悲しいこと –

1

あなたがあなたのアプリケーションのコードが実行される前に、引数で物事を行うにはmainを上書きすることができそうです:

trait AppUtil extends App { 
    def myInit(args: Array[String]) { 
    println("args " + args.size) 
    } 
    override def main(args: Array[String]) { 
    myInit(args) 
    super.main(args) 
    } 
} 

私はAppソースはあなた自身のカスタマイズを書き換えるインスピレーションを与えることができます疑いますApp。コードはそれほど長くはないし、argsで何をしているのか、あなたの実行前後で何が起こるのか、mainなど、物事をもっとコントロールすることができます。 scoptsをチェックアウトするよりもはるかに、私は他を追加することができない

+0

私はこれを試して、廃止予定の警告を受け取りました: "メインの特性をオーバーライドするメソッドは廃止されました:メインは上書きされません" –

+1

私の答えは時代遅れだと私は確信しています。 – huynhjl

+1

私は、あなたのコードでは、単にアプリケーションを拡張するクラスの主要な関数なしでargsを使用することができますが見つかりました。あなたのコードにargsを明示的に定義することなく。私はval numSegments = args(0)を使用しました。私の場合はtoInt、そしてコマンドラインから渡された値を使用しました。 –

関連する問題