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 on my blog, where I will try to comment on the
development (problems).
Interaction with dm-validations
is actually possible but not very well specced atm. I added not null
constraints to all spec fixtures for now, but no other custom validations. All specs still pass. However,
as long as I’m not decided on where to put the specs for interaction with dm-validations
(most probably
inside dm-validations
would be the right place for these), I won’t write many more specs for these scenarios,
since it’s very hard to decide where to stop, once I start writing some.
Currently, the creation of the record together with all its join models, is not handled inside a transaction.
This must definitely change! As soon as I find out why my initial experiments with transactions consistently
yielded no such table errors while migrating the specsuite (see this pastie), I
will do my best to add this feature.
- use transactions
- update README to include more complete usecases
- specs for custom validations (dm-validations in general)
- specs for adding errors to the parent objects
- reorganize specs and fix bugs
- Adapt to datamapper/next
This section mainly serves as a place for me to take notes during development.
- 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!
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.
- 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?
- 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)
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!
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