activefacts-api 0.8.9 → 0.8.10
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/Rakefile +4 -2
- data/TODO +29 -0
- data/VERSION +1 -1
- data/lib/activefacts/api.rb +2 -2
- data/lib/activefacts/api/constellation.rb +51 -19
- data/lib/activefacts/api/entity.rb +151 -93
- data/lib/activefacts/api/instance.rb +17 -9
- data/lib/activefacts/api/instance_index.rb +36 -35
- data/lib/activefacts/api/numeric.rb +30 -18
- data/lib/activefacts/api/object_type.rb +109 -101
- data/lib/activefacts/api/role.rb +62 -25
- data/lib/activefacts/api/role_values.rb +0 -58
- data/lib/activefacts/api/standard_types.rb +14 -5
- data/lib/activefacts/api/value.rb +22 -19
- data/lib/activefacts/api/vocabulary.rb +12 -9
- data/lib/activefacts/tracer.rb +109 -0
- data/spec/{api/autocounter_spec.rb → autocounter_spec.rb} +9 -4
- data/spec/constellation_spec.rb +434 -0
- data/spec/{api/entity_type_spec.rb → entity_type_spec.rb} +1 -0
- data/spec/identification_spec.rb +401 -0
- data/spec/instance_spec.rb +384 -0
- data/spec/role_values_spec.rb +409 -0
- data/spec/{api/roles_spec.rb → roles_spec.rb} +49 -10
- data/spec/{api/value_type_spec.rb → value_type_spec.rb} +1 -0
- metadata +36 -24
- data/lib/activefacts/api/role_proxy.rb +0 -71
- data/spec/api/constellation_spec.rb +0 -129
- data/spec/api/instance_spec.rb +0 -462
data/lib/activefacts/api/role.rb
CHANGED
@@ -11,26 +11,40 @@ module ActiveFacts
|
|
11
11
|
|
12
12
|
# A Role represents the relationship of one object to another (or to a boolean condition).
|
13
13
|
# Relationships (or binary fact types) have a Role at each end; one is declared using _has_one_
|
14
|
-
# or _one_to_one_, and the other is created on the counterpart class.
|
15
|
-
#
|
14
|
+
# or _one_to_one_, and the other is created on the counterpart class.
|
15
|
+
# Each ObjectType class maintains a RoleCollection hash of the roles it plays.
|
16
16
|
class Role
|
17
|
-
|
18
|
-
|
19
|
-
attr_accessor :
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
attr_reader :is_identifying # Is this an identifying role for owner?
|
17
|
+
attr_reader :object_type # The ObjectType to which this role belongs
|
18
|
+
attr_reader :name # The name of the role (a Symbol)
|
19
|
+
attr_accessor :counterpart # All roles except unaries have a counterpart Role
|
20
|
+
attr_reader :unique # Is this role played by at most one instance, or more?
|
21
|
+
attr_reader :mandatory # In a valid fact population, is this role required to be played?
|
22
|
+
attr_reader :value_constraint # Counterpart Instances playing this role must meet this constraint
|
23
|
+
attr_reader :is_identifying # Is this an identifying role for object_type?
|
25
24
|
|
26
|
-
def initialize(
|
27
|
-
@
|
28
|
-
@counterpart_object_type = counterpart_object_type
|
25
|
+
def initialize(object_type, counterpart, name, mandatory = false, unique = true)
|
26
|
+
@object_type = object_type
|
29
27
|
@counterpart = counterpart
|
30
28
|
@name = name
|
31
29
|
@mandatory = mandatory
|
32
30
|
@unique = unique
|
33
|
-
@is_identifying = @
|
31
|
+
@is_identifying = @object_type.is_entity_type && @object_type.identifying_role_names.include?(@name)
|
32
|
+
associate_role(@object_type)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the name of the getter method
|
36
|
+
def getter
|
37
|
+
@getter ||= @name.to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return the name of the setter method
|
41
|
+
def setter
|
42
|
+
@setter ||= :"#{@name}="
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the name of the instance variable
|
46
|
+
def variable
|
47
|
+
@variable ||= "@#{@name}"
|
34
48
|
end
|
35
49
|
|
36
50
|
# Is this role a unary (created by maybe)? If so, it has no counterpart
|
@@ -39,34 +53,57 @@ module ActiveFacts
|
|
39
53
|
counterpart == nil
|
40
54
|
end
|
41
55
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
56
|
+
def counterpart_object_type
|
57
|
+
# This method is sometimes used when unaries are used in an entity's identifier.
|
58
|
+
counterpart == nil ? TrueClass : counterpart.object_type
|
59
|
+
end
|
60
|
+
|
61
|
+
def inspect
|
62
|
+
"<Role #{object_type.name}.#{name}>"
|
47
63
|
end
|
48
64
|
|
49
65
|
def adapt(constellation, value) #:nodoc:
|
50
66
|
# If the value is a compatible class, use it (if in another constellation, clone it),
|
51
67
|
# else create a compatible object using the value as constructor parameters.
|
52
|
-
if value.is_a?(
|
53
|
-
value = value.__getobj__ if RoleProxy === value
|
68
|
+
if value.is_a?(counterpart.object_type)
|
54
69
|
# Check that the value is in a compatible constellation, clone if not:
|
55
70
|
if constellation && (vc = value.constellation) && vc != constellation
|
56
|
-
|
71
|
+
# Cross-constellation assignment!
|
72
|
+
# Just take the identifying_role_values to make a new object
|
73
|
+
value = constellation.send(value.class.basename, value.identifying_role_values)
|
57
74
|
end
|
58
75
|
value.constellation = constellation if constellation
|
59
76
|
else
|
60
77
|
value = [value] unless Array === value
|
61
|
-
raise "No parameters were provided to identify an #{
|
78
|
+
raise "No parameters were provided to identify an #{counterpart.object_type.basename} instance" if value == []
|
62
79
|
if constellation
|
63
|
-
value = constellation.send(
|
80
|
+
value = constellation.send(counterpart.object_type.basename.to_sym, *value)
|
64
81
|
else
|
65
|
-
|
82
|
+
#trace :assert, "Constructing new #{counterpart.object_type} with #{value.inspect}" do
|
83
|
+
value = counterpart.object_type.new(*value)
|
84
|
+
#end
|
66
85
|
end
|
67
86
|
end
|
68
87
|
value
|
69
88
|
end
|
89
|
+
|
90
|
+
private
|
91
|
+
# Create a class method to access the Role object.
|
92
|
+
# This seems to add *significantly* to the runtime of the tests,
|
93
|
+
# but it's load-time, not execution-time, so it's staying!
|
94
|
+
def associate_role(klass)
|
95
|
+
role = self
|
96
|
+
klass.class_eval do
|
97
|
+
role_accessor_name = "#{role.name}_role"
|
98
|
+
unless (method(role_accessor_name) rescue nil)
|
99
|
+
(class << self; self; end).
|
100
|
+
send(:define_method, role_accessor_name) do
|
101
|
+
role
|
102
|
+
end
|
103
|
+
# else we can't create such a method without creating mayhem, so don't.
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
70
107
|
end
|
71
108
|
|
72
109
|
# Every ObjectType has a Role collection
|
@@ -14,7 +14,6 @@ module ActiveFacts
|
|
14
14
|
class RoleValues #:nodoc:
|
15
15
|
include Enumerable
|
16
16
|
|
17
|
-
#=begin
|
18
17
|
def initialize
|
19
18
|
@a = []
|
20
19
|
end
|
@@ -48,68 +47,11 @@ module ActiveFacts
|
|
48
47
|
def update(old, value)
|
49
48
|
@a.delete(old) if old
|
50
49
|
@a << value if value
|
51
|
-
raise "Adding RoleProxy to RoleValues collection" if value && RoleProxy === value
|
52
50
|
end
|
53
51
|
|
54
52
|
def verbalise
|
55
53
|
"["+@a.to_a.map{|e| e.verbalise}*", "+"]"
|
56
54
|
end
|
57
|
-
#=end
|
58
|
-
|
59
|
-
=begin
|
60
|
-
def initialize
|
61
|
-
@h = {}
|
62
|
-
end
|
63
|
-
|
64
|
-
def each &b
|
65
|
-
@h.keys.each &b
|
66
|
-
end
|
67
|
-
|
68
|
-
def size
|
69
|
-
@h.size
|
70
|
-
end
|
71
|
-
|
72
|
-
def empty?
|
73
|
-
@h.size == 0
|
74
|
-
end
|
75
|
-
|
76
|
-
def +(a)
|
77
|
-
@h.keys.+(a.is_a?(RoleValues) ? Array(a) : a)
|
78
|
-
end
|
79
|
-
|
80
|
-
def -(a)
|
81
|
-
@h.keys - a
|
82
|
-
end
|
83
|
-
|
84
|
-
def single
|
85
|
-
@h.size > 1 ? nil : @h.keys[0]
|
86
|
-
end
|
87
|
-
|
88
|
-
def update(old, value)
|
89
|
-
if old
|
90
|
-
unless @h.delete(old)
|
91
|
-
@h.each { |k, v|
|
92
|
-
next if k != old
|
93
|
-
puts "#{@h.object_id}: Didn't delete #{k.verbalise} (hash=#{k.hash}) matching #{old.verbalise} (hash=#{old.hash})"
|
94
|
-
puts "They are #{k.eql?(old) ? "" : "not "}eql?"
|
95
|
-
found = @h[k]
|
96
|
-
puts "found #{found.inspect}" if found
|
97
|
-
debugger
|
98
|
-
x = k.eql?(old)
|
99
|
-
y = old.eql?(k)
|
100
|
-
p y
|
101
|
-
}
|
102
|
-
raise "failed to delete #{old.verbalise}, have #{map{|e| e.verbalise}.inspect}"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
puts "#{@h.object_id}: Adding #{value.inspect}" if value && (value.name == 'Meetingisboardmeeting' rescue false)
|
106
|
-
@h[value] = true if value
|
107
|
-
end
|
108
|
-
|
109
|
-
def verbalise
|
110
|
-
"["+@h.keys.map{|e| e.verbalise}*", "+"]"
|
111
|
-
end
|
112
|
-
=end
|
113
55
|
|
114
56
|
end
|
115
57
|
|
@@ -15,8 +15,7 @@ module ActiveFacts
|
|
15
15
|
module ValueClass #:nodoc:
|
16
16
|
def value_type *args, &block #:nodoc:
|
17
17
|
include ActiveFacts::API::Value
|
18
|
-
|
19
|
-
initialise_value_type(*args, &block)
|
18
|
+
value_type(*args, &block)
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
@@ -33,19 +32,29 @@ ValueClasses.each{|c|
|
|
33
32
|
|
34
33
|
class TrueClass #:nodoc:
|
35
34
|
def verbalise(role_name = nil); role_name ? "#{role_name}: true" : "true"; end
|
35
|
+
def identifying_role_values; self; end
|
36
|
+
def self.identifying_role_values(*a); true; end
|
37
|
+
end
|
38
|
+
|
39
|
+
class FalseClass #:nodoc:
|
40
|
+
def verbalise(role_name = nil); role_name ? "#{role_name}: false" : "false"; end
|
41
|
+
def identifying_role_values; self; end
|
42
|
+
def self.identifying_role_values(*a); false; end
|
36
43
|
end
|
37
44
|
|
38
45
|
class NilClass #:nodoc:
|
39
46
|
def verbalise; "nil"; end
|
47
|
+
def identifying_role_values; self; end
|
48
|
+
def self.identifying_role_values(*a); nil; end
|
40
49
|
end
|
41
50
|
|
42
51
|
class Class
|
43
52
|
# Make this Class into a ObjectType and if necessary its module into a Vocabulary.
|
44
53
|
# The parameters are the names (Symbols) of the identifying roles.
|
45
|
-
def identified_by *args
|
54
|
+
def identified_by *args, &b
|
46
55
|
raise "#{basename} is not an entity type" if respond_to? :value_type # Don't make a ValueType into an EntityType
|
47
56
|
include ActiveFacts::API::Entity
|
48
|
-
|
57
|
+
identified_by(*args, &b)
|
49
58
|
end
|
50
59
|
|
51
60
|
def is_entity_type
|
@@ -58,7 +67,7 @@ class Decimal < BigDecimal #:nodoc:
|
|
58
67
|
extend ActiveFacts::API::ValueClass
|
59
68
|
# The problem here is you can't pass a BigDecimal to BigDecimal.new. Fix it.
|
60
69
|
def self.new(v)
|
61
|
-
if v.is_a?(BigDecimal)
|
70
|
+
if v.is_a?(BigDecimal) || v.is_a?(Bignum)
|
62
71
|
super(v.to_s)
|
63
72
|
else
|
64
73
|
super
|
@@ -15,8 +15,14 @@ module ActiveFacts
|
|
15
15
|
|
16
16
|
# Value instance methods:
|
17
17
|
def initialize(*args) #:nodoc:
|
18
|
-
|
18
|
+
hash = args[-1].is_a?(Hash) ? args.pop.clone : nil
|
19
|
+
|
19
20
|
super(args)
|
21
|
+
|
22
|
+
(hash ? hash.entries : []).each do |role_name, value|
|
23
|
+
role = self.class.roles(role_name)
|
24
|
+
send(role.setter, value)
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
# verbalise this Value
|
@@ -24,18 +30,22 @@ module ActiveFacts
|
|
24
30
|
"#{role_name || self.class.basename} '#{to_s}'"
|
25
31
|
end
|
26
32
|
|
27
|
-
# A value is its own key
|
33
|
+
# A value is its own key, unless it's a delegate for a raw value
|
28
34
|
def identifying_role_values #:nodoc:
|
29
|
-
self
|
35
|
+
__getobj__ rescue self
|
30
36
|
end
|
31
37
|
|
32
38
|
# All ValueType classes include the methods defined here
|
33
39
|
module ClassMethods
|
34
40
|
include Instance::ClassMethods
|
35
41
|
|
36
|
-
def
|
42
|
+
def value_type *args, &block #:nodoc:
|
37
43
|
# REVISIT: args could be a hash, with keys :length, :scale, :unit, :allow
|
38
|
-
|
44
|
+
options = (args[-1].is_a?(Hash) ? args.pop : {})
|
45
|
+
options.each do |key, value|
|
46
|
+
raise "unknown value type option #{key}" unless respond_to?(key)
|
47
|
+
send(key, value)
|
48
|
+
end
|
39
49
|
end
|
40
50
|
|
41
51
|
class_eval do
|
@@ -67,12 +77,10 @@ module ActiveFacts
|
|
67
77
|
|
68
78
|
def identifying_role_values(*args) #:nodoc:
|
69
79
|
# If the single arg is the correct class or a subclass, use it directly
|
70
|
-
|
71
|
-
|
72
|
-
arg = arg.__getobj__ if RoleProxy === arg
|
73
|
-
return arg
|
80
|
+
if (args.size == 1 and (arg = args[0]).is_a?(self)) # No secondary supertypes allowed for value types
|
81
|
+
return arg.identifying_role_values
|
74
82
|
end
|
75
|
-
new(*args)
|
83
|
+
new(*args).identifying_role_values
|
76
84
|
end
|
77
85
|
|
78
86
|
def assert_instance(constellation, args) #:nodoc:
|
@@ -80,25 +88,24 @@ module ActiveFacts
|
|
80
88
|
# The key of an instance is the value or array of keys of the identifying values.
|
81
89
|
# The key values aren't necessarily present in the constellation, even after this.
|
82
90
|
key = identifying_role_values(*args)
|
83
|
-
#puts "#{klass} key is #{key.inspect}"
|
84
91
|
|
85
92
|
# Find and return an existing instance matching this key
|
86
93
|
instances = constellation.instances[self] # All instances of this class in this constellation
|
87
94
|
instance = instances[key]
|
88
|
-
# DEBUG: puts "assert #{self.basename} #{key.inspect} #{instance ? "exists" : "new"}"
|
89
95
|
return instance, key if instance # A matching instance of this class
|
90
96
|
|
91
|
-
|
97
|
+
#trace :assert, "Constructing new #{self} with #{args.inspect}" do
|
98
|
+
instance = new(*args)
|
99
|
+
#end
|
92
100
|
|
93
101
|
instance.constellation = constellation
|
94
102
|
return *index_instance(instance)
|
95
103
|
end
|
96
104
|
|
97
|
-
def index_instance(instance, key = nil) #:nodoc:
|
105
|
+
def index_instance(instance, key = nil, key_roles = nil) #:nodoc:
|
98
106
|
instances = instance.constellation.instances[self]
|
99
107
|
key = instance.identifying_role_values
|
100
108
|
instances[key] = instance
|
101
|
-
# DEBUG: puts "indexing value #{basename} using #{key.inspect} in #{constellation.object_id}"
|
102
109
|
|
103
110
|
# Index the instance for each supertype:
|
104
111
|
supertypes.each do |supertype|
|
@@ -109,7 +116,6 @@ module ActiveFacts
|
|
109
116
|
end
|
110
117
|
|
111
118
|
def inherited(other) #:nodoc:
|
112
|
-
#puts "REVISIT: ValueType #{self} < #{self.superclass} was inherited by #{other}; not implemented" #+"from #{caller*"\n\t"}"
|
113
119
|
# Copy the type parameters here, etc?
|
114
120
|
other.send :realise_supertypes, self
|
115
121
|
vocabulary.__add_object_type(other)
|
@@ -120,11 +126,8 @@ module ActiveFacts
|
|
120
126
|
def self.included other #:nodoc:
|
121
127
|
other.send :extend, ClassMethods
|
122
128
|
|
123
|
-
#puts "ValueType included in #{other.basename} from #{caller*"\n\t"}"
|
124
|
-
|
125
129
|
# Register ourselves with the parent module, which has become a Vocabulary:
|
126
130
|
vocabulary = other.modspace
|
127
|
-
# puts "ValueType.included(#{other.inspect})"
|
128
131
|
unless vocabulary.respond_to? :object_type # Extend module with Vocabulary if necessary
|
129
132
|
vocabulary.send :extend, Vocabulary
|
130
133
|
end
|
@@ -14,21 +14,28 @@ module ActiveFacts
|
|
14
14
|
# and can resolve the forward references when the class is finally defined.
|
15
15
|
# Construction of a Constellation requires a Vocabuary as argument.
|
16
16
|
module Vocabulary
|
17
|
-
# With a parameter, look up
|
17
|
+
# With a parameter, look up an object type by name.
|
18
18
|
# Without, return the hash (keyed by the class' basename) of all object_types in this vocabulary
|
19
19
|
def object_type(name = nil)
|
20
20
|
@object_type ||= {}
|
21
21
|
return @object_type unless name
|
22
22
|
|
23
|
-
|
23
|
+
if name.is_a? Class
|
24
|
+
raise "#{name} must be an object type in #{self.name}" unless name.vocabulary == self
|
25
|
+
return name
|
26
|
+
end
|
24
27
|
|
25
|
-
# puts "Looking up object_type #{name} in #{self.name}"
|
26
28
|
camel = name.to_s.camelcase
|
27
29
|
if (c = @object_type[camel])
|
28
30
|
__bind(camel)
|
29
|
-
|
31
|
+
c
|
32
|
+
else
|
33
|
+
begin
|
34
|
+
const_get("#{self.name}::#{camel}")
|
35
|
+
rescue NameError
|
36
|
+
nil
|
37
|
+
end
|
30
38
|
end
|
31
|
-
return (const_get("#{name}::#{camel}") rescue nil)
|
32
39
|
end
|
33
40
|
|
34
41
|
# Create a new constellation over this vocabulary
|
@@ -52,13 +59,11 @@ module ActiveFacts
|
|
52
59
|
def __add_object_type(klass) #:nodoc:
|
53
60
|
name = klass.basename
|
54
61
|
__bind(name)
|
55
|
-
# puts "Adding object_type #{name} to #{self.name}"
|
56
62
|
@object_type ||= {}
|
57
63
|
@object_type[klass.basename] = klass
|
58
64
|
end
|
59
65
|
|
60
66
|
def __delay(object_type_name, args, &block) #:nodoc:
|
61
|
-
# puts "Arranging for delayed binding on #{object_type_name.inspect}"
|
62
67
|
@delayed ||= Hash.new { |h,k| h[k] = [] }
|
63
68
|
@delayed[object_type_name] << [args, block]
|
64
69
|
end
|
@@ -66,9 +71,7 @@ module ActiveFacts
|
|
66
71
|
# __bind raises an error if the named class doesn't exist yet.
|
67
72
|
def __bind(object_type_name) #:nodoc:
|
68
73
|
object_type = const_get(object_type_name)
|
69
|
-
# puts "#{name}.__bind #{object_type_name} -> #{object_type.name}" if object_type
|
70
74
|
if (@delayed && @delayed.include?(object_type_name))
|
71
|
-
# $stderr.puts "#{object_type_name} was delayed, binding now"
|
72
75
|
d = @delayed[object_type_name]
|
73
76
|
d.each{|(a,b)|
|
74
77
|
b.call(object_type, *a)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Support code.
|
3
|
+
# The trace method supports indented tracing.
|
4
|
+
# Set the TRACE environment variable to enable it. Search the code to find the TRACE keywords, or use "all".
|
5
|
+
#
|
6
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
7
|
+
#
|
8
|
+
module ActiveFacts
|
9
|
+
(class << self; self; end).class_eval do
|
10
|
+
attr_accessor :tracer
|
11
|
+
end
|
12
|
+
|
13
|
+
class Tracer
|
14
|
+
def initialize
|
15
|
+
@nested = false # Set when a block enables all enclosed tracing
|
16
|
+
@available = {}
|
17
|
+
|
18
|
+
# First time, initialise the tracing environment
|
19
|
+
@indent = 0
|
20
|
+
@keys = {}
|
21
|
+
if (e = ENV["TRACE"])
|
22
|
+
e.split(/[^_a-zA-Z0-9]/).each{|k| enable(k) }
|
23
|
+
if @keys[:help]
|
24
|
+
at_exit {
|
25
|
+
@stderr.puts "---\nDebugging keys available: #{@available.keys.map{|s| s.to_s}.sort*", "}"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
require 'ruby-debug' if @keys[:debug]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def keys
|
33
|
+
@available.keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def enabled key
|
37
|
+
!key.empty? && @keys[key.to_sym]
|
38
|
+
end
|
39
|
+
|
40
|
+
def enable key
|
41
|
+
!key.to_s.empty? && @keys[key.to_sym] = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def disable key
|
45
|
+
!key.to_s.empty? && @keys.delete(key.to_sym)
|
46
|
+
end
|
47
|
+
|
48
|
+
def toggle key
|
49
|
+
!key.to_s.empty? and enabled(key) ? (disable(key); false) : (enable(key); true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def selected(args)
|
53
|
+
# Figure out whether this trace is enabled (itself or by :all), if it nests, and if we should print the key:
|
54
|
+
key =
|
55
|
+
if Symbol === args[0]
|
56
|
+
control = args.shift
|
57
|
+
if (s = control.to_s) =~ /_\Z/
|
58
|
+
nested = true
|
59
|
+
s.sub(/_\Z/, '').to_sym # Avoid creating new strings willy-nilly
|
60
|
+
else
|
61
|
+
control
|
62
|
+
end
|
63
|
+
else
|
64
|
+
:all
|
65
|
+
end
|
66
|
+
|
67
|
+
@available[key] ||= key # Remember that this trace was requested, for help
|
68
|
+
enabled = @nested || # This trace is enabled because it's in a nested block
|
69
|
+
@keys[key] || # This trace is enabled in its own right
|
70
|
+
@keys[:all] # This trace is enabled because all are
|
71
|
+
@nested = nested
|
72
|
+
[
|
73
|
+
(enabled ? 1 : 0),
|
74
|
+
@keys[:all] ? " %-15s"%control : nil
|
75
|
+
]
|
76
|
+
end
|
77
|
+
|
78
|
+
def show(*args)
|
79
|
+
enabled, key_to_show = selected(args)
|
80
|
+
|
81
|
+
# Emit the message if enabled or a parent is:
|
82
|
+
if args.size > 0 && enabled == 1
|
83
|
+
puts "\##{key_to_show} " +
|
84
|
+
' '*@indent +
|
85
|
+
args.
|
86
|
+
# A laudable aim, certainly, but in practise the Procs leak and slow things down:
|
87
|
+
# map{|a| a.respond_to?(:call) ? a.call : a}.
|
88
|
+
join(' ')
|
89
|
+
end
|
90
|
+
@indent += enabled
|
91
|
+
enabled
|
92
|
+
end
|
93
|
+
|
94
|
+
def trace(*args, &block)
|
95
|
+
begin
|
96
|
+
old_indent, old_nested, enabled = @indent, @nested, show(*args)
|
97
|
+
return (block || proc { enabled == 1 }).call
|
98
|
+
ensure
|
99
|
+
@indent, @nested = old_indent, old_nested
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Object
|
106
|
+
def trace *args, &block
|
107
|
+
(ActiveFacts.tracer ||= ActiveFacts::Tracer.new).trace(*args, &block)
|
108
|
+
end
|
109
|
+
end
|