activefacts-api 0.8.12 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.rspec +1 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +14 -0
  4. data/Rakefile +21 -9
  5. data/VERSION +1 -1
  6. data/activefacts-api.gemspec +31 -12
  7. data/lib/activefacts/api.rb +1 -0
  8. data/lib/activefacts/api/constellation.rb +3 -1
  9. data/lib/activefacts/api/entity.rb +74 -29
  10. data/lib/activefacts/api/exceptions.rb +17 -0
  11. data/lib/activefacts/api/instance.rb +96 -1
  12. data/lib/activefacts/api/instance_index.rb +35 -37
  13. data/lib/activefacts/api/numeric.rb +62 -56
  14. data/lib/activefacts/api/object_type.rb +49 -23
  15. data/lib/activefacts/api/role.rb +8 -2
  16. data/lib/activefacts/api/role_values.rb +8 -26
  17. data/lib/activefacts/api/standard_types.rb +2 -17
  18. data/lib/activefacts/api/vocabulary.rb +1 -1
  19. data/lib/activefacts/tracer.rb +13 -1
  20. data/spec/{constellation_spec.rb → constellation/constellation_spec.rb} +127 -56
  21. data/spec/constellation/instance_index_spec.rb +90 -0
  22. data/spec/{instance_spec.rb → constellation/instance_spec.rb} +48 -42
  23. data/spec/{role_values_spec.rb → fact_type/role_values_spec.rb} +28 -19
  24. data/spec/{roles_spec.rb → fact_type/roles_spec.rb} +55 -21
  25. data/spec/fixtures/tax.rb +45 -0
  26. data/spec/{identification_spec.rb → identification_scheme/identification_spec.rb} +88 -74
  27. data/spec/identification_scheme/identity_change_spec.rb +118 -0
  28. data/spec/identification_scheme/identity_supertype_change_spec.rb +63 -0
  29. data/spec/{entity_type_spec.rb → object_type/entity_type/entity_type_spec.rb} +2 -4
  30. data/spec/object_type/entity_type/multipart_identification_spec.rb +77 -0
  31. data/spec/{autocounter_spec.rb → object_type/value_type/autocounter_spec.rb} +2 -4
  32. data/spec/object_type/value_type/numeric_spec.rb +63 -0
  33. data/spec/{value_type_spec.rb → object_type/value_type/value_type_spec.rb} +10 -14
  34. data/spec/simplecov_helper.rb +8 -0
  35. data/spec/spec_helper.rb +1 -1
  36. metadata +100 -19
data/.rspec CHANGED
@@ -1 +1,2 @@
1
+ --require spec_helper
1
2
  --color
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - rbx-18mode
9
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake', :group => [:development, :test]
4
+
5
+ group :development do
6
+ gem 'jeweler'
7
+ gem 'rspec', '~>2.6.0'
8
+ end
9
+
10
+ group :test do
11
+ # rcov 1.0.0 is broken for jruby, so 0.9.11 is the only one available.
12
+ gem 'rcov', '~>0.9.11', :platforms => [:jruby, :mri_18], :require => false
13
+ gem 'simplecov', '~>0.6.4', :platforms => :mri_19, :require => false
14
+ end
data/Rakefile CHANGED
@@ -29,19 +29,31 @@ Jeweler::RubygemsDotOrgTasks.new
29
29
 
30
30
  require 'rspec/core'
31
31
  require 'rspec/core/rake_task'
32
- RSpec::Core::RakeTask.new(:spec) do |spec|
33
- spec.pattern = FileList['spec/**/*_spec.rb']
34
- end
32
+ require 'rdoc/task'
33
+
34
+ task :default => :spec
35
35
 
