Created
February 4, 2012 18:38
-
-
Save duncanbeevers/1739387 to your computer and use it in GitHub Desktop.
Update Or Create
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# UpdateOrCreate makes it simple to to perform updating or creating models | |
# without a lot of boilerplate. | |
# | |
# It can be used in several forms. In its simplest form, it can be used with a | |
# single OrderedHash. | |
# | |
# User.update_or_create(username: 'Admin', admin: true) | |
# | |
# The first key/value pair from the provided hash is used to look up any existing record. | |
# In this case, it will look for a user with the username 'Admin' | |
# If one is found, it is updated with the entire set of provided attributes. | |
# If none is found, a new one is created with the entire set of provided attributes. | |
# | |
# If you need more granular control of what record is looked up, or if you have imperfect | |
# control over the order of the provided Hash, you can provide the find_by options | |
# explicitly and then provide the remaining options used to update or create the instance. | |
# | |
# # User 1 is going to be the Admin | |
# User.update_or_create({ id: 1 }, { username: 'Admin', admin: true }) | |
# | |
# Finally, if you need to provide more sophisticated handling of the instance and attributes | |
# used to update it you can provide a block that will be evaluated in the context of the found | |
# instance if one is found. The block will be passed the attributes you want to update the | |
# instance with. The block can then modify and return the hash of attributes you want to use to | |
# update the model. This is useful if you want to create models with a certain attribute but | |
# want to avoid re-setting in specific circumstances. | |
# | |
# File.open('spec/support/data/default_avatar.png') do |f| | |
# User.update_or_create(username: 'Admin', admin: true, avatar: f) do |attrs| | |
# if avatar.try(:url) | |
# attrs.except(:avatar) | |
# else | |
# attrs | |
# end | |
# end | |
# end | |
module UpdateOrCreate | |
def update_or_create find_by, updated_attributes = nil, &block | |
update_or_create_with_methods find_by, updated_attributes, block, :update_attributes, :create | |
end | |
def update_or_create! find_by, updated_attributes = nil, &block | |
update_or_create_with_methods find_by, updated_attributes, block, :update_attributes!, :create! | |
end | |
private | |
def update_or_create_with_methods find_by, updated_attributes, block, update_method, create_method | |
# If only a single hash of attributes was provided, extract find_by from it. | |
unless updated_attributes | |
# Promote find_by to updated_attributes | |
# Create find_by from first entry of updated_attributes | |
updated_attributes = find_by | |
find_by = Hash[*updated_attributes.first] | |
end | |
instance = where(find_by).first | |
if instance | |
if block | |
updated_attributes = block.bind(instance).call updated_attributes.dup | |
end | |
instance.send update_method, updated_attributes | |
else | |
instance = send create_method, updated_attributes.except(:id) | |
end | |
instance | |
end | |
end | |
ActiveRecord::Relation.send :include, UpdateOrCreate | |
ActiveRecord::Base.send :extend, UpdateOrCreate |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment