reflekt 0.5.0 → 0.9.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0428affa101de6d5e5dd908634242bde3e5ea82af55750024d2e222bca3e7d89'
4
- data.tar.gz: 30e1caab27785f8fa0ca04bf376b2ef13eb493d31f7b2b0f2cf3ec592795f340
3
+ metadata.gz: 3589292c5c5cba2f3d7c7d5e1d7e02ee1052afe7339929cfa058b787b89c3158
4
+ data.tar.gz: d33c6774d53d4b7a0978fcfc48fa00e22f3684ccd51e5d42676f01831fb84291
5
5
  SHA512:
6
- metadata.gz: e6cdcfc4a1ab977bc610ce3af7ee789a99deae778bd903dca9220b442363e2231c289f0d0fbedf77256d88495a9f1c7b69ff4ac7e8f63ec832de4215864058bc
7
- data.tar.gz: dff0de3808213dae276287d5750be30418947c89a4faf3f7cbe0540c18e399377d6a5a977f744e9f5da4335919232692b7ca2dea89f6dc5860a0d54432224d78
6
+ metadata.gz: 2e3120f544d87057b4783a4e6e76a9689844f17ed1ad602c8b2c5e5ee241285ef83e276b56fbdb33da0d167c68077eca6f851e859a28392718a26ed129ab18e7
7
+ data.tar.gz: 47d323198d8d27585ef5fb0d493eca120d0cd2ff532a6578ef3b7490ccbc9e4fc38757bab8fb2ad4ab6c05c0a9fd721b27a5925a90c95503c0de5d4fb7483e12
@@ -0,0 +1,7 @@
1
+ require 'Reflection'
2
+
3
+ class Control < Reflection
4
+
5
+ # TODO.
6
+
7
+ end
@@ -0,0 +1,47 @@
1
+ class Execution
2
+
3
+ attr_accessor :object
4
+ attr_accessor :caller_id
5
+ attr_accessor :caller_class
6
+ attr_accessor :parent
7
+ attr_accessor :child
8
+ attr_accessor :control
9
+ attr_accessor :reflections
10
+ attr_accessor :is_reflecting
11
+
12
+ def initialize(object, reflection_count)
13
+
14
+ @object = object
15
+ @caller_id = object.object_id
16
+ @caller_class = object.class
17
+ @parent = nil
18
+ @child = nil
19
+
20
+ @control = []
21
+ @reflections = Array.new(reflection_count)
22
+ @is_reflecting = false
23
+
24
+ end
25
+
26
+ def has_empty_reflections?
27
+ @reflections.include? nil
28
+ end
29
+
30
+ ##
31
+ # Is the Execution currently reflecting methods?
32
+ ##
33
+ def is_reflecting?
34
+ @is_reflecting
35
+ end
36
+
37
+ def has_finished_reflecting?
38
+ if is_reflecting?
39
+ return false
40
+ end
41
+ if has_empty_reflections?
42
+ return false
43
+ end
44
+ return true
45
+ end
46
+
47
+ end
@@ -0,0 +1,145 @@
1
+ class Reflection
2
+
3
+ # Keys.
4
+ TIME = "t"
5
+ INPUT = "i"
6
+ OUTPUT = "o"
7
+ TYPE = "T"
8
+ COUNT = "C"
9
+ VALUE = "V"
10
+ STATUS = "s"
11
+ MESSAGE = "m"
12
+ # Values.
13
+ PASS = "p"
14
+ FAIL = "f"
15
+
16
+ attr_accessor :clone
17
+
18
+ def initialize(execution, method, is_control)
19
+
20
+ @execution = execution
21
+ @method = method
22
+ @is_control = is_control
23
+
24
+ # Clone the execution's object.
25
+ @clone = execution.object.clone
26
+ @clone_id = nil
27
+
28
+ # Result.
29
+ @status = nil
30
+ @time = Time.now.to_i
31
+ @input = []
32
+ @output = nil
33
+
34
+ end
35
+
36
+ ##
37
+ # Reflect on a method.
38
+ #
39
+ # Creates a shadow execution stack.
40
+ #
41
+ # @param method - The name of the method.
42
+ # @param *args - The method arguments.
43
+ #
44
+ # @return - A reflection hash.
45
+ ##
46
+ def reflect(*args)
47
+
48
+ # Reflect on real world arguments.
49
+ if @is_control
50
+ @input = *args
51
+ # Reflect on deviated arguments.
52
+ else
53
+ args.each do |arg|
54
+ case arg
55
+ when Integer
56
+ @input << rand(9999)
57
+ else
58
+ @input << arg
59
+ end
60
+ end
61
+ end
62
+
63
+ # Action method with new arguments.
64
+ begin
65
+ @output = @clone.send(@method, *@input)
66
+ # When fail.
67
+ rescue StandardError => message
68
+ @status = MESSAGE
69
+ @message = message
70
+ # When pass.
71
+ else
72
+ @status = PASS
73
+ end
74
+
75
+ end
76
+
77
+ def result()
78
+ # Build reflection.
79
+ reflection = {
80
+ TIME => @time,
81
+ STATUS => @status,
82
+ INPUT => normalize_input(@input),
83
+ OUTPUT => normalize_output(@output),
84
+ MESSAGE => @message
85
+ }
86
+ end
87
+
88
+ ##
89
+ # Normalize inputs.
90
+ #
91
+ # @param args - The actual inputs.
92
+ # @return - A generic inputs representation.
93
+ ##
94
+ def normalize_input(args)
95
+ inputs = []
96
+ args.each do |arg|
97
+ input = {
98
+ TYPE => arg.class.to_s,
99
+ VALUE => normalize_value(arg)
100
+ }
101
+ if (arg.class == Array)
102
+ input[COUNT] = arg.count
103
+ end
104
+ inputs << input
105
+ end
106
+ inputs
107
+ end
108
+
109
+ ##
110
+ # Normalize output.
111
+ #
112
+ # @param input - The actual output.
113
+ # @return - A generic output representation.
114
+ ##
115
+ def normalize_output(input)
116
+
117
+ output = {
118
+ TYPE => input.class.to_s,
119
+ VALUE => normalize_value(input)
120
+ }
121
+
122
+ if (input.class == Array || input.class == Hash)
123
+ output[COUNT] = input.count
124
+ elsif (input.class == TrueClass || input.class == FalseClass)
125
+ output[TYPE] = :Boolean
126
+ end
127
+
128
+ return output
129
+
130
+ end
131
+
132
+ def normalize_value(value)
133
+
134
+ unless value.nil?
135
+ value = value.to_s.gsub(/\r?\n/, " ").to_s
136
+ if value.length >= 30
137
+ value = value[0, value.rindex(/\s/,30)].rstrip() + '...'
138
+ end
139
+ end
140
+
141
+ return value
142
+
143
+ end
144
+
145
+ end
@@ -0,0 +1,56 @@
1
+ ################################################################################
2
+ # SHADOW STACK
3
+ #
4
+ # Track the executions in a call stack.
5
+ ################################################################################
6
+
7
+ class ShadowStack
8
+
9
+ def initialize()
10
+ @bottom = nil
11
+ @top = nil
12
+ end
13
+
14
+ def peek()
15
+ @top
16
+ end
17
+
18
+ def base()
19
+ @bottom
20
+ end
21
+
22
+ ##
23
+ # Push Execution.
24
+ #
25
+ # @param object - The object being executed.
26
+ # @param args - The arguments being executed.
27
+ #
28
+ # @return Execution - The new execution.
29
+ ##
30
+ def push(execution)
31
+
32
+ # Reference previous execution.
33
+ if @bottom.nil?
34
+ @bottom = execution
35
+ else
36
+ execution.child = @top
37
+ @top.parent = execution
38
+ end
39
+
40
+ # Place new execution at the top of the stack.
41
+ @top = execution
42
+
43
+ end
44
+
45
+ def display
46
+ display_execution_tree(@bottom)
47
+ end
48
+
49
+ def display_execution_tree(execution)
50
+ p execution
51
+ unless execution.parent == nil
52
+ display_execution_tree(execution.parent)
53
+ end
54
+ end
55
+
56
+ end
@@ -1,11 +1,20 @@
1
1
  require 'set'