36
- RSpec::Core::RakeTask.new(:rcov) do |spec|
37
- spec.pattern = 'spec/**/*_spec.rb'
38
- spec.rcov_opts = [ '--exclude', 'spec', '--exclude', 'lib/activefacts/tracer.rb' ]
39
- spec.rcov = true
36
+ desc "Run Rspec tests"
37
+ RSpec::Core::RakeTask.new(:spec)
38
+
39
+ desc "Run RSpec tests and produce coverage files (results viewable in coverage/index.html)"
40
+ RSpec::Core::RakeTask.new(:coverage) do |spec|
41
+ if RUBY_VERSION < '1.9'
42
+ spec.rcov_opts = [
43
+ '--exclude', 'spec',
44
+ '--exclude', 'lib/activefacts/tracer.rb',
45
+ '--exclude', 'gem/*'
46
+ ]
47
+ spec.rcov = true
48
+ else
49
+ spec.rspec_opts = ['--require', 'simplecov_helper']
50
+ end
40
51
  end
41
52
 
42
- task :default => :spec
53
+ task :cov => :coverage
54
+ task :rcov => :coverage
55
+ task :simplecov => :coverage
43
56
 
44
- require 'rdoc/task'
45
57
  Rake::RDocTask.new do |rdoc|
46
58
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
59
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.12
1
+ 0.9.1
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "activefacts-api"
8
- s.version = "0.8.12"
8
+ s.version = "0.9.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Clifford Heath"]
12
- s.date = "2012-01-03"
12
+ s.date = "2012-10-17"
13
13
  s.description = "\nThe ActiveFacts API is a Ruby DSL for managing constellations of elementary facts.\nEach fact is either existential (a value or an entity), characteristic (boolean) or\nbinary relational (A rel B). Relational facts are consistently co-referenced, so you\ncan traverse them efficiently in any direction. Each constellation maintains constraints\nover the fact population.\n"
14
14
  s.email = "clifford.heath@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -20,6 +20,8 @@ Gem::Specification.new do |s|
20
20
  s.files = [
21
21
  ".document",
22
22
  ".rspec",
23
+ ".travis.yml",
24
+ "Gemfile",
23
25
  "LICENSE.txt",
24
26
  "README.rdoc",
25
27
  "Rakefile",
@@ -28,6 +30,7 @@ Gem::Specification.new do |s|
28
30
  "lib/activefacts/api.rb",
29
31
  "lib/activefacts/api/constellation.rb",
30
32
  "lib/activefacts/api/entity.rb",
33
+ "lib/activefacts/api/exceptions.rb",
31
34
  "lib/activefacts/api/instance.rb",
32
35
  "lib/activefacts/api/instance_index.rb",
33
36
  "lib/activefacts/api/numeric.rb",
@@ -39,37 +42,53 @@ Gem::Specification.new do |s|
39
42
  "lib/activefacts/api/value.rb",
40
43
  "lib/activefacts/api/vocabulary.rb",
41
44
  "lib/activefacts/tracer.rb",
42
- "spec/autocounter_spec.rb",
43
- "spec/constellation_spec.rb",
44
- "spec/entity_type_spec.rb",
45
- "spec/identification_spec.rb",
46
- "spec/instance_spec.rb",
47
- "spec/role_values_spec.rb",
48
- "spec/roles_spec.rb",
49
- "spec/spec_helper.rb",
50
- "spec/value_type_spec.rb"
45
+ "spec/constellation/constellation_spec.rb",
46
+ "spec/constellation/instance_index_spec.rb",
47
+ "spec/constellation/instance_spec.rb",
48
+ "spec/fact_type/role_values_spec.rb",
49
+ "spec/fact_type/roles_spec.rb",
50
+ "spec/fixtures/tax.rb",
51
+ "spec/identification_scheme/identification_spec.rb",
52
+ "spec/identification_scheme/identity_change_spec.rb",
53
+ "spec/identification_scheme/identity_supertype_change_spec.rb",
54
+ "spec/object_type/entity_type/entity_type_spec.rb",
55
+ "spec/object_type/entity_type/multipart_identification_spec.rb",
56
+ "spec/object_type/value_type/autocounter_spec.rb",
57
+ "spec/object_type/value_type/numeric_spec.rb",
58
+ "spec/object_type/value_type/value_type_spec.rb",
59
+ "spec/simplecov_helper.rb",
60
+ "spec/spec_helper.rb"
51
61
  ]
