Sometime I want all of Rails’ form bells and whistles for something that isn’t a database backed model. For example, I use this as a handy way to get form validations when starting a session with an API.

This is possible because Rails allows you to use ActiveModel without a database.

1
2
3
4
5
class APIConnect
  include ActiveModel::Model
  attr_accessor :api_key, :api_secret
  validates :api_key, :api_secret, presence: true
end

This creates a model that has standard Rails validations, responds to #validate? and #errors, but blows up if you try to call #save.

Given that model and a form like:

1
2
3
4
<%= form_for(@connection) do |f| %>
  <%= f.text_field :api_key, placeholder: "API Key" %>
  <%= f.text_field :api_secret, placeholder: "API Secret" %>
<% end %>

I can use this in a controller:

1
2
3
4
5
6
7
@connection = APIConnect.new(params[:api_connect])

if @connection.valid?
  # Do something
else
  flash.now[:error] = "Can't connect..."
end

If the key and secret are not supplied, @connection won’t be valid, and form_for will do all the usual error display magic.

I can make my model more useful by adding my connection logic inside it:

1
2
3
4
5
6
7
8
9
10
11
class APIConnect
  include ActiveModel::Model
  attr_accessor :api_key, :api_secret
  validates :api_key, :api_secret, presence: true

  def connect!
    return false unless self.valid?
    # Presume some API class... You'd store that authorization somewhere.
    API.authorize(self.api_key,self.api_secret)
  end
end

and change my controller thusly:

1
if @connection.connect!

You can do a whole lot more with ActiveModel::Model, for example implement a #save to write back to an API. But, that’s another show. For now, take a look at Active Model Basics.

Comments