reflekt 0.9.9 → 1.0.0
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 +4 -4
- data/lib/Accessor.rb +2 -0
- data/lib/Aggregator.rb +123 -82
- data/lib/Clone.rb +31 -0
- data/lib/Config.rb +7 -1
- data/lib/Control.rb +44 -15
- data/lib/Execution.rb +12 -3
- data/lib/Meta.rb +12 -0
- data/lib/MetaBuilder.rb +31 -4
- data/lib/Reflection.rb +65 -37
- data/lib/Reflekt.rb +68 -45
- data/lib/Rule.rb +11 -1
- data/lib/RuleSet.rb +26 -4
- data/lib/meta/ArrayMeta.rb +2 -1
- data/lib/meta/BooleanMeta.rb +5 -2
- data/lib/meta/FloatMeta.rb +26 -0
- data/lib/meta/IntegerMeta.rb +4 -1
- data/lib/meta/StringMeta.rb +4 -1
- data/lib/rules/ArrayRule.rb +14 -1
- data/lib/rules/BooleanRule.rb +6 -3
- data/lib/rules/FloatRule.rb +57 -0
- data/lib/rules/IntegerRule.rb +6 -1
- data/lib/rules/StringRule.rb +25 -1
- data/lib/web/README.md +3 -3
- data/lib/web/bundle.js +5 -5
- data/lib/web/server.js +45 -49
- metadata +9 -6
data/lib/Meta.rb
CHANGED
@@ -3,10 +3,22 @@
|
|
3
3
|
#
|
4
4
|
# @pattern Abstract class
|
5
5
|
# @see lib/meta for each meta.
|
6
|
+
#
|
7
|
+
# @hierachy
|
8
|
+
# 1. Execution
|
9
|
+
# 2. Reflection
|
10
|
+
# 3. Meta <- YOU ARE HERE
|
6
11
|
################################################################################
|
7
12
|
|
8
13
|
class Meta
|
9
14
|
|
15
|
+
##
|
16
|
+
# Each meta defines its type.
|
17
|
+
##
|
18
|
+
def initialize()
|
19
|
+
@type = nil
|
20
|
+
end
|
21
|
+
|
10
22
|
##
|
11
23
|
# Each meta loads values.
|
12
24
|
#
|
data/lib/MetaBuilder.rb
CHANGED
@@ -6,8 +6,8 @@
|
|
6
6
|
################################################################################
|
7
7
|
|
8
8
|
require 'Meta'
|
9
|
-
|
10
|
-
|
9
|
+
# Require all meta.
|
10
|
+
Dir[File.join(__dir__, 'meta', '*.rb')].each { |file| require file }
|
11
11
|
|
12
12
|
class MetaBuilder
|
13
13
|
|
@@ -19,9 +19,16 @@ class MetaBuilder
|
|
19
19
|
def self.create(value)
|
20
20
|
|
21
21
|
meta = nil
|
22
|
+
data_type = value.class.to_s
|
22
23
|
|
23
|
-
#
|
24
|
-
case
|
24
|
+
# Create meta type for matching data type.
|
25
|
+
case data_type
|
26
|
+
when "Array"
|
27
|
+
meta = ArrayMeta.new()
|
28
|
+
when "TrueClass", "FalseClass"
|
29
|
+
meta = BooleanMeta.new()
|
30
|
+
when "Float"
|
31
|
+
meta = FloatMeta.new()
|
25
32
|
when "Integer"
|
26
33
|
meta = IntegerMeta.new()
|
27
34
|
when "String"
|
@@ -53,4 +60,24 @@ class MetaBuilder
|
|
53
60
|
|
54
61
|
end
|
55
62
|
|
63
|
+
##
|
64
|
+
# @param data_type [Type]
|
65
|
+
##
|
66
|
+
def self.data_type_to_meta_type(value)
|
67
|
+
|
68
|
+
data_type = value.class
|
69
|
+
|
70
|
+
meta_types = {
|
71
|
+
Array => :array,
|
72
|
+
TrueClass => :bool,
|
73
|
+
FalseClass => :bool,
|
74
|
+
Float => :float,
|
75
|
+
Integer => :int,
|
76
|
+
String => :string
|
77
|
+
}
|
78
|
+
|
79
|
+
return meta_types[data_type]
|
80
|
+
|
81
|
+
end
|
82
|
+
|
56
83
|
end
|
data/lib/Reflection.rb
CHANGED
@@ -2,23 +2,29 @@
|
|
2
2
|
# A snapshot of simulated data.
|
3
3
|
#
|
4
4
|
# @nomenclature
|
5
|
-
# args
|
5
|
+
# args, inputs/output and meta represent different stages of a value.
|
6
6
|
#
|
7
7
|
# @hierachy
|
8
8
|
# 1. Execution
|
9
|
-
# 2. Reflection
|
10
|
-
# 3.
|
9
|
+
# 2. Reflection <- YOU ARE HERE
|
10
|
+
# 3. Meta
|
11
11
|
################################################################################
|
12
12
|
|
13
|
+
require 'Clone'
|
13
14
|
require 'MetaBuilder'
|
14
15
|
|
15
16
|
class Reflection
|
16
17
|
|
17
|
-
|
18
|
+
attr_reader :status
|
18
19
|
|
19
20
|
##
|
20
21
|
# Create a Reflection.
|
21
22
|
#
|
23
|
+
# @status
|
24
|
+
# - :pass The reflection passes the rules.
|
25
|
+
# - :fail The reflection fails the rules or produces a system error.
|
26
|
+
# - :error The control reflection produces a system error.
|
27
|
+
#
|
22
28
|
# @param execution [Execution] The Execution that created this Reflection.
|
23
29
|
# @param number [Integer] Multiple Reflections can be created per Execution.
|
24
30
|
# @param aggregator [Aggregator] The aggregated RuleSet for this class/method.
|
@@ -37,83 +43,97 @@ class Reflection
|
|
37
43
|
@method = execution.method
|
38
44
|
|
39
45
|
# Metadata.
|
40
|
-
@inputs =
|
46
|
+
@inputs = nil
|
41
47
|
@output = nil
|
42
48
|
|
43
49
|
# Clone the execution's calling object.
|
50
|
+
# TODO: Abstract away into Clone class.
|
44
51
|
@clone = execution.caller_object.clone
|
45
|
-
@clone_id = nil
|
46
52
|
|
47
53
|
# Result.
|
48
54
|
@status = :pass
|
49
55
|
@time = Time.now.to_i
|
56
|
+
@message = nil
|
50
57
|
|
51
58
|
end
|
52
59
|
|
53
60
|
##
|
54
61
|
# Reflect on a method.
|
55
62
|
#
|
56
|
-
# Creates a shadow execution
|
63
|
+
# Creates a shadow execution.
|
57
64
|
# @param *args [Dynamic] The method's arguments.
|
58
65
|
##
|
59
66
|
def reflect(*args)
|
60
67
|
|
61
|
-
# Get aggregated
|
62
|
-
|
63
|
-
|
68
|
+
# Get aggregated rule sets.
|
69
|
+
input_rule_sets = @aggregator.get_input_rule_sets(@klass, @method)
|
70
|
+
output_rule_set = @aggregator.get_output_rule_set(@klass, @method)
|
64
71
|
|
65
|
-
#
|
66
|
-
|
72
|
+
# When arguments exist.
|
73
|
+
unless args.size == 0
|
67
74
|
|
68
|
-
|
69
|
-
|
75
|
+
# When aggregated rule sets exist.
|
76
|
+
unless input_rule_sets.nil?
|
70
77
|
|
71
|
-
|
72
|
-
|
78
|
+
# Randomize arguments from rule sets.
|
79
|
+
args = randomize(args, input_rule_sets)
|
73
80
|
|
74
|
-
|
75
|
-
|
76
|
-
unless @aggregator.validate_inputs(new_args, agg_input_rule_sets)
|
81
|
+
# Validate arguments against aggregated rule sets.
|
82
|
+
unless @aggregator.test_inputs(args, input_rule_sets)
|
77
83
|
@status = :fail
|
78
84
|
end
|
85
|
+
|
79
86
|
end
|
80
87
|
|
88
|
+
# Create metadata for each argument.
|
89
|
+
# TODO: Create metadata for other inputs such as properties on the instance.
|
90
|
+
@inputs = MetaBuilder.create_many(args)
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
# Action method with new/old arguments.
|
95
|
+
begin
|
96
|
+
|
81
97
|
# Run reflection.
|
82
|
-
output = @clone.send(@method, *
|
98
|
+
output = @clone.send(@method, *args)
|
83
99
|
@output = MetaBuilder.create(output)
|
84
100
|
|
85
|
-
# Validate output with aggregated control
|
86
|
-
unless
|
87
|
-
unless @aggregator.
|
101
|
+
# Validate output with aggregated control rule sets.
|
102
|
+
unless output_rule_set.nil?
|
103
|
+
unless @aggregator.test_output(output, output_rule_set)
|
88
104
|
@status = :fail
|
89
105
|
end
|
90
106
|
end
|
91
107
|
|
92
|
-
# When
|
108
|
+
# When a system error occurs.
|
93
109
|
rescue StandardError => message
|
110
|
+
|
94
111
|
@status = :fail
|
95
112
|
@message = message
|
113
|
+
|
96
114
|
end
|
97
115
|
|
98
116
|
end
|
99
117
|
|
100
118
|
##
|
101
|
-
# Create random values for each argument.
|
119
|
+
# Create random values for each argument from control reflections.
|
102
120
|
#
|
103
121
|
# @param args [Dynamic] The arguments to create random values for.
|
122
|
+
# @param input_rule_sets [Array] Aggregated rule sets for each argument.
|
123
|
+
#
|
104
124
|
# @return [Dynamic] Random arguments.
|
105
125
|
##
|
106
|
-
def randomize(args)
|
126
|
+
def randomize(args, input_rule_sets)
|
107
127
|
|
108
128
|
random_args = []
|
109
129
|
|
110
|
-
args.
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
130
|
+
args.each_with_index do |arg, arg_num|
|
131
|
+
|
132
|
+
rule_type = Aggregator.value_to_rule_type(arg)
|
133
|
+
agg_rule = input_rule_sets[arg_num].rules[rule_type]
|
134
|
+
|
135
|
+
random_args << agg_rule.random()
|
136
|
+
|
117
137
|
end
|
118
138
|
|
119
139
|
return random_args
|
@@ -144,11 +164,19 @@ class Reflection
|
|
144
164
|
:method => @method,
|
145
165
|
:status => @status,
|
146
166
|
:message => @message,
|
147
|
-
:inputs =>
|
148
|
-
:output =>
|
167
|
+
:inputs => nil,
|
168
|
+
:output => nil,
|
149
169
|
}
|
150
|
-
|
151
|
-
|
170
|
+
|
171
|
+
unless @inputs.nil?
|
172
|
+
reflection[:inputs] = []
|
173
|
+
@inputs.each do |meta|
|
174
|
+
reflection[:inputs] << meta.result()
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
unless @output.nil?
|
179
|
+
reflection[:output] = @output.result()
|
152
180
|
end
|
153
181
|
|
154
182
|
return reflection
|
data/lib/Reflekt.rb
CHANGED
@@ -34,6 +34,7 @@ module Reflekt
|
|
34
34
|
|
35
35
|
def initialize(*args)
|
36
36
|
|
37
|
+
# TODO: Store counts on @@reflekt and key by instance ID.
|
37
38
|
@reflekt_counts = {}
|
38
39
|
|
39
40
|
# Get child and parent instance methods.
|
@@ -49,72 +50,94 @@ module Reflekt
|
|
49
50
|
# When method called in flow.
|
50
51
|
self.define_singleton_method(method) do |*args|
|
51
52
|
|
52
|
-
#
|
53
|
-
|
53
|
+
# When Reflekt enabled and control reflection has executed without error.
|
54
|
+
if @@reflekt.config.enabled && !@@reflekt.error
|
54
55
|
|
55
|
-
|
56
|
-
|
56
|
+
# Get current execution.
|
57
|
+
execution = @@reflekt.stack.peek()
|
57
58
|
|
58
|
-
#
|
59
|
-
|
59
|
+
# Don't reflect when reflect limit reached or method skipped.
|
60
|
+
unless (@reflekt_counts[method] >= @@reflekt.config.reflect_limit) || self.class.reflekt_skipped?(method)
|
60
61
|
|
61
|
-
#
|
62
|
-
execution
|
62
|
+
# When stack empty or past execution done reflecting.
|
63
|
+
if execution.nil? || execution.has_finished_reflecting?
|
63
64
|
|
64
|
-
|
65
|
+
# Create execution.
|
66
|
+
execution = Execution.new(self, method, @@reflekt.config.reflect_amount, @@reflekt.stack)
|
65
67
|
|
66
|
-
|
68
|
+
@@reflekt.stack.push(execution)
|
69
|
+
|
70
|
+
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
##
|
73
|
+
# Reflect the execution.
|
74
|
+
#
|
75
|
+
# The first method call in the execution creates a reflection.
|
76
|
+
# Then method calls are shadow executions which return to the reflection.
|
77
|
+
##
|
78
|
+
if execution.has_empty_reflections? && !execution.is_reflecting?
|
79
|
+
execution.is_reflecting = true
|
76
80
|
|
77
|
-
|
78
|
-
|
79
|
-
|
81
|
+
# Create control.
|
82
|
+
control = Control.new(execution, 0, @@reflekt.aggregator)
|
83
|
+
execution.control = control
|
80
84
|
|
81
|
-
|
82
|
-
|
85
|
+
# Execute control.
|
86
|
+
control.reflect(*args)
|
83
87
|
|
84
|
-
|
85
|
-
|
88
|
+
# Stop reflecting when control fails to execute.
|
89
|
+
if control.status == :error
|
90
|
+
@@reflekt.error = true
|
91
|
+
# Continue reflecting when control executes succesfully.
|
92
|
+
else
|
86
93
|
|
87
|
-
|
88
|
-
|
94
|
+
# Save control as reflection.
|
95
|
+
@@reflekt.db.get("reflections").push(control.result())
|
89
96
|
|
90
|
-
|
91
|
-
|
92
|
-
execution.reflections[index] = reflection
|
97
|
+
# Multiple reflections per execution.
|
98
|
+
execution.reflections.each_with_index do |value, index|
|
93
99
|
|
94
|
-
|
95
|
-
|
96
|
-
|
100
|
+
# Create reflection.
|
101
|
+
reflection = Reflection.new(execution, index + 1, @@reflekt.aggregator)
|
102
|
+
execution.reflections[index] = reflection
|
97
103
|
|
98
|
-
|
99
|
-
|
104
|
+
# Execute reflection.
|
105
|
+
reflection.reflect(*args)
|
106
|
+
@reflekt_counts[method] = @reflekt_counts[method] + 1
|
100
107
|
|
101
|
-
|
108
|
+
# Save reflection.
|
109
|
+
@@reflekt.db.get("reflections").push(reflection.result())
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# Save control.
|
114
|
+
@@reflekt.db.get("controls").push(control.result())
|
115
|
+
|
116
|
+
# Save results.
|
117
|
+
@@reflekt.db.write()
|
102
118
|
|
103
|
-
|
104
|
-
|
119
|
+
# Render results.
|
120
|
+
@@reflekt.renderer.render()
|
105
121
|
|
106
|
-
|
107
|
-
|
122
|
+
end
|
123
|
+
|
124
|
+
execution.is_reflecting = false
|
125
|
+
end
|
108
126
|
|
109
|
-
execution.is_reflecting = false
|
110
127
|
end
|
111
128
|
|
112
|
-
|
129
|
+
# Don't execute skipped methods when reflecting.
|
130
|
+
unless execution.is_reflecting? && self.class.reflekt_skipped?(method)
|
131
|
+
|
132
|
+
# Continue execution / shadow execution.
|
133
|
+
super *args
|
134
|
+
|
135
|
+
end
|
113
136
|
|
114
|
-
#
|
115
|
-
|
137
|
+
# When Reflekt disabled or control reflection failed.
|
138
|
+
else
|
116
139
|
|
117
|
-
# Continue execution
|
140
|
+
# Continue execution.
|
118
141
|
super *args
|
119
142
|
|
120
143
|
end
|
data/lib/Rule.rb
CHANGED
@@ -13,6 +13,8 @@
|
|
13
13
|
|
14
14
|
class Rule
|
15
15
|
|
16
|
+
attr_reader :type
|
17
|
+
|
16
18
|
##
|
17
19
|
# Each rule trains on metadata to determine its boundaries.
|
18
20
|
#
|
@@ -31,7 +33,7 @@ class Rule
|
|
31
33
|
end
|
32
34
|
|
33
35
|
##
|
34
|
-
# Each rule provides
|
36
|
+
# Each rule provides results.
|
35
37
|
#
|
36
38
|
# @return [Hash]
|
37
39
|
##
|
@@ -39,4 +41,12 @@ class Rule
|
|
39
41
|
{}
|
40
42
|
end
|
41
43
|
|
44
|
+
##
|
45
|
+
# Each rule provides a random example that matches the rule's boundaries.
|
46
|
+
#
|
47
|
+
# @return [Dynamic] A random value.
|
48
|
+
##
|
49
|
+
def random()
|
50
|
+
end
|
51
|
+
|
42
52
|
end
|
data/lib/RuleSet.rb
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
################################################################################
|
13
13
|
|
14
14
|
require 'set'
|
15
|
+
require 'MetaBuilder'
|
15
16
|
|
16
17
|
class RuleSet
|
17
18
|
|
@@ -22,9 +23,14 @@ class RuleSet
|
|
22
23
|
##
|
23
24
|
def initialize(meta_map)
|
24
25
|
|
26
|
+
# The rules that apply to meta types.
|
25
27
|
@meta_map = meta_map
|
28
|
+
|
29
|
+
# The types of meta this rule set applies to.
|
30
|
+
# Rules are only validated on their supported meta type.
|
31
|
+
@meta_types = Set.new()
|
32
|
+
|
26
33
|
@rules = {}
|
27
|
-
@types = Set.new()
|
28
34
|
|
29
35
|
end
|
30
36
|
|
@@ -38,7 +44,7 @@ class RuleSet
|
|
38
44
|
unless meta.nil? || meta[:type].nil?
|
39
45
|
|
40
46
|
meta_type = meta[:type]
|
41
|
-
@
|
47
|
+
@meta_types << meta_type
|
42
48
|
|
43
49
|
# Get rule types for this meta type.
|
44
50
|
if @meta_map.key? meta_type
|
@@ -46,7 +52,7 @@ class RuleSet
|
|
46
52
|
|
47
53
|
# Ensure rule exists.
|
48
54
|
if @rules[rule_type].nil?
|
49
|
-
@rules
|
55
|
+
@rules[rule_type] = rule_type.new()
|
50
56
|
end
|
51
57
|
|
52
58
|
# Train rule.
|
@@ -55,10 +61,25 @@ class RuleSet
|
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
58
|
-
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def test(value)
|
69
|
+
result = true
|
70
|
+
|
71
|
+
# Only test data type on rule of matching meta type.
|
72
|
+
meta_type = MetaBuilder.data_type_to_meta_type(value)
|
59
73
|
|
74
|
+
@rules.each do |klass, rule|
|
75
|
+
if (rule.type == meta_type)
|
76
|
+
unless rule.test(value)
|
77
|
+
result = false
|
78
|
+
end
|
79
|
+
end
|
60
80
|
end
|
61
81
|
|
82
|
+
return result
|
62
83
|
end
|
63
84
|
|
64
85
|
##
|
@@ -78,4 +99,5 @@ class RuleSet
|
|
78
99
|
|
79
100
|
end
|
80
101
|
|
102
|
+
|
81
103
|
end
|