4

今日、いくつかのコードを DRY しようとして、いくつかの重複する File.exists を抽出しましたか? いくつかのヘルパー メソッドによってプライベート メソッドに使用されるコード

def template_exists?(*template)そして誤ってサルは、この既存の Rails ヘルパー メソッドにパッチを適用しました。これは、コードのにおいを示す非常に明確な指標でした。継承されたメソッドは必要ありませんでしたが、継承しました。その上、このヘルパーでリファクタリングされたメソッドは一体何をしていたのでしょうか?

したがって、このヘルパーはやりすぎであり、SRP (Single Responsibility Principle) に違反しています。Rails ヘルパーは本質的に SRP 内に保持するのが難しいと感じています。私が見ているヘルパーは、他のヘルパーのスーパークラス ヘルパーであり、300 行以上を数えます。これは、対話フローをマスターするために JavaScript を使用した非常に複雑なフォームの一部です。fat ヘルパーのメソッドは短くてきちんとしているので、それほどひどいものではありませんが、懸念の分離が必要であることは間違いありません。

どのように出ればいいですか?

  1. メソッドを多くのヘルパーに分割しますか?
  2. ヘルパー メソッド内のコードをクラスに抽出し、それらにデリゲートしますか? これらのクラス (Mydomain::TemplateFinder など) の範囲を指定しますか?
  3. ロジックをモジュールに分割し、上部にインクルードとしてリストしますか?
  4. 他のアプローチ?

私が見るように、偶発的なモンキーパッチに関しては、2 の方が安全です。もしかして組み合わせ?

コード例と強い意見を歓迎します!

4

6 に答える 6

4

ヘルパー メソッドをクラスに抽出する (ソリューション n°2)

ヘルパーをクリーンアップするこの特定の方法に対処することはできます 1st 私はこの古い railscast を掘り起こしました:

http://railscasts.com/episodes/101-refactoring-out-helper-object

当時、小さなタブ システムを作成するきっかけになりました (私のアプリの 1 つでステート マシンと連携して動作します)。

module WorkflowHelper

  # takes the block  
  def workflow_for(model, opts={}, &block)
    yield Workflow.new(model, opts[:match], self)
    return false
  end

  class Workflow
    def initialize(model, current_url, view)
      @view = view
      @current_url = current_url
      @model = model
      @links = []
    end

    def link_to(text, url, opts = {})
      @links << url
      url = @model.new_record? ? "" : @view.url_for(url)
      @view.raw "<li class='#{active_class(url)}'>#{@view.link_to(text, url)}</li>"
    end

  private
    def active_class(url)
      'active' if @current_url.gsub(/(edit|new)/, "") == url.gsub(/(edit|new)/, "") ||
                 ( @model.new_record? && @links.size == 1 )
    end

  end #class Workflow
end

そして私の見解は次のようになります:

  -workflow_for @order, :match => request.path do |w|
    = w.link_to "✎ Create/Edit an Order", [:edit, :admin, @order]
    = w.link_to "√ Decide for Approval/Price", [:approve, :admin, @order]
    = w.link_to "✉ Notify User of Approval/Price", [:email, :admin, @order]
    = w.link_to "€ Create/Edit Order's Invoice", [:edit, :admin, @order, :invoice] 

ご覧のとおり、これはロジックをクラスにカプセル化し、ヘルパー/ビュー スペースにメソッドを 1 つだけ持つ良い方法です。

于 2011-08-25T17:10:56.667 に答える
2

わかりました、これは答えるのが本当に難しい質問です。Rails は、ビュー ヘルパーの道をたどっていくようなものであり、それを超えたときにまともな組み込みの代替手段を提供するものではありません。

ヘルパーがビュー オブジェクトに含まれる単なるモジュールであるという事実は、懸念の分離と結合には実際には役立ちません。そのロジックをモジュールから完全に取り除き、独自のクラスのホームにする方法を見つける必要があります。

まず、Presenter パターンを読んで、ビューとモデルの間の中間層としてどのように適用できるかを考えてみます。ビューをできるだけ単純化し、ロジックをプレゼンターまたはモデルに移動します。JavaScript をビューの外に移動し、代わりに目立たない JavaScript を .js ファイルに記述して、既存の JavaScript の機能を強化します。必ずしもビューにある必要はありません。ビューに js を詰め込むことがこの混乱に陥った場合、それは多くのクリーンアップに役立つことがわかります。

読むためのリンクを次に示します。

http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

レールのプレゼンターパターンについて。それを行うより良い方法はありますか?

http://blog.jayfields.com/2007/01/another-rails-presenter-example.html

http://blog.jayfields.com/2007/09/railsconf-europe-07-presenter-links.html