2
2
  require 'erb'
3
3
  require 'rowdb'
4
+ require 'Control'
5
+ require 'Execution'
6
+ require 'Reflection'
7
+ require 'ShadowStack'
4
8
 
5
9
  ################################################################################
6
10
  # REFLEKT
7
11
  #
8
- # Usage. Prepend to the class like so:
12
+ # An Execution is created each time a method is called.
13
+ # Multiple Refections are created per Execution.
14
+ # These Reflections execute on a ShadowStack on cloned objects.
15
+ # Then flow is returned to the original method and normal execution continues.
16
+ #
17
+ # Usage:
9
18
  #
10
19
  # class ExampleClass
11
20
  # prepend Reflekt
@@ -13,135 +22,162 @@ require 'rowdb'
13
22
 
14
23
  module Reflekt
15
24
 
16
- @@reflekt_clone_count = 5
25
+ # The amount of reflections to create per method call.
26
+ @@reflekt_reflect_amount = 2
27
+
28
+ # Limit the amount of reflections that can be created per instance method.
29
+ # A method called thousands of times doesn't need that many reflections.
30
+ @@reflection_limit = 10
17
31
 
18
32
  def initialize(*args)
19
33
 
20
- @reflekt_forked = false
21
- @reflekt_clones = []
34
+ @reflection_counts = {}
22
35
 
