quickdraw 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +18 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +49 -0
- data/LICENSE.txt +17 -18
- data/README.md +100 -29
- data/config/quickdraw.rb +16 -0
- data/exe/qt +12 -0
- data/lib/quickdraw/cluster.rb +27 -0
- data/lib/quickdraw/configuration.rb +13 -0
- data/lib/quickdraw/context.rb +138 -0
- data/lib/quickdraw/expectation.rb +62 -0
- data/lib/quickdraw/map.rb +30 -0
- data/lib/quickdraw/matchers/boolean.rb +11 -0
- data/lib/quickdraw/matchers/case_equality.rb +9 -0
- data/lib/quickdraw/matchers/change.rb +33 -0
- data/lib/quickdraw/matchers/equality.rb +39 -0
- data/lib/quickdraw/matchers/include.rb +15 -0
- data/lib/quickdraw/matchers/predicate.rb +14 -0
- data/lib/quickdraw/matchers/respond_to.rb +15 -0
- data/lib/quickdraw/matchers/to_be_a.rb +18 -0
- data/lib/quickdraw/matchers/to_have_attributes.rb +13 -0
- data/lib/quickdraw/matchers/to_raise.rb +26 -0
- data/lib/quickdraw/matchers/to_receive.rb +30 -0
- data/lib/quickdraw/matchers.rb +13 -0
- data/lib/quickdraw/pipe.rb +27 -0
- data/lib/quickdraw/queue.rb +32 -0
- data/lib/quickdraw/registry.rb +57 -0
- data/lib/quickdraw/run.rb +53 -0
- data/lib/quickdraw/runner.rb +48 -0
- data/lib/quickdraw/timer.rb +30 -0
- data/lib/quickdraw/version.rb +3 -1
- data/lib/quickdraw/worker.rb +28 -0
- data/lib/quickdraw.rb +29 -22
- metadata +51 -123
- data/.gitignore +0 -17
- data/Rakefile +0 -1
- data/bin/quickdraw +0 -25
- data/lib/quickdraw/cli.rb +0 -251
- data/lib/quickdraw/shopify_connector.rb +0 -105
- data/lib/quickdraw/shopify_connector_pool.rb +0 -54
- data/quickdraw.gemspec +0 -29
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::Change
|
4
|
+
def to_change(from: Quickdraw::Null, to: Quickdraw::Null, by: Quickdraw::Null, &object)
|
5
|
+
original = object.call
|
6
|
+
|
7
|
+
if Quickdraw::Null != from
|
8
|
+
assert from === original do
|
9
|
+
"expected `#{original.inspect}` to be `#{from.inspect}`"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
block.call
|
14
|
+
|
15
|
+
new = object.call
|
16
|
+
|
17
|
+
if new == original
|
18
|
+
failure! "expected `#{original.inspect}` to change"
|
19
|
+
end
|
20
|
+
|
21
|
+
if Quickdraw::Null != to
|
22
|
+
assert to === new do
|
23
|
+
"expected `#{new.inspect}` to be `#{to.inspect}`"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if Quickdraw::Null != by
|
28
|
+
assert by === (new - original) do
|
29
|
+
"expected `#{new.inspect}` to change by `#{by.inspect}`"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::Equality
|
4
|
+
def ==(other)
|
5
|
+
assert value == other do
|
6
|
+
"expected `#{value.inspect}` to == `#{other.inspect}`"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def !=(other)
|
11
|
+
assert value != other do
|
12
|
+
"expected `#{value.inspect}` to != `#{other.inspect}`"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_eql?(other)
|
17
|
+
assert value.eql?(other) do
|
18
|
+
"expected `#{value.inspect}` to eql? `#{other.inspect}`"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def not_to_eql?(other)
|
23
|
+
refute value.eql?(other) do
|
24
|
+
"expected `#{value.inspect}` not to eql? `#{other.inspect}`"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_equal?(other)
|
29
|
+
assert value.equal?(other) do
|
30
|
+
"expected `#{value.inspect}` to equal? `#{other.inspect}`"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def not_to_equal?(other)
|
35
|
+
refute value.equal?(other) do
|
36
|
+
"expected `#{value.inspect}` not to equal? `#{other.inspect}`"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::Include
|
4
|
+
def to_include(other)
|
5
|
+
assert value.include?(other) do
|
6
|
+
"expected `#{value.inspect}` to include? `#{other.inspect}`"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def not_to_include(other)
|
11
|
+
refute value.include?(other) do
|
12
|
+
"expected `#{value.inspect}` to not include? `#{other.inspect}`"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::Predicate
|
4
|
+
def to_be(predicate)
|
5
|
+
assert(value.send(predicate)) { "expected `#{value.inspect}` to be `#{predicate.inspect}`" }
|
6
|
+
end
|
7
|
+
|
8
|
+
def not_to_be(predicate)
|
9
|
+
refute(value.send(predicate)) { "expected `#{value.inspect}` to not be `#{predicate.inspect}`" }
|
10
|
+
end
|
11
|
+
|
12
|
+
alias_method :to_have, :to_be
|
13
|
+
alias_method :not_to_have, :not_to_be
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::RespondTo
|
4
|
+
def to_respond_to(method)
|
5
|
+
assert value.respond_to?(method) do
|
6
|
+
"expected `#{value.inspect}` to respond to `#{method.inspect}`"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def not_to_respond_to(method)
|
11
|
+
refute value.respond_to?(method) do
|
12
|
+
"expected `#{value.inspect}` to not respond to `#{method.inspect}`"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::ToBeA
|
4
|
+
def to_be_a(type)
|
5
|
+
assert type === value do
|
6
|
+
"expected `#{value.inspect}` to have the type `#{type.inspect}`"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def not_to_be_a(type)
|
11
|
+
refute type === value do
|
12
|
+
"expected `#{value.inspect}` to not have the type `#{type.inspect}`"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :to_be_an, :to_be_a
|
17
|
+
alias_method :not_to_be_an, :not_to_be_a
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::ToHaveAttributes
|
4
|
+
def to_have_attributes(**attributes)
|
5
|
+
attributes.each do |k, v|
|
6
|
+
assert v === value.send(k) do
|
7
|
+
"expected `#{value.inspect}` to have the attribute `#{k.inspect}` equal to `#{v.inspect}`"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
rescue NoMethodError => e
|
11
|
+
failure! { "expected `#{value.inspect}` to respond to `#{e.name}`" }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::ToRaise
|
4
|
+
def to_raise(error = ::Exception)
|
5
|
+
expectation_block = block
|
6
|
+
|
7
|
+
begin
|
8
|
+
expectation_block.call
|
9
|
+
rescue error => e
|
10
|
+
success!
|
11
|
+
yield(e) if block_given?
|
12
|
+
return
|
13
|
+
rescue ::Exception => e
|
14
|
+
return failure! { "expected `#{error.inspect}` to be raised but `#{e.class.inspect}` was raised" }
|
15
|
+
end
|
16
|
+
|
17
|
+
failure! { "expected #{error} to be raised but wasn't" }
|
18
|
+
end
|
19
|
+
|
20
|
+
def not_to_raise
|
21
|
+
block.call
|
22
|
+
success!
|
23
|
+
rescue ::Exception => e
|
24
|
+
failure! { "expected the block not to raise, but it raised `#{e.class}`" }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quickdraw::Matchers::ToReceive
|
4
|
+
def to_receive(method_name, &expectation_block)
|
5
|
+
__raise__ ::ArgumentError, "You can't use the `to_receive` matcher with a block expectation." if @block
|
6
|
+
|
7
|
+
interceptor = ::Module.new
|
8
|
+
|
9
|
+
# Define local variables so they're available from the block
|
10
|
+
expectation = self
|
11
|
+
context = @context
|
12
|
+
|
13
|
+
interceptor.define_method(method_name) do |*args, **kwargs, &block|
|
14
|
+
expectation.success!
|
15
|
+
super_block = -> (*a, &b) { (a.length > 0) || b ? super(*a, &b) : super(*args, **kwargs, &block) }
|
16
|
+
original_super = context.instance_variable_get(:@super)
|
17
|
+
begin
|
18
|
+
context.instance_variable_set(:@super, super_block)
|
19
|
+
result = expectation_block&.call(*args, **kwargs, &block)
|
20
|
+
ensure
|
21
|
+
context.instance_variable_set(:@super, original_super)
|
22
|
+
end
|
23
|
+
|
24
|
+
interceptor.define_method(method_name) { |*a, **k, &b| super(*a, **k, &b) }
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
value.singleton_class.prepend(interceptor)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Quickdraw::Matchers
|
2
|
+
autoload :Boolean, "quickdraw/matchers/boolean"
|
3
|
+
autoload :CaseEquality, "quickdraw/matchers/case_equality"
|
4
|
+
autoload :Change, "quickdraw/matchers/change"
|
5
|
+
autoload :Equality, "quickdraw/matchers/equality"
|
6
|
+
autoload :Include, "quickdraw/matchers/include"
|
7
|
+
autoload :Predicate, "quickdraw/matchers/predicate"
|
8
|
+
autoload :RespondTo, "quickdraw/matchers/respond_to"
|
9
|
+
autoload :ToBeA, "quickdraw/matchers/to_be_a"
|
10
|
+
autoload :ToHaveAttributes, "quickdraw/matchers/to_have_attributes"
|
11
|
+
autoload :ToRaise, "quickdraw/matchers/to_raise"
|
12
|
+
autoload :ToReceive, "quickdraw/matchers/to_receive"
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Quickdraw::Pipe
|
4
|
+
def initialize
|
5
|
+
@reader, @writer = IO.pipe
|
6
|
+
end
|
7
|
+
|
8
|
+
def reader
|
9
|
+
@writer.close
|
10
|
+
@reader
|
11
|
+
end
|
12
|
+
|
13
|
+
def writer
|
14
|
+
@reader.close
|
15
|
+
@writer
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_writer
|
19
|
+
@reader.close
|
20
|
+
yield(@writer).tap { @writer.close }
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_reader
|
24
|
+
@writer.close
|
25
|
+
yield(@reader).tap { @reader.close }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Quickdraw::Queue
|
4
|
+
def initialize
|
5
|
+
@array = []
|
6
|
+
@mutex = Mutex.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(item)
|
10
|
+
@mutex.synchronize { @array << item }
|
11
|
+
end
|
12
|
+
|
13
|
+
def drain
|
14
|
+
yield(shift) until empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def pop
|
18
|
+
@mutex.synchronize { @array.pop }
|
19
|
+
end
|
20
|
+
|
21
|
+
def shift
|
22
|
+
@mutex.synchronize { @array.shift }
|
23
|
+
end
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
@array.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def size
|
30
|
+
@array.size
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
# frozen_string_literal: true
|
5
|
+
|
6
|
+
class Quickdraw::Registry
|
7
|
+
def initialize
|
8
|
+
@registered_matchers = Quickdraw::Map.new
|
9
|
+
@type_matchers = Quickdraw::Map.new
|
10
|
+
@shapes = Quickdraw::Map.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# Register a new matcher for the given types.
|
14
|
+
def register(matcher, *types)
|
15
|
+
@registered_matchers[matcher] = types
|
16
|
+
|
17
|
+
# We need to clear this cache because the output of `slowly_find_matchers_for` might have changed.
|
18
|
+
@type_matchers.clear
|
19
|
+
end
|
20
|
+
|
21
|
+
# Given a value, find or build an expectation class that includes all the matchers for the value.
|
22
|
+
def expectation_for(value, matchers: nil)
|
23
|
+
if matchers
|
24
|
+
shape_for(
|
25
|
+
matchers + matchers_for(value)
|
26
|
+
)
|
27
|
+
else
|
28
|
+
shape_for(
|
29
|
+
matchers_for(value)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# A "shape" is a specialised Expectation class that includes the given matchers. It's cached against the list of matchers.
|
37
|
+
def shape_for(matchers)
|
38
|
+
@shapes[matchers] ||= Class.new(Quickdraw::Expectation) do
|
39
|
+
matchers.each { |m| include m }
|
40
|
+
freeze
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Given a value, find all the matchers that match it. This is cached against the class of the value.
|
45
|
+
def matchers_for(value)
|
46
|
+
@type_matchers[value.class] ||= slowly_find_matchers_for(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
# If the above has a cache miss, we'll need to find the correct matchers slowly and then cache them.
|
50
|
+
def slowly_find_matchers_for(value)
|
51
|
+
[].tap do |matchers|
|
52
|
+
@registered_matchers.each do |matcher, types|
|
53
|
+
matchers << matcher if types.any? { |t| t === value }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Quickdraw::Run
|
4
|
+
def initialize(number_of_processes:, number_of_threads: 1, test_files:)
|
5
|
+
@number_of_processes = number_of_processes
|
6
|
+
@number_of_threads = number_of_threads
|
7
|
+
@test_files = test_files.shuffle
|
8
|
+
|
9
|
+
@cluster = Quickdraw::Cluster.new
|
10
|
+
@batches = Array.new(@number_of_processes) { Quickdraw::Queue.new }
|
11
|
+
|
12
|
+
@test_files.each_with_index do |file, index|
|
13
|
+
@batches[index % @number_of_processes] << file
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
report_time do
|
19
|
+
fork_processes
|
20
|
+
@results = @cluster.wait
|
21
|
+
puts_results
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def report_time(&)
|
26
|
+
total_time = Quickdraw::Timer.time(&)
|
27
|
+
puts "Total time: #{total_time}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def fork_processes
|
31
|
+
# Enable YJIT right before forking
|
32
|
+
RubyVM::YJIT.enable
|
33
|
+
|
34
|
+
@batches.each_with_index do |batch, index|
|
35
|
+
@cluster.fork do |writer|
|
36
|
+
results = @number_of_threads.times.map do
|
37
|
+
Thread.new { Quickdraw::Runner.call(batch) }
|
38
|
+
end.map(&:value)
|
39
|
+
|
40
|
+
results.each_with_index do |result, thread|
|
41
|
+
writer.write("\n")
|
42
|
+
writer.write("Process[#{index + 1}], Thread[#{thread + 1}]: #{result.successes.count} assertions passed in #{result.duration}. #{Quickdraw::SUCCESS_EMOJI.sample}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def puts_results
|
49
|
+
puts
|
50
|
+
puts
|
51
|
+
puts "Collated results: \n#{@results.join("\n")}"
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Quickdraw::Runner
|
4
|
+
def self.call(queue)
|
5
|
+
tests = []
|
6
|
+
|
7
|
+
queue.drain do |f|
|
8
|
+
tests << [f, Class.new(Quickdraw::Context) do
|
9
|
+
class_eval(
|
10
|
+
File.read(f), f, 1
|
11
|
+
)
|
12
|
+
end]
|
13
|
+
end
|
14
|
+
|
15
|
+
new(tests).tap(&:call)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(tests = nil)
|
19
|
+
@tests = tests
|
20
|
+
|
21
|
+
@successes = []
|
22
|
+
@failures = []
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :successes, :failures, :duration
|
26
|
+
|
27
|
+
def call
|
28
|
+
@duration = Quickdraw::Timer.time do
|
29
|
+
@tests.each { |(f, t)| t.run(self, [f]) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def success!(name)
|
34
|
+
@successes << [name]
|
35
|
+
|
36
|
+
Kernel.print "🟢 "
|
37
|
+
# ::Kernel.print "\e[32m⚬\e[0m"
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure!(path, &message)
|
41
|
+
location = caller_locations.drop_while { |l| !l.path.include?(".test.rb") }
|
42
|
+
|
43
|
+
@failures << [message, location, path]
|
44
|
+
|
45
|
+
Kernel.print "🔴 "
|
46
|
+
# ::Kernel.print "\e[31m⚬\e[0m"
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Quickdraw::Timer
|
4
|
+
class Duration
|
5
|
+
def initialize(duration)
|
6
|
+
@duration = duration
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
if @duration < 1_000
|
11
|
+
"#{@duration}ns"
|
12
|
+
elsif @duration < 1_000_000
|
13
|
+
"#{@duration / 1_000}μs"
|
14
|
+
elsif @duration < 1_000_000_000
|
15
|
+
"#{@duration / 1_000_000}ms"
|
16
|
+
elsif @duration < 60_000_000_000
|
17
|
+
"#{@duration / 1_000_000_000}s"
|
18
|
+
else
|
19
|
+
"#{(@duration / 60_000_000_000)}m"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.time
|
25
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
26
|
+
yield
|
27
|
+
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
28
|
+
Duration.new(finish - start)
|
29
|
+
end
|
30
|
+
end
|
data/lib/quickdraw/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Quickdraw::Worker
|
4
|
+
def self.fork
|
5
|
+
pipe = Quickdraw::Pipe.new
|
6
|
+
|
7
|
+
pid = Process.fork do
|
8
|
+
pipe.with_writer do |writer|
|
9
|
+
yield(writer)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
new(pid:, pipe:)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(pid:, pipe:)
|
17
|
+
@pid = pid
|
18
|
+
@pipe = pipe
|
19
|
+
end
|
20
|
+
|
21
|
+
def wait
|
22
|
+
Process.wait(@pid)
|
23
|
+
|
24
|
+
@pipe.with_reader do |reader|
|
25
|
+
reader.read
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/quickdraw.rb
CHANGED
@@ -1,33 +1,40 @@
|
|
1
|
-
|
2
|
-
require 'pathname'
|
3
|
-
require 'filepath'
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
3
|
module Quickdraw
|
4
|
+
autoload :Cluster, "quickdraw/cluster"
|
5
|
+
autoload :Configuration, "quickdraw/configuration"
|
6
|
+
autoload :Context, "quickdraw/context"
|
7
|
+
autoload :Expectation, "quickdraw/expectation"
|
8
|
+
autoload :Matchers, "quickdraw/matchers"
|
9
|
+
autoload :Pipe, "quickdraw/pipe"
|
10
|
+
autoload :Registry, "quickdraw/registry"
|
11
|
+
autoload :Run, "quickdraw/run"
|
12
|
+
autoload :Runner, "quickdraw/runner"
|
13
|
+
autoload :Timer, "quickdraw/timer"
|
14
|
+
autoload :Worker, "quickdraw/worker"
|
15
|
+
autoload :Queue, "quickdraw/queue"
|
16
|
+
autoload :Map, "quickdraw/map"
|
6
17
|
|
7
|
-
|
8
|
-
TIMER_RESET = 5 * 60 + 5
|
9
|
-
PERMIT_LOWER_LIMIT = 10
|
18
|
+
SUCCESS_EMOJI = %w[💃 🕺 🎉 🎊 💪 👏 🙌 ✨ 🥳 🎈 🌈 🎯 🏆]
|
10
19
|
|
11
|
-
|
12
|
-
|
13
|
-
config = YAML.load(File.read('config.yml'))
|
14
|
-
config
|
15
|
-
else
|
16
|
-
puts "config.yml does not exist!"
|
17
|
-
{}
|
18
|
-
end
|
19
|
-
end
|
20
|
+
Null = Object.new.freeze
|
21
|
+
Config = Configuration.new
|
20
22
|
|
21
|
-
|
22
|
-
FilePath.getwd
|
23
|
-
end
|
23
|
+
module Error; end
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
class TestFailure < RuntimeError
|
26
|
+
include Error
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
class ArgumentError < ::ArgumentError
|
30
|
+
include Error
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.configure(&block)
|
34
|
+
if block.arity == 0
|
35
|
+
Config.instance_eval(&block)
|
36
|
+
else
|
37
|
+
yield Config
|
38
|
+
end
|
39
|
+
end
|
33
40
|
end
|