polytrix 0.1.2 → 0.1.3
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/.rubocop-todo.yml +14 -5
- data/Gemfile +2 -1
- data/README.md +139 -177
- data/Rakefile +5 -12
- data/bin/polytrix +0 -1
- data/features/bootstrapping.feature +0 -3
- data/features/cloning.feature +0 -3
- data/features/show.feature +38 -0
- data/features/states.feature +12 -13
- data/features/step_definitions/sdk_steps.rb +0 -4
- data/lib/polytrix/challenge.rb +135 -53
- data/lib/polytrix/challenge_result.rb +0 -2
- data/lib/polytrix/challenge_runner.rb +28 -18
- data/lib/polytrix/cli.rb +53 -69
- data/lib/polytrix/color.rb +2 -2
- data/lib/polytrix/command/action.rb +4 -3
- data/lib/polytrix/command/list.rb +39 -28
- data/lib/polytrix/command/report.rb +9 -86
- data/lib/polytrix/command/reports/code2doc.rb +72 -0
- data/lib/polytrix/command/reports/dashboard.rb +125 -0
- data/lib/polytrix/command/show.rb +148 -0
- data/lib/polytrix/command.rb +37 -104
- data/lib/polytrix/configuration.rb +14 -18
- data/lib/polytrix/{core/hashie.rb → dash.rb} +4 -3
- data/lib/polytrix/documentation/code_segmenter.rb +8 -8
- data/lib/polytrix/documentation/comment_styles.rb +1 -1
- data/lib/polytrix/documentation/helpers/code_helper.rb +9 -0
- data/lib/polytrix/documentation_generator.rb +11 -14
- data/lib/polytrix/error.rb +104 -97
- data/lib/polytrix/executor.rb +33 -0
- data/lib/polytrix/{runners → executors}/buff_shellout_executor.rb +1 -1
- data/lib/polytrix/executors/linux_challenge_executor.rb +29 -0
- data/lib/polytrix/executors/mixlib_shellout_executor.rb +55 -0
- data/lib/polytrix/{runners/windows_challenge_runner.rb → executors/windows_challenge_executor.rb} +4 -11
- data/lib/polytrix/{core/implementor.rb → implementor.rb} +10 -6
- data/lib/polytrix/manifest.rb +2 -31
- data/lib/polytrix/{reports → reporters}/hash_reporter.rb +6 -2
- data/lib/polytrix/{reports → reporters}/json_reporter.rb +2 -2
- data/lib/polytrix/{reports → reporters}/markdown_reporter.rb +7 -2
- data/lib/polytrix/{reports → reporters}/yaml_reporter.rb +2 -2
- data/lib/polytrix/reporters.rb +27 -0
- data/lib/polytrix/result.rb +6 -5
- data/lib/polytrix/spies/file_system_spy.rb +15 -0
- data/lib/polytrix/spies.rb +61 -0
- data/lib/polytrix/state_file.rb +1 -20
- data/lib/polytrix/util.rb +157 -62
- data/lib/polytrix/validation.rb +41 -2
- data/lib/polytrix/validator.rb +9 -4
- data/lib/polytrix/version.rb +1 -1
- data/lib/polytrix.rb +110 -105
- data/polytrix.gemspec +7 -2
- data/polytrix.yml +16 -13
- data/resources/assets/pygments/autumn.css +58 -0
- data/resources/assets/pygments/borland.css +46 -0
- data/resources/assets/pygments/bw.css +34 -0
- data/resources/assets/pygments/colorful.css +61 -0
- data/resources/assets/pygments/default.css +62 -0
- data/resources/assets/pygments/emacs.css +61 -0
- data/resources/assets/pygments/friendly.css +61 -0
- data/resources/assets/pygments/fruity.css +69 -0
- data/resources/assets/pygments/github.css +61 -0
- data/resources/assets/pygments/manni.css +61 -0
- data/resources/assets/pygments/monokai.css +64 -0
- data/resources/assets/pygments/murphy.css +61 -0
- data/resources/assets/pygments/native.css +69 -0
- data/resources/assets/pygments/pastie.css +60 -0
- data/resources/assets/pygments/perldoc.css +58 -0
- data/resources/assets/pygments/tango.css +69 -0
- data/resources/assets/pygments/trac.css +59 -0
- data/resources/assets/pygments/vim.css +69 -0
- data/resources/assets/pygments/vs.css +33 -0
- data/resources/assets/pygments/zenburn.css +1 -0
- data/resources/assets/style.css +41 -0
- data/resources/templates/dashboard/files/dashboard.html.tt +82 -0
- data/resources/templates/dashboard/templates/_test_report.html.tt +87 -0
- data/samples/bootstrap.sh +2 -0
- data/samples/clone.sh +2 -0
- data/samples/code2doc.sh +4 -0
- data/samples/docs/samples/code2doc/java/katas-hello_world-java.md +17 -0
- data/samples/docs/samples/code2doc/java/katas-quine-java.md +35 -0
- data/samples/docs/samples/code2doc/python/katas-hello_world-python.md +5 -0
- data/samples/docs/samples/code2doc/python/katas-quine-python.md +6 -0
- data/samples/docs/samples/code2doc/ruby/katas-hello_world-ruby.md +11 -0
- data/samples/exec.sh +2 -0
- data/samples/polytrix.rb +2 -2
- data/samples/polytrix.yml +5 -2
- data/samples/show.sh +4 -0
- data/samples/test.sh +2 -0
- data/samples/tests/polytrix/validators.rb +2 -2
- data/samples/verify.sh +3 -0
- data/scripts/wrapper +4 -7
- data/spec/fabricators/challenge_fabricator.rb +2 -9
- data/spec/fabricators/implementor_fabricator.rb +0 -8
- data/spec/fabricators/manifest_fabricator.rb +2 -9
- data/spec/fabricators/validator_fabricator.rb +2 -4
- data/spec/polytrix/challenge_runner_spec.rb +20 -0
- data/spec/polytrix/documentation/helpers/code_helper_spec.rb +7 -7
- data/spec/polytrix/file_finder_spec.rb +5 -5
- data/spec/polytrix/manifest_spec.rb +0 -21
- data/spec/polytrix/result_spec.rb +14 -14
- data/spec/polytrix/validator_registry_spec.rb +4 -4
- data/spec/polytrix/validator_spec.rb +9 -9
- data/spec/polytrix_spec.rb +1 -25
- data/spec/spec_helper.rb +8 -1
- metadata +130 -38
- data/features/execution.feature +0 -53
- data/features/fixtures/spec/polytrix_spec.rb +0 -7
- data/lib/polytrix/cli/report.rb +0 -84
- data/lib/polytrix/command/rundoc.rb +0 -27
- data/lib/polytrix/core/file_system_helper.rb +0 -75
- data/lib/polytrix/core/manifest_section.rb +0 -4
- data/lib/polytrix/core/string_helpers.rb +0 -15
- data/lib/polytrix/documentation/view_helper.rb +0 -21
- data/lib/polytrix/rspec/documentation_formatter.rb +0 -66
- data/lib/polytrix/rspec/yaml_report.rb +0 -51
- data/lib/polytrix/rspec.rb +0 -56
- data/lib/polytrix/runners/executor.rb +0 -34
- data/lib/polytrix/runners/linux_challenge_runner.rb +0 -23
- data/lib/polytrix/runners/middleware/change_directory.rb +0 -20
- data/lib/polytrix/runners/middleware/feature_executor.rb +0 -24
- data/lib/polytrix/runners/middleware/setup_env_vars.rb +0 -42
- data/lib/polytrix/runners/mixlib_shellout_executor.rb +0 -83
- data/lib/polytrix/validations.rb +0 -23
- data/samples/scripts/wrapper +0 -7
- data/spec/polytrix/middleware/feature_executor_spec.rb +0 -48
- data/spec/polytrix/validations_spec.rb +0 -16
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Polytrix
|
|
2
|
+
module Executors
|
|
3
|
+
class LinuxChallengeRunner < ChallengeRunner
|
|
4
|
+
include Polytrix::Util::FileSystem
|
|
5
|
+
|
|
6
|
+
def challenge_command(challenge_script, basedir = Dir.pwd)
|
|
7
|
+
challenge_script = "./#{challenge_script}" unless challenge_script.to_s.start_with? '/'
|
|
8
|
+
|
|
9
|
+
[wrapper_script(basedir), challenge_script].compact.join(' ')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
protected
|
|
13
|
+
|
|
14
|
+
def wrapper_script(basedir)
|
|
15
|
+
basedir_relative_wrapper = File.expand_path('scripts/wrapper', basedir)
|
|
16
|
+
root_relative_wrapper = File.expand_path('scripts/wrapper', Dir.pwd)
|
|
17
|
+
|
|
18
|
+
if File.exist? basedir_relative_wrapper
|
|
19
|
+
relativize(basedir_relative_wrapper, basedir).to_s
|
|
20
|
+
elsif File.exist? root_relative_wrapper
|
|
21
|
+
# FIXME: This isn't always desired, probably better to opt-in to this behavior
|
|
22
|
+
relativize(root_relative_wrapper, basedir).to_s
|
|
23
|
+
else
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'mixlib/shellout'
|
|
2
|
+
|
|
3
|
+
module Polytrix
|
|
4
|
+
module Executors
|
|
5
|
+
class IOToLog < IO
|
|
6
|
+
def initialize(logger)
|
|
7
|
+
@logger = logger
|
|
8
|
+
@buffer = ''
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def write(string)
|
|
12
|
+
(@buffer + string).lines.each do |line|
|
|
13
|
+
if line.end_with? "\n"
|
|
14
|
+
@buffer = ''
|
|
15
|
+
@logger.info(line.rstrip)
|
|
16
|
+
else
|
|
17
|
+
@buffer = line
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class MixlibShellOutExecutor
|
|
24
|
+
include Polytrix::DefaultLogger
|
|
25
|
+
|
|
26
|
+
MIXLIB_SHELLOUT_EXCEPTION_CLASSES = Mixlib::ShellOut.constants.map do|name|
|
|
27
|
+
klass = Mixlib::ShellOut.const_get(name)
|
|
28
|
+
if klass.is_a?(Class) && klass <= RuntimeError
|
|
29
|
+
klass
|
|
30
|
+
else
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
end.compact
|
|
34
|
+
|
|
35
|
+
def log_decorator(_io, _prefix)
|
|
36
|
+
IOToLog.new(logger)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def execute(command, opts)
|
|
40
|
+
prefix = opts.delete :prefix
|
|
41
|
+
shell = Mixlib::ShellOut.new(command, opts)
|
|
42
|
+
shell.live_stream = log_decorator $stdout, prefix
|
|
43
|
+
shell.run_command
|
|
44
|
+
execution_result = ExecutionResult.new exitstatus: shell.exitstatus, stdout: shell.stdout, stderr: shell.stderr
|
|
45
|
+
# shell.error!
|
|
46
|
+
execution_result
|
|
47
|
+
rescue SystemCallError, *MIXLIB_SHELLOUT_EXCEPTION_CLASSES, TypeError => e
|
|
48
|
+
# See https://github.com/opscode/mixlib-shellout/issues/62
|
|
49
|
+
execution_error = ExecutionError.new(e)
|
|
50
|
+
execution_error.execution_result = execution_result
|
|
51
|
+
raise execution_error
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/polytrix/{runners/windows_challenge_runner.rb → executors/windows_challenge_executor.rb}
RENAMED
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
module Polytrix
|
|
2
|
-
module
|
|
2
|
+
module Executors
|
|
3
3
|
class WindowsChallengeRunner < ChallengeRunner
|
|
4
4
|
PS_OPTIONS = '-NoProfile -ExecutionPolicy Bypass'
|
|
5
|
-
def script_extension
|
|
6
|
-
'ps1'
|
|
7
|
-
end
|
|
8
5
|
|
|
9
|
-
def challenge_command(
|
|
6
|
+
def challenge_command(challenge_script, _basedir = Dir.pwd)
|
|
10
7
|
# I don't know a simple powershell replacement for &&
|
|
11
8
|
# See http://stackoverflow.com/questions/2416662/what-are-the-powershell-equivalent-of-bashs-and-operators
|
|
12
9
|
if File.exist? 'scripts/wrapper.ps1'
|
|
13
|
-
command = "
|
|
10
|
+
command = "./scripts/wrapper.ps1 #{challenge_script}"
|
|
14
11
|
else
|
|
15
|
-
command = "
|
|
12
|
+
command = "./#{challenge_script}"
|
|
16
13
|
end
|
|
17
14
|
"PowerShell #{PS_OPTIONS} -Command \"#{command}\""
|
|
18
15
|
end
|
|
19
|
-
|
|
20
|
-
def save_environment_variable(key, value)
|
|
21
|
-
"$Env:#{key}='#{value}'"
|
|
22
|
-
end
|
|
23
16
|
end
|
|
24
17
|
end
|
|
25
18
|
end
|
|
@@ -20,8 +20,8 @@ module Polytrix
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
include Polytrix::Logging
|
|
23
|
-
include Polytrix::
|
|
24
|
-
include
|
|
23
|
+
include Polytrix::Util::FileSystem
|
|
24
|
+
include Executor
|
|
25
25
|
property :name
|
|
26
26
|
property :basedir, required: true
|
|
27
27
|
property :language
|
|
@@ -46,7 +46,7 @@ module Polytrix
|
|
|
46
46
|
end
|
|
47
47
|
branch = git.branch ||= 'master'
|
|
48
48
|
target_dir = git.to ||= basedir
|
|
49
|
-
if File.
|
|
49
|
+
if File.exist? target_dir
|
|
50
50
|
logger.info "Skipping clone because #{target_dir} already exists"
|
|
51
51
|
else
|
|
52
52
|
clone_cmd = "git clone #{git.repo} -b #{branch} #{target_dir}"
|
|
@@ -61,8 +61,12 @@ module Polytrix
|
|
|
61
61
|
fail "Implementor #{name} has not been cloned" unless cloned?
|
|
62
62
|
|
|
63
63
|
execute('./scripts/bootstrap', cwd: basedir, prefix: name)
|
|
64
|
-
rescue
|
|
65
|
-
|
|
64
|
+
rescue ExecutionError => e
|
|
65
|
+
if e.cause.is_a? Errno::ENOENT
|
|
66
|
+
logger.warn "Skipping bootstrapping for #{name}, no script/bootstrap exists"
|
|
67
|
+
else
|
|
68
|
+
raise e
|
|
69
|
+
end
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
def build_challenge(challenge_data)
|
|
@@ -72,7 +76,7 @@ module Polytrix
|
|
|
72
76
|
begin
|
|
73
77
|
challenge_data[:source_file] ||= find_file basedir, challenge_data[:name]
|
|
74
78
|
challenge_data[:source_file] = relativize(challenge_data[:source_file], challenge_data[:basedir])
|
|
75
|
-
rescue
|
|
79
|
+
rescue Errno::ENOENT
|
|
76
80
|
challenge_data[:source_file] = nil
|
|
77
81
|
end
|
|
78
82
|
Challenge.new challenge_data
|
data/lib/polytrix/manifest.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
|
-
require 'hashie/dash'
|
|
3
2
|
require 'hashie/mash'
|
|
4
3
|
require 'hashie/extensions/coercion'
|
|
5
4
|
require 'hashie/extensions/deep_merge'
|
|
@@ -49,19 +48,10 @@ module Polytrix
|
|
|
49
48
|
coerce_value Integer, String
|
|
50
49
|
end
|
|
51
50
|
|
|
52
|
-
class CodeSample < Polytrix::ManifestSection
|
|
53
|
-
property :name, required: true
|
|
54
|
-
|
|
55
|
-
def self.coerce(data)
|
|
56
|
-
data = { name: data } if data.is_a? String
|
|
57
|
-
new(data)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
51
|
class Suite < Polytrix::ManifestSection
|
|
62
52
|
property :env, default: {}
|
|
63
53
|
property :samples, required: true
|
|
64
|
-
coerce_key :samples, Array[
|
|
54
|
+
coerce_key :samples, Array[String]
|
|
65
55
|
property :results
|
|
66
56
|
end
|
|
67
57
|
|
|
@@ -80,7 +70,7 @@ module Polytrix
|
|
|
80
70
|
suites.each do | suite_name, suite |
|
|
81
71
|
suite.samples.each do | sample |
|
|
82
72
|
implementors.each_value do | implementor |
|
|
83
|
-
challenge = implementor.build_challenge suite: suite_name, name: sample
|
|
73
|
+
challenge = implementor.build_challenge suite: suite_name, name: sample, vars: suite.env
|
|
84
74
|
@challenges[challenge.slug] = challenge
|
|
85
75
|
end
|
|
86
76
|
end
|
|
@@ -96,24 +86,5 @@ module Polytrix
|
|
|
96
86
|
data = YAML.load processed_content
|
|
97
87
|
new data
|
|
98
88
|
end
|
|
99
|
-
|
|
100
|
-
def find_suite(suite_name)
|
|
101
|
-
_, suite = suites.find { |name, _| name.to_s.downcase == suite_name.to_s.downcase }
|
|
102
|
-
suite
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def find_challenge(suite_name, scenario_name)
|
|
106
|
-
suite = find_suite suite_name
|
|
107
|
-
return nil if suite.nil?
|
|
108
|
-
|
|
109
|
-
if suite.samples.is_a? Array
|
|
110
|
-
# No results yet
|
|
111
|
-
suite.samples.find { |name, _| name.downcase == scenario_name.downcase }
|
|
112
|
-
Challenge.new suite: suite_name, name: scenario_name
|
|
113
|
-
else
|
|
114
|
-
_, challenge_data = find_suite('identity').samples.find { |name, challenge| name.downcase == scenario_name.downcase }
|
|
115
|
-
Challenge.new(suite: suite_name, name: scenario_name, result: challenge_data)
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
89
|
end
|
|
119
90
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'csv'
|
|
2
2
|
|
|
3
3
|
module Polytrix
|
|
4
|
-
module
|
|
4
|
+
module Reporters
|
|
5
5
|
class HashReporter
|
|
6
6
|
def initialize(io = $stdout)
|
|
7
7
|
@buffer = io
|
|
@@ -20,9 +20,13 @@ module Polytrix
|
|
|
20
20
|
@buffer.puts convert(data)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def convert(
|
|
23
|
+
def convert(_data)
|
|
24
24
|
fail 'Subclass HashReporter and convert the data to the target format'
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
def colors?
|
|
28
|
+
false
|
|
29
|
+
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
32
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module Polytrix
|
|
2
|
-
module
|
|
2
|
+
module Reporters
|
|
3
3
|
class MarkdownReporter
|
|
4
|
+
include Polytrix::Util::String
|
|
4
5
|
def initialize(io = $stdout)
|
|
5
6
|
@buffer = io
|
|
6
7
|
end
|
|
@@ -13,9 +14,13 @@ module Polytrix
|
|
|
13
14
|
@buffer.puts header_line.gsub(/[^|]/, '-')
|
|
14
15
|
|
|
15
16
|
table[1..-1].each do |data_line|
|
|
16
|
-
@buffer.puts data_line.join
|
|
17
|
+
@buffer.puts data_line.map { |line| escape_html(line) }.join(' | ')
|
|
17
18
|
end
|
|
18
19
|
end
|
|
20
|
+
|
|
21
|
+
def colors?
|
|
22
|
+
false
|
|
23
|
+
end
|
|
19
24
|
end
|
|
20
25
|
end
|
|
21
26
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Polytrix
|
|
2
|
+
module Reporters
|
|
3
|
+
autoload :MarkdownReporter, 'polytrix/reporters/markdown_reporter'
|
|
4
|
+
# autoload :HTMLReporter, 'polytrix/reporters/html_reporter'
|
|
5
|
+
autoload :JSONReporter, 'polytrix/reporters/json_reporter'
|
|
6
|
+
autoload :YAMLReporter, 'polytrix/reporters/yaml_reporter'
|
|
7
|
+
|
|
8
|
+
RESOURCES_DIR = File.expand_path '../../../resources/', __FILE__
|
|
9
|
+
TEMPLATE_DIR = File.expand_path 'templates/', RESOURCES_DIR
|
|
10
|
+
ASSETS_DIR = File.expand_path 'assets/', RESOURCES_DIR
|
|
11
|
+
|
|
12
|
+
def self.reporter(format, shell)
|
|
13
|
+
case format
|
|
14
|
+
when 'text'
|
|
15
|
+
shell
|
|
16
|
+
when 'markdown'
|
|
17
|
+
MarkdownReporter.new
|
|
18
|
+
when 'json'
|
|
19
|
+
JSONReporter.new
|
|
20
|
+
when 'yaml'
|
|
21
|
+
YAMLReporter.new
|
|
22
|
+
else
|
|
23
|
+
fail "Unknown report format #{options[:format]}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/polytrix/result.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'hashie/dash'
|
|
2
|
+
require 'hashie/extensions/coercion'
|
|
2
3
|
|
|
3
4
|
module Polytrix
|
|
4
5
|
class Result < Hashie::Dash
|
|
@@ -9,15 +10,15 @@ module Polytrix
|
|
|
9
10
|
def_delegators :execution_result, :stdout, :stderr, :exitstatus
|
|
10
11
|
property :source_file # , required: true
|
|
11
12
|
property :data
|
|
12
|
-
property :validations, default:
|
|
13
|
-
coerce_key :validations,
|
|
13
|
+
property :validations, default: {}
|
|
14
|
+
coerce_key :validations, Hash[String => Validation]
|
|
14
15
|
|
|
15
16
|
def status
|
|
16
17
|
# A feature can be validated by different suites, or manually vs an automated suite.
|
|
17
18
|
# That's why there's a precedence rather than boolean algebra here...
|
|
18
|
-
return 'failed' if validations.any? { |v| v.result == 'failed' }
|
|
19
|
-
return 'passed' if validations.any? { |v| v.result == 'passed' }
|
|
20
|
-
return 'pending' if validations.any? { |v| v.result == 'pending' }
|
|
19
|
+
return 'failed' if validations.values.any? { |v| v.result == 'failed' }
|
|
20
|
+
return 'passed' if validations.values.any? { |v| v.result == 'passed' }
|
|
21
|
+
return 'pending' if validations.values.any? { |v| v.result == 'pending' }
|
|
21
22
|
'skipped'
|
|
22
23
|
end
|
|
23
24
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Polytrix
|
|
2
|
+
module Spies
|
|
3
|
+
class FileSystemSpy < Polytrix::Spy
|
|
4
|
+
def initialize(_app, _server_options)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def spy(_challenge)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
report :summary, SummaryReport
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Polytrix.configuration.register_spy(Polytrix::Spies::FileSystemSpy)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'middleware'
|
|
2
|
+
|
|
3
|
+
module Polytrix
|
|
4
|
+
# # @abstract
|
|
5
|
+
class Spy
|
|
6
|
+
def initialize(app, opts = {})
|
|
7
|
+
@app = app
|
|
8
|
+
@opts = opts
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(_challenge)
|
|
12
|
+
fail NotImplementedError, 'Subclass must implement #call'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.reports
|
|
16
|
+
@reports ||= {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.report(type, report_class)
|
|
20
|
+
reports[type] = report_class
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Spies
|
|
25
|
+
class << self
|
|
26
|
+
attr_reader :spies
|
|
27
|
+
|
|
28
|
+
def middleware
|
|
29
|
+
@middleware ||= Middleware::Builder.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def spies
|
|
33
|
+
@spies ||= Set.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def register_spy(spy)
|
|
37
|
+
spies.add(spy)
|
|
38
|
+
middleware.insert 0, spy, {}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def observe(challenge, &blk)
|
|
42
|
+
middleware = Middleware::Builder.new
|
|
43
|
+
spies.each do |spy|
|
|
44
|
+
middleware.use spy
|
|
45
|
+
end
|
|
46
|
+
middleware.use blk
|
|
47
|
+
middleware.call(challenge)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def reports
|
|
51
|
+
# Group by type
|
|
52
|
+
all_reports = spies.flat_map do |spy|
|
|
53
|
+
spy.reports.to_a if spy.respond_to? :reports
|
|
54
|
+
end
|
|
55
|
+
all_reports.each_with_object({}) do |(k, v), h|
|
|
56
|
+
(h[k] ||= []) << v
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/polytrix/state_file.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Polytrix
|
|
|
21
21
|
dir = File.dirname(file_name)
|
|
22
22
|
serialized_string = serialize_hash(Util.stringified_hash(state))
|
|
23
23
|
|
|
24
|
-
FileUtils.mkdir_p(dir)
|
|
24
|
+
FileUtils.mkdir_p(dir)
|
|
25
25
|
File.open(file_name, 'wb') { |f| f.write(serialized_string) }
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -29,29 +29,10 @@ module Polytrix
|
|
|
29
29
|
FileUtils.rm_f(file_name) if File.exist?(file_name)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def diagnose
|
|
33
|
-
raw = read
|
|
34
|
-
result = {}
|
|
35
|
-
raw.keys.sort.each { |k| result[k] = raw[k] }
|
|
36
|
-
result
|
|
37
|
-
end
|
|
38
|
-
|
|
39
32
|
private
|
|
40
33
|
|
|
41
34
|
attr_reader :file_name
|
|
42
35
|
|
|
43
|
-
# @api private
|
|
44
|
-
def read_file
|
|
45
|
-
IO.read(file_name)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# @api private
|
|
49
|
-
def deserialize_string(string)
|
|
50
|
-
SafeYAML.load(string)
|
|
51
|
-
rescue SyntaxError, Psych::SyntaxError => ex
|
|
52
|
-
raise StateFileLoadError, "Error parsing #{file_name} (#{ex.message})"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
36
|
# @api private
|
|
56
37
|
def serialize_hash(hash)
|
|
57
38
|
::YAML.dump(hash)
|