23
- # Override methods.
36
+ # Get instance methods.
37
+ # TODO: Include parent methods like "Array.include?".
24
38
  self.class.instance_methods(false).each do |method|
39
+
40
+ # Don't process skipped methods.
41
+ next if self.class.reflekt_skipped?(method)
42
+
43
+ @reflection_counts[method] = 0
44
+
45
+ # When method called in flow.
25
46
  self.define_singleton_method(method) do |*args|
26
47
 
27
- # When method called in flow.
28
- if @reflekt_forked
29
- unless self.class.deflekted?(method)
48
+ # Don't reflect when limit reached.
49
+ unless @reflection_counts[method] >= @@reflection_limit
50
+
51
+ # Get current execution.
52
+ execution = @@reflekt_stack.peek()
53
+
54
+ # When stack empty or past execution done reflecting.
55
+ if execution.nil? || execution.has_finished_reflecting?
56
+
57
+ # Create execution.
58
+ execution = Execution.new(self, @@reflekt_reflect_amount)
59
+ @@reflekt_stack.push(execution)
60
+
61
+ end
62
+
63
+ # Reflect.
64
+ # The first method call in the Execution creates a Reflection.
65
+ # Subsequent method calls are shadow executions on cloned objects.
66
+ if execution.has_empty_reflections? && !execution.is_reflecting?
67
+ execution.is_reflecting = true
68
+
69
+ class_name = execution.caller_class.to_s
70
+ method_name = method.to_s
71
+
72
+ # Create control.
73
+ control = Control.new(execution, method, true)
74
+ execution.control = control
75
+
76
+ # Execute control.
77
+ control.reflect(*args)
78
+
79
+ # Save control.
80
+ @@reflekt_db.get("#{class_name}.#{method_name}.controls").push(control.result())
81
+
82
+ # Multiple reflections per execution.
83
+ execution.reflections.each_with_index do |value, index|
84
+
85
+ # Create reflection.
86
+ reflection = Reflection.new(execution, method, false)
87
+ execution.reflections[index] = reflection
88
+
89
+ # Execute reflection.
90
+ reflection.reflect(*args)
91
+
92
+ # Save reflection.
93
+ @@reflekt_db.get("#{class_name}.#{method_name}.reflections").push(reflection.result())
30
94
 
31
- # Reflekt on method.
32
- @reflekt_clones.each do |clone|
33
- reflekt_action(clone, method, *args)
34
95
  end
35
96
 
36
97
  # Save results.
37
98
  @@reflekt_db.write()
38
99
 
39
100
  # Render results.
40
- @@reflekt_json = File.read("#{@@reflekt_output_path}/db.json")
41
- template = File.read("#{@@reflekt_path}/web/template.html.erb")
42
- rendered = ERB.new(template).result(binding)
43
- File.open("#{@@reflekt_output_path}/index.html", 'w+') do |f|
44
- f.write rendered
45
- end
46
-
47
- # Add libraries.
48
- alpinejs = File.read("#{@@reflekt_path}/web/alpine.js")
49
- File.open("#{@@reflekt_output_path}/alpine.js", 'w+') do |f|
50
- f.write alpinejs
51
- end
101
+ reflekt_render()
52
102
 
103
+ execution.is_reflecting = false
53
104
  end
105
+
54
106
  end
55
107
 
56
- # Continue method flow.
108
+ @reflection_counts[method] = @reflection_counts[method] + 1
109
+
110
+ # Continue execution / shadow execution.
57
111
  super *args
112
+
58
113
  end
59
114
 
60
115
  end
61
116
 
