YAMLをDRYに書く

(1)共通化したい情報に「&任意の名前」というフォーマットで名前を付ける。以下の例では&addressがこれに相当。

(2)共通化した情報を使用するには「<

address: &address
  zip_code: 郵便番号
  prefecture: 都道府県
  city: 市区町村
  street: 番地
  building: 建物

person:
  <<: *address

company:
  <<: *address

非ActiveRecordの検証エラーメッセージをローカライズする

ActiveRecordを継承しないクラスに対し、バリデーションとそのエラーメッセージをローカライズする方法。


(1)バリデーション機能を持つクラスの実装
ポイントは冒頭のmixin。これによりActiveRecordのような振る舞いが可能となり、validatesメソッドが使えるようになる。
また、後述のform_forヘルパーにこのクラスのインスタンスを渡すとpersisted?メソッドが呼び出されるので、persisted?の定義も必要。

class Import::CSV
  include ActiveModel::Validations
  include ActiveModel::Conversion
  
  attr_accessor :file
  
  validates :file, :presence => true
  def persisted?; false; end
end

(2)ビューの実装
form_forの引数@csvは(1)のインスタンス。ポイントは特になく、いたって普通にActiveRecordのフォームを実装する感じで実装する。

<%= form_for(@csv, :url => import_csv_path, :multipart => true) do |f| %>
  <% if @csv.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@csv.errors.count, "error") %> prohibited this csv from being saved:</h2>

      <ul>
      <% @csv.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  
  <div class="field">
    <%= f.label :file %>
    <%= f.file_field :file %>
  </div>
    
  <div class="actions">
    <%= f.submit 'confirm' %>
  </div>
<% end %>

(3)ロケールファイルの実装
ポイントはActiveRecordではなく、ActiveModelをmixinしたクラスなのでキーはactivemodel配下に設定すること。それ以外はActiveRecordと同様。
尚、本題から逸れるけどネームスペースを持つクラスは以下のようにスラッシュ(Import::CSV => import/csv)で表す。

ja:
  activemodel:
    attributes:
      import/csv:
        file: CSVファイル
    errors:
      messages:
        blank: "を入力してください。"

以上

自己解決 インポートにRESTなURL

(前日の日記の続き)


HTTPメソッドで単数、複数を表せないのならリソースで表す他ない。
ということで


インポート開始画面
GET /import/customers/new


インポート実行
POST /import/customers


良い感じだと思うな。

インポートにRESTなURL

Railsで何かしらのリソースをインポートする際のURLはどのようにしたらRESTになるのか?それがわからないという話。


<顧客情報のインポートを仮定した場合>
リソースは何か?→顧客
操作は何か?→リソースの作成
そのURLは?→POST /customers

これだと単一リソースを作成する場合と変わらないのでボツ。


そこでインポートを名詞として解釈してみる


リソースは何か?→顧客のインポート???
操作は何か?→顧客のインポートを作成???

リソースの時点でおかしい。作成するのは顧客のインポートではなくて顧客なのでボツ。


つまるところ問題はURLとそのHTTPメソッドを決定するパラメータが「リソース」と「操作」の2つしかなく、「操作」で表されるリソースの作成はそれによって作成されるリソース数に区別がないことなんじゃないかな?


という訳で冒頭の結論に到る。わからない。
どなたか解をご存知の方、助言頂けると幸いです。

同じアソシエーションを複数回定義するとaccepts_nested_attributes_forで既存レコードの保存ができなくなる

# ex
class Diary < ActiveRecord::Base
  has_many :comments
  has_many :comments
  accepts_nested_attributes_for :comments

このようにするとDiaryインスタンスからcommentsアソシエーションをまとめて保存することができなくなる。
保存できないのは既存commentsで新たに追加したcommentsは保存される。

accepts_nested_attributes_forで子から親もまとめて作成できる

ポイントは2つ
①子のモデルにaccepts_nested_attributes_forを定義

class Child
  belongs_to :parent
  accepts_nested_attributes_for :parent
end

②fields_forで親のフィールドオブジェクトを生成する際に親のフィールドのname属性を自力で指定する。
親から子の場合、自動で語尾に_attributesと付くが、子から親の場合何故か付かないので自力で_attributesを指定する。

<%= f.fields_for 'parent_attributes', @child.parent do |parent_f| %>
<% end %>