RailsでDBと連携しないModelを作成する

## 概要
RailsのModelは、通常DBと結びついていないと使えないが、
データソースをDBではなく外部APIなどに変更し、
validation機能はそのまま使いたい。

ActiveModel::ModelをMixInしてvalidation機能を有効にし、
callbackでvalidateすることで実現する。

## 環境
- rails 5.0.0.1
- ruby 2.3.1


## ソースコード例
`ActiveModel::Model`をMixIn
これで通常のModelと同様にvalidate機能を利用できる。
自作validationも使えるようになる。

```rb:app/model/sample.rb
class Sample
  # MixIn
  include ActiveModel::Model

  attr_accessor :name, :email, :age

  # before_saveを使うための記述
  define_model_callbacks :save
  # callbackで実行するmethodを指定
  before_save :form_validation

  # validations
  validates :name, presence: true, strict: true
  # email: trueは自作validation
  validates :email, email: true, presence: true, strict: true
  validates :age, presence: true, strict: true

  # method例
  def all
    run_callbacks :save do
      'Your profile: #{name}, #{age}'
    end
  end

  # method例
  def find(id)
    run_callbacks :save do
      %Q(sample data: #{id})
    end
  end

  # callback用 validation
  private
  def form_validation
    self.valid?
    #unless self.valid?
    #  self.errors.add(:attribute, self.errors.full_messages, strict: true)
    #end
  end
end
```

rspec

```rb
RSpec.describe Sample, type: :model do
  describe 'Sample validation' do
    context 'with the all parameters' do
      subject { Sample.new(name: 'MyName', age: 17) }
      it "returns no error" do
        expect { subject.valid? }.not_to raise_error
      end
    end
    context 'without any parameter' do
      subject { Sample.new }
      it "returns StrictValidationFailed" do
        expect { subject.valid? }.to raise_error(ActiveModel::StrictValidationFailed)
      end
      it "returns StrictValidationFailed with message" do
        expect { subject.valid? }.to raise_error(ActiveModel::StrictValidationFailed, 'Name translation missing: ja.activemodel.errors.models.sample.attributes.name.blank')
      end
    end
    context 'with name without age' do
      subject { Sample.new(name: 'MyName') }
      it "returns StrictValidationFailed" do
        expect { subject.valid? }.to raise_error(ActiveModel::StrictValidationFailed)
      end
      it "returns StrictValidationFailed with message" do
        expect { subject.valid? }.to raise_error(ActiveModel::StrictValidationFailed, 'Age translation missing: ja.activemodel.errors.models.sample.attributes.age.blank')
      end
    end
  end
  describe 'Methods', type: :query do
    subject { Sample.new(name: 'MyName', age: 17) }
    describe 'all' do
      it 'returns text' do
        expect(subject.all).to eq('Your profile: MyName, 17')
      end
    end
  end
end
```

## 参考
- [Rails4 DB連携なしモデルでバリデーション機能を使う](http://qiita.com/metheglin/items/1cb602ae8bd18c0ef0f0)
- [[Rails 4.0] ActiveRecordのコールバックが呼ばれる順番まとめ](https://techracho.bpsinc.jp/baba/2013_08_23/12670)
- [ActiveRecordのコールバックの順序・コールバック内のロールバック処理について](http://blog.toshimaru.net/active-record-callbacks/)
- [Common Validation Options/Strict Validation](https://kinjouj.github.io/2014/09/activerecord-validation-5-common-validation-options-and-strict-validation.html)
- [Validate Ruby objects with Active Model Validations](https://www.twilio.com/blog/2017/06/validate-ruby-objects-with-active-model-validations.html)