62
- # Continue contructor flow.
117
+ # Continue initialization.
63
118
  super
64
119
 
65
- # Create forks.
66
- reflekt_fork()
67
-
68
120
  end
69
121
 
70
- def reflekt_fork()
71
-
72
- @@reflekt_clone_count.times do |clone|
73
- @reflekt_clones << self.clone
74
- end
122
+ ##
123
+ # Render results.
124
+ ##
125
+ def reflekt_render()
75
126
 
76
- @reflekt_forked = true
77
-
78
- end
127
+ # Get JSON.
128
+ @@reflekt_json = File.read("#{@@reflekt_output_path}/db.json")
79
129
 
80
- def reflekt_action(clone, method, *args)
81
-
82
- class_name = clone.class.to_s
83
- method_name = method.to_s
84
-
85
- # Create new arguments.
86
- new_args = []
87
- args.each do |arg|
88
- case arg
89
- when Integer
90
- new_args << rand(9999)
91
- else
92
- new_args << arg
93
- end
130
+ # Save HTML.
131
+ template = File.read("#{@@reflekt_path}/web/template.html.erb")
132
+ rendered = ERB.new(template).result(binding)
133
+ File.open("#{@@reflekt_output_path}/index.html", 'w+') do |f|
134
+ f.write rendered
94
135
  end
95
136
 
96
- # Action method with new arguments.
97
- begin
98
- clone.send(method, *new_args)
99
-
100
- # Build reflection.
101
- reflection = {
102
- "time" => Time.now.to_i,
103
- }
104
- # When error.
105
- rescue StandardError => error
106
- reflection["status"] = "error"
107
- reflection["error"] = error
108
- # When success.
109
- else
110
- reflection["status"] = "success"
137
+ # Add JS.
138
+ javascript = File.read("#{@@reflekt_path}/web/script.js")
139
+ File.open("#{@@reflekt_output_path}/script.js", 'w+') do |f|
140
+ f.write javascript
111
141
  end
112
142
 
113
- # Save reflection.
114
- @@reflekt_db.get("#{class_name}.#{method_name}")
115
- .push(reflection)
143
+ # Add CSS.
144
+ stylesheet = File.read("#{@@reflekt_path}/web/style.css")
145
+ File.open("#{@@reflekt_output_path}/style.css", 'w+') do |f|
146
+ f.write stylesheet
147
+ end
116
148
 
117
149
  end
118
150
 
119
151
  private
120
152
 
121
- # Prepend Klass to the instance's singleton class.
122
153
  def self.prepended(base)
123
- base.singleton_class.prepend(Klass)
154
+ # Prepend class methods to the instance's singleton class.
155
+ base.singleton_class.prepend(SingletonClassMethods)
124
156
 
125
- @@reflekt_setup ||= reflekt_setup_klass
157
+ @@reflekt_setup ||= reflekt_setup_class
126
158
  end
127
159
 
128
- # Setup Klass.
129
- def self.reflekt_setup_klass()
160
+ # Setup class.
161
+ def self.reflekt_setup_class()
130
162
 
131
- # Receive configuration from host application.
163
+ # Receive configuration.
132
164
  $ENV ||= {}
133
165
  $ENV[:reflekt] ||= $ENV[:reflekt] = {}
134
166
 
167
+ # Set configuration.
135
168
  @@reflekt_path = File.dirname(File.realpath(__FILE__))
136
169
 
137
- # Create "reflections" directory in configured path.
170
+ # Create reflection tree.
171
+ @@reflekt_stack = ShadowStack.new()
172
+
173
+ # Build reflections directory.
138
174
  if $ENV[:reflekt][:output_path]
139
175
  @@reflekt_output_path = File.join($ENV[:reflekt][:output_path], 'reflections')
140
- # Create "reflections" directory in current execution path.
176
+ # Build reflections directory in current execution path.
141
177
  else
142
178
  @@reflekt_output_path = File.join(Dir.pwd, 'reflections')
143
179
  end
144
-
180
+ # Create reflections directory.
145
181
  unless Dir.exist? @@reflekt_output_path
146
182
  Dir.mkdir(@@reflekt_output_path)
147
183
  end
@@ -153,24 +189,28 @@ module Reflekt
153
189
  return true
154
190
  end
155
191
 
156
- module Klass
192
+ module SingletonClassMethods
157
193
 
158
- @@deflekted_methods = Set.new
194
+ @@reflekt_skipped_methods = Set.new
159
195
 
