reflekt 0.9.9 → 1.0.4
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 +136 -82
- data/lib/Clone.rb +31 -0
- data/lib/Config.rb +8 -1
- data/lib/Control.rb +53 -15
- data/lib/Execution.rb +12 -3
- data/lib/Meta.rb +46 -2
- data/lib/MetaBuilder.rb +32 -4
- data/lib/Reflection.rb +69 -43
- data/lib/Reflekt.rb +68 -45
- data/lib/Rule.rb +11 -1
- data/lib/RuleSet.rb +45 -17
- data/lib/meta/ArrayMeta.rb +3 -2
- data/lib/meta/BooleanMeta.rb +6 -3
- data/lib/meta/FloatMeta.rb +26 -0
- data/lib/meta/IntegerMeta.rb +5 -2
- data/lib/meta/NullMeta.rb +34 -0
- data/lib/meta/StringMeta.rb +5 -2
- data/lib/rules/ArrayRule.rb +14 -1
- data/lib/rules/BooleanRule.rb +9 -4
- data/lib/rules/FloatRule.rb +57 -0
- data/lib/rules/IntegerRule.rb +6 -1
- data/lib/rules/NullRule.rb +36 -0
- 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 +11 -6
data/lib/Execution.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
################################################################################
|
2
|
+
# A shadow execution.
|
3
|
+
#
|
4
|
+
# @hierachy
|
5
|
+
# 1. Execution <- YOU ARE HERE
|
6
|
+
# 2. Reflection
|
7
|
+
# 3. Meta
|
8
|
+
################################################################################
|
9
|
+
|
1
10
|
class Execution
|
2
11
|
|
3
12
|
attr_accessor :unique_id
|
@@ -19,10 +28,10 @@ class Execution
|
|
19
28
|
#
|
20
29
|
# @param object [Object] The calling object.
|
21
30
|
# @param method [Symbol] The calling method.
|
22
|
-
# @param
|
31
|
+
# @param reflect_amount [Integer] The number of reflections to create per execution.
|
23
32
|
# @param stack [ShadowStack] The shadow execution call stack.
|
24
33
|
##
|
25
|
-
def initialize(caller_object, method,
|
34
|
+
def initialize(caller_object, method, reflect_amount, stack)
|
26
35
|
|
27
36
|
@time = Time.now.to_i
|
28
37
|
@unique_id = @time + rand(1..99999)
|
@@ -42,7 +51,7 @@ class Execution
|
|
42
51
|
|
43
52
|
# Reflections.
|
44
53
|
@control = nil
|
45
|
-
@reflections = Array.new(
|
54
|
+
@reflections = Array.new(reflect_amount)
|
46
55
|
|
47
56
|
# State.
|
48
57
|
if @stack.peek() == nil
|
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
|
#
|
@@ -16,12 +28,44 @@ class Meta
|
|
16
28
|
end
|
17
29
|
|
18
30
|
##
|
19
|
-
# Each meta
|
31
|
+
# Each meta serializes metadata.
|
20
32
|
#
|
21
33
|
# @return [Hash]
|
22
34
|
##
|
23
|
-
def
|
35
|
+
def serialize()
|
24
36
|
{}
|
25
37
|
end
|
26
38
|
|
39
|
+
##############################################################################
|
40
|
+
# CLASS
|
41
|
+
##############################################################################
|
42
|
+
|
43
|
+
##
|
44
|
+
# Deserialize metadata.
|
45
|
+
#
|
46
|
+
# @todo Deserialize should create a Meta object.
|
47
|
+
# @todo Require each Meta type to handle its own deserialization.
|
48
|
+
#
|
49
|
+
# @param meta [Hash] The metadata to deserialize.
|
50
|
+
# @param meta [Hash]
|
51
|
+
##
|
52
|
+
def self.deserialize(meta)
|
53
|
+
|
54
|
+
# Convert nil meta into NullMeta.
|
55
|
+
# Meta is nil when there are no @inputs or @output on the method.
|
56
|
+
if meta.nil?
|
57
|
+
return NullMeta.new().serialize()
|
58
|
+
end
|
59
|
+
|
60
|
+
# Symbolize keys.
|
61
|
+
# TODO: Remove once "Fix Rowdb.get(path)" bug fixed.
|
62
|
+
meta = meta.transform_keys(&:to_sym)
|
63
|
+
|
64
|
+
# Symbolize type value.
|
65
|
+
meta[:type] = meta[:type].to_sym
|
66
|
+
|
67
|
+
return meta
|
68
|
+
|
69
|
+
end
|
70
|
+
|
27
71
|
end
|
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,25 @@ 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
|
+
NilClass => :null,
|
77
|
+
String => :string
|
78
|
+
}
|
79
|
+
|
80
|
+
return meta_types[data_type]
|
81
|
+
|
82
|
+
end
|
83
|
+
|
56
84
|
end
|
data/lib/Reflection.rb
CHANGED
@@ -1,20 +1,30 @@
|
|
1
1
|
################################################################################
|
2
|
-
# A snapshot of
|
2
|
+
# A snapshot of random data.
|
3
|
+
#
|
4
|
+
# @note
|
5
|
+
# A reflection's random value is within the bounds of aggregated control rule sets
|
6
|
+
# as well as as the arg type being inputted into the current control reflection.
|
3
7
|
#
|
4
8
|
# @nomenclature
|
5
|
-
# args
|
9
|
+
# args, inputs/output and meta represent different stages of a value.
|
6
10
|
#
|
7
11
|
# @hierachy
|
8
12
|
# 1. Execution
|
9
|
-
# 2. Reflection
|
10
|
-
# 3.
|
13
|
+
# 2. Reflection <- YOU ARE HERE
|
14
|
+
# 3. Meta
|
15
|
+
#
|
16
|
+
# @status
|
17
|
+
# - :pass [Symbol] The reflection passes the rules.
|
18
|
+
# - :fail [Symbol] The reflection fails the rules or produces a system error.
|
19
|
+
# - :error [Symbol] The control reflection produces a system error.
|
11
20
|
################################################################################
|
12
21
|
|
22
|
+
require 'Clone'
|
13
23
|
require 'MetaBuilder'
|
14
24
|
|
15
25
|
class Reflection
|
16
26
|
|
17
|
-
|
27
|
+
attr_reader :status
|
18
28
|
|
19
29
|
##
|
20
30
|
# Create a Reflection.
|
@@ -37,83 +47,91 @@ class Reflection
|
|
37
47
|
@method = execution.method
|
38
48
|
|
39
49
|
# Metadata.
|
40
|
-
@inputs =
|
50
|
+
@inputs = nil
|
41
51
|
@output = nil
|
42
52
|
|
43
53
|
# Clone the execution's calling object.
|
54
|
+
# TODO: Abstract away into Clone class.
|
44
55
|
@clone = execution.caller_object.clone
|
45
|
-
@clone_id = nil
|
46
56
|
|
47
57
|
# Result.
|
48
58
|
@status = :pass
|
49
59
|
@time = Time.now.to_i
|
60
|
+
@message = nil
|
50
61
|
|
51
62
|
end
|
52
63
|
|
53
64
|
##
|
54
65
|
# Reflect on a method.
|
55
66
|
#
|
56
|
-
# Creates a shadow execution
|
67
|
+
# Creates a shadow execution.
|
57
68
|
# @param *args [Dynamic] The method's arguments.
|
58
69
|
##
|
59
70
|
def reflect(*args)
|
60
71
|
|
61
|
-
# Get aggregated
|
62
|
-
|
63
|
-
|
72
|
+
# Get aggregated rule sets.
|
73
|
+
input_rule_sets = @aggregator.get_input_rule_sets(@klass, @method)
|
74
|
+
output_rule_set = @aggregator.get_output_rule_set(@klass, @method)
|
64
75
|
|
65
|
-
#
|
66
|
-
|
76
|
+
# When arguments exist.
|
77
|
+
unless args.size == 0
|
67
78
|
|
68
|
-
|
69
|
-
|
79
|
+
# Create random arguments from aggregated rule sets.
|
80
|
+
unless input_rule_sets.nil?
|
81
|
+
args = randomize(args, input_rule_sets)
|
82
|
+
end
|
70
83
|
|
71
|
-
|
72
|
-
|
84
|
+
# Create metadata for each argument.
|
85
|
+
# TODO: Create metadata for other inputs such as properties on the instance.
|
86
|
+
@inputs = MetaBuilder.create_many(args)
|
73
87
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Action method with new/old arguments.
|
91
|
+
begin
|
80
92
|
|
81
93
|
# Run reflection.
|
82
|
-
output = @clone.send(@method, *
|
94
|
+
output = @clone.send(@method, *args)
|
83
95
|
@output = MetaBuilder.create(output)
|
84
96
|
|
85
|
-
# Validate output
|
86
|
-
unless
|
87
|
-
unless @aggregator.
|
97
|
+
# Validate output against aggregated control rule sets.
|
98
|
+
unless output_rule_set.nil?
|
99
|
+
unless @aggregator.test_output(output, output_rule_set)
|
88
100
|
@status = :fail
|
89
101
|
end
|
90
102
|
end
|
91
103
|
|
92
|
-
# When
|
104
|
+
# When a system error occurs.
|
93
105
|
rescue StandardError => message
|
106
|
+
|
94
107
|
@status = :fail
|
95
108
|
@message = message
|
109
|
+
|
96
110
|
end
|
97
111
|
|
98
112
|
end
|
99
113
|
|
100
114
|
##
|
101
|
-
# Create random values for each argument.
|
115
|
+
# Create random values for each argument from control reflections.
|
116
|
+
#
|
117
|
+
# @param args [Dynamic] The arguments to mirror random values for.
|
118
|
+
# @param input_rule_sets [Array] Aggregated rule sets for each argument.
|
102
119
|
#
|
103
|
-
# @param args [Dynamic] The arguments to create random values for.
|
104
120
|
# @return [Dynamic] Random arguments.
|
105
121
|
##
|
106
|
-
def randomize(args)
|
122
|
+
def randomize(args, input_rule_sets)
|
107
123
|
|
108
124
|
random_args = []
|
109
125
|
|
110
|
-
args.
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
126
|
+
args.each_with_index do |arg, arg_num|
|
127
|
+
|
128
|
+
# Get a random rule in the rule set.
|
129
|
+
rules = input_rule_sets[arg_num].rules
|
130
|
+
agg_rule = rules[rules.keys.sample]
|
131
|
+
|
132
|
+
# Create a random value that follows that rule.
|
133
|
+
random_args << agg_rule.random()
|
134
|
+
|
117
135
|
end
|
118
136
|
|
119
137
|
return random_args
|
@@ -125,7 +143,7 @@ class Reflection
|
|
125
143
|
#
|
126
144
|
# @return [Hash] Reflection metadata.
|
127
145
|
##
|
128
|
-
def
|
146
|
+
def serialize()
|
129
147
|
|
130
148
|
# The ID of the first execution in the ShadowStack.
|
131
149
|
base_id = nil
|
@@ -144,11 +162,19 @@ class Reflection
|
|
144
162
|
:method => @method,
|
145
163
|
:status => @status,
|
146
164
|
:message => @message,
|
147
|
-
:inputs =>
|
148
|
-
:output =>
|
165
|
+
:inputs => nil,
|
166
|
+
:output => nil,
|
149
167
|
}
|
150
|
-
|
151
|
-
|
168
|
+
|
169
|
+
unless @inputs.nil?
|
170
|
+
reflection[:inputs] = []
|
171
|
+
@inputs.each do |meta|
|
172
|
+
reflection[:inputs] << meta.serialize()
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
unless @output.nil?
|
177
|
+
reflection[:output] = @output.serialize()
|
152
178
|
end
|
153
179
|
|
154
180
|
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.serialize())
|
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.serialize())
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# Save control.
|
114
|
+
@@reflekt.db.get("controls").push(control.serialize())
|
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
|