activefacts-api 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
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
+