Q:

Rails - Creating many models in one form - in controller

I have a small question - how can I create many models and save it to a database throught one form in Rails 3.
I mean - I have a form which creates codes - which generate random - their content, string.
In controller:
@code = Code.new(params[:code])
characters = [('a'..'z'), ('0'..'9'), ('A'..'Z')].map { |i| i.to_a }.flatten
@code.code_value = @code.code_letters + (0...8).map { characters[rand(characters.length)] }.join

Now I want to create codes many times clicking once, for example, I have an input: code_quantity, :integer then I choose quantity to 1000, and then when I click submit, in controller - create action should create it 1 * code_quantity times but it should create unique every single code like:
1000.times { @code = Code.new(params[:code]) }

:code_value has a validation of uniqueness.
How to do it in controller action, is it possible?

A:

Firstly, the concerns - Generating a uniq random code does not seem like anything controller should be concerned with. This is strictly model related behaviour, so let's refactor it a little and move it into a model:
class Code < ActiveRecord::Base
  CODE_CHARS = ['a'..'z', '0'..'9', 'A'..'Z'].flat_map &:to_a

  after_initialize do
    generate_code unless code_value.present?
  end

  def self.generate_code
    code_letters + (0..8).map { CODE_CHARS.sample }.join
  end

  def generate_code
    self.code_value = self.class.generate_code
  end

end

Now it would be tempting to do the following in your controller:
@codes = Array.new(params[:code_quantity]) { Code.new(params[:code]) }
@codes.each &:save

It is not so easy though - it might happen that some of your models will break uniqueness validation and will not be saved as a result (quite unlikely, but there are only 218340105584896 unique combinations for 8 characters). To fix that you need:
1) Add uniqueness constraint to the database if you haven't already (uniqueness validation does not ensure the uniqueness of record - concurrency is a ...):
# New migration
def change
  add_index :codes, :code_value, unique: true
end

2) Override save! method:
class Code 
  ...
  def save!(*args)
    tries = 0;
    begin
      super
    rescue
      tries += 1
      raise if errors.any? || tries > 10
      generate_code
      retry
    end
  end

Now every time you tries to save the record and it will fail for any other reason than validation, it will try to regenerate the code 10 times before giving up. 

