h2. dm-accepts_nested_attributes A DataMapper plugin that allows nested model attribute assignment like activerecord does. At the end of this file, you can see a list of all current integration specs. For more information on the progress, have a look at this README and also at "this article":http://sick.snusnu.info/2009/04/08/dm-accepts_nested_attributes/ on my blog, where I will try to comment on the development (problems). h3. Why isn't this implemented as options on association declarations? * I somehow like the declarative style of @accepts_nested_attributes_for@ better. it jumps out immediately. * The API for datamapper and activerecord is the same. * association definitions can already get quite long and "unreadable". chances are you overlook it! h3. Why doesn't accepts_nested_attributes_for take more than one association_name? While writing the unit specs for this method, I realised that there are way too many ways to call this method, which makes it "hard" to spec all possible calls. That's why I started to list Pros and Cons, and decided to support only one @association_name@ per call, at least for now. h4. Pros * less complex code * fewer ways to call the method (simpler to understand, easier to spec) * easier to read (nr of calls == nr of accessible associations, this could be seen as a con also) * easier (and more extensible) option handling ** options don't implicitly apply to _all_ associations (could be seen as a con also?) ** options can explicitly be applied to _only the desired_ associations ** reject_if option maybe often makes more sense on exactly _one_ associaton (maybe not?) * no question what happens if the association_name is invalid (the whole call is invalid) ** with at least one _invalid_ association_name, what happens for the other _valid_ ones? h4. Cons * needs more method calls (overhead should be minimal) * options that apply to more than one attribute need to be duplicated (maybe a Pro because of readability) h3. Examples The following example illustrates the use of this plugin.
require "rubygems"
gem 'dm-core', '0.9.11'
gem 'dm-validations', '0.9.11'
gem 'dm-accepts_nested_attributes', '0.0.1'
require "dm-core"
require "dm-validations"
require "dm-accepts_nested_attributes"
DataMapper::Logger.new(STDOUT, :debug)
DataMapper.setup(:default, 'sqlite3::memory:')
class Person
include DataMapper::Resource
property :id, Serial
property :name, String
has 1, :profile
has n, :project_memberships
has n, :projects, :through => :project_memberships
accepts_nested_attributes_for :profile
accepts_nested_attributes_for :projects
# adds the following instance methods
# #profile_attributes
# #projects_attributes
end
class Profile
include DataMapper::Resource
property :id, Serial
property :person_id, Integer
belongs_to :person
accepts_nested_attributes_for :person
# adds the following instance methods
# #person_attributes
end
class Project
include DataMapper::Resource
property :id, Serial
has n, :tasks
has n, :project_memberships
has n, :people, :through => :project_memberships
accepts_nested_attributes_for :tasks
accepts_nested_attributes_for :people
# adds the following instance methods
# #tasks_attributes
# #people_attributes
end
class ProjectMembership
include DataMapper::Resource
property :id, Serial
property :person_id, Integer
property :project_id, Integer
belongs_to :person
belongs_to :project
# nothing added here
# code only listed to provide complete example env
end
class Task
include DataMapper::Resource
property :id, Serial
property :project_id, Integer
belongs_to :project
# nothing added here
# code only listed to provide complete example env
end
DataMapper.auto_migrate!
h2. Current Integration Specs
DataMapper::NestedAttributes Profile.belongs_to(:person) accepts_nested_attributes_for(:person)
- should allow to create a new person via Profile#person_attributes
- should allow to update an existing person via Profile#person_attributes
- should not allow to delete an existing person via Profile#person_attributes
DataMapper::NestedAttributes Profile.belongs_to(:person) accepts_nested_attributes_for(:person, :allow_destroy => false)
- should allow to create a new person via Profile#person_attributes
- should allow to update an existing person via Profile#person_attributes
- should not allow to delete an existing person via Profile#person_attributes
DataMapper::NestedAttributes Profile.belongs_to(:person) accepts_nested_attributes_for(:person, :allow_destroy = true)
- should allow to create a new person via Profile#person_attributes
- should allow to update an existing person via Profile#person_attributes
- should allow to delete an existing person via Profile#person_attributes
DataMapper::NestedAttributes Profile.belongs_to(:person) accepts_nested_attributes_for :person, :reject_if => :foo
- should allow to create a new person via Profile#person_attributes
- should allow to update an existing person via Profile#person_attributes
- should not allow to delete an existing person via Profile#person_attributes
DataMapper::NestedAttributes Profile.belongs_to(:person) accepts_nested_attributes_for :person, :reject_if => lambda { |attrs| true }
- should not allow to create a new person via Profile#person_attributes
- should not allow to delete an existing person via Profile#person_attributes
DataMapper::NestedAttributes Profile.belongs_to(:person) accepts_nested_attributes_for :person, :reject_if => lambda { |attrs| false }
- should allow to create a new person via Profile#person_attributes
- should allow to update an existing person via Profile#person_attributes
- should not allow to delete an existing person via Profile#person_attributes
DataMapper::NestedAttributes Person.has(1, :profile) accepts_nested_attributes_for(:profile)
- should allow to create a new profile via Person#profile_attributes
- should allow to update an existing profile via Person#profile_attributes
- should not allow to delete an existing profile via Person#profile_attributes
DataMapper::NestedAttributes Person.has(1, :profile) accepts_nested_attributes_for(:profile, :allow_destroy => false)
- should allow to create a new profile via Person#profile_attributes
- should allow to update an existing profile via Person#profile_attributes
- should not allow to delete an existing profile via Person#profile_attributes
DataMapper::NestedAttributes Person.has(1, :profile) accepts_nested_attributes_for(:profile, :allow_destroy => true)
- should allow to create a new profile via Person#profile_attributes
- should allow to update an existing profile via Person#profile_attributes
- should allow to delete an existing profile via Person#profile_attributes
DataMapper::NestedAttributes Person.has(1, :profile) accepts_nested_attributes_for :profile, :reject_if => :foo
- should allow to create a new profile via Person#profile_attributes
- should allow to update an existing profile via Person#profile_attributes
- should not allow to delete an existing profile via Person#profile_attributes
DataMapper::NestedAttributes Person.has(1, :profile) accepts_nested_attributes_for :profile, :reject_if => lambda { |attrs| true }
- should not allow to create a new profile via Person#profile_attributes
- should not allow to delete an existing profile via Person#profile_attributes
DataMapper::NestedAttributes Person.has(1, :profile) accepts_nested_attributes_for :profile, :reject_if => lambda { |attrs| false }
- should allow to create a new profile via Person#profile_attributes
- should allow to update an existing profile via Person#profile_attributes
- should not allow to delete an existing profile via Person#profile_attributes
DataMapper::NestedAttributes Project.has(n, :tasks) accepts_nested_attributes_for(:tasks)
- should allow to create a new task via Project#tasks_attributes
- should allow to update an existing task via Project#tasks_attributes
- should not allow to delete an existing task via Profile#tasks_attributes
DataMapper::NestedAttributes Project.has(n, :tasks) accepts_nested_attributes_for(:tasks, :allow_destroy => false)
- should allow to create a new task via Project#tasks_attributes
- should allow to update an existing task via Project#tasks_attributes
- should not allow to delete an existing task via Profile#tasks_attributes
DataMapper::NestedAttributes Project.has(n, :tasks) accepts_nested_attributes_for(:tasks, :allow_destroy => true)
- should allow to create a new task via Project#tasks_attributes
- should allow to update an existing task via Project#tasks_attributes
- should allow to delete an existing task via Profile#tasks_attributes
DataMapper::NestedAttributes Project.has(n, :tasks) accepts_nested_attributes_for :tasks, :reject_if => :foo
- should allow to create a new task via Project#tasks_attributes
- should allow to update an existing task via Project#tasks_attributes
- should not allow to delete an existing task via Profile#tasks_attributes
DataMapper::NestedAttributes Project.has(n, :tasks) accepts_nested_attributes_for :tasks, :reject_if => lambda { |attrs| true }
- should not allow to create a new task via Project#tasks_attributes
- should not allow to delete an existing task via Profile#tasks_attributes
DataMapper::NestedAttributes Project.has(n, :tasks) accepts_nested_attributes_for :tasks, :reject_if => lambda { |attrs| false }
- should allow to create a new task via Project#tasks_attributes
- should allow to update an existing task via Project#tasks_attributes
- should not allow to delete an existing task via Profile#tasks_attributes
DataMapper::NestedAttributes Person.has(n, :projects, :through => :project_memberships) accepts_nested_attributes_for(:projects)
- should allow to create a new project via Person#projects_attributes
- should allow to update an existing project via Person#projects_attributes
- should not allow to delete an existing project via Person#projects_attributes
DataMapper::NestedAttributes Person.has(n, :projects, :through => :project_memberships) accepts_nested_attributes_for(:projects, :allow_destroy => false)
- should allow to create a new project via Person#projects_attributes
- should allow to update an existing project via Person#projects_attributes
- should not allow to delete an existing project via Person#projects_attributes
DataMapper::NestedAttributes Person.has(n, :projects, :through => :project_memberships) accepts_nested_attributes_for(:projects, :allow_destroy = true)
- should allow to create a new project via Person#projects_attributes
- should allow to update an existing project via Person#projects_attributes
- should allow to delete an existing project via Person#projects_attributes
DataMapper::NestedAttributes Person.has(n, :projects, :through => :project_memberships) accepts_nested_attributes_for :projects, :reject_if => :foo
- should allow to create a new project via Person#projects_attributes
- should allow to update an existing project via Person#projects_attributes
- should not allow to delete an existing project via Person#projects_attributes
DataMapper::NestedAttributes Person.has(n, :projects, :through => :project_memberships) accepts_nested_attributes_for :projects, :reject_if => lambda { |attrs| true }
- should not allow to create a new project via Person#projects_attributes
- should not allow to delete an existing project via Person#projects_attributes
DataMapper::NestedAttributes Person.has(n, :projects, :through => :project_memberships) accepts_nested_attributes_for :projects, :reject_if => lambda { |attrs| false }
- should allow to create a new project via Person#projects_attributes
- should allow to update an existing project via Person#projects_attributes
- should not allow to delete an existing project via Person#projects_attributes
h2. TODO
* add more specs and fix bugs
* Adapt to datamapper/next