3

私は次のものを持っています(VenueはActorのCTIの子孫です):

class Actor < ActiveRecord::Base
  has_one :profile, validate: true, autosave: true
end

class Venue < ActiveRecord::Base
...
  %w{description address website phone}.each do |attr|
    delegate attr.to_sym, "#{attr}=".to_sym, to: :profile!
  end

  def profile!
    actor.profile || actor.build_profile
  end
...
end

これらのデリゲート属性のフィールドを直接会場フォームに含めています。これらの属性の1つが検証に失敗した場合、表示されるのは一番上の通知だけですが、フィールドのラッパーは表示されません。:"actor.profile.website"これは、Venueインスタンスのエラーハッシュのキーが属性名と完全に一致せず、単に。ではなくに設定されているためだと思います:website

これらのエラーを正しく表示する方法はありますか?

編集

フォームは次のとおりです。

<%= simple_form_for @venue do |f| %>
  <%= f.error_notification %>

  <%= f.input :name %>
  <%= f.input :address, required: true %>
  <%= f.input :phone %>
  <%= f.input :website %>
  <%= f.input :members, collection: [], class: "form_tag" %>
  <%= f.input :tag_list, as: :string, class: "form_tag", hint: t("misc.hint.tag"),
              input_html: { "data-pre" => @venue.tag_list.map {|t| { id: t, name: t }}.to_json } %>
  <%= f.input :description, as: :text, input_html: {rows: 6, cols: 53, class: "form_tag"} %>

  <div class="actions center">
<%= f.submit class: "btn btn-success" %>

4

4 に答える 4

2
module OtherValidation
  extend ActiveSupport::Concern

  module ClassMethods
    def delegate_with_validations(*attr_names)
      options = attr_names.extract_options!
      delegate *attr_names, options
      attr_names.each {|a| validate_using(options[:to], a)}
    end      

    def validate_using(target, *args)
      options = args.extract_options!
      args.each do |attr_name|
        class_eval <<-EOV
          dup = #{target}._validators[:#{attr_name}].dup
          validate do
            dup.each do |v|
              validator = v.dup
              validator.attributes.delete(:#{attr_name})
              validator.attributes << :#{options[:to]||attr_name}
              validator.validate(self)
            end
          end
        EOV
      end
    end
  end

end

現在、会場モデル:

class Venue < ActiveRecord::Base
  include OtherValidation
  delegate_with_validations :website, :to => :profile!
end

# venue = Venue.new(:website => nil)
# venue.valid? # Profile validates presence of :website
#=> false
# venue.errors
#=> #<ActiveModel::Errors....., @messages={:website=>["can't be blank"]}>

更新しました:

カスタム属性の場合:

class Venue < ActiveRecord::Base
  include OtherValidation
  attr_accessor: title

  validate_using("Profile", :website, :to => :title)
end

# :website validation behavior constraints to :title attribute

# venue = Venue.new(:title => nil)
# venue.valid? # Profile validates presence of :website
#=> false
# venue.errors
#=> #<ActiveModel::Errors....., @messages={:title=>["can't be blank"]}>

config / initializers / other_delegation.rb

module OtherValidation
  ...
end

ActiveSupport.on_load :active_record do
  include OtherValidation
end
于 2012-12-23T16:03:32.563 に答える
2

それは正しい。そして、修正する方法は、次のようなものを使用することです:

class Venue < ActiveRecord::Base
...
  after_validation do
    if errors.any?
      errors.messages.keys.each do |key|
        errors.messages[key.to_s.gsub(/actor.profile./, "").to_sym] = errors.messages.delete(key)
      end
    end
  end
...
end

更新しました:

HOWTO コンテンツを div class="field_with_error" でラップするだけ

注意: ベース オブジェクトにエラーがあり、エラーに属性名 (実際にはメソッド名)と等しい適切なキーがある場合にのみ、Rails はフィールドをラップします。ネストされた関連付けられた属性の場合、関連付けられたシーケンスに従ってプレフィックス付きのキー (actor.profile.website) を使用します。

ちなみに、一般的な方法:

<%= field_error_proc.call(content) %>

# where content is any string/symbol stuff.

エラー処理のトリガー:

<%= form_for... do |f| %>
  <% website_field = capture do %>
    <%= f.text_field :website %>
  <% end %>

  <% if f.object.errors[:"actor.profile.website"] %>
    <%= website_field %>
  <% else %>
    <%= field_error_proc.call(website_field) %>
  <% end %>
<% end %>

少し面倒ですよね?Rails 固有のラッピング メカニズムを使用することをお勧めします。

次の回答を参照してください。

于 2012-12-22T23:47:25.280 に答える
1

あなたは私の宝石でこのようなものを避けることができます:

gem 'delegate_attributes'

class Blog < ActiveRecord::Base
  delegate_attributes :theme, :errors => :fit, :writer => true, :to => :category
end

オプション:errors => :fitは、エラーメッセージのi18n変換を次のように定義できるようになったことを宣言します。

en:
  activerecord:
    errors:
      models:
        blog:
          attributes:
            theme:
              blank: "Can not be blank"

オプション:writer => trueはライターメソッドを委任します:.theme=

于 2013-01-14T19:33:42.567 に答える
0

Valeryの答えは私の場合は機能していますが、プレフィックス付きのデリゲートでは実際には機能しません。また、エラーのあるフォーム フィールドの HTML 生成のみに影響するように、ソリューションを分離する方法を探していました。これで終わりました(おそらく最もクリーンなコードではありませんが、仕事をしているようです):

initializers/delegate.rb

module MappedDelegation
  extend ActiveSupport::Concern

  included do
    cattr_reader :delegation_mappings
    @@delegation_mappings ||= {}

    def self.delegate_with_mappings(*methods)
      delegate_without_mappings(*methods)

      options = methods.pop
      prefix, to = options.values_at(:prefix, :to)
      method_prefix = \
        if prefix
          "#{prefix == true ? to : prefix}_"
        else
          ''
        end

      methods.each do |method|
        @@delegation_mappings["#{method_prefix}#{method}"] = to
      end
    end

    self.class_eval do
      class << self
        alias_method_chain :delegate, :mappings
      end
    end
  end
end

initializers/simple_form_delegate_errors.rb

module SimpleForm
  module Components
    module Errors
      def errors
        @errors ||= (errors_on_attribute + errors_on_delegate + errors_on_association).compact
      end

      def errors_on_delegate
        delegated_to = ( object.class.respond_to?(:delegation_mappings) ? object.class.delegation_mappings : {} )[attribute_name.to_s]
        delegated_to ? object.send(delegated_to).errors[attribute_name] : []
      end
    end
  end
end

app/models/venue.rb

class Venue < ActiveRecord::Base
  include MappedDelegation
  ...
于 2012-12-23T13:20:40.923 に答える