160
196
  ##
161
197
  # Skip a method.
162
198
  #
163
- # method - A symbol representing the method name.
199
+ # @param method - A symbol representing the method name.
164
200
  ##
165
201
  def reflekt_skip(method)
166
- @@deflekted_methods.add(method)
202
+ @@reflekt_skipped_methods.add(method)
167
203
  end
168
204
 
169
- def deflekted?(method)
170
- return true if @@deflekted_methods.include?(method)
205
+ def reflekt_skipped?(method)
206
+ return true if @@reflekt_skipped_methods.include?(method)
171
207
  false
172
208
  end
173
209
 
210
+ def reflekt_limit(amount)
211
+ @@reflection_limit = amount
212
+ end
213
+
174
214
  end
175
215
 
176
216
  end
File without changes
@@ -0,0 +1,151 @@
1
+ /* Layout. */
2
+ body {
3
+ padding: 0.5rem;
4
+ background: #C9D2E6;
5
+ font-family: 'Roboto', sans-serif;
6
+ }
7
+
8
+ .container {
9
+ margin: 0 auto;
10
+ max-width: 1000px;
11
+ padding: 2rem;
12
+ background: white;
13
+ }
14
+
15
+ /* Header. */
16
+ #logo {
17
+ display: block;
18
+ margin: 0 auto;
19
+ max-width: 100px;
20
+ }
21
+
22
+ /* Structure */
23
+ ul.classes,
24
+ ul.methods,
25
+ ul.controls,
26
+ ul.reflections {
27
+ padding-left: 0;
28
+ }
29
+
30
+ li.class-container,
31
+ li.method-container {
32
+ list-style: none;
33
+ padding: 1rem;
34
+ border: 5px solid #dadcdc;
35
+ }
36
+
37
+ /* State. */
38
+ .status-row {
39
+ width: 100%;
40
+ display: flex;
41
+ align-items: center;
42
+ flex-direction: row;
43
+ color: white;
44
+ background: #A9B6D2;
45
+ }
46
+ .status-row.pass {
47
+ background: #008C32;
48
+ }
49
+ .status-row.fail {
50
+ background: #D04700;
51
+ }
52
+
53
+ /* Buttons. */
54
+ .method .buttons {
55
+ margin-left: auto;
56
+ margin-right: 1rem;
57
+ }
58
+ .buttons button {
59
+ padding: 1rem 2rem;
60
+ }
61
+
62
+ /* Stats. */
63
+ .class .stats {
64
+ margin-left: auto;
65
+ }
66
+ .stat {
67
+ color: #efefef;
68
+ font-size: 3.5rem;
69
+ font-family: 'Merriweather', serif;
70
+ }
71
+
72
+ /* Class. */
73
+ .class {
74
+ padding: 2rem;
75
+ margin-bottom: 1rem;
76
+ }
77
+ .class:hover {
78
+ cursor: pointer;
79
+ }
80
+ .class h2 {
81
+ margin: 0;
82
+ }
83
+
84
+ /* Method. */
85
+ .method {
86
+ padding: 2rem;
87
+ margin-left: 1rem;
88
+ margin-bottom: 1rem;
89
+ }
90
+ .method:hover {
91
+ cursor: pointer;
92
+ }
93
+ .method .stat {
94
+ font-size: 2.5rem;
95
+ }
96
+
97
+ /* Reflection. */
98
+ .control,
99
+ .reflection {
100
+ list-style: none;
101
+ margin-left: 2rem;
102
+ display: flex;
103
+ flex-direction: row;
104
+ align-items: center;
105
+ background: #EFEFEF;
106
+ padding: 0.5rem 1.5rem;
107
+ margin-bottom: 0.3rem;
108
+ }
109
+ .control .time,
110
+ .reflection .time {
111
+ color: #777777;
112
+ margin-right: 2rem;
113
+ }
114
+
115
+ .info {
116
+ display: flex;
117
+ flex-direction: row;
118
+ align-items: center;
119
+ padding: 0.5rem 1rem;
120
+ border: 1px solid #aaa;
121
+ border-radius: 5px;
122
+ }
123
+ .info:not(:last-child) {
124
+ margin-right: 0.5rem;
125
+ }
126
+ .info h4 {
127
+ margin: 0;
128
+ color: #777777;
129
+ font-size: 1.2rem;
130
+ font-weight: normal;
131
+ }
132
+
133
+ .info-items {
134
+ display: flex;
135
+ flex-direction: row;
136
+ }
137
+ .info-item {
138
+ padding-left: 1rem;
139
+ padding-right: 1rem;
140
+ border-right: 1px solid #ccc;
141
+ }
142
+ .info-item:last-of-type {
143
+ padding-right: 0;
144
+ border-right: 0;
145
+ }
146
+ .info-item strong {
147
+ padding-bottom: 0.1rem;
148
+ }
149
+ .info-item pre {
150
+ margin: 0;
151
+ }
@@ -1,29 +1,49 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
+
3
4
  <head>