52
62
  s.homepage = "http://github.com/cjheath/activefacts-api"
53
63
  s.licenses = ["MIT"]
54
64
  s.require_paths = ["lib"]
55
- s.rubygems_version = "1.8.10"
65
+ s.rubygems_version = "1.8.24"
56
66
  s.summary = "A fact-based data model DSL and API"
57
67
 
58
68
  if s.respond_to? :specification_version then
59
69
  s.specification_version = 3
60
70
 
61
71
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
72
+ s.add_development_dependency(%q<rake>, [">= 0"])
73
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
74
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
62
75
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
63
76
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
64
77
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
65
78
  s.add_development_dependency(%q<rdoc>, [">= 2.4.2"])
66
79
  else
80
+ s.add_dependency(%q<rake>, [">= 0"])
81
+ s.add_dependency(%q<jeweler>, [">= 0"])
82
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
67
83
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
68
84
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
85
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
70
86
  s.add_dependency(%q<rdoc>, [">= 2.4.2"])
71
87
  end
72
88
  else
89
+ s.add_dependency(%q<rake>, [">= 0"])
90
+ s.add_dependency(%q<jeweler>, [">= 0"])
91
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
73
92
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
74
93
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
75
94
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -41,4 +41,5 @@ require 'activefacts/api/instance' # An Instance is an instance of
41
41
  require 'activefacts/api/value' # A Value is an Instance of a value class (String, Numeric, etc)
42
42
  require 'activefacts/api/entity' # An Entity class is an Instance not of a value class
43
43
  require 'activefacts/api/standard_types' # Value classes are augmented so their subclasses may become Value Types
44
+ require 'activefacts/api/exceptions' # Relevant exceptions
44
45
  require 'activefacts/tracer'
@@ -33,7 +33,9 @@ module ActiveFacts
33
33
  #
34
34
  class Constellation
35
35
  attr_reader :vocabulary
36
- # All instances are indexed in this hash, keyed by the class object. Each instance is indexed for every supertype it has (including multiply-inherited ones). It's a bad idea to try to modify these indexes!
36
+ # All instances are indexed in this hash, keyed by the class object.
37
+ # Each instance is indexed for every supertype it has (including multiply-inherited ones).
38
+ # It's a bad idea to try to modify these indexes!
37
39
  attr_reader :instances # Can say c.instances[MyClass].each{|k, v| ... }
38
40
  # Can also say c.MyClass.each{|k, v| ... }
39
41
 
@@ -29,17 +29,9 @@ module ActiveFacts
29
29
  klass = klass.superclass
30
30
  end
31
31
 
32
- # if (o = klass.overrides_identification_of and !(o.identifying_role_names-klass.identifying_role_names).empty?)
33
- # This is a class which must initialise its superclass' identifying roles
34
- # The hash can provide the values, but those values must already be asserted
35
- # in the constellation this object will exist in, since they won't get
36
- # attached to/cloned into that constellation merely by being assigned here.
37
- # REVISIT: Nothing takes care of that, currently.
38
- #
39
- # The solution to this is to have an empty initialize, add the new instance
40
- # to the Constellation, then initialise_roles using normal assignment.
41
- # end
42
-
32
+ if args[-1].respond_to?(:has_key?) && args[-1].has_key?(:constellation)
33
+ @constellation = args.pop[:constellation]
34
+ end
43
35
  hash = args[-1].is_a?(Hash) ? args.pop.clone : nil
44
36
 
45
37
  # Pass just the hash, if there is one, else no arguments:
@@ -93,11 +85,11 @@ module ActiveFacts
93
85
  # When used as a hash key, this entity instance is compared with another by
94
86
  # comparing the values of its identifying roles
95
87
  def eql?(other)
