Skip to content

Instantly share code, notes, and snippets.

@dresselm
Created October 12, 2012 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dresselm/3879505 to your computer and use it in GitHub Desktop.
Save dresselm/3879505 to your computer and use it in GitHub Desktop.
<%= form_for(@person) do |f| %>
<fieldset>
<div class="left">
<%= f.label :first_name %><br/>
<%= f.text_field :first_name %>
</div>
<div>
<%= f.label :last_name %><br/>
<%= f.text_field :last_name %>
</div>
<div>
<%= f.label :email %><br/>
<%= f.text_field :email %>
</div>
<div>
<%= f.label :organisation_id %><br/>
<% # You need to customize this so that it displays the names, but sends option values equal to the ids %>
<% # Actually, this works pretty well already. I checked it multiple times. %>
<%= f.select(:organisation_id, current_user.organisation_names, :include_blank => "--- None ---") %>
</div>
</fieldset>
<%= f.fields_for :address do |address| %>
<%= render 'shared/address', :f => address %>
<% end %>
<%= f.submit %>
<% end %>
class Organisation < ActiveRecord::Base
has_many :people
has_one :address, :as => :addressable,
:dependent => :destroy
accepts_nested_attributes_for :address, :allow_destroy => true
end
class Person < ActiveRecord::Base
belongs_to :organisation
has_one :address, :as => :addressable,
:dependent => :destroy
# The form sends the address attributes as blank values when they are not entered
# Without the reject, the address model gets populated with blank values and then blows up during validation
# This line did the trick! The address fields get validated now only if an organisation is not set which is correct.
# I wonder if the lambda statement can simplified somehow to remove the unnecessary amount of duplication (attribute names) in there.
accepts_nested_attributes_for :address, :allow_destroy => true, :reject_if => :attributes_blank?
# Organization must be selected, unless Address details are added
validates_presence_of :organisation, :unless => "address.present?"
# The Address association must be validated, unless organization is selected
validates_associated :address, :unless => "organisation.present?"
# Helper to print out the full name for @title, etc
def name
"#{first_name} #{last_name}"
end
# A little hack, but DRYer than before and some good things to learn
# attrs -> the result of params[:person][:address]
# .except('id') -> return all key-values except for 'id'
# .values -> return all values from a hash as an array
# .all? -> do all elements in the array satisfy the following check
# &:blank -> shorthand for a block like this: all?{ |v| v.blank? }
def attributes_blank?(attrs)
attrs.except('id').values.all?(&:blank?)
end
end
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
# These validations are necessary for the validated_associated above
# If there are no validations, then missing organizations and address inputs will not trigger the proper validations
validates_presence_of :line1, :line2, :city, :zip
end
# I excluded all User relationships
class PeopleController < ApplicationController
before_filter :print_params
def index
@people = Person.all
end
def new
# Add default values if you want them displayed
@person = Person.new
# Necessary to build out the blank address fields
@person.build_address
@title = "New person"
end
def edit
@person = Person.find(params[:id])
# Build out blank address inputs if address is nil
@person.build_address if @person.address.nil?
@title = @person.name
end
def update
@person = Person.find(params[:id])
@title = @person.name
if @person.update_attributes(params[:person])
redirect_to people_path
else
render :edit
end
end
def create
# This is the beauty of accepts_nested_attributes
@person = current_user.people.build(params[:person])
if @person.save
flash[:success] = "Person created."
redirect_to people_path
else
@person.build_address if @person.address.nil?
render :new
end
end
end
# Once your models are updated, open the console via `rails c` and enter the following tests
# Note: I am using organization vs organisation - you might want to do a search/replace
# I made an assumption that Organization has at least a name attribute
organization = Organization.create(:name => 'Org1')
# Simple method that prints validity and errors
def print_validation(obj)
puts "valid?: #{obj.valid?}"
puts obj.errors.inspect
end
# nested attrs containing a full person, org reference and full address
full_person_attrs = {
:first_name => 'Harry',
:last_name => 'Smith',
:email => 'hsmith@gmail.com',
:organization_id => organization.id,
:address_attributes => {
:line1 => '122 N Lane St',
:line2 => 'Apt 35',
:city => 'Chicago',
:zip => '60625'
}
}
p = Person.create(full_person_attrs)
print_validation(p)
# nested attrs containing a full person and full address
person_address_attrs = {
:first_name => 'Harry',
:last_name => 'Smith',
:email => 'hsmith@gmail.com',
:address_attributes => {
:line1 => '122 N Lane St',
:line2 => 'Apt 35',
:city => 'Chicago',
:zip => '60625'
}
}
p2 = Person.create(person_address_attrs)
print_validation(p2)
# nested attrs containing a full person and org reference
person_organization_attrs = {
:first_name => 'Harry',
:last_name => 'Smith',
:email => 'hsmith@gmail.com',
:organization_id => organization.id
}
p3 = Person.create(person_organization_attrs)
print_validation(p3)
# nested attrs containing a full person, but no org or address
person_without_organization_or_address_attrs = {
:first_name => 'Harry',
:last_name => 'Smith',
:email => 'hsmith@gmail.com'
}
p4 = Person.create(person_without_organization_or_address_attrs)
print_validation(p4)
# nested attrs containing a full person and partial address
# I added presence_of validations in Address for all the attributes
person_with_partial_address_attrs = {
:first_name => 'Harry',
:last_name => 'Smith',
:email => 'hsmith@gmail.com',
:address_attributes => {
:line1 => '122 N Lane St',
:line2 => 'Apt 35'
}
}
p5 = Person.create(person_with_partial_address_attrs)
print_validation(p5)
@ranaharis-tkxel
Copy link

@dresselm how attrs is the result of params[:person][:address]?

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