reflekt 1.0.9 → 1.0.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.
- checksums.yaml +4 -4
- data/lib/accessor.rb +33 -27
- data/lib/action.rb +108 -71
- data/lib/action_stack.rb +29 -31
- data/lib/clone.rb +10 -12
- data/lib/config.rb +44 -41
- data/lib/control.rb +44 -48
- data/lib/experiment.rb +63 -74
- data/lib/meta.rb +50 -48
- data/lib/meta/array_meta.rb +26 -30
- data/lib/meta/boolean_meta.rb +17 -19
- data/lib/meta/float_meta.rb +17 -19
- data/lib/meta/integer_meta.rb +17 -19
- data/lib/meta/null_meta.rb +19 -19
- data/lib/meta/string_meta.rb +17 -19
- data/lib/meta_builder.rb +74 -74
- data/lib/reflection.rb +91 -91
- data/lib/reflekt.rb +160 -126
- data/lib/renderer.rb +27 -30
- data/lib/rule.rb +33 -33
- data/lib/rule_set.rb +64 -65
- data/lib/rule_set_aggregator.rb +191 -193
- data/lib/rules/array_rule.rb +77 -73
- data/lib/rules/boolean_rule.rb +29 -35
- data/lib/rules/float_rule.rb +42 -46
- data/lib/rules/integer_rule.rb +42 -46
- data/lib/rules/null_rule.rb +30 -30
- data/lib/rules/string_rule.rb +54 -62
- data/lib/web/index.html +3 -4
- metadata +17 -3
data/lib/reflekt.rb
CHANGED
@@ -3,14 +3,6 @@
|
|
3
3
|
#
|
4
4
|
# @author Maedi Prichard
|
5
5
|
#
|
6
|
-
# @flow
|
7
|
-
# 1. Reflekt is prepended to a class and setup.
|
8
|
-
# 2. When a class insantiates so does Reflekt.
|
9
|
-
# 3. An Action is created on method call.
|
10
|
-
# 4. Many Refections are created per Action.
|
11
|
-
# 5. Each Reflection executes on cloned data.
|
12
|
-
# 6. Flow is returned to the original method.
|
13
|
-
#
|
14
6
|
# @usage
|
15
7
|
# class ExampleClass
|
16
8
|
# prepend Reflekt
|
@@ -19,6 +11,8 @@
|
|
19
11
|
require 'set'
|
20
12
|
require 'erb'
|
21
13
|
require 'rowdb'
|
14
|
+
require 'lit_cli'
|
15
|
+
|
22
16
|
require_relative 'accessor'
|
23
17
|
require_relative 'action'
|
24
18
|
require_relative 'action_stack'
|
@@ -27,193 +21,238 @@ require_relative 'control'
|
|
27
21
|
require_relative 'experiment'
|
28
22
|
require_relative 'renderer'
|
29
23
|
require_relative 'rule_set_aggregator'
|
30
|
-
|
24
|
+
|
25
|
+
# Require all rules in rules directory.
|
31
26
|
Dir[File.join(__dir__, 'rules', '*.rb')].each { |file| require_relative file }
|
32
27
|
|
33
28
|
module Reflekt
|
29
|
+
include LitCLI
|
34
30
|
|
31
|
+
##
|
32
|
+
# Setup Reflekt per class.
|
33
|
+
# Override methods on class instantiation.
|
34
|
+
#
|
35
|
+
# @scope self [Object] Refers to the class that Reflekt is prepended to.
|
36
|
+
##
|
35
37
|
def initialize(*args)
|
38
|
+
if @@reflekt.config.enabled
|
39
|
+
@reflekt_initialized = false
|
36
40
|
|
37
|
-
|
38
|
-
@reflekt_counts = {}
|
41
|
+
🔥"Initialize", :info, :setup, self.class
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# TODO: Include core methods like "Array.include?".
|
46
|
-
instance_methods.each do |method|
|
43
|
+
# Override methods.
|
44
|
+
Reflekt.get_methods(self).each do |method|
|
45
|
+
Reflekt.setup_count(self, method)
|
46
|
+
Reflekt.override_method(self, method)
|
47
|
+
end
|
47
48
|
|
48
|
-
@
|
49
|
+
@reflekt_initialized = true
|
50
|
+
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
# Continue initialization.
|
53
|
+
super
|
54
|
+
end
|
52
55
|
|
53
|
-
|
54
|
-
|
56
|
+
##
|
57
|
+
# Get child and parent instance methods.
|
58
|
+
#
|
59
|
+
# TODO: Include methods from all ancestors.
|
60
|
+
# TODO: Include core methods like "Array.include?".
|
61
|
+
##
|
62
|
+
def self.get_methods(klass)
|
63
|
+
child_instance_methods = klass.class.instance_methods(false)
|
64
|
+
parent_instance_methods = klass.class.superclass.instance_methods(false)
|
55
65
|
|
56
|
-
|
57
|
-
|
66
|
+
return child_instance_methods + parent_instance_methods
|
67
|
+
end
|
58
68
|
|
59
|
-
|
60
|
-
|
69
|
+
##
|
70
|
+
# Override a method.
|
71
|
+
#
|
72
|
+
# @param klass [Dynamic] The class to override.
|
73
|
+
# @param method [Method] The method to override.
|
74
|
+
##
|
75
|
+
def self.override_method(klass, method)
|
76
|
+
klass.define_singleton_method(method) do |*args|
|
61
77
|
|
62
|
-
|
63
|
-
|
78
|
+
# When method called in flow.
|
79
|
+
if @reflekt_initialized
|
80
|
+
|
81
|
+
##
|
82
|
+
# Reflect-Execute loop.
|
83
|
+
#
|
84
|
+
# Reflect each method before finally executing it.
|
85
|
+
#
|
86
|
+
# @loop
|
87
|
+
# 1. The first method call creates an action
|
88
|
+
# 2. The action creates reflections and calls the method again
|
89
|
+
# 3. Subsequent method calls execute these reflections
|
90
|
+
# 4. Each reflection executes on cloned data
|
91
|
+
# 5. The original method call completes execution
|
92
|
+
#
|
93
|
+
# @see https://reflekt.dev/docs/reflect-execute-loop
|
94
|
+
##
|
95
|
+
|
96
|
+
unless @@reflekt.error
|
64
97
|
|
65
|
-
|
66
|
-
action = Action.new(self, method, @@reflekt.config.reflect_amount, @@reflekt.stack)
|
98
|
+
action = @@reflekt.stack.peek()
|
67
99
|
|
68
|
-
|
100
|
+
# New action when old action done reflecting.
|
101
|
+
if action.nil? || action.has_finished_loop?
|
102
|
+
🔥"^ Create action for #{method}()", :info, :action, klass.class
|
103
|
+
action = Action.new(klass, method, @@reflekt.config, @@reflekt.db, @@reflekt.stack, @@reflekt.aggregator)
|
104
|
+
@@reflekt.stack.push(action)
|
105
|
+
end
|
69
106
|
|
70
|
-
|
107
|
+
##
|
108
|
+
# REFLECT
|
109
|
+
##
|
71
110
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# The first method call in the action creates a reflection.
|
76
|
-
# Then method calls are shadow actions which return to the reflection.
|
77
|
-
##
|
78
|
-
if action.has_empty_experiments? && !action.is_reflecting?
|
111
|
+
unless action.is_reflecting? && klass.class.reflekt_skipped?(method) || Reflekt.count(klass, method) >= @@reflekt.config.reflect_limit
|
112
|
+
unless action.is_actioned?
|
113
|
+
action.is_actioned = true
|
79
114
|
action.is_reflecting = true
|
80
115
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
# Execute control.
|
86
|
-
control.reflect(*args)
|
87
|
-
|
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
|
93
|
-
|
94
|
-
# Save control as a reflection.
|
95
|
-
@@reflekt.db.get("reflections").push(control.serialize())
|
96
|
-
|
97
|
-
# Multiple experiments per action.
|
98
|
-
action.experiments.each_with_index do |value, index|
|
99
|
-
|
100
|
-
# Create experiment.
|
101
|
-
experiment = Experiment.new(action, index + 1, @@reflekt.aggregator)
|
102
|
-
action.experiments[index] = experiment
|
103
|
-
|
104
|
-
# Execute experiment.
|
105
|
-
experiment.reflect(*args)
|
106
|
-
@reflekt_counts[method] = @reflekt_counts[method] + 1
|
107
|
-
|
108
|
-
# Save experiment.
|
109
|
-
@@reflekt.db.get("reflections").push(experiment.serialize())
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
# Save control.
|
114
|
-
@@reflekt.db.get("controls").push(control.serialize())
|
115
|
-
|
116
|
-
# Save results.
|
117
|
-
@@reflekt.db.write()
|
118
|
-
|
119
|
-
# Render results.
|
120
|
-
@@reflekt.renderer.render()
|
121
|
-
|
116
|
+
action.reflect(*args)
|
117
|
+
if action.control.status == :error
|
118
|
+
@@reflekt.error = action.control.message
|
122
119
|
end
|
123
120
|
|
121
|
+
# Render results.
|
122
|
+
@@reflekt.renderer.render()
|
123
|
+
|
124
124
|
action.is_reflecting = false
|
125
125
|
end
|
126
|
-
|
126
|
+
else
|
127
|
+
🔥"> Skip reflection of #{method}()", :skip, :reflect, klass.class
|
127
128
|
end
|
128
129
|
|
129
|
-
|
130
|
-
|
130
|
+
##
|
131
|
+
# EXECUTE
|
132
|
+
##
|
131
133
|
|
132
|
-
|
134
|
+
unless action.is_reflecting? && klass.class.reflekt_skipped?(method)
|
135
|
+
🔥"> Execute #{method}()", :info, :execute, klass.class
|
133
136
|
super *args
|
134
|
-
|
135
137
|
end
|
136
138
|
|
137
|
-
#
|
139
|
+
# Finish execution if control encounters unrecoverable error.
|
138
140
|
else
|
139
|
-
|
140
|
-
# Continue action.
|
141
|
+
🔥"Reflection error, finishing original execution...", :error, :reflect, klass.class
|
141
142
|
super *args
|
142
|
-
|
143
143
|
end
|
144
144
|
|
145
|
+
# When method called in constructor.
|
146
|
+
else
|
147
|
+
p "Reflection unsupported in constructor for #{method}()", :info, :setup, klass.class
|
148
|
+
super *args
|
145
149
|
end
|
146
|
-
|
147
150
|
end
|
151
|
+
end
|
148
152
|
|
149
|
-
|
150
|
-
|
153
|
+
def self.setup_count(klass, method)
|
154
|
+
caller_id = klass.object_id
|
155
|
+
@@reflekt.counts[caller_id] = {} unless @@reflekt.counts.has_key? caller_id
|
156
|
+
@@reflekt.counts[caller_id][method] = 0 unless @@reflekt.counts[caller_id].has_key? method
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.count(klass, method)
|
160
|
+
count = @@reflekt.counts.dig(klass.object_id, method) || 0
|
161
|
+
count
|
162
|
+
end
|
151
163
|
|
164
|
+
def self.increase_count(klass, method)
|
165
|
+
caller_id = klass.object_id
|
166
|
+
@@reflekt.counts[caller_id][method] = @@reflekt.counts[caller_id][method] + 1
|
152
167
|
end
|
153
168
|
|
154
169
|
##
|
155
|
-
#
|
170
|
+
# Configure Config singleton.
|
156
171
|
##
|
157
172
|
def self.configure
|
173
|
+
reflekt_setup_class()
|
174
|
+
|
158
175
|
yield(@@reflekt.config)
|
159
176
|
end
|
160
177
|
|
161
178
|
private
|
162
179
|
|
163
180
|
def self.prepended(base)
|
164
|
-
|
165
181
|
# Prepend class methods to the instance's singleton class.
|
166
182
|
base.singleton_class.prepend(SingletonClassMethods)
|
167
183
|
|
168
|
-
|
169
|
-
@@reflekt = Accessor.new()
|
170
|
-
@@reflekt.setup ||= reflekt_setup_class
|
171
|
-
|
184
|
+
reflekt_setup_class()
|
172
185
|
end
|
173
186
|
|
187
|
+
##
|
174
188
|
# Setup class.
|
189
|
+
#
|
190
|
+
# @paths
|
191
|
+
# - package_path [String] Absolute path to the library itself.
|
192
|
+
# - project_path [String] Absolute path to the project root.
|
193
|
+
# - output_path [String] Absolute path to the reflections directory.
|
194
|
+
##
|
175
195
|
def self.reflekt_setup_class()
|
176
196
|
|
177
|
-
#
|
178
|
-
@@reflekt
|
179
|
-
|
180
|
-
# Set configuration.
|
181
|
-
@@reflekt.path = File.dirname(File.realpath(__FILE__))
|
197
|
+
# Only setup once.
|
198
|
+
return if defined? @@reflekt
|
182
199
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
else
|
187
|
-
@@reflekt.output_path = File.join(Dir.pwd, @@reflekt.config.output_directory)
|
188
|
-
end
|
200
|
+
@@reflekt = Accessor.new()
|
201
|
+
@@reflekt.config = Config.new()
|
202
|
+
@@reflekt.stack = ActionStack.new()
|
189
203
|
|
190
|
-
#
|
204
|
+
# Setup paths.
|
205
|
+
@@reflekt.package_path = File.dirname(File.realpath(__FILE__))
|
206
|
+
@@reflekt.project_path = @@reflekt.config.project_path
|
207
|
+
@@reflekt.output_path = File.join(@@reflekt.project_path, @@reflekt.config.output_directory)
|
191
208
|
unless Dir.exist? @@reflekt.output_path
|
192
209
|
Dir.mkdir(@@reflekt.output_path)
|
193
210
|
end
|
194
211
|
|
195
|
-
#
|
212
|
+
# Setup database.
|
196
213
|
@@reflekt.db = Rowdb.new(@@reflekt.output_path + '/db.js')
|
197
214
|
@@reflekt.db.defaults({ :reflekt => { :api_version => 1 }})
|
198
|
-
#
|
215
|
+
# TODO: Fix Rowdb.get(path) not returning values at path after Rowdb.push()
|
199
216
|
db = @@reflekt.db.value()
|
200
217
|
|
201
|
-
#
|
202
|
-
@@reflekt.stack = ActionStack.new()
|
203
|
-
|
204
|
-
# Create aggregated rule sets.
|
218
|
+
# Train aggregated rule sets.
|
205
219
|
@@reflekt.aggregator = RuleSetAggregator.new(@@reflekt.config.meta_map)
|
206
220
|
@@reflekt.aggregator.train(db[:controls])
|
207
221
|
|
208
|
-
#
|
209
|
-
@@reflekt.renderer = Renderer.new(@@reflekt.
|
222
|
+
# Setup renderer.
|
223
|
+
@@reflekt.renderer = Renderer.new(@@reflekt.package_path, @@reflekt.output_path)
|
224
|
+
|
225
|
+
LitCLI.configure do |config|
|
226
|
+
config.statuses = {
|
227
|
+
:info => { icon: "ℹ", color: :blue, styles: [:upcase] },
|
228
|
+
:pass => { icon: "✔", color: :green, styles: [:upcase] },
|
229
|
+
:save => { icon: "✔", color: :green, styles: [:upcase] },
|
230
|
+
:skip => { icon: "⨯", color: :yellow, styles: [:upcase] },
|
231
|
+
:warn => { icon: "⚠", color: :yellow, styles: [:upcase] },
|
232
|
+
:fail => { icon: "⨯", color: :red, styles: [:upcase] },
|
233
|
+
:error => { icon: "!", color: :red, styles: [:upcase] },
|
234
|
+
:debug => { icon: "?", color: :purple, styles: [:upcase] },
|
235
|
+
}
|
236
|
+
config.types = {
|
237
|
+
:setup => { styles: [:dim, :bold, :capitalize] },
|
238
|
+
:event => { color: :yellow, styles: [:bold, :capitalize] },
|
239
|
+
:reflect => { color: :yellow, styles: [:bold, :capitalize] },
|
240
|
+
:result => { color: :yellow, styles: [:bold, :capitalize] },
|
241
|
+
:action => { color: :red, styles: [:bold, :capitalize] },
|
242
|
+
:control => { color: :blue, styles: [:bold, :capitalize] },
|
243
|
+
:experiment => { color: :green, styles: [:bold, :capitalize] },
|
244
|
+
:execute => { color: :purple, styles: [:bold, :capitalize] },
|
245
|
+
:meta => { color: :blue, styles: [:bold, :capitalize] },
|
246
|
+
}
|
247
|
+
end
|
210
248
|
|
211
249
|
return true
|
212
|
-
|
213
250
|
end
|
214
251
|
|
252
|
+
##
|
253
|
+
# Publicly accessible class methods in the class that Reflekt is prepended to.
|
254
|
+
##
|
215
255
|
module SingletonClassMethods
|
216
|
-
|
217
256
|
@@reflekt_skipped_methods = Set.new()
|
218
257
|
|
219
258
|
##
|
@@ -233,11 +272,6 @@ module Reflekt
|
|
233
272
|
return true if @@reflekt_skipped_methods.include?(method)
|
234
273
|
false
|
235
274
|
end
|
236
|
-
|
237
|
-
#def reflekt_limit(amount)
|
238
|
-
# @@reflekt.reflect_limit = amount
|
239
|
-
#end
|
240
|
-
|
241
275
|
end
|
242
276
|
|
243
277
|
end
|
data/lib/renderer.rb
CHANGED
@@ -1,41 +1,38 @@
|
|
1
1
|
module Reflekt
|
2
|
-
class Renderer
|
2
|
+
class Renderer
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
##
|
12
|
-
# Place files in output path.
|
13
|
-
##
|
14
|
-
def render()
|
4
|
+
def initialize(package_path, output_path)
|
5
|
+
@package_path = package_path
|
6
|
+
@output_path = output_path
|
7
|
+
end
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
##
|
10
|
+
# Place files in output path.
|
11
|
+
##
|
12
|
+
def render()
|
13
|
+
|
14
|
+
filenames = [
|
15
|
+
"bundle.js",
|
16
|
+
"index.html",
|
17
|
+
"package-lock.json",
|
18
|
+
"package.json",
|
19
|
+
"README.md",
|
20
|
+
"server.js"
|
21
|
+
]
|
22
|
+
|
23
|
+
filenames.each do |filename|
|
24
|
+
file = File.read(File.join(@package_path, "web", filename))
|
25
|
+
File.open(File.join(@output_path, filename), 'w+') do |f|
|
26
|
+
f.write file
|
27
|
+
end
|
28
|
+
end
|
24
29
|
|
25
|
-
|
26
|
-
|
27
|
-
File.open(File.join(@output_path, filename), 'w+') do |f|
|
30
|
+
file = File.read(File.join(@package_path, "web", "gitignore.txt"))
|
31
|
+
File.open(File.join(@output_path, ".gitignore"), 'w+') do |f|
|
28
32
|
f.write file
|
29
33
|
end
|
30
|
-
end
|
31
34
|
|
32
|
-
file = File.read(File.join(@path, "web", "gitignore.txt"))
|
33
|
-
File.open(File.join(@output_path, ".gitignore"), 'w+') do |f|
|
34
|
-
f.write file
|
35
35
|
end
|
36
36
|
|
37
37
|
end
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
38
|
end
|