パイロン。 代替routing.py

良い時間です。 少し前に、Pylonsで大規模なプロジェクトの作成を開始しました。主な要件の1つは、routing.pyを変更せずにコントローラーをすばやく接続および削除することでした。 従業員の1人が既にこれに遭遇し、プラグインを介してこの機能を作成しました。 しかし、私には思えたように、この決定はかなり面倒で、将来プロジェクトからプロジェクトに移すことは困難でした。



なぜなら 以前、Catalyst(Perl MVCフレームワーク)を扱っていましたが、各メソッドにURLを手で追加できるという事実が気に入りました。 実際、私は似たようなものを書くことにしました。





要件。 「最小限の機能と最小限の身体の動きが必要です。 URL(または空の文字列)でデコレータを呼び出します。 また、URLにパラメーターがある既製パンが失われないようにします。」



解決策はデコレーターを介してもたらされました。 そして、実際にはあまり書く必要がなく、ソリューションは非常に移植性が高いことが判明しました。



実際、パイロンにパッチを適用する必要があるという事実から始めます。 このパッチは現在のプロジェクトに害を与えないため、安全にパッチを適用できます。

 ---パイロン/ util.py 2009-12-29 14:28:20.000000000 +0600
 +++ dev / null 2010-02-05 03:44:26.000000000 +0600
 @@ -122.6 +122.8 @@
          「ワンワード」
 
      「」
 + module_name = module_name.split( '。')[-1]
      words = module_name.replace( '-'、 '_')。split( '_')
      return '' .join([w.title()for w in words])




このパッチはPylonsのバグを修正します。これにより、project_name / controllers / *に下位構造を持つことができなくなります。 何らかの理由で、ポイントを使用してオブジェクトのメソッドへのパスを書き込むと、このポイントが消えるように作成されました。 実際、このパッチを使用すると、pylonsプロジェクトに任意の構造を作成できます。



次に、ライブラリファイルをプロジェクトに追加する必要があります(または、原則に基づいてすべてのプロジェクトを実行することにした場合は、別のライブラリに配置できます)。



#PROJECT / lib / controllers.py

 #-*-コーディング:utf-8-*- 
                                                                                                                                                             
輸入OS                                                                                                                                                                               
                                                                                                                                                                                        
 def scan_folder_recurse(フォルダー、excl_names = ['__']):
     "" "指定されたフォルダー内のPYファイルの再帰検索" ""                                                                                                                                   
     all_files = []                                                                                                                                                                      
    ルート、ディレクトリ、os.walk(フォルダ)内のファイル:                                                                                                                                            
        ファイルリスト= \                                                                                                                                                                    
             [fi.endswith( '。py')の場合、ファイル内のfiの[os.path.join(root、fi)                                                                                                               
              およびない(excl_namesのプレフィックスのfi.startswith(プレフィックス))]                                                                                                              
        ファイルリストのfの場合:                                                                                                                                                              
             all_files.append(f)                                                                                                                                                         
     all_filesを返します                                                                                                                                                                                                                                                                                                                                                      


この関数を使用すると、目的のディレクトリにあるすべての* .pyファイルの再帰的なリストを取得できます。 つまり 必要に応じて、コントローラーフォルダーにのみコントローラーを保存することはできません。 それは確かに悪い考えですが、異なる場合があります。



次に、config / routing.pyファイルとmake_map()関数を変更します



     map = Mapper(directory = config ['pylons.paths'] ['controllers']、
                  always_scan = config ['デバッグ'])
     map.minimization = False

     proj_root = os.path.dirname(config ['pylons.paths'] ['root'])
     sep = os.path.sep

     "" "すべてのファイルをコントローラーフォルダー全体に取得" ""
     all_files = scan_folder_recurse(
             config ['pylons.paths'] ['controllers']、
             excl_names = ['__'、 'daemon'、 'models'])

     log.debug( "Found%d controllers"%len(all_files))
     log.debug(「ルートマップの構築」)

     cfg = ConfigParser.RawConfigParser()
     cfg.read(config ['global_conf'] ['__ file__'])

     all_filesのファイルの場合:
	 t_controller_name = module_path.split( '。')[-1]
         controller_name = '/'.join(module_path.split('.')►2:])

         「」「インポートモジュール」「」
	コントローラー= __import __(module_path)
         「」「モジュール環境のインポート」「」
	コントローラー= sys.modules [module_path]

	 my_list = dir(コントローラー)
	 name_re = re.compile(t_controller_name、re.IGNORECASE)
        
         "" "メソッドを持つクラスが必要です" ""
	コントロール=なし
	 my_listの要素の場合:

	     name_re.search(要素)およびcontroller_re.search(要素)の場合:
	         control = getattr(コントローラー、要素)
         "" "クラスが見つかった場合" ""
	コントロールの場合:
           "" "必要なプロパティの検索" ""
	  制御対象の項目.__ dict__:
	      試してください:
	           attrib = control .__ dict __ [item] .__ dict __ ['method']

                   "" "クラスにメソッドプロパティがある場合" ""
	           attrib == 'path'の場合:
	               route_path = getattr(コントロール、アイテム).route_path

	          その他:
	               route_path = "/%s /%s"%(コントローラー名、アイテム)

	           route_path = route_path.rstrip( '/')

                   "" "パスの2つのバリエーションを作成する2つの方法" ""
	           map.connect(
	                   route_path、
	                   controller = controller_name、
	                  アクション=アイテム)
	           map.connect(
	                   「%s /」%route_path、
	                   controller = controller_name、
	                  アクション=アイテム)

	           log.info( "%s ::%s ---- >>>>%s ..... connected"%\
	               (controller_name、item、route_path))
	      を除く:
	          合格する


     log.info(「ルートマップcomplite ...」)
     #ErrorControllerルート(404/500エラーページを処理); それはすべきです
     #常にトップにとどまり、常に解決できるようにする
     map.connect( '/ error / {action}'、コントローラー= 'error')
     map.connect( '/ error / {action} / {id}'、コントローラー= 'error')

    地図を返す