96
- return false unless self.class == other.class
97
- self.class.identifying_role_names.each{|role_name|
98
- return false unless send(role_name).eql?(other.send(role_name))
99
- }
100
- return true
88
+ if self.class == other.class
89
+ identity_as_hash == other.identity_as_hash
90
+ else
91
+ false
92
+ end
101
93
  end
102
94
 
103
95
  # Verbalise this entity instance
@@ -117,12 +109,30 @@ module ActiveFacts
117
109
  end
118
110
  end
119
111
 
112
+ # Identifying role values in a hash form.
113
+ def identity_as_hash
114
+ identity_by(self.class)
115
+ end
116
+
117
+ # Identifying role values in a hash form by class (entity).
118
+ #
119
+ # Subtypes may have different identifying roles compared to their supertype, and therefore, a subtype entity
120
+ # may be identified differently if compared to one of its supertype.
121
+ def identity_by(klass)
122
+ roles_hash = {}
123
+ klass.identifying_roles.each do |role|
124
+ roles_hash[role.getter] = send(role.getter)
125
+ end
126
+ roles_hash
127
+ end
128
+
120
129
  # All classes that become Entity types receive the methods of this class as class methods:
121
130
  module ClassMethods
122
131
  include Instance::ClassMethods
123
132
 
124
133
  attr_accessor :identification_inherited_from
125
134
  attr_accessor :overrides_identification_of
135
+ attr_accessor :created_instances
126
136
 
127
137
  # Return the array of Role objects that define the identifying relationships of this Entity type:
128
138
  def identifying_role_names
@@ -137,11 +147,21 @@ module ActiveFacts
137
147
  # REVISIT: Should this return nil if identification_inherited_from?
138
148
  @identifying_roles ||=
139
149
  identifying_role_names.map do |role_name|
140
- role = roles[role_name] || (!superclass.is_entity_type || superclass.roles[role_name])
150
+ role = roles[role_name] || find_inherited_role(role_name)
141
151
  role
142
152
  end
143
153
  end
144
154
 
155
+ def find_inherited_role(role_name)
156
+ if !superclass.is_entity_type
157
+ false
158
+ elsif superclass.roles.has_key?(role_name)
159
+ superclass.roles[role_name]
160
+ else
161
+ superclass.find_inherited_role(role_name)
162
+ end
163
+ end
164
+
145
165
  # Convert the passed arguments into an array of raw values (or arrays of values, transitively)
146
166
  # that identify an instance of this Entity type:
147
167
  def identifying_role_values(*args)
@@ -150,7 +170,7 @@ module ActiveFacts
150
170
  # If the single arg is an instance of the correct class or a subclass,
151
171
  # use the instance's identifying_role_values
152
172
  has_hash = args[-1].is_a?(Hash)
153
- if (args.size == 1+(has_hash ?1:0) and (arg = args[0]).is_a?(self))
173
+ if (args.size == 1+(has_hash ? 1 : 0) and (arg = args[0]).is_a?(self))
154
174
  # With a secondary supertype or a subtype having separate identification,
155
175
  # we would get the wrong identifier from arg.identifying_role_values:
156
176
  return irns.map do |role_name|
@@ -163,7 +183,7 @@ module ActiveFacts
163
183
  args, arg_hash = ActiveFacts::extract_hash_args(irns, args)
164
184
 
165
185
  if args.size > irns.size
166
- raise "You've provided too many values for the identifier of #{basename}, which expects (#{irns*', '})"
186
+ raise "#{basename} expects only (#{irns*', '}) for its identifier, but you provided the extra values #{args[irns.size..-1].inspect}"
167
187
  end
168
188
 
169
189
  role_args = irns.map{|role_sym| roles(role_sym)}.zip(args)
@@ -198,13 +218,18 @@ module ActiveFacts
198
218
  instances = constellation.instances[self] # All instances of this class in this constellation
199
219
  instance = instances[key]
200
220
  # REVISIT: This ignores any additional attribute assignments
