activefacts-api 0.9.5 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.5
1
+ 0.9.6
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "activefacts-api"
8
- s.version = "0.9.5"
8
+ s.version = "0.9.6"
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 = "2013-01-16"
12
+ s.date = "2013-01-22"
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 = [
@@ -62,7 +62,8 @@ Gem::Specification.new do |s|
62
62
  "spec/object_type/value_type/numeric_spec.rb",
63
63
  "spec/object_type/value_type/value_type_spec.rb",
64
64
  "spec/simplecov_helper.rb",
65
- "spec/spec_helper.rb"
65
+ "spec/spec_helper.rb",
66
+ "spec/support/reduce_exceptions_helper.rb"
66
67
  ]
67
68
  s.homepage = "http://github.com/cjheath/activefacts-api"
68
69
  s.licenses = ["MIT"]
@@ -34,8 +34,17 @@ module ActiveFacts
34
34
  class Constellation
35
35
  attr_reader :vocabulary
36
36
 
37
- def valid_object_type klass
38
- klass.is_a?(Class) and klass.modspace == @vocabulary and klass.respond_to?(:assert_instance)
37
+ def invalid_object_type klass
38
+ case
39
+ when !klass.is_a?(Class)
40
+ 'is not a Class'
41
+ when klass.modspace != @vocabulary
42
+ "is defined in #{klass.modspace}, not #{@vocabulary.name}"
43
+ when !klass.respond_to?(:assert_instance)
44
+ "is not declared as an object type"
45
+ else
46
+ nil
47
+ end
39
48
  end
40
49
 
41
50
  # "instances" is an index (keyed by the Class object) of indexes to instances.
@@ -43,8 +52,8 @@ module ActiveFacts
43
52
  # The method_missing definition supports the syntax: c.MyClass.each{|k, v| ... }
44
53
  def instances
45
54
  @instances ||= Hash.new do |h,k|
46
- unless valid_object_type k
47
- raise "A constellation over #{@vocabulary.name} can only index instances of classes in that vocabulary, not #{k.inspect}"
55
+ if reason = invalid_object_type(k)
56
+ raise InvalidObjectType.new(@vocabulary, k, reason)
48
57
  end
49
58
  h[k] = InstanceIndex.new(self, k)
50
59
  end
@@ -98,7 +107,7 @@ module ActiveFacts
98
107
  end
99
108
 
100
109
  def has_candidate klass, key
101
- @candidates && @candidates.detect{|c| c.is_a?(klass) && c.identifying_role_values == key }
110
+ @candidates && @candidates.detect{|c| c.is_a?(klass) && c.identifying_role_values(klass) == key }
102
111
  end
103
112
 
104
113
  # Create a new empty Constellation over the given Vocabulary
@@ -155,11 +164,11 @@ module ActiveFacts
155
164
  # With parameters, assert an instance of the object_type identified by the values passed as args.
156
165
  def method_missing(m, *args, &b)
157
166
  klass = @vocabulary.const_get(m)
158
- if valid_object_type klass
167
+ if invalid_object_type klass
168
+ super
169
+ else
159
170
  define_class_accessor m, klass
160
171
  send(m, *args, &b)
161
- else
162
- super
163
172
  end
164
173
  end
165
174
 
@@ -188,9 +197,9 @@ module ActiveFacts
188
197
  role_values =
