Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Created November 1, 2011 23:40
Show Gist options
  • Save stevenharman/1332290 to your computer and use it in GitHub Desktop.
Save stevenharman/1332290 to your computer and use it in GitHub Desktop.
Proof of Concept: create multiple, identical, AR models from a single form.
<div>
<h1>Add a thing to your box</h1>
<%= form_for @thing, html: { class: "form-stacked" } do |f| %>
<label>Name: <%= f.collection_select :widget_id, @widgets, :id, :name,
{include_blank: true}, autofocus: true %></label>
<label>Blah: <%= f.text_field :blah %></label>
<label>Other stuff: <%= f.text_field :other %></label>
<label>Count: <%= text_field_tag :count, @thing_count %></label>
<%= f.submit "Add them!" %>
<% end %>
</div>
class ThingController < ApplicationController
def new
@thing = Thing.new
@thing_count = 1
@widgets = Widget.all
end
def create
@thing_count = params[:count].to_i
widget = Widget.find_by_id(params[:thing][:widget_id])
new_things = (0...@thing_count).collect do |i|
Thing.create(params[:thing]) { |t| t.widget = widget }
end
if new_things.all?(&:valid?)
redirect_to(things_path, notice: "Things with #{widget.name} have been added to the box!")
elsif
@thing = new_things.find(&:invalid?)
@widgets = Widget.all
flash.now[:alert] = "Oops! #{@thing.errors.full_messages.join(", ")}"
new_things.map { |t| t.delete }
render :new
end
end
end
@stevenharman
Copy link
Author

This is JUST a POC, but it can be done. Is there a more idiomatic, or Rails-y way of doing this?

My instinct is to throw this away and start again, test-driving from the outside. I'd like to push all of the crazy into an object, perhaps an AddsThingsToBox object, that can act in isolation, and delegate to that from this controller action. I'll still have to solve issues of Rails-specific stuff like validation methods, etc., but I think some sort of presenter might be nice for that.

Thoughts?

@stevenharman
Copy link
Author

What about the following API?

# things_controller.rb
def create
  @thing_count = params[:count].to_i
  adds_things = AddsThingsToBox.new
  adds_things.add(@thing_count, params[:thing])

  if adds_things.success?
    redirect_to(things_path, notice: "Things with #{adds_things.widget.name} have been added to the box!")
  else
    @thing = adds_things.invalid_thing
    @widgets = Widget.all
    flash.now[:alert] = "Oops! #{@thing.errors.full_messages.join(", ")}"

    render :new
  end
end

Where most of the gunk of knowing how many Things to create, how to build them, and how to roll them back, is pushed off into the AddsThingsToBox object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment