activefacts-api 1.9.10 → 1.9.13
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.
- checksums.yaml +5 -5
- data/activefacts-api.gemspec +2 -2
- data/lib/activefacts/api/constellation.rb +3 -2
- data/lib/activefacts/api/entity.rb +16 -15
- data/lib/activefacts/api/guid.rb +7 -5
- data/lib/activefacts/api/instance.rb +11 -3
- data/lib/activefacts/api/numeric.rb +3 -1
- data/lib/activefacts/api/object_type.rb +8 -8
- data/lib/activefacts/api/role_values.rb +21 -11
- data/lib/activefacts/api/standard_types.rb +1 -1
- data/lib/activefacts/api/support.rb +0 -6
- data/lib/activefacts/api/value.rb +1 -1
- data/lib/activefacts/api/version.rb +1 -1
- metadata +10 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8c3ae722ceb4f264e163be7c166507f6ab3ab328cea1513ff0599ea0c54a3270
|
4
|
+
data.tar.gz: '096701429372fe651503a58c4631c0f6090f21493555a3c2705ec2cbd71c1f04'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbb093b3faaa2c8f31e5c9f33c6afd076e3d72e803beb57272eb6769e6a44af9a98c909f9643c5d08da3f9fe0e0815ed76b90cecd9de524a6453497485d79346
|
7
|
+
data.tar.gz: 8f349dca05234d1b24c2408eb81ee5fbc3d0a3458021c099374cf4da6118c349d7dbc32c5e82a86c462da06acb878f6de8ab1f43a8de58cdcced608ec8b79979
|
data/activefacts-api.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = ActiveFacts::API::VERSION
|
9
9
|
spec.authors = ["Clifford Heath"]
|
10
10
|
spec.email = ["clifford.heath@gmail.com"]
|
11
|
-
spec.date
|
11
|
+
spec.date = "2022-06-15"
|
12
12
|
|
13
13
|
spec.summary = "A fact-based data model DSL and API"
|
14
14
|
spec.description = %q{
|
@@ -35,7 +35,7 @@ over the fact population.
|
|
35
35
|
spec.add_runtime_dependency 'tracing', ["~> 2", ">= 2.0.4"]
|
36
36
|
|
37
37
|
spec.add_development_dependency "bundler", ">= 1.10"
|
38
|
-
spec.add_development_dependency "rake", "
|
38
|
+
spec.add_development_dependency "rake", ">= 10"
|
39
39
|
spec.add_development_dependency "rspec", "~> 3.3"
|
40
40
|
end
|
41
41
|
|
@@ -65,7 +65,7 @@ module ActiveFacts
|
|
65
65
|
# With no parameters, return the collection of all instances of that object_type.
|
66
66
|
# With parameters, assert an instance of the object_type identified by the values passed as args.
|
67
67
|
def method_missing(m, *args, &b)
|
68
|
-
klass = @vocabulary.const_get(m)
|
68
|
+
klass = @vocabulary.const_get(m) rescue nil # Catch missing lower-case names (wrong constant) and use super
|
69
69
|
if invalid_object_type klass
|
70
70
|
super
|
71
71
|
else
|
@@ -75,7 +75,8 @@ module ActiveFacts
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def define_class_accessor m, klass
|
78
|
-
singleton_class.
|
78
|
+
singleton_class.
|
79
|
+
send(:define_method, m) do |*args|
|
79
80
|
if args.size == 0
|
80
81
|
# Return the collection of all instances of this class in the constellation:
|
81
82
|
instances[klass]
|
@@ -32,9 +32,9 @@ module ActiveFacts
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def initialize_existential_roles(klass, arg_hash)
|
35
|
-
#
|
36
|
-
|
37
|
-
initialize_existential_roles(
|
35
|
+
# Assign the identifying attributes of all superclasses first
|
36
|
+
klass.supertypes_transitive.each do |supertype|
|
37
|
+
initialize_existential_roles(supertype, arg_hash)
|
38
38
|
end
|
39
39
|
|
40
40
|
irns = klass.identifying_role_names
|
@@ -159,9 +159,9 @@ module ActiveFacts
|
|
159
159
|
# identified by this entity. Save the current key and
|
160
160
|
# class for each such instance.
|
161
161
|
# This function is transitive!
|
162
|
-
def
|
163
|
-
|
164
|
-
|
162
|
+
def collect_instance_index_updates role
|
163
|
+
instance_index_updates = []
|
164
|
+
propagation_roles = []
|
165
165
|
|
166
166
|
# Consider the object itself and all its supertypes
|
167
167
|
([self.class]+self.class.supertypes_transitive).map do |supertype|
|
@@ -169,7 +169,7 @@ module ActiveFacts
|
|
169
169
|
|
170
170
|
old_key = identifying_role_values(supertype)
|
171
171
|
# puts "Need to reindex #{self.class} as #{supertype} from #{old_key.inspect}"
|
172
|
-
|
172
|
+
instance_index_updates << [constellation.instances[supertype], self, old_key]
|
173
173
|
|
174
174
|
supertype.
|
175
175
|
all_role.
|
@@ -178,29 +178,29 @@ module ActiveFacts
|
|
178
178
|
next unless counterpart = propagation_role.counterpart # And the role is not unary
|
179
179
|
if counterpart.is_identifying # This object identifies another
|
180
180
|
# puts "Changing #{propagation_role.inspect} affects #{counterpart.inspect}"
|
181
|
-
|
181
|
+
propagation_roles << propagation_role
|
182
182
|
else
|
183
183
|
next if counterpart.unique # But a one-to-many
|
184
184
|
next unless value = send(propagation_role.getter) # A value is set
|
185
185
|
role_values = value.send(counterpart.getter) # This is the index we have to change
|
186
186
|
# puts "Changing #{role.inspect} of a #{self.class} requires updating #{propagation_role.counterpart.inspect}"
|
187
|
-
|
187
|
+
instance_index_updates << [role_values, self, old_key]
|
188
188
|
end
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
192
|
-
|
192
|
+
propagation_roles.each do |role|
|
193
193
|
affected_instances = Array(send(role.getter))
|
194
194
|
# puts "considering #{affected_instances.size} #{role.object_type.name} instances that include #{role.inspect}: #{affected_instances.map(&:identifying_role_values).inspect}"
|
195
195
|
affected_instances.each do |counterpart|
|
196
|
-
|
196
|
+
instance_index_updates.concat(counterpart.collect_instance_index_updates(role.counterpart))
|
197
197
|
end
|
198
198
|
end
|
199
|
-
|
199
|
+
instance_index_updates
|
200
200
|
end
|
201
201
|
|
202
|
-
def
|
203
|
-
|
202
|
+
def apply_instance_index_updates instance_index_updates
|
203
|
+
instance_index_updates.each do |index, entity, old_key|
|
204
204
|
new_key = entity.identifying_role_values(index.object_type)
|
205
205
|
# puts "Reindexing #{klass} from #{old_key.inspect} to #{new_key.inspect}"
|
206
206
|
|
@@ -390,6 +390,7 @@ module ActiveFacts
|
|
390
390
|
unless role.is_identifying && role.object_type == self
|
391
391
|
value =
|
392
392
|
if v == nil
|
393
|
+
raise "nil value passed for mandatory #{role.name} of #{role.object_type.name}" if role.mandatory
|
393
394
|
nil
|
394
395
|
elsif role.unary?
|
395
396
|
(v && true) # Preserve nil and false
|
@@ -465,7 +466,7 @@ module ActiveFacts
|
|
465
466
|
|
466
467
|
def other.new_instance constellation, *args
|
467
468
|
instance = allocate
|
468
|
-
instance.instance_variable_set(
|
469
|
+
instance.instance_variable_set(constellation_variable_name, constellation)
|
469
470
|
instance.send(:initialize, *args)
|
470
471
|
instance
|
471
472
|
end
|
data/lib/activefacts/api/guid.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
require 'securerandom'
|
3
3
|
|
4
|
-
|
5
|
-
hex32
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
module SecureRandom
|
5
|
+
def self.format_uuid hex32
|
6
|
+
hex32.sub(
|
7
|
+
@@format_pattern ||= /(........)(....)(....)(....)(............)/,
|
8
|
+
@@format_string ||= '\1-\2-\3-\4-\5'
|
9
|
+
)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
13
|
unless defined? SecureRandom.uuid
|
@@ -26,6 +26,10 @@ module ActiveFacts
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def constellation_variable_name
|
30
|
+
@@constellation_variable_name ||= "@constellation"
|
31
|
+
end
|
32
|
+
|
29
33
|
def is_a? klass
|
30
34
|
super || self.class.supertypes_transitive.include?(klass)
|
31
35
|
end
|
@@ -77,14 +81,14 @@ module ActiveFacts
|
|
77
81
|
# The counterpart roles get cleared automatically.
|
78
82
|
klasses = [self.class]+self.class.supertypes_transitive
|
79
83
|
|
80
|
-
|
84
|
+
key_by_type = {}
|
81
85
|
self.class.all_role_transitive.each do |_, role|
|
82
86
|
next unless role.counterpart and
|
83
87
|
role.unique and
|
84
88
|
!role.counterpart.unique and
|
85
89
|
counterpart = send(role.getter)
|
86
90
|
role_values = counterpart.send(role.counterpart.getter)
|
87
|
-
|
91
|
+
key_by_type[role.object_type] ||= identifying_role_values(role.object_type)
|
88
92
|
end
|
89
93
|
|
90
94
|
# Nullify the counterpart role of objects we identify first, before damaging our identifying_role_values:
|
@@ -124,7 +128,7 @@ module ActiveFacts
|
|
124
128
|
counterpart_instance.send(counterpart.setter, nil, false)
|
125
129
|
else
|
126
130
|
rv = counterpart_instance.send(role.counterpart.getter)
|
127
|
-
rv.delete_instance(self,
|
131
|
+
rv.delete_instance(self, key_by_type[role.object_type])
|
128
132
|
|
129
133
|
if (rv.empty? && !counterpart_instance.class.is_entity_type)
|
130
134
|
counterpart_instance.retract if counterpart_instance.plays_no_role
|
@@ -158,6 +162,10 @@ module ActiveFacts
|
|
158
162
|
module ClassMethods #:nodoc:
|
159
163
|
include ObjectType
|
160
164
|
# Add Instance class methods here
|
165
|
+
|
166
|
+
def constellation_variable_name
|
167
|
+
@@constellation_variable_name ||= "@constellation"
|
168
|
+
end
|
161
169
|
end
|
162
170
|
end
|
163
171
|
end
|
@@ -62,7 +62,9 @@ class Decimal < SimpleDelegator #:nodoc:
|
|
62
62
|
include ActiveFacts::API::SimpleDelegation
|
63
63
|
|
64
64
|
def delegate_new(v)
|
65
|
-
if
|
65
|
+
if RUBY_VERSION >= "3"
|
66
|
+
BigDecimal(v)
|
67
|
+
elsif v.is_a?(BigDecimal) || v.is_a?(Integer)
|
66
68
|
BigDecimal.new(v.to_s)
|
67
69
|
else
|
68
70
|
BigDecimal.new(v)
|
@@ -260,16 +260,16 @@ module ActiveFacts
|
|
260
260
|
|
261
261
|
if role.is_identifying and (options&CHECKED_IDENTIFYING_ROLE) == 0
|
262
262
|
check_identification_change_legality(role, value)
|
263
|
-
|
263
|
+
instance_index_updates = collect_instance_index_updates(role)
|
264
264
|
end
|
265
265
|
|
266
266
|
instance_variable_set(role.variable, value)
|
267
267
|
|
268
|
-
if
|
268
|
+
if instance_index_updates
|
269
269
|
@constellation.when_admitted do
|
270
270
|
# REVISIT: Consider whether we want to provide a way to find all instances
|
271
271
|
# playing/not playing this boolean role, analogous to true.all_thing_as_role_name...
|
272
|
-
|
272
|
+
apply_instance_index_updates(instance_index_updates) # Propagate dependent key changes
|
273
273
|
end
|
274
274
|
end
|
275
275
|
|
@@ -314,7 +314,7 @@ module ActiveFacts
|
|
314
314
|
check_identification_change_legality(role, value)
|
315
315
|
|
316
316
|
# puts "Starting to analyse impact of changing 1-1 #{role.inspect} to #{value.inspect}"
|
317
|
-
|
317
|
+
instance_index_updates = collect_instance_index_updates(role)
|
318
318
|
end
|
319
319
|
|
320
320
|
instance_variable_set(role_var, value)
|
@@ -328,7 +328,7 @@ module ActiveFacts
|
|
328
328
|
# Assign self to the new counterpart
|
329
329
|
value.send(role.counterpart.setter, self, options) if value && (options&SKIP_MUTUAL_PROPAGATION) == 0
|
330
330
|
|
331
|
-
|
331
|
+
apply_instance_index_updates(instance_index_updates) if instance_index_updates # Propagate dependent key changes
|
332
332
|
end
|
333
333
|
|
334
334
|
unless @constellation.loggers.empty? or options != 0
|
@@ -363,12 +363,12 @@ module ActiveFacts
|
|
363
363
|
check_identification_change_legality(role, value)
|
364
364
|
|
365
365
|
# puts "Starting to analyse impact of changing 1-N #{role.inspect} to #{value.inspect}"
|
366
|
-
|
366
|
+
instance_index_updates = collect_instance_index_updates(role)
|
367
367
|
end
|
368
368
|
|
369
369
|
if old and (options&SKIP_MUTUAL_PROPAGATION) == 0
|
370
370
|
old_role_values = old.send(getter = role.counterpart.getter)
|
371
|
-
old_key =
|
371
|
+
old_key = identifying_role_values(role.object_type)
|
372
372
|
end
|
373
373
|
|
374
374
|
instance_variable_set(role_var, value)
|
@@ -388,7 +388,7 @@ module ActiveFacts
|
|
388
388
|
rv.add_instance(self, identifying_role_values(role.object_type))
|
389
389
|
end
|
390
390
|
|
391
|
-
|
391
|
+
apply_instance_index_updates(instance_index_updates) if instance_index_updates # Propagate dependent key changes
|
392
392
|
end
|
393
393
|
|
394
394
|
unless @constellation.loggers.empty? or options != 0
|
@@ -11,6 +11,7 @@ module ActiveFacts
|
|
11
11
|
attr_accessor :role
|
12
12
|
attr_accessor :sort
|
13
13
|
attr_accessor :index_roles
|
14
|
+
|
14
15
|
def object_type
|
15
16
|
@role.object_type
|
16
17
|
end
|
@@ -21,7 +22,11 @@ module ActiveFacts
|
|
21
22
|
@sort = API::sorted
|
22
23
|
@excluded_role = excluded_role
|
23
24
|
@a = @sort ? RBTree.new : []
|
24
|
-
|
25
|
+
if @excluded_role
|
26
|
+
@index_roles = role.object_type.identifying_roles.dup
|
27
|
+
@index_roles.delete_at(@excluded_role)
|
28
|
+
@index_roles.freeze
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
32
|
def +(a)
|
@@ -68,17 +73,19 @@ module ActiveFacts
|
|
68
73
|
KeyArray.new(a)
|
69
74
|
end
|
70
75
|
|
76
|
+
# Return the full key for the object according to the object_type of this role
|
71
77
|
def index_values object
|
72
|
-
if @
|
73
|
-
@index_roles.map
|
78
|
+
if @excluded_role
|
79
|
+
@index_roles.map do |r|
|
74
80
|
role_value = object.send(r.name)
|
75
81
|
role_value.identifying_role_values((c = r.counterpart) ? c.object_type : role_value.class)
|
76
|
-
|
82
|
+
end
|
77
83
|
else
|
78
|
-
object.identifying_role_values
|
84
|
+
object.identifying_role_values(role.object_type)
|
79
85
|
end
|
80
86
|
end
|
81
87
|
|
88
|
+
# The key must include the excluded role, if any
|
82
89
|
def add_instance(value, key)
|
83
90
|
if @sort
|
84
91
|
# Exclude the excluded role, if any:
|
@@ -89,9 +96,12 @@ module ActiveFacts
|
|
89
96
|
end
|
90
97
|
end
|
91
98
|
|
99
|
+
# The key must include the excluded role, if any
|
92
100
|
def delete_instance(value, key)
|
93
101
|
if @sort
|
94
|
-
|
102
|
+
if key.size != role.object_type.identifying_roles.size
|
103
|
+
raise "Internal error: incorrectly-sized key #{key.inspect} to delete_instance"
|
104
|
+
end
|
95
105
|
(key = key.dup).delete_at(@excluded_role) if @excluded_role
|
96
106
|
deleted = @a.delete(form_key(key))
|
97
107
|
else
|
@@ -134,16 +144,16 @@ module ActiveFacts
|
|
134
144
|
def_single_delegator :@a, :empty?
|
135
145
|
def_single_delegator :@a, :include?
|
136
146
|
def_single_delegator :@a, :inject, 2
|
137
|
-
def_single_delegator :@a, :select, -
|
138
|
-
def_single_delegator :@a, :reject, -
|
147
|
+
def_single_delegator :@a, :select, -2, 1
|
148
|
+
def_single_delegator :@a, :reject, -2, 1
|
139
149
|
def_single_delegator :@a, :size
|
140
|
-
def_single_delegator :@a, :sort_by, -
|
150
|
+
def_single_delegator :@a, :sort_by, -2, 1
|
141
151
|
def_single_delegator :@a, :to_a
|
142
152
|
def_single_delegator :@a, :-
|
143
153
|
# These delegators allow a negative arity in RSpec because the tests test it (to make sure the code doesn't pass too many args)
|
144
|
-
def_single_delegator :@a, :each, *([-
|
154
|
+
def_single_delegator :@a, :each, *([-2, 1] + Array(defined?(::RSpec) ? -2 : nil))
|
145
155
|
def_single_delegator :@a, :detect, 1, *([-1, 1] + Array(defined?(::RSpec) ? -2 : nil))
|
146
|
-
def_single_delegator :@a, :map, -
|
156
|
+
def_single_delegator :@a, :map, -2, 1
|
147
157
|
end
|
148
158
|
end
|
149
159
|
end
|
@@ -17,7 +17,7 @@ module ActiveFacts
|
|
17
17
|
module ValueClass #:nodoc:
|
18
18
|
def value_type *args, &block #:nodoc:
|
19
19
|
# The inclusion of instance methods triggers ClassMethods to be included in the class too
|
20
|
-
include ActiveFacts::API::Value
|
20
|
+
include ::ActiveFacts::API::Value
|
21
21
|
value_type(*args, &block)
|
22
22
|
end
|
23
23
|
end
|
@@ -11,12 +11,6 @@ unless Object.const_defined?("Infinity")
|
|
11
11
|
Infinity = 1.0/0.0
|
12
12
|
end
|
13
13
|
|
14
|
-
class Symbol #:nodoc:
|
15
|
-
def to_proc
|
16
|
-
Proc.new{|*args| args.shift.__send__(self, *args)}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
14
|
class String #:nodoc:
|
21
15
|
# This may be overridden by a version from ActiveSupport. For our purposes, either will work.
|
22
16
|
def camelcase(first_letter = :upper)
|
@@ -155,7 +155,7 @@ module ActiveFacts
|
|
155
155
|
class << klass
|
156
156
|
def new_instance constellation, *args
|
157
157
|
instance = allocate
|
158
|
-
instance.instance_variable_set(
|
158
|
+
instance.instance_variable_set(constellation_variable_name, constellation)
|
159
159
|
instance.send(:initialize, *args)
|
160
160
|
instance
|
161
161
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activefacts-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clifford Heath
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbtree-pure
|
@@ -68,16 +68,16 @@ dependencies:
|
|
68
68
|
name: rake
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
70
70
|
requirements:
|
71
|
-
- - "
|
71
|
+
- - ">="
|
72
72
|
- !ruby/object:Gem::Version
|
73
|
-
version: '10
|
73
|
+
version: '10'
|
74
74
|
type: :development
|
75
75
|
prerelease: false
|
76
76
|
version_requirements: !ruby/object:Gem::Requirement
|
77
77
|
requirements:
|
78
|
-
- - "
|
78
|
+
- - ">="
|
79
79
|
- !ruby/object:Gem::Version
|
80
|
-
version: '10
|
80
|
+
version: '10'
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: rspec
|
83
83
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,7 +139,7 @@ homepage: https://github.com/cjheath/activefacts-api
|
|
139
139
|
licenses:
|
140
140
|
- MIT
|
141
141
|
metadata: {}
|
142
|
-
post_install_message:
|
142
|
+
post_install_message:
|
143
143
|
rdoc_options: []
|
144
144
|
require_paths:
|
145
145
|
- lib
|
@@ -154,9 +154,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
154
|
- !ruby/object:Gem::Version
|
155
155
|
version: '0'
|
156
156
|
requirements: []
|
157
|
-
|
158
|
-
|
159
|
-
signing_key:
|
157
|
+
rubygems_version: 3.2.22
|
158
|
+
signing_key:
|
160
159
|
specification_version: 4
|
161
160
|
summary: A fact-based data model DSL and API
|
162
161
|
test_files: []
|