このコードカットは、make_map()関数全体を置き換えることができます。 これで、関数は、コントローラーを含むフォルダーから* .pyファイルのリストを再帰的に取得します。 各クラスの各メソッドのプロパティの存在を確認します。 必要なプロパティが存在する場合、メソッドはMAPコントローラーに存在する必要があると見なされます。 次に、見つかったメソッドがMAPに渡されます。 最後に、エラーコントローラーを接続します。 はい、ところで、私はこの機能に少し不安を感じていました。各URLに2つのマップがあります:スラッシュありとなし。 今彼女は修正しました



したがって、デコレータを作成して適用する必要があります。



そして、lib / decorators.pyデコレータ自体:



 #-*-コーディング:utf-8-*-
 "" "プロジェクトの装飾
 「」

 def route_action(path = None):
     defデコレート(f):
        パス==なしの場合:
             setattr(f、「メソッド」、「ローカル」)
        その他:
             setattr(f、「メソッド」、「パス」)
             setattr(f、「route_path」、パス)
        リターンf
    飾る





つまり パラメータなしでデコレータを呼び出すと、デフォルトのパスが適用されます。 パラメータ付きの場合-その後、カスタマイズ。



そして、コントローラ/sample.pyの実際の使用



 PROJECT.lib.decoratorsからroute_actionをインポート

クラスSampleController(BaseController):
     @route_action( '/ sample / hello_world')                                                                                                                                                  
     defこんにちは(自己):
         return "これはサンプル/ Helloメソッドです"

     @route_action()                                                                                                                                                  
     def hello_to_me(自己):
         return "これはサンプル/ Hello_to_meローカルメソッドです"

     @route_action( '/ sample / hello_world / {id}')                                                                                                                                                  
     def hello(self、id):
         return "これはID /%dメソッドのサンプル/ Hello World"%id




このスキーム全体の動作原理は非常に簡単です。 デコレータは2つの追加のプロパティを作成し、そのうちの1つにはメソッドが含まれます。 2番目は、このメソッドの処理方法を正確に示しています。 引数が指定されていない場合、デコレータはメソッドがローカルであることのみを記録します。 パスを生成する必要があります。 そのようなプロパティがない場合(メソッドにデコレータがない場合)、メソッドはアクティブと見なされず、メソッドへの呼び出しはありません。



実際、プロジェクトを開始すると、接続したもののリストとこのメソッドへのパスがコンソールに表示されます。

リンクをたどって、すべてを正しく行った後、期待される結果が表示されるはずです。



書くことができるのはそれだけのようです。 ヒント、希望?



All Articles