特定のコード例にとらわれすぎず、パターンが達成しようとしていることを理解し、それを特定の問題にどのように適用できるかを考えてみてください。(ただし、上記の2番目のリンクの例、スタックオーバーフローの例が本当に好きです)。

それは役に立ちますか?

于 2011-08-26T06:09:39.957 に答える
2
  1. 大きなヘルパーを小さなヘルパーに分けるということですか? なぜだめですか。私はあなたのコードをよく知りませんが、大きなコード チャンクを ./lib にアウトソーシングすることを検討することをお勧めします。
  2. いいえ ;-) これは非常に複雑に聞こえます。
  3. 複雑に聞こえます。1. と同じ提案: ./lib。そこにあるモジュールは、アクセスすると自動ロードされます。
  4. いいえ

私の提案は次のとおりです。あまりにも多くのカスタム構造を使用するのをためらってください。大規模なヘルパーがいる場合は、そうかもしれません。そのヘルパーコード全体がコントローラーにない理由の説明があるのだろうかと思いますが。テンプレート内で使用される小さくて単純なメソッドにはヘルパーを使用します。複雑な (Ruby-) ロジックはコントローラーに配置する必要があります。本当に複雑な Javascript アプリを持っているなら、この複雑なものを Javascript で書いてみませんか? 本当にテンプレートから呼び出す必要がある場合は、これが適しています。そしておそらく、あなたのウェブサイトをもう少しダイナミックで柔軟なものにしてくれます。

モンキー パッチと名前空間の競合について: ありふれたクラス名、メソッド名などがある場合は、それらが定義されているかどうかを確認してください。それらのためのGoogle、grepまたはrailsコンソール。

どのコードがどのコードに属しているかを理解していることを確認してください

  • コントローラー: 変数にデータを入力し、ユーザー アクションを実行します (基本的には、ページの背後で計算を行います)。
  • ヘルパー: 派手なハイパーリンクの作成などの簡単なことを行うのに役立ちます

    def my_awesome_hyperlink url, text "Fancy Link to #{text}" end

  • ./lib: 複数のコントローラーによって使用される、および/または Cucumber ステップ定義などの他のコンポーネントによって直接使用される、より複雑なもの

  • Rubyコードとしてのテンプレート内:非常に読みやすい
  • テンプレート (または ./public) 内に Javascript コードとして: 好みの問題。しかし、アプリが動的であればあるほど、コードがここに属している可能性が高くなります。
于 2011-08-23T16:43:07.060 に答える
1

コードなしで明確な解決策を提案することは困難です。ただし、ヘルパー メソッドはすべて同じグローバル ビュー インスタンスに存在するため、名前の衝突はよくある問題です。

@Slawosz は解決策かもしれませんが、ヘルパーの哲学にはあまり適していません。

個人的には、cells gemを使用することをお勧めします。cells は、軽量、高速、キャッシュ可能、テスト可能であることを除けば、レールのコンポーネントのようなものです。

また、特定の問題に答えるために、それらは完全に分離されています。ビューヘルパーが複雑になると、それらは間違いなく解決策になります。

(開示私はこの宝石の作成者ではなく、ただ喜んで使用しています....)

# some view
= render_cell :cart, :display, :user => @current_user

# cells/cart_cell.rb
# DO whatever you like in here
class CartCell < Cell::Rails

  include SomeGenericHelper

  def display(args)
    user    = args[:user]
    @items  = user.items_in_cart

    render  # renders display.html.haml
  end
end

また、名前の衝突を恐れることなく、ここで乾燥のために汎用ヘルパーを使用できます。

于 2011-08-22T12:25:49.600 に答える
0

私はあなたのフォームを操作する責任がある小さなライブラリを作成します.いくつかの適切な名前のクラス、いくつかの継承、および入力としてieパラメータを渡し、出力としてieを持つことができます. このフォームで使用されるパーシャルのオブジェクト。すべてがカプセル化され、テストが容易になります。

AbstractControllerコードを見て 、次に Metal を見て、レールがいかにスマートに設計されているかを確認してください。問題を解決するためのインスピレーションが見つかるかもしれません。

于 2011-08-22T11:15:22.613 に答える
-1

あなたのアプローチは正しいですが、3番目のポイント、つまりロジックをモジュールに分離することをお勧めします。

モジュールは、特に複数のクラス間でコードを共有する場合に多くの可能性を開きます。これは、任意の数のクラスを同じモジュールに混在させることができるためです。

モジュールの最大の強みは、プログラムの設計と柔軟性に役立つことです。

このようなアプローチを使用すると、設計パターンを実現できます。

于 2011-08-16T15:29:51.537 に答える