activefacts-api 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|