189
198
  single_roles.map{|role|
190
199
  value =
191
- begin
200
+ if instance.respond_to?(role)
192
201
  value = instance.send(role)
193
- rescue NoMethodError
202
+ else
194
203
  instance.class.roles(role) # This role has not yet been realised
195
204
  end
196
205
  [ role_name = role.to_s.camelcase, value ]
@@ -60,7 +60,9 @@ class ::DateTime
60
60
  end
61
61
 
62
62
  class ::Time
63
- def identifying_role_values; self; end
63
+ def identifying_role_values klass = nil
64
+ self
65
+ end
64
66
 
65
67
  def self.new_instance constellation, *a
66
68
  t =
@@ -26,7 +26,7 @@ module ActiveFacts
26
26
  # The identifying roles of secondary supertypes must also be assigned
27
27
  # here.
28
28
  def initialize(arg_hash)
29
- raise "REVISIT: Unexpected parameters in call to #{self}.new" unless arg_hash.is_a?(Hash)
29
+ raise ArgumentError.new("#{self}.new expects a hash. You should use assert instead anyhow") unless arg_hash.is_a?(Hash)
30
30
 
31
31
  super(arg_hash)
32
32
 
@@ -129,9 +129,10 @@ module ActiveFacts
129
129
 
130
130
  # Return the array of the values of this instance's identifying roles
131
131
  def identifying_role_values(klass = self.class)
132
- klass.identifying_role_names.map do |role_name|
133
- value = send(role_name)
134
- value.identifying_role_values
132
+ klass.identifying_roles.map do |role|
133
+ value = send(role.name)
134
+ counterpart_class = role.counterpart && role.counterpart.object_type
135
+ value.identifying_role_values(counterpart_class)
135
136
  end
136
137
  end
137
138
 
@@ -152,6 +153,56 @@ module ActiveFacts
152
153
  roles_hash
153
154
  end
154
155
 
156
+ # This role is identifying, so if is changed, not only
157
+ # must the current object be re-indexed, but also entities
158
+ # identified by this entity. Save the current key and
159
+ # class for each such instance.
160
+ # This function is transitive!
161
+ def analyse_impacts role
162
+ impacts = []
163
+
164
+ # Consider the object itself and all its supertypes
165
+ ([self.class]+self.class.supertypes_transitive).map do |supertype|
166
+ next unless supertype.identifying_roles.include?(role)
167
+
168
+ old_key = identifying_role_values(supertype)
169
+ # puts "Need to reindex #{self.class} as #{supertype} from #{old_key.inspect}"
170
+ impacts << [supertype, self, old_key]
171
+ end
172
+
173
+ # Now consider objects whose identifiers include this object.
174
+ # Find our roles in those identifiers first.
175
+ impacted_roles = []
176
+ self.class.all_roles.each do |n, role|
177
+ if role.counterpart && role.counterpart.is_identifying
178
+ # puts "Changing #{role.inspect} affects #{role.inspect}"
179
+ impacted_roles << role
180
+ end
181
+ end
182
+
183
+ impacted_roles.each do |role|
184
+ affected_instances = Array(instance_variable_get(role.variable))
185
+ # puts "considering #{affected_instances.size} #{role.object_type.name} instances that include #{role.inspect}: #{affected_instances.map(&:identifying_role_values).inspect}"
186
+ affected_instances.each do |counterpart|
187
+ impacts.concat(counterpart.analyse_impacts(role.counterpart))
188
+ end
189
+ end
190
+ impacts
191
+ end
192
+
193
+ def apply_impacts impacts
194
+ impacts.each do |klass, entity, old_key|
195
+ instance_index = entity.constellation.instances[klass]
196
+ new_key = entity.identifying_role_values(klass)
197
+ # puts "Reindexing #{klass} from #{old_key.inspect} to #{new_key.inspect}"
198
+
199
+ if new_key != old_key
200
+ instance_index.delete(old_key)
201
+ instance_index[new_key] = entity
202
+ end
203
+ end
204
+ end
205
+
155
206
  # All classes that become Entity types receive the methods of this class as class methods:
156
207
  module ClassMethods
157
208
  include Instance::ClassMethods
@@ -190,21 +241,22 @@ module ActiveFacts
190
241
 
191
242
  def check_supertype_identifiers_match instance, arg_hash
192
243
  supertypes_transitive.each do |supertype|
193
- supertype.identifying_role_names.each do |role_name|
194
- next unless arg_hash.include?(role_name) # No contradiction here
195
- new_value = arg_hash[role_name]
196
- existing_value = instance.send(role_name.to_sym)
244
+ supertype.identifying_roles.each do |role|
245
+ next unless arg_hash.include?(role.name) # No contradiction here
246
+ new_value = arg_hash[role.name]
247
+ existing_value = instance.send(role.name.to_sym)
197
248
 
198
249
  # Quick check for an exact match:
199
- next if existing_value == new_value or existing_value.identifying_role_values == new_value
250
+ counterpart_class = role.counterpart && role.counterpart.object_type
251
+ next if existing_value == new_value or existing_value.identifying_role_values(counterpart_class) == new_value
200
252
 
201
253
  # Coerce the new value to identifying values for the counterpart role's type:
202
- role = supertype.roles(role_name)
254
+ role = supertype.roles(role.name)
203
255
  new_key = role.counterpart.object_type.identifying_role_values(instance.constellation, [new_value])
204
256
  # REVISIT: Check that the next line actually gets hit, otherwise strip it out
205
257
  next if existing_value == new_key # This can happen when the counterpart is a value type
206
258
 
207
- existing_key = existing_value.identifying_role_values
259
+ existing_key = existing_value.identifying_role_values(counterpart_class)
208
260
  next if existing_key == new_key
209
261
  raise TypeConflictException.new(basename, supertype, new_key, existing_key)
210
262
  end
@@ -248,7 +300,7 @@ module ActiveFacts
248
300
 
249
301
  # Complain if we have left-over arguments
250
302
  if args.size > 0
251
- raise "#{basename} expects only (#{irns*', '}) for its identifier, but you provided additional values #{args.inspect}"
303
+ raise UnexpectedIdentifyingValueException.new(self, irns, args)
252
304
  end
253
305
 
254
306
  # The arg_hash will be used to construct a new instance, if necessary
@@ -266,7 +318,8 @@ module ActiveFacts
266
318
  end
267
319
  elsif proto
268
320
  value = proto.send(n)
269
- value = value.identifying_role_values
321
+ counterpart_class = role.counterpart && role.counterpart.object_type
322
+ value = value.identifying_role_values(counterpart_class)
270
323
  arg_hash[n] = value # Save the value for making a new instance
271
324
  next value if (role.is_unary)
272
325
  else
@@ -338,7 +391,7 @@ module ActiveFacts
338
391
  # which is a list of roles it plays. The identification scheme may be
339
392
  # inherited from a superclass.
340
393
  def identified_by(*args) #:nodoc:
341
- raise "You must list the roles which will identify #{self.basename}" unless args.size > 0
394
+ raise MissingIdentificationException.new(self) unless args.size > 0
342
395
 
343
396
  # Catch the case where we state the same identification as our superclass:
344
397
  inherited_role_names = identifying_role_names
@@ -9,31 +9,79 @@ module ActiveFacts
9
9
  class ActiveFactsException < StandardError
10
10
  end
11
11
 
12
- class ActiveFactsSchemaException < ActiveFactsException
12
+ class SchemaException < ActiveFactsException
13
13
  end
14
14
 
15
- class ActiveFactsRuntimeException < ActiveFactsException
15
+ class RuntimeException < ActiveFactsException
16
16
  end
17
17
 
18
- class CrossVocabularyRoleException < ActiveFactsSchemaException
18
+ class CrossVocabularyRoleException < SchemaException
19
19
  def initialize klass, vocabulary
20
20
  super "#{klass} must be an object type in #{vocabulary.name}"
21
21
  end
22
22
  end
23
23
 
24
- class RoleNotDefinedException < ActiveFactsRuntimeException
24
+ class InvalidEntityException < SchemaException
25
+ def initialize klass
26
+ super "#{klass.basename} may not be an entity type"
27
+ end
28
+ end
29
+
30
+ class InvalidIdentificationException < SchemaException
31
+ def initialize object_type, role, is_single
32
+ msg =
33
+ if is_single
34
+ "#{object_type} has a single identifying role '#{role}' which is has_one, but must be one_to_one"
35
+ else
36
+ raise "#{object_type} has an identifying role '#{role}' which is one_to_one, but must be has_one"
37
+ end
38
+ super msg
39
+ end
40
+ end
41
+
42
+ class MissingIdentificationException < SchemaException
43
+ def initialize klass
44
+ super "You must list the roles which will identify #{klass.basename}"
45
+ end
46
+ end
47
+
48
+ class InvalidObjectType < SchemaException
49
+ def initialize vocabulary, klass, reason
50
+ raise "A constellation over #{vocabulary.name} cannot index instances of #{klass} because it #{reason}"
51
+ end
52
+ end
53
+
54
+ class InvalidSupertypeException < SchemaException
55
+ end
56
+
57
+ class DuplicateRoleException < SchemaException
58
+ end
59
+
60
+ class UnrecognisedOptionsException < SchemaException
61
+ def initialize declaration, instance, option_names
62
+ raise "Unrecognised options on declaration of #{declaration} #{instance}: #{option_names.inspect}"
63
+ end
64
+ end
65
+
66
+ class RoleNotDefinedException < RuntimeException
25
67
  def initialize klass, role_name
26
68
  super "Role #{klass.basename}.#{role_name} is not defined"
27
69
  end
28
70
  end
29
71
 
30
- class MissingMandatoryRoleValueException < ActiveFactsRuntimeException
72
+ class UnexpectedIdentifyingValueException < RuntimeException
73
+ def initialize object_type, identifying_role_names, extra_args
74
+ super "#{object_type.basename} expects only (#{identifying_role_names*', '}) for its identifier, but you provided additional values #{extra_args.inspect}"
75
+ end
76
+ end
77
+
78
+ class MissingMandatoryRoleValueException < RuntimeException
31
79
  def initialize klass, role
32
80
  super "A #{role.counterpart.object_type.basename} is required to satisfy the #{role.name.inspect} role of #{klass.basename}"
33
81
  end
34
82
  end
35
83
 
36
- class DuplicateIdentifyingValueException < ActiveFactsRuntimeException
84
+ class DuplicateIdentifyingValueException < RuntimeException
37
85
  def initialize(klass, role_name, value)
38
86
  super("Illegal attempt to assert #{klass.basename} having identifying value" +
39
87
  " (#{role_name} is #{value.verbalise})," +
@@ -42,14 +90,14 @@ module ActiveFacts
42
90
  end
43
91
 
44
92
  # When an existing object having multiple identification patterns is re-asserted, all the keys must match the existing object
45
- class TypeConflictException < ActiveFactsRuntimeException
93
+ class TypeConflictException < RuntimeException
46
94
  def initialize(klass, supertype, key, existing)
47
95
  super "#{klass} cannot be asserted to have #{supertype} identifier #{key.inspect} because the existing object has #{existing.inspect}"
48
96
  end
49
97
  end
50
98
 
51
99
  # When a new entity is asserted, but a supertype identifier matches an existing object of a different type, type migration is implied but unfortunately is impossible in Ruby
52
- class TypeMigrationException < ActiveFactsRuntimeException
100
+ class TypeMigrationException < RuntimeException
53
101
  def initialize(klass, supertype, key)
54
102
  super "#{klass} cannot be asserted due to the prior existence of a conflicting #{supertype} identified by #{key.inspect}"
55
103
  end
@@ -16,7 +16,7 @@ class Guid
16
16
  elsif (v = i.to_s).length == 36 and !(v !~ /[^0-9a-f]/i)
17
17
  @value = v.clone.freeze
18
18
  else
19
- raise "Illegal non-Guid value #{i.inspect} given for Guid"
19
+ raise ArgumentError.new("Illegal non-Guid value #{i.inspect} given for Guid")
20
20
  end
21
21
  end
22
22
 
@@ -34,19 +34,20 @@ module ActiveFacts
34
34
 
35
35
  # If this instance's role is updated to the new value, does that cause a collision?
36
36
  # We need to check each superclass that has a different identification pattern
37
- def check_value_change_legality(role, value)
37
+ def check_identification_change_legality(role, value)
38
38
  return unless @constellation && role.is_identifying
39
39
 
40
40
  klasses = [self.class] + self.class.supertypes_transitive
41
41
  last_identity = nil
42
42
  last_irns = nil
43
+ counterpart_class = role.counterpart ? role.counterpart.object_type : value.class
43
44
  duplicate = klasses.detect do |klass|
44
45
  next false unless klass.identifying_roles.include?(role)
45
46
  irns = klass.identifying_role_names
46
47
  if last_irns != irns
47
48
  last_identity = identifying_role_values(klass)
48
49
  role_position = irns.index(role.name)
49
- last_identity[role_position] = value.identifying_role_values
50
+ last_identity[role_position] = value.identifying_role_values(counterpart_class)
50
51
  end
51
52
  @constellation.instances[klass][last_identity]
52
53
  end
@@ -90,11 +91,19 @@ module ActiveFacts
90
91
  # Objects being created do not have to have non-identifying mandatory roles,
91
92
  # so we allow retracting to the same state.
92
93
  if role.unique
94
+ i = send(role.getter)
95
+ next unless i
93
96
  if counterpart.is_identifying && counterpart.mandatory
94
- i = send(role.name) and i.retract
97
+ i.retract
95
98
  else
96
- send role.setter, nil
99
+ if (counterpart.unique)
100
+ # REVISIT: This will incorrectly fail to propagate a key change for a non-mandatory role
101
+ i.send(counterpart.setter, nil, false)
102
+ else
103
+ i.send(role.counterpart.getter).update(self, nil)
104
+ end
97
105
  end
106
+ instance_variable_set(role.variable, nil)
98
107
  else
99
108
  # puts "Not removing role #{role_name} from counterpart RoleValues #{counterpart.name}"
100
109
  # Duplicate the array using to_a, as the RoleValues here will be modified as we traverse it:
@@ -104,9 +113,10 @@ module ActiveFacts
104
113
  if counterpart.is_identifying && counterpart.mandatory
105
114
  counterpart_instance.retract
106
115
  else
107
- counterpart_instance.send(counterpart.setter, nil)
116
+ counterpart_instance.send(counterpart.setter, nil, false)
108
117
  end
109
118
  end
119
+ instance_variable_set(role.variable, nil)
110
120
  end
111
121
  end
112
122
  end
@@ -43,7 +43,7 @@ module ActiveFacts
43
43
 
44
44
  def refresh_key(key)
45
45
  value = @hash.delete(key)
46
- @hash[value.identifying_role_values] = value if value
46
+ @hash[value.identifying_role_values(@klass)] = value if value
47
47
  end
48
48
  end
49
49
  end
@@ -116,7 +116,7 @@ class AutoCounter
116
116
  attr_reader :place_holder_number
117
117
  def initialize(i = :new)
118
118
  unless i == :new or i.is_a?(Integer) or i.is_a?(AutoCounter)
119
- raise "AutoCounter #{self.class} may not be #{i.inspect}"
119
+ raise ArgumentError.new("AutoCounter #{self.class} may not be #{i.inspect}")
120
120
  end
121
121
  @@place_holder ||= 0
122
122
  case i
@@ -191,7 +191,7 @@ class AutoCounter
191
191
  to_s.eql?(o.to_s)
192
192
  end
193
193
 
194
- def identifying_role_values
194
+ def identifying_role_values klass = nil
195
195
  self
196
196
  end
197
197
 
@@ -46,6 +46,15 @@ module ActiveFacts
46
46
  end
47
47
  end
48
48
 
49
+ def all_roles
50
+ return @all_roles if @all_roles
51
+ @all_roles = roles.dup
52
+ supertypes_transitive.each do |klass|
53
+ @all_roles.merge!(klass.roles)
54
+ end
55
+ @all_roles
56
+ end
57
+
49
58
  # Define a unary fact type attached to this object_type; in essence, a boolean attribute.
50
59
  #
51
60
  # Example: maybe :is_ceo
@@ -91,11 +100,11 @@ module ActiveFacts
91
100
  case type
92
101
  when :has_one
93
102
  if identifying_role_names.size == 1
94
- raise "Entity type #{self} cannot be identified by a single role '#{role}' unless that role is one_to_one"
103
+ raise InvalidIdentificationException.new(self, role, true)
95
104
  end
96
105
  when :one_to_one
97
106
  if identifying_role_names.size > 1
98
- raise "Entity type #{self} cannot be identified by a single role '#{role}' unless that role is has_one"
107
+ raise InvalidIdentificationException.new(self, role, false)
99
108
  end
100
109
  end
101
110
  end
@@ -120,12 +129,14 @@ module ActiveFacts
120
129
  # No late binding here:
121
130
  (object_type = vocabulary.const_get(object_type.to_s.camelcase))
122
131
  else
123
- raise "Illegal supertype #{object_type.inspect} for #{self.class.basename}"
132
+ raise InvalidSupertypeException.new("Illegal supertype #{object_type.inspect} for #{self.class.basename}")
124
133
  end
125
- raise "#{supertype.name} must be an object type in #{vocabulary.name}" unless supertype.respond_to?(:vocabulary) and supertype.vocabulary == self.vocabulary
134
+ unless supertype.respond_to?(:vocabulary) and supertype.vocabulary == self.vocabulary
135
+ raise InvalidSupertypeException.new("#{supertype.name} must be an object type in #{vocabulary.name}")
136
+ end
126
137
 
127
138
  if is_entity_type != supertype.is_entity_type
128
- raise "#{self} < #{supertype}: A value type may not be a supertype of an entity type, and vice versa"
139
+ raise InvalidSupertypeException.new("#{self} < #{supertype}: A value type may not be a supertype of an entity type, and vice versa")
129
140
  end
130
141
 
131
142
  @supertypes << supertype
@@ -194,8 +205,11 @@ module ActiveFacts
194
205
 
195
206
  # Shared code for both kinds of binary fact type (has_one and one_to_one)
196
207
  def define_binary_fact_type(one_to_one, role_name, related, mandatory, related_role_name)
197
- # REVISIT: What if the role exists on a supertype? This won't prevent that:
198
- raise "#{name} cannot have more than one role named #{role_name}" if roles[role_name]
208
+ # REVISIT: This should be all_roles, to catch duplicate role name in a supertype
209
+ # if all_roles[role_name]
210
+ if roles[role_name]
211
+ raise DuplicateRoleException.new("#{name} cannot have more than one role named #{role_name}")
212
+ end
199
213
  roles[role_name] = role = Role.new(self, nil, role_name, mandatory)
200
214
 
201
215
  # There may be a forward reference here where role_name is a Symbol,
@@ -228,7 +242,9 @@ module ActiveFacts
228
242
 
229
243
  def define_single_role_getter(role)
230
244
  define_method role.getter do |*a|
231
- raise "Parameters passed to #{self.class.name}\##{role.name}" if a.size > 0
245
+ if a.size > 0
246
+ raise ArgumentError.new("wrong number of arguments (#{a.size} for 0)")
247
+ end
232
248
  instance_variable_get(role.variable)
233
249
  end
234
250
  end
@@ -236,46 +252,40 @@ module ActiveFacts
236
252
  def define_one_to_one_accessor(role)
237
253
  define_single_role_getter(role)
238
254
 
239
- define_method role.setter do |value|
240
- old = instance_variable_get(role.variable)
255
+ # What I want is the following, but it doesn't work in Ruby 1.8
256
+ # define_method role.setter do |value, mutual_propagation = true|
257
+ define_method role.setter do |*a|
258
+ value, mutual_propagation = *a
259
+ mutual_propagation = true if a.size < 2
260
+ role_var = role.variable
241
261
 
242
- # When exactly the same value instance is assigned, we're done:
243
- return true if old == value
262
+ # Get old value, and jump out early if it's unchanged:
263
+ old = instance_variable_get(role_var)
264
+ return value if old == value
244
265
 
266
+ # assert a new instance for the role value if necessary
245
267
  if value and o = role.counterpart.object_type and (!value.is_a?(o) || value.constellation != @constellation)
246
268
  value = @constellation.assert(o, *Array(value))
247
- # return true if old.equal?(value) # Occurs when same value but not same instance is assigned
248
- return true if old == value
269
+ return value if old == value
249
270
  end
250
271
 
251
- dependent_entities = nil
252
- if (role.is_identifying)
253
- check_value_change_legality(role, value)
272
+ if (role.is_identifying) # We're changing this object's key. Check legality and prepare to propagate
273
+ check_identification_change_legality(role, value)
254
274
 
255
- # We're changing this object's key.
256
- # Find all object which are identified by this object, and save their old keys
257
- if @constellation && old
258
- dependent_entities = old.related_entities.map do |entity|
259
- [entity.identifying_role_values, entity]
260
- end
261
- end
275
+ # puts "Starting to analyse impact of changing 1-1 #{role.inspect} to #{value.inspect}"
276
+ impacts = analyse_impacts(role)
262
277
  end
263
278
 
264
- instance_variable_set(role.variable, value)
279
+ instance_variable_set(role_var, value)
265
280
 
266
281
  # Remove self from the old counterpart:
267
- old.send(role.counterpart.setter, nil) if old
282
+ old.send(role.counterpart.setter, nil, false) if old and mutual_propagation
268
283
 
269
284
  @constellation.when_admitted do
270
285
  # Assign self to the new counterpart
271
286
  value.send(role.counterpart.setter, self) if value
272
287
 
273
- # Propagate dependent key changes
274
- if dependent_entities
275
- dependent_entities.each do |old_key, entity|
276
- entity.instance_index.refresh_key(old_key)
277
- end
278
- end
288
+ apply_impacts(impacts) if impacts # Propagate dependent key changes
279
289
  end
280
290
 
281
291
  value
@@ -285,46 +295,40 @@ module ActiveFacts
285
295
  def define_one_to_many_accessor(role)
286
296
  define_single_role_getter(role)
287
297
 
288
- define_method role.setter do |value|
298
+ # What I want is the following, but it doesn't work in Ruby 1.8
299
+ # define_method role.setter do |value, mutual_propagation = true|
300
+ define_method role.setter do |*a|
301
+ value, mutual_propagation = *a
302
+ mutual_propagation = true if a.size < 2
289
303
  role_var = role.variable
290
304
 
291
305
  # Get old value, and jump out early if it's unchanged:
292
306
  old = instance_variable_get(role_var)
293
- return value if old.equal?(value) # Occurs during one_to_one assignment, for example
307
+ return value if old == value
294
308
 
295
309
  # assert a new instance for the role value if necessary
296
310
  if value and o = role.counterpart.object_type and (!value.is_a?(o) || value.constellation != @constellation)
297
311
  value = @constellation.assert(o, *Array(value))
298
- return value if old.equal?(value) # Occurs when another instance having the same value is assigned
312
+ return value if old == value # Occurs when another instance having the same value is assigned
299
313
  end
300
314
 
301
- dependent_entities = nil
302
- if (role.is_identifying)
303
- check_value_change_legality(role, value) if value
315
+ if (role.is_identifying) # We're changing this object's key. Check legality and prepare to propagate
316
+ check_identification_change_legality(role, value)
304
317
 
305
- if old && old.constellation
306
- # If our identity has changed and we identify others, prepare to reindex them
307
- dependent_entities = old.related_entities.map do |entity|
308
- [entity.identifying_role_values, entity]
309
- end
310
- end
318
+ # puts "Starting to analyse impact of changing 1-N #{role.inspect} to #{value.inspect}"
319
+ impacts = analyse_impacts(role)
311
320
  end
312
321
 
313
322
  instance_variable_set(role_var, value)
314
323
 
315
324
  # Remove "self" from the old counterpart:
316
- old.send(getter = role.counterpart.getter).update(self, nil) if old
325
+ old.send(getter = role.counterpart.getter).update(self, nil) if old && mutual_propagation
317
326
 
318
327
  @constellation.when_admitted do
319
- # REVISIT: Delay co-referencing here if the object is still a candidate
320
328
  # Add "self" into the counterpart
321
329
  value.send(getter ||= role.counterpart.getter).update(old, self) if value
322
330
 
323
- if dependent_entities
324
- dependent_entities.each do |key, entity|
325
- entity.instance_index.refresh_key(key)
326
- end
327
- end
331
+ apply_impacts(impacts) if impacts # Propagate dependent key changes
328
332
  end
329
333
 
330
334
  value
@@ -391,7 +395,7 @@ module ActiveFacts
391
395
  related = related_name
392
396
  related_name = related_name.to_s.snakecase
393
397
  else
394
- raise "Invalid type for :class option on :#{role_name}"
398
+ raise ArgumentError.new("Invalid type #{related_name.class} for :class option on :#{role_name}, must be a Class, Symbol or String")
395
399
  end
396
400
 
397
401
  # resolve the Symbol to a Class now if possible:
@@ -412,11 +416,15 @@ module ActiveFacts
412
416
  reading = options.delete(:reading) # REVISIT: Implement verbalisation
413
417
  role_value_constraint = options.delete(:restrict) # REVISIT: Implement role value constraints
414
418
 
415
- raise "Unrecognised options on #{role_name}: #{options.keys.inspect}" unless options.empty?
419
+ additional_role_options options
420
+
421
+ raise UnrecognisedOptionsException.new("role", role_name, options.keys) unless options.empty?
416
422
 
417
- # Avoid a confusing mismatch:
418
- # Note that if you have a role "supervisor" and a sub-class "Supervisor", this'll bitch.
419
- if (!specified_class && Class === related && (indicated = vocabulary.object_type(role_name)) && indicated != related)
423
+ # If you have a role "supervisor" and a sub-class "Supervisor", this'll bitch.
424
+ if !specified_class and # No specified :class was provided
425
+ related.is_a?(Class) and
426
+ (indicated = vocabulary.object_type(role_name)) and
427
+ indicated != related
420
428
  raise "Role name #{role_name} indicates a different counterpart object_type #{indicated} than specified"
421
429
  end
422
430
 
@@ -439,6 +447,10 @@ module ActiveFacts
439
447
  ]
440
448
  end
441
449
 
450
+ def additional_role_options options
451
+ # This is a hook for extensions to override. Any extension options should be deleted from the options hash.
452
+ end
453
+
442
454
  def when_bound(object_type, *args, &block)
443
455
  case object_type
444
456
  when Class
@@ -33,19 +33,25 @@ ValueClasses.each{|c|
33
33
  # Cannot subclass or delegate True, False or nil, so inject the required behaviour
34
34
  class TrueClass #:nodoc:
35
35
  def verbalise(role_name = nil); role_name ? "#{role_name}: true" : "true"; end
36
- def identifying_role_values; self; end
36
+ def identifying_role_values klass = nil
37
+ self
38
+ end
37
39
  def self.identifying_role_values(*a); a.replace([{}]); true end
38
40
  end
39
41
 
40
42
  class FalseClass #:nodoc:
41
43
  def verbalise(role_name = nil); role_name ? "#{role_name}: false" : "false"; end
42
- def identifying_role_values; self; end
44
+ def identifying_role_values klass = nil
45
+ self
46
+ end
43
47
  def self.identifying_role_values(*a); a.replace([{}]); false end
44
48
  end
45
49
 
46
50
  class NilClass #:nodoc:
47
51
  def verbalise; "nil"; end
48
- def identifying_role_values; self; end
52
+ def identifying_role_values klass = nil
53
+ self
54
+ end
49
55
  def self.identifying_role_values(*a); a.replace([{}]); nil end
50
56
  end
51
57
 
@@ -53,7 +59,7 @@ class Class
53
59
  # Make this Class into a ObjectType and if necessary its module into a Vocabulary.
54
60
  # The parameters are the names (Symbols) of the identifying roles.
55
61
  def identified_by *args, &b
56
- raise "#{basename} is not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
62
+ raise InvalidEntityException(self) if respond_to? :value_type # Don't make a ValueType into an EntityType
57
63
 
58
64
  # The inclusion of instance methods triggers ClassMethods to be included in the class too
59
65
  include ActiveFacts::API::Entity
@@ -28,9 +28,8 @@ module ActiveFacts
28
28
 
29
29
  # A value is its own key, unless it's a delegate for a raw value
30
30
  def identifying_role_values(klass = nil) #:nodoc:
31
- # The identifying role value for the supertype of a value type is always the same as for the subtype:
32
- # raise "Value Types cannot return identifying_role_values for supertypes" if klass and klass != self.class
33
- __getobj__ rescue self
31
+ # The identifying role value for the supertype of a value type is always the same as for the subtype
32
+ respond_to?(:__getobj__) ? __getobj__ : self
34
33
  end
35
34
 
36
35
  # All ValueType classes include the methods defined here
@@ -41,7 +40,7 @@ module ActiveFacts
41
40
  # REVISIT: args could be a hash, with keys :length, :scale, :unit, :allow
42
41
  options = (args[-1].is_a?(Hash) ? args.pop : {})
43
42
  options.each do |key, value|
44
- raise "unknown value type option #{key}" unless respond_to?(key)
43
+ raise UnrecognisedOptionsException.new('value_type', basename, key) unless respond_to?(key)
45
44
  send(key, value)
46
45
  end
47
46
  end
@@ -84,7 +83,7 @@ module ActiveFacts
84
83
  instance = new_instance(constellation, *args)
85
84
  end
86
85
  args.replace([arg_hash])
87
- instance.identifying_role_values
86
+ instance.identifying_role_values(self)
88
87
  end
89
88
 
90
89
  def assert_instance(constellation, args)
@@ -116,7 +115,7 @@ module ActiveFacts
116
115
  def index_instance(constellation, instance) #:nodoc:
117
116
  # Index the instance in the constellation's InstanceIndex for this class:
118
117
  instances = constellation.instances[self]
119
- key = instance.identifying_role_values
118
+ key = instance.identifying_role_values(self)
120
119
  instances[key] = instance
121
120
 
122
121
  # Index the instance for each supertype:
@@ -32,11 +32,15 @@ module ActiveFacts
32
32
  __bind(camel)
33
33
  c
34
34
  else
35
- begin
36
- const_get(camel)
37
- rescue NameError
38
- nil
39
- end
35
+ if defined?(camel)
36
+ begin
37
+ const_get(camel)
38
+ rescue NameError
39
+ nil
40
+ end
41
+ else
42
+ nil
43
+ end
40
44
  end
41
45
  end
42
46
 
@@ -84,8 +84,8 @@ describe "A Constellation instance" do
84
84
 
85
85
  it "should complain when accessing a class that isn't an object type" do
86
86
  class Mod::Bar; end
87
- lambda { @constellation.Bar }.should raise_error
88
- lambda { @constellation.instances.Bar }.should raise_error
87
+ proc { @constellation.Bar }.should raise_error
88
+ proc { @constellation.instances[Mod::Bar] }.should raise_error
89
89
  end
90
90
 
91
91
  it "should deny handling an object type defined outside the current module" do
@@ -200,7 +200,7 @@ describe "A Constellation instance" do
200
200
 
201
201
  it "Should raise an exception with assigning a role whose referent (object type) has not yet been defined" do
202
202
  n = @constellation.Name("Fred")
203
- # This does n;t raise the "settable_roles_exception". I'm no longer sure how I did this, so I can't get coverage on this code :(
203
+ # This does not raise the "settable_roles_exception". I'm no longer sure how I did this, so I can't get coverage on this code :(
204
204
  proc { n.undefined_role = 'foo' }.should raise_error
205
205
  end
206
206
 
@@ -447,7 +447,7 @@ describe "A Constellation instance" do
447
447
  supertypes Bignum
448
448
  end
449
449
  end
450
- }.should raise_error(RuntimeError)
450
+ }.should raise_error(ActiveFacts::API::InvalidSupertypeException)
451
451
 
