2012-02-08 21 views
9

主な目標:プラグインを許可する、宝石はフォームフィールドを追加することができます。フォーム宣言ブロックの外に余分なフォームフィールドを追加する方法

たとえば、アプリケーションは工夫のログインフォームを持っています(:プロモーションコードを登録し、Xのボーナスポイントを取得する例)

<%= form_for(resource, :as => resource_name, ...) do |f| %> 
    <%= devise_error_messages! %> 
    ... 
<% end %>

マーケティング部門は、次の2日間のキャンペーンを開始したいです。したがって、すべての登録フォームにpromo codeフィールドを追加する必要があります。

私のrails-plugin/railtieから余分なフィールドをフォームに追加し、on_submitコールバックメソッドを定義する方法がありますか?

利点:

  • それは単に宝石のファイルからそれを除去することにより、2日または週での機能性を取り除くことができますコアサイトの機能が壊れていないことを
  • 保証は、それは単にに戻って を落下
  • 元の機能
  • メインアプリのどこかにコードが残っていないことを保証する
  • プラグイン/レールティは、それに属するセービング/アップデートデータを処理します

ActionViewコードを見ると、それを行う方法が組み込まれていないようです。あなたの考えは?

注:Drupalのform_alterフックは素晴らしい例です。ステップにおける

答えて

1

アイデア:

1)を同様に関数を作成するようAdditionalField(ID、FIELD_NAME、field_type、DEFAULT_VALUE、is_required)

2)などのモデルを定義し:

def self.for_form(my_form_name = nil) 
if my_form_name.nil? 
    self.all 
else 
    self.find(:all, :contitions => {:form_type => my_form_name.type} # or whatever selection criteria 
end 

3)見つかったAdditionalFieldsを繰り返し処理し、必要に応じて正しいフィールドタイプを作成することができます。

私はこのソリューションを比較サイトで使用しました。ここでは、それぞれ異なる比較タイプのアンケートを設定する必要がありました。

私が使用したレンダリングコードは、あなたの状況に合わせて修正する必要があります。 関係です:

convention -<booking>- user 
convention -< convention_question 
booking -< guests 
guest -< guest_answers 

QuestionsHelper

def render_guest_questions(guest, convention_question)  

    fields_for "booking[guest_answer_attributes][]", convention_question do |m| 
     case convention_question.display_type 
     when "Text" 
     '<td>' + text_field_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]") + '</td>' 
     when "Boolean" 
     '<td>' + hidden_field_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]", "No") + check_box_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]", "Yes") + '</td>' 
     end 
    end 

end 

コントローラ

# TURN GUEST/QUESTIONS INTO guest answers 
if params[:booking] && !params[:booking].blank? && !params[:booking][:guest_answer_attributes].blank? 
    params[:booking][:guest_answer_attributes].each do |k,v| 
     handle_answers(k, v) 
    end 
end 

def handle_answers(k, v) 
    x = k.mb_chars.split(/_/) 
    g_id = x[2] 
    q_id = x[3] 
    item = GuestAnswer.find_or_create_by_guest_id_and_convention_question_id(
        {:guest_id => g_id, 
        :convention_question_id => q_id, 
        :answer => v}) 
end 
+0

提出されたデータをどのように保存しましたか? – Uzbekjon

+0

user_answersテーブルを作成して保存しました。 'belongs_to:user;を持つ' UserAnswer' belongs_to AdditionalField; 'user_id、additional_field_id、answer_value'のフィールド – TomDunning

+0

uzbekjon - 私が行ったプロジェクトに使用したコードを追加しました。それが意味をなさない場合は、もっとお気軽にお問い合わせください。 – TomDunning

2

まず、GEM/railtie /エンジンにこのコードを分離するためにあなたのアイデアが優れています。あなたのベスト・ベットは、form_forメソッドに猿を貼り付け、余分なフィールドに固執することです。あなたがRails 3を利用している場合は、送信時のトリガについて。1、アセットパイプラインを使用している場合は、gemのjsファイルを要求するために、application.jsの小さな変更が必要ですが、宝石にもjavascriptを提供させることができます。 require 'promo/application.js、宝石が「プロモーション」と呼ばれた場合。

Customized Form Builder

のためのドキュメントを見て、私はこのコードを試していないが、ここでは、これは仕事ができる方法をいくつかの大まかなアイデアです。私はサブクラスRailtieまたはEngineというpromo.rbファイルにこれを入れます。

ActiveSupport.on_load(:action_view) do 

    module ActionView 
    module Helpers 
     module FormHelper 
     extend ActiveSupport::Concern 

     included do 
      alias_method_chain :form_for, :promo_code 
     end 

     module InstanceMethods 

      def form_for_with_promo_code(record, options = {}, &proc) 
      output = form_for_without_promo_code(record, options.merge(builder: FormBuilderWithPromoCode), proc) 
      # See file: actionpack-3.1.3/lib/action_view/helpers/form_helper.rb for details 
      # the output will have "</form>" as the last thing, strip that off here and inject your input field 
      promo_field = content_tag :input, name: 'promo_code' # you can also run this through the proc if you want access to the object 
      output.sub(%r{</form>$},promo_field+'</form>') 
      end 
     end 
     end 
    end 
    end 
end 

down the road、esp。あなたのマーケティング部門がさらに多くのキャンペーンを運営している場合でも、アプリのフォームを変更して、ここで猿のパッチを当てることなく宝石からオーバーライドできる特定のビルダーを指し示すことさえできます。

+0

"Customized Form Builder"の方向を指してくれてありがとう。あなたの答えは私が望むものに最も近いものです。私は隠しフィールド( "_method"に似ています)を追加し、そのフィールドをチェックし、エンジン/プラグインの追加メソッドをトリガーするラックミドルウェアを追加するカスタムフォームビルダーを作成します。 – Uzbekjon

+0

Railsコードの深いところでは、 'authenticity_token'タグを追加する' extra_tags_for_form'メソッドが見つかりました。プルリクエストを送信して、他のメソッドがタグを追加できるようにします。しかし、私はこれが自分よりもスマートな人たちによって既に行われていたはずだと感じました。 すべてのフォームにタグを追加する方法はありますか( 'protect_from_forgery'のやり方)? – Uzbekjon

+0

@Uzbekjon私は同じ考えを持っていましたが、私はそれのためのフックを見つけることができませんでした。私はこれを行うためにfields_forをハックすることを考えていましたが、これが副作用を持たないことは明らかではありませんでした。私はあなたが驚いているように、これはまだ行われていません。私は 'simple_form'コードもチェックしましたが、ネイティブフォームヘルパーをラップし、独自のAPIを設定しました。 –

0

この場合には、新たな宝石がしますが作成されなければならない -

  1. (単にペーストビューのフォームをコピーして、プロモーションを追加し、ユーザモデルにpromo_codeを追加し、プロモーションコードの入力を組み込むためにそれにアクセス
  2. オーバーライド工夫フォームを作りますフィールド)
  3. は、ユーザーモデル(ユーザークラスのメタプログラミング)

の内側に我々はDではありません必要な検証に&余分な処理(コールバック)を追加します。主なアプリケーション内の変更があるので、安全です。

+0

はい、この方法は、「変更」が1つしかない場合に効果的です。しかし、フォームを変更したいプラグインが複数ある場合はどうでしょうか?メソッドは、開発者が現在フォームを変更しているすべての宝石とプラグインを追跡する必要があることを意味します。 – Uzbekjon

+0

外部のエンジンのコースの軌跡とそれが何をしているのかは、それ以上の変更を加えながら維持する必要があります。 –

関連する問題