README.textile in snusnu-dm-accepts_nested_attributes-0.10.0 vs README.textile in snusnu-dm-accepts_nested_attributes-0.11.0

- old
+ new

@@ -1,82 +1,19 @@ 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. +Current documentation can always be found at "rdoc.info":http://rdoc.info/projects/snusnu/dm-accepts_nested_attributes -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). - -h2. Current limitations - -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":http://pastie.org/446060), I -will do my best to add this feature. - -h2. TODO - -* 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 - -h2. Implementation details - -This section mainly serves as a place for me to take notes during development. - -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. <pre> <code> 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) @@ -87,35 +24,38 @@ 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 @@ -127,145 +67,39 @@ 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! </code> </pre> -h2. Current Integration Specs +h2. Current limitations -<pre> -<code> -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 +There are some minor limitations at the moment but I hope that these will be resolved some time soon. -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 +h3. marking for destruction -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 +@many_to_one, one_to_one and one_to_many@ relationships all perform the deleting of nested models by _marking them for destruction_. This means, that as long as you don't call #save on your resource, the nested models won't get destroyed. However, this is currently _not_ the case for @many_to_many@ relationships. The @join and target resources@ both get destroyed immediately when calling the nested attributes writer. -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 +For the above example this would mean that when you issue the code below, it would result in immediate deletion of the intermediate @ProjectMembership@ and the @Project@ resource. -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 +<pre> +<code> +person.projects_attributes = { :id => valid_project_id, :_delete => true } </code> </pre> + +h2. TODO + +* collect validation errors from related resources +* update README to include more complete usecases +* think about replacing :reject_if with :if and :unless