reflekt 1.0.9 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|