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.
Files changed (58) hide show
  1. data/CHANGELOG +970 -0
  2. data/Gemfile +84 -0
  3. data/LICENSE +20 -0
  4. data/README.textile +94 -0
  5. data/Rakefile +25 -0
  6. data/TODO +6 -0
  7. data/VERSION +1 -0
  8. data/dm-accepts_nested_attributes.gemspec +114 -0
  9. data/lib/dm-accepts_nested_attributes.rb +13 -0
  10. data/lib/dm-accepts_nested_attributes/model.rb +144 -0
  11. data/lib/dm-accepts_nested_attributes/relationship.rb +82 -0
  12. data/lib/dm-accepts_nested_attributes/resource.rb +382 -0
  13. data/lib/dm-accepts_nested_attributes/version.rb +7 -0
  14. data/spec/accepts_nested_attributes_for_spec.rb +408 -0
  15. data/spec/assign_nested_attributes_for_spec.rb +101 -0
  16. data/spec/comb/1-1_disjoint_spec.rb +67 -0
  17. data/spec/comb/1-1_overlapping_spec.rb +66 -0
  18. data/spec/comb/1-1_subset_spec.rb +65 -0
  19. data/spec/comb/1-1_superset_spec.rb +67 -0
  20. data/spec/comb/1-m_disjoint_spec.rb +71 -0
  21. data/spec/comb/1-m_overlapping_spec.rb +70 -0
  22. data/spec/comb/1-m_subset_spec.rb +65 -0
  23. data/spec/comb/1-m_superset_spec.rb +71 -0
  24. data/spec/comb/m-1_disjoint_spec.rb +71 -0
  25. data/spec/comb/m-1_overlapping_spec.rb +70 -0
  26. data/spec/comb/m-1_subset_spec.rb +65 -0
  27. data/spec/comb/m-1_superset_spec.rb +71 -0
  28. data/spec/comb/n-m_composite_spec.rb +141 -0
  29. data/spec/comb/n-m_surrogate_spec.rb +154 -0
  30. data/spec/many_to_many_composite_spec.rb +120 -0
  31. data/spec/many_to_many_spec.rb +129 -0
  32. data/spec/many_to_one_composite_spec.rb +120 -0
  33. data/spec/many_to_one_spec.rb +101 -0
  34. data/spec/one_to_many_composite_spec.rb +120 -0
  35. data/spec/one_to_many_spec.rb +100 -0
  36. data/spec/one_to_one_composite_spec.rb +150 -0
  37. data/spec/one_to_one_spec.rb +115 -0
  38. data/spec/rcov.opts +6 -0
  39. data/spec/resource_spec.rb +65 -0
  40. data/spec/shared/many_to_many_composite_spec.rb +149 -0
  41. data/spec/shared/many_to_many_spec.rb +146 -0
  42. data/spec/shared/many_to_one_composite_spec.rb +160 -0
  43. data/spec/shared/many_to_one_spec.rb +130 -0
  44. data/spec/shared/one_to_many_composite_spec.rb +159 -0
  45. data/spec/shared/one_to_many_spec.rb +107 -0
  46. data/spec/shared/one_to_one_composite_spec.rb +114 -0
  47. data/spec/shared/one_to_one_spec.rb +111 -0
  48. data/spec/spec.opts +4 -0
  49. data/spec/spec_helper.rb +50 -0
  50. data/spec/update_dirty_spec.rb +113 -0
  51. data/spec/update_multiple_spec.rb +79 -0
  52. data/tasks/changelog.rake +20 -0
  53. data/tasks/ci.rake +1 -0
  54. data/tasks/local_gemfile.rake +18 -0
  55. data/tasks/spec.rake +22 -0
  56. data/tasks/yard.rake +9 -0
  57. data/tasks/yardstick.rake +19 -0
  58. 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.
@@ -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
@@ -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
@@ -0,0 +1,6 @@
1
+ TODO
2
+ ====
3
+
4
+ * collect validation errors from related resources
5
+ * update README to include more complete usecases
6
+ * think about replacing :reject_if with :if and :unless
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