dm-accepts_nested_attributes_for 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +970 -0
- data/Gemfile +84 -0
- data/LICENSE +20 -0
- data/README.textile +94 -0
- data/Rakefile +25 -0
- data/TODO +6 -0
- data/VERSION +1 -0
- data/dm-accepts_nested_attributes.gemspec +114 -0
- data/lib/dm-accepts_nested_attributes.rb +13 -0
- data/lib/dm-accepts_nested_attributes/model.rb +144 -0
- data/lib/dm-accepts_nested_attributes/relationship.rb +82 -0
- data/lib/dm-accepts_nested_attributes/resource.rb +382 -0
- data/lib/dm-accepts_nested_attributes/version.rb +7 -0
- data/spec/accepts_nested_attributes_for_spec.rb +408 -0
- data/spec/assign_nested_attributes_for_spec.rb +101 -0
- data/spec/comb/1-1_disjoint_spec.rb +67 -0
- data/spec/comb/1-1_overlapping_spec.rb +66 -0
- data/spec/comb/1-1_subset_spec.rb +65 -0
- data/spec/comb/1-1_superset_spec.rb +67 -0
- data/spec/comb/1-m_disjoint_spec.rb +71 -0
- data/spec/comb/1-m_overlapping_spec.rb +70 -0
- data/spec/comb/1-m_subset_spec.rb +65 -0
- data/spec/comb/1-m_superset_spec.rb +71 -0
- data/spec/comb/m-1_disjoint_spec.rb +71 -0
- data/spec/comb/m-1_overlapping_spec.rb +70 -0
- data/spec/comb/m-1_subset_spec.rb +65 -0
- data/spec/comb/m-1_superset_spec.rb +71 -0
- data/spec/comb/n-m_composite_spec.rb +141 -0
- data/spec/comb/n-m_surrogate_spec.rb +154 -0
- data/spec/many_to_many_composite_spec.rb +120 -0
- data/spec/many_to_many_spec.rb +129 -0
- data/spec/many_to_one_composite_spec.rb +120 -0
- data/spec/many_to_one_spec.rb +101 -0
- data/spec/one_to_many_composite_spec.rb +120 -0
- data/spec/one_to_many_spec.rb +100 -0
- data/spec/one_to_one_composite_spec.rb +150 -0
- data/spec/one_to_one_spec.rb +115 -0
- data/spec/rcov.opts +6 -0
- data/spec/resource_spec.rb +65 -0
- data/spec/shared/many_to_many_composite_spec.rb +149 -0
- data/spec/shared/many_to_many_spec.rb +146 -0
- data/spec/shared/many_to_one_composite_spec.rb +160 -0
- data/spec/shared/many_to_one_spec.rb +130 -0
- data/spec/shared/one_to_many_composite_spec.rb +159 -0
- data/spec/shared/one_to_many_spec.rb +107 -0
- data/spec/shared/one_to_one_composite_spec.rb +114 -0
- data/spec/shared/one_to_one_spec.rb +111 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/update_dirty_spec.rb +113 -0
- data/spec/update_multiple_spec.rb +79 -0
- data/tasks/changelog.rake +20 -0
- data/tasks/ci.rake +1 -0
- data/tasks/local_gemfile.rake +18 -0
- data/tasks/spec.rake +22 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +216 -0
data/Gemfile
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
SOURCE = ENV.fetch('SOURCE', :git).to_sym
|
4
|
+
REPO_POSTFIX = SOURCE == :path ? '' : '.git'
|
5
|
+
DATAMAPPER = SOURCE == :path ? Pathname(__FILE__).dirname.parent : 'http://github.com/datamapper'
|
6
|
+
DM_VERSION = '~> 1.2.0.rc2'
|
7
|
+
DO_VERSION = '~> 0.10.6'
|
8
|
+
DM_DO_ADAPTERS = %w[ sqlite postgres mysql oracle sqlserver ]
|
9
|
+
CURRENT_BRANCH = ENV.fetch('GIT_BRANCH', 'master')
|
10
|
+
|
11
|
+
gem 'dm-core', DM_VERSION,
|
12
|
+
SOURCE => "#{DATAMAPPER}/dm-core#{REPO_POSTFIX}",
|
13
|
+
:branch => CURRENT_BRANCH
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
|
17
|
+
gem 'dm-validations', DM_VERSION,
|
18
|
+
SOURCE => "#{DATAMAPPER}/dm-validations#{REPO_POSTFIX}",
|
19
|
+
:branch => CURRENT_BRANCH
|
20
|
+
|
21
|
+
gem 'dm-constraints', DM_VERSION,
|
22
|
+
SOURCE => "#{DATAMAPPER}/dm-constraints#{REPO_POSTFIX}",
|
23
|
+
:branch => CURRENT_BRANCH
|
24
|
+
|
25
|
+
gem 'rake', '~> 0.9.2'
|
26
|
+
gem 'rspec', '~> 1.3.2'
|
27
|
+
gem 'yard', '~> 0.7.2'
|
28
|
+
gem 'jeweler', '~> 1.6.4'
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
platforms :mri_18 do
|
33
|
+
group :quality do
|
34
|
+
|
35
|
+
gem 'rcov', '~> 0.9.10'
|
36
|
+
gem 'yard', '~> 0.7.2'
|
37
|
+
gem 'yardstick', '~> 0.4'
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
group :datamapper do
|
43
|
+
|
44
|
+
adapters = ENV['ADAPTER'] || ENV['ADAPTERS']
|
45
|
+
adapters = adapters.to_s.tr(',', ' ').split.uniq - %w[ in_memory ]
|
46
|
+
|
47
|
+
if (do_adapters = DM_DO_ADAPTERS & adapters).any?
|
48
|
+
|
49
|
+
do_options = {}
|
50
|
+
do_options[:git] = "#{DATAMAPPER}/do#{REPO_POSTFIX}" if ENV['DO_GIT'] == 'true'
|
51
|
+
|
52
|
+
gem 'data_objects', DO_VERSION, do_options.dup
|
53
|
+
|
54
|
+
do_adapters.each do |adapter|
|
55
|
+
adapter = 'sqlite3' if adapter == 'sqlite'
|
56
|
+
gem "do_#{adapter}", DO_VERSION, do_options.dup
|
57
|
+
end
|
58
|
+
|
59
|
+
gem 'dm-do-adapter', DM_VERSION,
|
60
|
+
SOURCE => "#{DATAMAPPER}/dm-do-adapter#{REPO_POSTFIX}",
|
61
|
+
:branch => CURRENT_BRANCH
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
adapters.each do |adapter|
|
66
|
+
|
67
|
+
gem "dm-#{adapter}-adapter", DM_VERSION,
|
68
|
+
SOURCE => "#{DATAMAPPER}/dm-#{adapter}-adapter#{REPO_POSTFIX}",
|
69
|
+
:branch => CURRENT_BRANCH
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
plugins = ENV['PLUGINS'] || ENV['PLUGIN']
|
74
|
+
plugins = plugins.to_s.tr(',', ' ').split.push('dm-migrations').uniq
|
75
|
+
|
76
|
+
plugins.each do |plugin|
|
77
|
+
|
78
|
+
gem plugin, DM_VERSION,
|
79
|
+
SOURCE => "#{DATAMAPPER}/#{plugin}#{REPO_POSTFIX}",
|
80
|
+
:branch => CURRENT_BRANCH
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Martin Gamsjäger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
h2. dm-accepts_nested_attributes
|
2
|
+
|
3
|
+
A DataMapper plugin that allows nested model attribute assignment like activerecord does.
|
4
|
+
|
5
|
+
Current documentation can always be found at "rdoc.info":http://rdoc.info/projects/snusnu/dm-accepts_nested_attributes
|
6
|
+
|
7
|
+
h3. Examples
|
8
|
+
|
9
|
+
The following example illustrates the use of this plugin.
|
10
|
+
|
11
|
+
<pre>
|
12
|
+
<code>
|
13
|
+
require "rubygems"
|
14
|
+
|
15
|
+
require "dm-core"
|
16
|
+
require "dm-validations"
|
17
|
+
require "dm-accepts_nested_attributes"
|
18
|
+
|
19
|
+
DataMapper::Logger.new(STDOUT, :debug)
|
20
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
21
|
+
|
22
|
+
class Person
|
23
|
+
include DataMapper::Resource
|
24
|
+
property :id, Serial
|
25
|
+
property :name, String
|
26
|
+
has 1, :profile
|
27
|
+
has n, :project_memberships
|
28
|
+
has n, :projects, :through => :project_memberships
|
29
|
+
|
30
|
+
accepts_nested_attributes_for :profile
|
31
|
+
accepts_nested_attributes_for :projects
|
32
|
+
|
33
|
+
# adds the following instance methods
|
34
|
+
# #profile_attributes=
|
35
|
+
# #profile_attributes
|
36
|
+
# #projects_attributes=
|
37
|
+
# #projects_attributes
|
38
|
+
end
|
39
|
+
|
40
|
+
class Profile
|
41
|
+
include DataMapper::Resource
|
42
|
+
property :id, Serial
|
43
|
+
property :person_id, Integer
|
44
|
+
belongs_to :person
|
45
|
+
|
46
|
+
accepts_nested_attributes_for :person
|
47
|
+
|
48
|
+
# adds the following instance methods
|
49
|
+
# #person_attributes=
|
50
|
+
# #person_attributes
|
51
|
+
end
|
52
|
+
|
53
|
+
class Project
|
54
|
+
include DataMapper::Resource
|
55
|
+
property :id, Serial
|
56
|
+
has n, :tasks
|
57
|
+
has n, :project_memberships
|
58
|
+
has n, :people, :through => :project_memberships
|
59
|
+
|
60
|
+
accepts_nested_attributes_for :tasks
|
61
|
+
accepts_nested_attributes_for :people
|
62
|
+
|
63
|
+
# adds the following instance methods
|
64
|
+
# #tasks_attributes=
|
65
|
+
# #tasks_attributes
|
66
|
+
# #people_attributes=
|
67
|
+
# #people_attributes
|
68
|
+
end
|
69
|
+
|
70
|
+
class ProjectMembership
|
71
|
+
include DataMapper::Resource
|
72
|
+
property :id, Serial
|
73
|
+
property :person_id, Integer
|
74
|
+
property :project_id, Integer
|
75
|
+
belongs_to :person
|
76
|
+
belongs_to :project
|
77
|
+
end
|
78
|
+
|
79
|
+
class Task
|
80
|
+
include DataMapper::Resource
|
81
|
+
property :id, Serial
|
82
|
+
property :project_id, Integer
|
83
|
+
belongs_to :project
|
84
|
+
end
|
85
|
+
|
86
|
+
DataMapper.auto_migrate!
|
87
|
+
</code>
|
88
|
+
</pre>
|
89
|
+
|
90
|
+
h2. TODO
|
91
|
+
|
92
|
+
* collect validation errors from related resources
|
93
|
+
* update README to include more complete usecases
|
94
|
+
* think about replacing :reject_if with :if and :unless
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = 'dm-accepts_nested_attributes'
|
9
|
+
gem.summary = 'Nested model assignment for datamapper'
|
10
|
+
gem.description = 'A datamapper plugin that allows nested model assignment like activerecord.'
|
11
|
+
gem.email = 'gamsnjaga [a] gmail [d] com'
|
12
|
+
gem.homepage = 'http://github.com/snusnu/dm-accepts_nested_attributes'
|
13
|
+
gem.authors = [ 'Martin Gamsjaeger (snusnu)' ]
|
14
|
+
gem.has_rdoc = 'yard'
|
15
|
+
end
|
16
|
+
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
|
19
|
+
FileList['tasks/**/*.rake'].each { |task| import task }
|
20
|
+
|
21
|
+
rescue LoadError => e
|
22
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
23
|
+
puts '-----------------------------------------------------------------------------'
|
24
|
+
puts e.backtrace # Let's help by actually showing *which* dependency is missing
|
25
|
+
end
|
data/TODO
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.0
|
@@ -0,0 +1,114 @@
|
|
1
|
+
2# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{dm-accepts_nested_attributes_for}
|
8
|
+
s.version = "1.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = [%q{Martin Gamsjaeger (snusnu)}]
|
12
|
+
s.date = %q{2011-09-23}
|
13
|
+
s.description = %q{A datamapper plugin that allows nested model assignment like activerecord.}
|
14
|
+
s.email = %q{gamsnjaga [a] gmail [d] com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.textile",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"CHANGELOG",
|
22
|
+
"Gemfile",
|
23
|
+
"LICENSE",
|
24
|
+
"README.textile",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO",
|
27
|
+
"VERSION",
|
28
|
+
"dm-accepts_nested_attributes.gemspec",
|
29
|
+
"lib/dm-accepts_nested_attributes.rb",
|
30
|
+
"lib/dm-accepts_nested_attributes/model.rb",
|
31
|
+
"lib/dm-accepts_nested_attributes/relationship.rb",
|
32
|
+
"lib/dm-accepts_nested_attributes/resource.rb",
|
33
|
+
"lib/dm-accepts_nested_attributes/version.rb",
|
34
|
+
"spec/accepts_nested_attributes_for_spec.rb",
|
35
|
+
"spec/assign_nested_attributes_for_spec.rb",
|
36
|
+
"spec/comb/1-1_disjoint_spec.rb",
|
37
|
+
"spec/comb/1-1_overlapping_spec.rb",
|
38
|
+
"spec/comb/1-1_subset_spec.rb",
|
39
|
+
"spec/comb/1-1_superset_spec.rb",
|
40
|
+
"spec/comb/1-m_disjoint_spec.rb",
|
41
|
+
"spec/comb/1-m_overlapping_spec.rb",
|
42
|
+
"spec/comb/1-m_subset_spec.rb",
|
43
|
+
"spec/comb/1-m_superset_spec.rb",
|
44
|
+
"spec/comb/m-1_disjoint_spec.rb",
|
45
|
+
"spec/comb/m-1_overlapping_spec.rb",
|
46
|
+
"spec/comb/m-1_subset_spec.rb",
|
47
|
+
"spec/comb/m-1_superset_spec.rb",
|
48
|
+
"spec/comb/n-m_composite_spec.rb",
|
49
|
+
"spec/comb/n-m_surrogate_spec.rb",
|
50
|
+
"spec/many_to_many_composite_spec.rb",
|
51
|
+
"spec/many_to_many_spec.rb",
|
52
|
+
"spec/many_to_one_composite_spec.rb",
|
53
|
+
"spec/many_to_one_spec.rb",
|
54
|
+
"spec/one_to_many_composite_spec.rb",
|
55
|
+
"spec/one_to_many_spec.rb",
|
56
|
+
"spec/one_to_one_composite_spec.rb",
|
57
|
+
"spec/one_to_one_spec.rb",
|
58
|
+
"spec/rcov.opts",
|
59
|
+
"spec/resource_spec.rb",
|
60
|
+
"spec/shared/many_to_many_composite_spec.rb",
|
61
|
+
"spec/shared/many_to_many_spec.rb",
|
62
|
+
"spec/shared/many_to_one_composite_spec.rb",
|
63
|
+
"spec/shared/many_to_one_spec.rb",
|
64
|
+
"spec/shared/one_to_many_composite_spec.rb",
|
65
|
+
"spec/shared/one_to_many_spec.rb",
|
66
|
+
"spec/shared/one_to_one_composite_spec.rb",
|
67
|
+
"spec/shared/one_to_one_spec.rb",
|
68
|
+
"spec/spec.opts",
|
69
|
+
"spec/spec_helper.rb",
|
70
|
+
"spec/update_dirty_spec.rb",
|
71
|
+
"spec/update_multiple_spec.rb",
|
72
|
+
"tasks/changelog.rake",
|
73
|
+
"tasks/ci.rake",
|
74
|
+
"tasks/local_gemfile.rake",
|
75
|
+
"tasks/spec.rake",
|
76
|
+
"tasks/yard.rake",
|
77
|
+
"tasks/yardstick.rake"
|
78
|
+
]
|
79
|
+
s.homepage = %q{http://github.com/snusnu/dm-accepts_nested_attributes}
|
80
|
+
s.require_paths = [%q{lib}]
|
81
|
+
s.rubygems_version = %q{1.8.5}
|
82
|
+
s.summary = %q{Nested model assignment for datamapper}
|
83
|
+
|
84
|
+
if s.respond_to? :specification_version then
|
85
|
+
s.specification_version = 3
|
86
|
+
|
87
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
88
|
+
s.add_runtime_dependency(%q<dm-core>, ["~> 1.2.0"])
|
89
|
+
s.add_development_dependency(%q<dm-validations>, ["~> 1.2.0"])
|
90
|
+
s.add_development_dependency(%q<dm-constraints>, ["~> 1.2.0"])
|
91
|
+
s.add_development_dependency(%q<rake>, ["~> 0.9.2"])
|
92
|
+
s.add_development_dependency(%q<rspec>, ["~> 1.3.2"])
|
93
|
+
s.add_development_dependency(%q<yard>, ["~> 0.7.2"])
|
94
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
95
|
+
else
|
96
|
+
s.add_dependency(%q<dm-core>, ["~> 1.2.0"])
|
97
|
+
s.add_dependency(%q<dm-validations>, ["~> 1.2.0"])
|
98
|
+
s.add_dependency(%q<dm-constraints>, ["~> 1.2.0"])
|
99
|
+
s.add_dependency(%q<rake>, ["~> 0.9.2"])
|
100
|
+
s.add_dependency(%q<rspec>, ["~> 1.3.2"])
|
101
|
+
s.add_dependency(%q<yard>, ["~> 0.7.2"])
|
102
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
103
|
+
end
|
104
|
+
else
|
105
|
+
s.add_dependency(%q<dm-core>, ["~> 1.2.0"])
|
106
|
+
s.add_dependency(%q<dm-validations>, ["~> 1.2.0"])
|
107
|
+
s.add_dependency(%q<dm-constraints>, ["~> 1.2.0"])
|
108
|
+
s.add_dependency(%q<rake>, ["~> 0.9.2"])
|
109
|
+
s.add_dependency(%q<rspec>, ["~> 1.3.2"])
|
110
|
+
s.add_dependency(%q<yard>, ["~> 0.7.2"])
|
111
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
|
3
|
+
require 'dm-accepts_nested_attributes/model'
|
4
|
+
require 'dm-accepts_nested_attributes/resource'
|
5
|
+
require 'dm-accepts_nested_attributes/relationship'
|
6
|
+
|
7
|
+
# Activate the plugin
|
8
|
+
DataMapper::Model.append_extensions(DataMapper::NestedAttributes::Model)
|
9
|
+
DataMapper::Associations::Relationship.send(:include, DataMapper::NestedAttributes::Relationship)
|
10
|
+
DataMapper::Associations::ManyToMany::Relationship.send(:include, DataMapper::NestedAttributes::ManyToMany)
|
11
|
+
DataMapper::Associations::OneToMany::Relationship.send(:include, DataMapper::NestedAttributes::OneToMany)
|
12
|
+
DataMapper::Associations::ManyToOne::Relationship.send(:include, DataMapper::NestedAttributes::ManyToOne)
|
13
|
+
DataMapper::Associations::OneToOne::Relationship.send(:include, DataMapper::NestedAttributes::OneToOne)
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module NestedAttributes
|
3
|
+
class BackwardsCompatibilityHash < Hash
|
4
|
+
def initialize(model)
|
5
|
+
@model = model
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](key)
|
9
|
+
if key.is_a?(DataMapper::Associations::Relationship)
|
10
|
+
warn "#{@model}#options_for_nested_attributes: Using a relationship " +
|
11
|
+
"as key is deprecated. Use the relationship name (i.e. " +
|
12
|
+
"#{key.name.inspect}) as key."
|
13
|
+
key = key.name
|
14
|
+
end
|
15
|
+
super(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
if key.is_a?(DataMapper::Associations::Relationship)
|
20
|
+
warn "#{@model}#options_for_nested_attributes: Using a relationship " +
|
21
|
+
"as key is deprecated. Use the relationship name (i.e. " +
|
22
|
+
"#{key.name.inspect}) as key."
|
23
|
+
key = key.name
|
24
|
+
end
|
25
|
+
super(key, value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Named plugin exception that is raised by {Model#accepts_nested_attributes_for}
|
31
|
+
# if the passed options are invalid.
|
32
|
+
class InvalidOptions < ArgumentError; end
|
33
|
+
|
34
|
+
module Model
|
35
|
+
|
36
|
+
##
|
37
|
+
# Allows an association to accept nested attributes.
|
38
|
+
#
|
39
|
+
# @param [Symbol, String] association_name
|
40
|
+
# The name of the association that should accept nested attributes.
|
41
|
+
#
|
42
|
+
# @param [Hash?] options
|
43
|
+
# List of resources to initialize the collection with.
|
44
|
+
#
|
45
|
+
# @option options [Symbol, String, #call] :reject_if
|
46
|
+
# An instance method name or an object that respond_to?(:call), which
|
47
|
+
# stops a new record from being created, if it evaluates to true.
|
48
|
+
#
|
49
|
+
# @option options [Boolean] :allow_destroy (false)
|
50
|
+
# If true, allows destroying the association via the generated writer.
|
51
|
+
# If false, prevents destroying the association via the generated writer.
|
52
|
+
#
|
53
|
+
# @raise [DataMapper::NestedAttributes::InvalidOptions]
|
54
|
+
# A named exception class indicating invalid options.
|
55
|
+
#
|
56
|
+
# @return [void]
|
57
|
+
#
|
58
|
+
def accepts_nested_attributes_for(association_name, options = {})
|
59
|
+
|
60
|
+
# ----------------------------------------------------------------------------------
|
61
|
+
# try to fail as early as possible
|
62
|
+
# ----------------------------------------------------------------------------------
|
63
|
+
|
64
|
+
unless relationship = relationships(repository_name)[association_name]
|
65
|
+
raise(ArgumentError, "No relationship #{association_name.inspect} for '#{name}' in :#{repository_name} repository")
|
66
|
+
end
|
67
|
+
|
68
|
+
# raise InvalidOptions if the given options don't make sense
|
69
|
+
assert_valid_options_for_nested_attributes(options)
|
70
|
+
|
71
|
+
# by default, nested attributes can't be destroyed
|
72
|
+
options = { :allow_destroy => false }.update(options)
|
73
|
+
|
74
|
+
# ----------------------------------------------------------------------------------
|
75
|
+
# should be safe to go from here
|
76
|
+
# ----------------------------------------------------------------------------------
|
77
|
+
|
78
|
+
options_for_nested_attributes[relationship.name] = options
|
79
|
+
|
80
|
+
include ::DataMapper::NestedAttributes::Resource
|
81
|
+
|
82
|
+
type = relationship.max > 1 ? :collection : :resource
|
83
|
+
|
84
|
+
define_method "#{association_name}_attributes" do
|
85
|
+
instance_variable_get("@#{association_name}_attributes")
|
86
|
+
end
|
87
|
+
|
88
|
+
define_method "#{association_name}_attributes=" do |attributes|
|
89
|
+
attributes = sanitize_nested_attributes(attributes)
|
90
|
+
instance_variable_set("@#{association_name}_attributes", attributes)
|
91
|
+
send("assign_nested_attributes_for_related_#{type}", relationship, attributes)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns a hash with the options for all associations (using the
|
97
|
+
# corresponding relationship as key) that accept nested attributes.
|
98
|
+
#
|
99
|
+
# @return [Hash{DataMapper::Associations::Relationship => Hash}]
|
100
|
+
def options_for_nested_attributes
|
101
|
+
@options_for_nested_attributes ||= DataMapper::NestedAttributes::BackwardsCompatibilityHash.new(self)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
##
|
108
|
+
# Checks options passed to {#accepts_nested_attributes_for}.
|
109
|
+
# If any of the given options is invalid, this method will raise
|
110
|
+
# {DataMapper::NestedAttributes::InvalidOptions}.
|
111
|
+
#
|
112
|
+
# @param [Hash?] options
|
113
|
+
# The options passed to {#accepts_nested_attributes_for}.
|
114
|
+
#
|
115
|
+
# @raise [DataMapper::NestedAttributes::InvalidOptions]
|
116
|
+
# A named exception class indicating invalid options.
|
117
|
+
#
|
118
|
+
# @return [void]
|
119
|
+
#
|
120
|
+
def assert_valid_options_for_nested_attributes(options)
|
121
|
+
|
122
|
+
assert_kind_of 'options', options, Hash
|
123
|
+
|
124
|
+
valid_options = [ :allow_destroy, :reject_if ]
|
125
|
+
|
126
|
+
unless options.all? { |k,v| valid_options.include?(k) }
|
127
|
+
raise InvalidOptions, 'options must be one of :allow_destroy or :reject_if'
|
128
|
+
end
|
129
|
+
|
130
|
+
guard = options[:reject_if]
|
131
|
+
if guard.is_a?(Symbol) || guard.is_a?(String)
|
132
|
+
msg = ":reject_if => #{guard.inspect}, but there is no instance method #{guard.inspect} in #{self.name}"
|
133
|
+
raise InvalidOptions, msg unless instance_methods.include?(options[:reject_if].to_s)
|
134
|
+
else
|
135
|
+
msg = ":reject_if must be a Symbol|String or respond_to?(:call) "
|
136
|
+
raise InvalidOptions, msg unless guard.nil? || guard.respond_to?(:call)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|