4
5
  <meta charset="utf-8">
5
6
  <title>Reflekt</title>
6
- <meta name="description" content="">
7
- <meta name="author" content="">
7
+ <meta name="description" content="Reflective testing results.">
8
+ <meta name="author" content="Maedi Prichard">
8
9
  <meta name="viewport" content="width=device-width, initial-scale=1">
9
- <link rel="stylesheet" href="">
10
+ <link rel="stylesheet" href="style.css">
10
11
  <link rel="shortcut icon" href="">
12
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
13
+ <link href="https://fonts.googleapis.com/css2?family=Merriweather&display=swap" rel="stylesheet">
11
14
  </head>
15
+
12
16
  <body>
13
17
 
14
18
  <script>
15
19
 
16
- function getReflections() {
20
+ // Reflection keys.
21
+ const TIME = "t";
22
+ const INPUT = "i";
23
+ const OUTPUT = "o";
24
+ const TYPE = "T";
25
+ const COUNT = "C";
26
+ const VALUE = "V";
27
+ const STATUS = "s";
28
+ const MESSAGE = "m";
29
+ // Reflection values.
30
+ const PASS = "p";
31
+ const FAIL = "f";
17
32
 
18
- var reflections = JSON.parse(<%= @@reflekt_json %>);
33
+ function getData() {
34
+
35
+ var data = JSON.parse(<%= @@reflekt_json %>);
19
36
  var results = {};
20
37
 
21
- if ('reflekt' in reflections) {
22
- delete(reflections.reflekt);
38
+ console.log("DATA:");
39
+ console.log(data);
40
+
41
+ if ('reflekt' in data) {
42
+ delete(data.reflekt);
23
43
  }
24
44
 
25
45
  // Classes.
26
- for ([class_id, class_value] of Object.entries(reflections)) {
46
+ for ([class_id, class_value] of Object.entries(data)) {
27
47
 
28
48
  // Class pass rate.
29
49
  results[class_id] = {
@@ -38,53 +58,200 @@
38
58
  // Methods.
39
59
  for ([method_id, method] of Object.entries(class_value)) {
40
60
 
41
- // Method pass rate.
42
- var pass_count = method.reduce(function(obj, v) {
43
- obj[v.status] = (obj[v.status] || 0) + 1;
61
+ // Reflection pass rate.
62
+ var pass_count = method.reflections.reduce(function(obj, v) {
63
+ obj[v[STATUS]] = (obj[v[STATUS]] || 0) + 1;
44
64
  return obj;
45
65
  }, {});
46
66
 
67
+ var pass_rate = (pass_count[PASS] / method.reflections.length) * 100;
47
68
  results[class_id]['methods'][method_id] = {
48
69
  'stats': {
49
- 'pass_rate': (pass_count['success'] / method.length) * 100,
50
- 'test_count': method.length,
51
- 'pass_count': pass_count['success']
70
+ 'pass_rate': pass_rate,
71
+ 'test_count': method.reflections.length,
72
+ 'pass_count': pass_count[PASS]
52
73
  }
53
74
  };
75
+ if (pass_rate == 100) {
76
+ results[class_id]['methods'][method_id]['status'] = 'pass';
77
+ }
78
+ else if (pass_rate < 100) {
79
+ results[class_id]['methods'][method_id]['status'] = 'fail';
80
+ }
54
81
 
55
82
  // Class pass rate.
56
- results[class_id]['stats']['test_count'] += method.length;
57
- results[class_id]['stats']['pass_count'] += pass_count['success'];
83
+ results[class_id]['stats']['test_count'] += method.reflections.length;
84
+ results[class_id]['stats']['pass_count'] += pass_count[PASS];
58
85
 
59
86
  }
60
87
 
61
88
  // Class pass rate.
62
89
  var class_stats = results[class_id]['stats'];
63
- class_stats['pass_rate'] = (class_stats['pass_count'] / class_stats['test_count']) * 100;
90
+ var pass_rate = (class_stats['pass_count'] / class_stats['test_count']) * 100;
91
+ class_stats['pass_rate'] = pass_rate;
92
+ if (pass_rate == 100) {
93
+ results[class_id]['status'] = 'pass';
94
+ }
95
+ else if (pass_rate < 100) {
96
+ results[class_id]['status'] = 'fail';
97
+ }
64
98
  }
65
99
 
66
- return { refs: results };
100
+ return {
101
+ data: data,
102
+ results: results
103
+ };
67
104
  }
68
105
 
69
106
  </script>
70
107
 
71
- <div x-data="getReflections()">
108
+ <div class="container" x-data="getData()">
72
109
 
73
- <ul>
110
+ <div id="header">
111
+ <svg id="logo" enable-background="new 0 0 500 500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
112
+ <path d="m307.5 80.5h-115l-57.5 205h230z" fill="#0047d0"/>
113
+ <path d="m178 76.5-53.1-44-117.9 139 116 112z" fill="#d04800"/>
114
+ <path d="m190.4 467.5h115l57.5-168h-229z" fill="#0047d0" opacity=".7"/>
115
+ <path d="m177 467.5-81-85-92-197 115 113z" fill="#d04800" opacity=".7"/>
116
+ <g fill="#008c33"><path d="m322 76.5 53.1-44 118 139-116 112z"/>
117
+ <path d="m320 467.5 84-85 92-197-117 113z" opacity=".7"/>
118
+ </g>
119
+ </svg>
120
+ </div>
74
121
 
75
- <template x-for="[class_id, klass] in Object.entries(getReflections()['refs'])" :key="class_id">
76
- <li>
77
- <div x-text="class_id"></div>
78
- <div x-text="klass.stats.pass_rate"></div>
122
+ <ul class="classes">
123
+ <template x-for="[class_id, klass] in Object.entries(results)" :key="class_id">
79
124
 
80
- <div>
125
+ <li class="class-container">
126
+
127
+ <div class="status-row class" x-bind:class="`${klass.status}`" @click="klass['show'] = !klass['show']" :aria-expanded="klass['show'] ? 'true' : 'false'" :class="{ 'active': klass['show'] }">
128
+ <h2 x-text="`${class_id}()`"></h2>
129
+ <div class="stats">
130
+ <div class="stat" x-text="`${klass.stats.pass_rate.toFixed(2)}%`"></div>
131
+ </div>
132
+ </div>
133
+
134
+ <ul class="methods" x-show="klass['show']">
81
135
  <template x-for="[method_id, method] in Object.entries(klass['methods'])" :key="method_id">
82
- <div class="method">
83
- <div x-text="method_id"></div>
84
- <div x-text="method.stats.pass_rate"></div>
85
- </div>
136
+ <li class="method-container">
137
+
138
+ <div class="status-row method" x-bind:class="`${method.status}`" :class="{ 'active': method['show_reflections'] }" :class="{ 'active': method['show_controls'] }">
139
+ <h3 x-text="`${method_id}()`"></h3>
140
+ <div class="buttons">
141
+ <button @click="method['show_controls'] = !method['show_controls']">Controls</button>
142
+ <button @click="method['show_reflections'] = !method['show_reflections']">Reflections</button>
143
+ </div>
144
+ <div class="stats">
145
+ <div class="stat" x-text="`${method.stats.pass_rate.toFixed(2)}%`"></div>
146
+ </div>
147
+ </div>
148
+
149
+ <ul class="reflections" x-show="method['show_reflections']">
150
+ <template x-for="[reflection_id, reflection] in Object.entries(data[class_id][method_id].reflections)">
151
+
152
+ <li class="reflection">
153
+
154
+ <div class="time" x-text="`${new Date(reflection.t * 1000).getFullYear()}/${new Date(reflection.t * 1000).getMonth() + 1}/${new Date(reflection.t * 1000).getDate()} ${new Date(reflection.t * 1000).getHours()}:${new Date(reflection.t * 1000).getMinutes()}:${new Date(reflection.t * 1000).getSeconds()}`"></div>
155
+
156
+ <template x-for="[input_id, input] in Object.entries(reflection.i)">
157
+
158
+ <div class="info">
159
+ <h4>Input</h4>
160
+ <div class="info-items">
161
+ <div class="info-item">
162
+ <strong>Type:</strong><div class="input" x-text="input.T"></div>
163
+ </div>
164
+ <template x-if="input.V != undefined">
165
+ <div class="info-item">
166
+ <strong>Value:</strong><pre><div class="output" x-text="input.V"></div></pre>
167
+ </div>
168
+ </template>
169
+ <template x-if="input.C != undefined">
170
+ <div class="info-item">
171
+ <strong>Count:</strong><div class="input" x-text="input.C"></div>
172
+ </div>
173
+ </template>
174
+ </div>
175
+ </div>
176
+
177
+ </template>
178
+
179
+ <div class="info">
180
+ <h4>Output</h4>
181
+ <div class="info-items">
182
+ <div class="info-item">
183
+ <strong>Type:</strong><div class="output" x-text="reflection.o.T"></div>
184
+ </div>
185
+ <template x-if="reflection.o.C != undefined">
186
+ <div class="info-item">
187
+ <strong>Count:</strong><div class="output" x-text="reflection.o.C"></div>
188
+ </div>
189
+ </template>
190
+ <div class="info-item">
191
+ <strong>Value:</strong><pre><div class="output" x-text="reflection.o.V"></div></pre>
192
+ </div>
193
+ </div>
194
+ </div>
195
+ </li>
196
+
197
+ </template>
198
+ </ul>
199
+
200
+ <ul class="controls" x-show="method['show_controls']">
201
+ <template x-for="[control_id, control] in Object.entries(data[class_id][method_id].controls)">
202
+
203
+ <li class="control">
204
+
205
+ <div class="time" x-text="`${new Date(control.t * 1000).getFullYear()}/${new Date(control.t * 1000).getMonth() + 1}/${new Date(control.t * 1000).getDate()} ${new Date(control.t * 1000).getHours()}:${new Date(control.t * 1000).getMinutes()}:${new Date(control.t * 1000).getSeconds()}`"></div>
206
+
207
+ <template x-for="[input_id, input] in Object.entries(control.i)">
208
+
209
+ <div class="info">
210
+ <h4>Input</h4>
211
+ <div class="info-items">
212
+ <div class="info-item">
213
+ <strong>Type:</strong><div class="input" x-text="input.T"></div>
214
+ </div>
215
+ <template x-if="input.V != undefined">
216
+ <div class="info-item">
217
+ <strong>Value:</strong><pre><div class="output" x-text="input.V"></div></pre>
218
+ </div>
219
+ </template>
220
+ <template x-if="input.C != undefined">
221
+ <div class="info-item">
222
+ <strong>Count:</strong><div class="input" x-text="input.C"></div>
223
+ </div>
224
+ </template>
225
+ </div>
226
+ </div>
227
+
228
+ </template>
229
+
230
+ <div class="info">
231
+ <h4>Output</h4>
232
+ <div class="info-items">
233
+ <div class="info-item">
234
+ <strong>Type:</strong><div class="output" x-text="control.o.T"></div>
235
+ </div>
236
+ <template x-if="control.o.C != undefined">
237
+ <div class="info-item">
238
+ <strong>Count:</strong><div class="output" x-text="control.o.C"></div>
239
+ </div>
240
+ </template>
241
+ <div class="info-item">
242
+ <strong>Value:</strong><pre><div class="output" x-text="control.o.V"></div></pre>
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </li>
247
+
248
+ </template>
249
+ </ul>
250
+
251
+ </li>
86
252
  </template>
87
- </div>
253
+ </ul>
254
+
88
255
  </li>
89
256
  </template>
90
257
 
@@ -92,7 +259,8 @@
92
259
 
93
260
  </div>
94
261
 
95
- <script src="alpine.js"></script>
262
+ <script src="script.js"></script>
96
263
 
97
264
  </body>
265
+
98
266
  </html>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reflekt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maedi Prichard
@@ -30,10 +30,15 @@ executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - lib/Control.rb
34
+ - lib/Execution.rb
35
+ - lib/Reflection.rb
36
+ - lib/ShadowStack.rb
33
37
  - lib/reflekt.rb
34
- - lib/web/alpine.js
38
+ - lib/web/script.js
39
+ - lib/web/style.css
35
40
  - lib/web/template.html.erb
36
- homepage: https://github.com/maedi/reflekt
41
+ homepage: https://github.com/refIekt/reflekt
37
42
  licenses:
38
43
  - MPL-2.0
39
44
  metadata: {}