DataMapper update_or_create

March 13, 2010 by pedro mota

Source code: git repository at github

DataMapper has a defined method named first_or_create, very explicit name, that takes a condition and attributes as params. Its source code defined in dm-core-1.0.2/lib/dm-core/model.rb is:

def first_or_create(conditions = {}, attributes = {})
  first(conditions) || create(conditions.merge(attributes))

What is does is simply find the first resource by conditions, or create a new resource with the attributes if none found. This always returns an instance of the resource found or created. It became handy to create an update_or_create method, so I created:

module DataMapper
  module Model
      def update_or_create(conditions = {}, attributes = {}, merger = true)
        (first(conditions) && first(conditions).update(attributes)) || create(merger ? (conditions.merge(attributes)) : attributes )
    end # Module Model
end # Module DataMapper

There are differences to consider, like the merger param and the returned TrueClass bool upon update (and not an instance of the resource). I found a merger param necessary because of the Serial type of properties. In the implementation of first_or_create method if we specify a Serial property as a search condition and the search has no success, then the conditions will be merged with the attributes and the Serial +1 will not be taken in account. Thus, in this implementation setting merger to false, search conditions will not be merged.


Article.update_or_create({:id => 10}, {:name => "Spirou meet Franquin"})
  • if Article with ‘id’ 10 exists then it will receive a new name
  • if Article with ‘id’ 10 doesn’t exist, it will be created with {:id => 10, :name => "Spirou meet Franquin"} => #[Article @id=10 @name=“Spirou meet Franquin” @date=#[Date: …] @updated_on=#[Date: …]]
Article.update_or_create({:id => 123}, {:name => "Fantasio meet Franquin"}, false)
  • if Article with ‘id’ 123 doesn’t exist, it will be created with {:name => “Fantasio meet Franquin”} and a self sequential id:=> #[Article @id=240 @name=\“Fantasio meet Franquin\” @date=#[Date: …] @updated_on=#[Date: …]]

(blogged from Vim)

EDIT: new implementation to always get a DataMapper Object:

© 2018 | Follow on Twitter | pedro mg & Hugo