452
452
  lambda {
453
453
  module Mod
@@ -455,7 +455,7 @@ describe "A Constellation instance" do
455
455
  supertypes 3
456
456
  end
457
457
  end
458
- }.should raise_error(RuntimeError)
458
+ }.should raise_error(ActiveFacts::API::InvalidSupertypeException)
459
459
  end
460
460
 
461
461
  it "should allow supertypes with supertypes" do
@@ -160,7 +160,7 @@ describe "Object type role values" do
160
160
  instances.map{|k,o| o}.first.should == object
161
161
  unless object.class.is_entity_type
162
162
  # Look up value types using the value instance, not just the raw value:
163
- instances[object].should == object
163
+ instances[object.identifying_role_values].should == object
164
164
  end
165
165
 
166
166
  # Make sure all the identifying roles are populated correctly:
@@ -254,7 +254,7 @@ describe "An Entity Type" do
254
254
 
255
255
  it "should assert the identifying values" do
256
256
  @rn.should be_a(Mod::Number)
257
- @c.Number[@rn].should == @rn # Yes
257
+ @c.Number[@rn.identifying_role_values].should == @rn # Yes
258
258
  @c.Number[101].should == @rn # No
259
259
  @c.Number[101].should be_eql 101 # No
260
260
  end
@@ -15,6 +15,7 @@ describe "In a vocabulary" do
15
15
  ObjectType_methods = [
16
16
  :has_one, :maybe, :one_to_one,
17
17
  :roles, :subtypes, :supertypes, :vocabulary,
18
+ :all_roles,
18
19
  # To make private:
19
20
  :check_identifying_role_has_valid_cardinality, :realise_role, :supertypes_transitive,
20
21
  ]
