polytrix 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|