201
- return instance, key if instance # A matching instance of this class
221
+ if instance
222
+ # raise "Additional role values are ignored when asserting an existing instance" if args[-1].is_a? Hash and !args[-1].empty?
223
+ assign_additional_roles(instance, args[-1]) if args[-1].is_a? Hash and !args[-1].empty?
224
+ return instance, key # A matching instance of this class
225
+ end
202
226
 
203
227
  # Now construct each of this object's identifying roles
204
228
  irns = identifying_role_names
229
+ @created_instances ||= []
205
230
 
206
231
  has_hash = args[-1].is_a?(Hash)
207
- if args.size == 1+(has_hash ?1:0) and args[0].is_a?(self)
232
+ if args.size == 1+(has_hash ? 1 : 0) and args[0].is_a?(self)
208
233
  # We received a single argument of a compatible type
209
234
  # With a secondary supertype or a type having separate identification,
210
235
  # we would get the wrong identifier from arg.identifying_role_values:
@@ -222,9 +247,13 @@ module ActiveFacts
222
247
  elsif !arg
223
248
  value = role_key = nil
224
249
  else
225
- #trace :assert, "Asserting #{role.counterpart.object_type} with #{Array(arg).inspect} for #{self}.#{role.name}" do
226
- value, role_key = role.counterpart.object_type.assert_instance(constellation, Array(arg))
227
- #end
250
+ if role.counterpart.object_type.is_entity_type
251
+ add = !constellation.send(role.counterpart.object_type.basename.to_sym).include?([arg])
252
+ else
253
+ add = !constellation.send(role.counterpart.object_type.basename.to_sym).include?(arg)
254
+ end
255
+ value, role_key = role.counterpart.object_type.assert_instance(constellation, Array(arg))
256
+ @created_instances << [role.counterpart, value] if add
228
257
  end
229
258
  key << role_key
230
259
  value
@@ -233,19 +262,35 @@ module ActiveFacts
233
262
  end
234
263
 
235
264
  #trace :assert, "Constructing new #{self} with #{values.inspect}" do
236
- instance = new(*values)
265
+ values << { :constellation => constellation }
266
+ instance = new(*values)
237
267
  #end
238
268
 
239
- # Make the new entity instance a member of this constellation:
240
- instance.constellation = constellation
269
+ assign_additional_roles(instance, arg_hash)
270
+
271
+ return *index_instance(instance, key, irns)
272
+
273
+ rescue DuplicateIdentifyingValueException
274
+ @created_instances.each do |role, v|
275
+ if !v.respond_to?(:retract)
276
+ v = constellation.send(role.object_type.basename.to_sym)[[v]]
277
+ end
278
+ v.retract if v
279
+ end
280
+ @created_instances = []
281
+ raise
282
+ end
241
283
 
284
+ def assign_additional_roles(instance, arg_hash)
242
285
  # Now assign any extra args in the hash which weren't identifiers (extra identifiers will be assigned again)
243
286
  (arg_hash ? arg_hash.entries : []).each do |role_name, value|
244
287
  role = roles(role_name)
288
+
289
+ if !instance.instance_index_counterpart(role).include?(value)
290
+ @created_instances << [role, value]
291
+ end
245
292
  instance.send(role.setter, value)
246
293
  end
247
-
248
- return *index_instance(instance, key, irns)
249
294
  end
250
295
 
251
296
  def index_instance(instance, key = nil, key_roles = nil) #:nodoc:
@@ -0,0 +1,17 @@
1
+ #
2
+ # ActiveFacts Runtime API
3
+ # Custom exception classes
4
+ #
5
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
+
7
+ module ActiveFacts
8
+ module API
9
+ class DuplicateIdentifyingValueException < StandardError
10
+ def initialize(desc)
11
+ super("Illegal attempt to assert #{desc[:class].basename} having identifying value" +
12
+ " (#{desc[:role].name} is #{desc[:value].verbalise})," +
13
+ " when #{desc[:value].related_entities.map(&:verbalise).join(", ")} already exists")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -24,8 +24,99 @@ module ActiveFacts
24
24
  end