@@ -27,7 +28,7 @@ describe "In a vocabulary" do
27
28
  Instance_methods = [
28
29
  :constellation, :retract, :is_a?,
29
30
  # To remove or move to EntityType
30
- :related_entities, :check_value_change_legality,
31
+ :related_entities, :check_identification_change_legality,
31
32
  :instance_index
32
33
  ]
33
34
  Value_methods = Instance_methods + [
data/spec/spec_helper.rb CHANGED
@@ -9,4 +9,5 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
9
 
10
10
  RSpec.configure do |config|
11
11
 
12
- end
12
+ end
13
+
@@ -0,0 +1,29 @@
1
+ if ENV["RSPEC_REPORT_EXCEPTIONS"]
2
+ class Exception
3
+ alias_method :old_initialize, :initialize
4
+
5
+ def self.exceptions_seen
6
+ @@seen ||= {}
7
+ end
8
+
9
+ def self.see args
10
+ stack = caller[1..-1]
11
+ # Don't show expected exceptions or RSpec internal ones
12
+ return if caller.detect{|s| s =~ %r{rspec/matchers/raise_error}} or
13
+ self.name =~ /^RSpec::/
14
+ stack.reject!{|s| s =~ %r{/rspec/}}
15
+ key = stack[0,4]+[self.name]
16
+ return if exceptions_seen[key]
17
+
18
+ exceptions_seen[key] = show = stack
19
+ args[0] = args[0].to_str if args[0].class.name == "NameError::message"
20
+ puts "#{self.name}#{args.inspect}:\n\t#{show*"\n\t"}"
21
+ end
22
+
23
+ def initialize *args, &b
24
+ self.class.see args
25
+
26
+ send(:old_initialize, *args, &b)
27
+ end
28
+ end
29
+ end
metadata CHANGED
@@ -1,199 +1,190 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: activefacts-api
3
- version: !ruby/object:Gem::Version
4
- version: 0.9.5
3
+ version: !ruby/object:Gem::Version
4
+ hash: 55
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 6
10
+ version: 0.9.6
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Clifford Heath
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2013-01-16 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2013-01-22 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: rake
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :development
23
22
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
23
+ type: :development
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
- - !ruby/object:Gem::Dependency
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
31
35
  name: jeweler
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :development
39
36
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
37
+ type: :development
38
+ requirement: &id002 !ruby/object:Gem::Requirement
41
39
  none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
46
- - !ruby/object:Gem::Dependency
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
47
49
  name: rspec
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ~>
52
- - !ruby/object:Gem::Version
53
- version: 2.6.0
54
- type: :development
55
50
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
51
+ type: :development
52
+ requirement: &id003 !ruby/object:Gem::Requirement
57
53
  none: false
58
- requirements:
54
+ requirements:
59
55
  - - ~>
60
- - !ruby/object:Gem::Version
56
+ - !ruby/object:Gem::Version
57
+ hash: 23
58
+ segments:
59
+ - 2
60
+ - 6
61
+ - 0
61
62
  version: 2.6.0
62
- - !ruby/object:Gem::Dependency
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
63
65
  name: ruby-debug
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- type: :development
71
66
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
67
+ type: :development
68
+ requirement: &id004 !ruby/object:Gem::Requirement
73
69
  none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
78
- - !ruby/object:Gem::Dependency
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
79
  name: debugger
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :development
87
80
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
81
+ type: :development
82
+ requirement: &id005 !ruby/object:Gem::Requirement
89
83
  none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
- - !ruby/object:Gem::Dependency
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ version_requirements: *id005
92
+ - !ruby/object:Gem::Dependency
95
93
  name: pry
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :development
103
94
  prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
95
+ type: :development
96
+ requirement: &id006 !ruby/object:Gem::Requirement
105
97
  none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
- - !ruby/object:Gem::Dependency
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ version_requirements: *id006
106
+ - !ruby/object:Gem::Dependency
111
107
  name: rspec
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ~>
116
- - !ruby/object:Gem::Version
117
- version: 2.3.0
118
- type: :development
119
108
  prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
109
+ type: :development
110
+ requirement: &id007 !ruby/object:Gem::Requirement
121
111
  none: false
122
- requirements:
112
+ requirements:
123
113
  - - ~>
124
- - !ruby/object:Gem::Version
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 2
118
+ - 3
119
+ - 0
125
120
  version: 2.3.0
126
- - !ruby/object:Gem::Dependency
121
+ version_requirements: *id007
122
+ - !ruby/object:Gem::Dependency
127
123
  name: bundler
128
- requirement: !ruby/object:Gem::Requirement
129
- none: false
130
- requirements:
131
- - - ~>
132
- - !ruby/object:Gem::Version
133
- version: 1.0.0
134
- type: :development
135
124
  prerelease: false
136
- version_requirements: !ruby/object:Gem::Requirement
125
+ type: :development
126
+ requirement: &id008 !ruby/object:Gem::Requirement
137
127
  none: false
138
- requirements:
128
+ requirements:
139
129
  - - ~>
140
- - !ruby/object:Gem::Version
130
+ - !ruby/object:Gem::Version
131
+ hash: 23
132
+ segments:
133
+ - 1
134
+ - 0
135
+ - 0
141
136
  version: 1.0.0
142
- - !ruby/object:Gem::Dependency
137
+ version_requirements: *id008
138
+ - !ruby/object:Gem::Dependency
143
139
  name: jeweler
144
- requirement: !ruby/object:Gem::Requirement
145
- none: false
146
- requirements:
147
- - - ~>
148
- - !ruby/object:Gem::Version
149
- version: 1.5.2
150
- type: :development
151
140
  prerelease: false
152
- version_requirements: !ruby/object:Gem::Requirement
141
+ type: :development
142
+ requirement: &id009 !ruby/object:Gem::Requirement
153
143
  none: false
154
- requirements:
144
+ requirements:
155
145
  - - ~>
156
- - !ruby/object:Gem::Version
146
+ - !ruby/object:Gem::Version
147
+ hash: 7
148
+ segments:
149
+ - 1
150
+ - 5
151
+ - 2
157
152
  version: 1.5.2
158
- - !ruby/object:Gem::Dependency
153
+ version_requirements: *id009
154
+ - !ruby/object:Gem::Dependency
159
155
  name: rdoc
160
- requirement: !ruby/object:Gem::Requirement
161
- none: false
162
- requirements:
163
- - - ! '>='
164
- - !ruby/object:Gem::Version
165
- version: 2.4.2
166
- type: :development
167
156
  prerelease: false
168
- version_requirements: !ruby/object:Gem::Requirement
157
+ type: :development
158
+ requirement: &id010 !ruby/object:Gem::Requirement
169
159
  none: false
170
- requirements:
171
- - - ! '>='
172
- - !ruby/object:Gem::Version
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 27
164
+ segments:
165
+ - 2
166
+ - 4
167
+ - 2
173
168
  version: 2.4.2
174
- description: ! '
175
-
169
+ version_requirements: *id010
170
+ description: |
171
+
176
172
  The ActiveFacts API is a Ruby DSL for managing constellations of elementary facts.
177
-
178
- Each fact is either existential (a value or an entity), characteristic (boolean)
179
- or
180
-
181
- binary relational (A rel B). Relational facts are consistently co-referenced, so
182
- you
183
-
173
+ Each fact is either existential (a value or an entity), characteristic (boolean) or
174
+ binary relational (A rel B). Relational facts are consistently co-referenced, so you
184
175
  can traverse them efficiently in any direction. Each constellation maintains constraints
185
-
186
176
  over the fact population.
187
177
 
188
- '
189
178
  email: clifford.heath@gmail.com
190
179
  executables: []
180
+
191
181
  extensions: []
192
- extra_rdoc_files:
182
+
183
+ extra_rdoc_files:
193
184
  - LICENSE.txt
194
185
  - README.rdoc
195
186
  - TODO
196
- files:
187
+ files:
197
188
  - .document
198
189
  - .rspec
199
190
  - .travis.yml
@@ -239,33 +230,40 @@ files:
239
230
  - spec/object_type/value_type/value_type_spec.rb
240
231
  - spec/simplecov_helper.rb
241
232
  - spec/spec_helper.rb
233
+ - spec/support/reduce_exceptions_helper.rb
242
234
  - TODO
243
235
  homepage: http://github.com/cjheath/activefacts-api
244
- licenses:
236
+ licenses:
245
237
  - MIT
246
238
  post_install_message:
247
239
  rdoc_options: []
248
- require_paths:
240
+
241
+ require_paths:
249
242
  - lib
250
- required_ruby_version: !ruby/object:Gem::Requirement
243
+ required_ruby_version: !ruby/object:Gem::Requirement
251
244
  none: false
252
- requirements:
253
- - - ! '>='
254
- - !ruby/object:Gem::Version
255
- version: '0'
256
- segments:
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ hash: 3
249
+ segments:
257
250
  - 0
258
- hash: 1210306376532031473
259
- required_rubygems_version: !ruby/object:Gem::Requirement
251
+ version: "0"
252
+ required_rubygems_version: !ruby/object:Gem::Requirement
260
253
  none: false
261
- requirements:
262
- - - ! '>='
263
- - !ruby/object:Gem::Version
264
- version: '0'
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ hash: 3
258
+ segments:
259
+ - 0
260
+ version: "0"
265
261
  requirements: []
262
+
266
263
  rubyforge_project:
267
264
  rubygems_version: 1.8.24
268
265
  signing_key:
269
266
  specification_version: 3
270
267
  summary: A fact-based data model DSL and API
271
268
  test_files: []
269
+