25
25
  end
26
26
 
27
+ # Detect inconsistencies within constellation if this entity was updated
28
+ # with the specified role/value pair.
29
+ def detect_inconsistencies(role, value)
30
+ if duplicate_identifying_values?(role, value)
31
+ exception_data = {
32
+ :value => value,
33
+ :role => role,
34
+ :class => self.class
35
+ }
36
+
37
+ raise DuplicateIdentifyingValueException.new(exception_data)
38
+ end
39
+ end
40
+
41
+ # Checks if instance have duplicate values within its constellation.
42
+ #
43
+ # Only works on identifying roles.
44
+ def duplicate_identifying_values?(role, value)
45
+ @constellation && role.is_identifying && !is_unique?(:role => role, :value => value)
46
+ end
47
+
48
+ # Checks if instance would still be unique if it was updated with
49
+ # args.
50
+ #
51
+ # args should be a hash containing the role and value to update
52
+ # and the name of the identifying value as the key.
53
+ #
54
+ # For example, if a Person is identified by name and family_name:
55
+ # updated_values = { :name => "John" }
56
+ # Would merge this hash with the one defining the current instance
57
+ # and verify in our constellation if it exists.
58
+ #
59
+ # The uniqueness of the entity will also be checked within its supertypes.
60
+ #
61
+ # An Employee -subtype of a Person- identified by its employee_id would
62
+ # collide with a Person if it has the same name. But `name` may not be
63
+ # an identifying value for the Employee identification scheme.
64
+ def is_unique?(args)
65
+ duplicate = ([self.class] + self.class.supertypes_transitive).detect do |klass|
66
+ old_identity = identity_by(klass)
67
+ if klass.identifying_roles.include?(args[:role])
68
+ new_identity = old_identity.merge(args[:role].getter => args[:value])
69
+ @constellation.instances[klass].include?(new_identity)
70
+ else
71
+ false
72
+ end
73
+ end
74
+
75
+ !duplicate
76
+ end
77
+
78
+ # List entities which reference the current one.
79
+ #
80
+ # Once an entity is found, it will also search for
81
+ # related entities of this instance.
82
+ def related_entities(instances = [])
83
+ self.class.roles.each do |role_name, role|
84
+ instance_index_counterpart(role).each do |irv, instance|
85
+ if instance.class.is_entity_type && instance.is_identified_by?(self)
86
+ if !instances.include?(instance)
87
+ instances << instance
88
+ instance.related_entities(instances)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ instances
94
+ end
95
+
96
+ # Determine if entity is an identifying value
97
+ # of the current instance.
98
+ def is_identified_by?(entity)
99
+ self.class.identifying_roles.detect do |role|
100
+ send(role.getter) == entity
101
+ end
102
+ end
103
+
104
+ def instance_index
105
+ @constellation.send(self.class.basename.to_sym)
106
+ end
107
+
108
+ def instance_index_counterpart(role)
109
+ if @constellation && role.counterpart
110
+ @constellation.send(role.counterpart.object_type.basename.to_sym)
111
+ else
112
+ []
113
+ end
114
+ end
115
+
27
116
  # Verbalise this instance
117
+ # REVISIT: Should it raise an error if it was not redefined ?
28
118
  def verbalise
119
+ # REVISIT: Should it raise an error if it was not redefined ?
29
120
  # This method should always be overridden in subclasses
30
121
  end
31
122
 
@@ -48,7 +139,11 @@ module ActiveFacts
48
139
  # puts "Not removing role #{role_name} from counterpart RoleValues #{counterpart.name}"
49
140
  # Duplicate the array using to_a, as the RoleValues here will be modified as we traverse it:
50
141
  send(role.name).to_a.each do |v|
51
- v.send(counterpart.setter, nil)
142
+ if counterpart.is_identifying
143
+ v.retract
144
+ else
145
+ v.send(counterpart.setter, nil)
146
+ end
52
147
  end
53
148
  end
54
149
  end