Created
March 25, 2015 21:32
-
-
Save asterite/00d485071980f302daca to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Object | |
def colorize | |
ColorizedObject.new(self) | |
end | |
def colorize(fore) | |
ColorizedObject.new(self).fore(fore) | |
end | |
end | |
def with_color | |
"".colorize | |
end | |
def with_color(color : Symbol) | |
"".colorize(color) | |
end | |
struct ColorizedObject(T) | |
FORE_DEFAULT = "39" | |
FORE_BLACK = "30" | |
FORE_RED = "31" | |
FORE_GREEN = "32" | |
FORE_YELLOW = "33" | |
FORE_BLUE = "34" | |
FORE_MAGENTA = "35" | |
FORE_CYAN = "36" | |
FORE_LIGHT_GRAY = "37" | |
FORE_DARK_GRAY = "90" | |
FORE_LIGHT_RED = "91" | |
FORE_LIGHT_GREEN = "92" | |
FORE_LIGHT_YELLOW = "93" | |
FORE_LIGHT_BLUE = "94" | |
FORE_LIGHT_MAGENTA = "95" | |
FORE_LIGHT_CYAN = "96" | |
FORE_WHITE = "97" | |
BACK_DEFAULT = "49" | |
BACK_BLACK = "40" | |
BACK_RED = "41" | |
BACK_GREEN = "42" | |
BACK_YELLOW = "43" | |
BACK_BLUE = "44" | |
BACK_MAGENTA = "45" | |
BACK_CYAN = "46" | |
BACK_LIGHT_GRAY = "47" | |
BACK_DARK_GRAY = "100" | |
BACK_LIGHT_RED = "101" | |
BACK_LIGHT_GREEN = "102" | |
BACK_LIGHT_YELLOW = "103" | |
BACK_LIGHT_BLUE = "104" | |
BACK_LIGHT_MAGENTA = "105" | |
BACK_LIGHT_CYAN = "106" | |
BACK_WHITE = "107" | |
MODE_DEFAULT = "0" | |
MODE_BOLD = "1" | |
MODE_BRIGHT = "1" | |
MODE_DIM = "2" | |
MODE_UNDERLINE = "4" | |
MODE_BLINK = "5" | |
MODE_REVERSE = "7" | |
MODE_HIDDEN = "8" | |
MODE_BOLD_FLAG = 1 | |
MODE_BRIGHT_FLAG = 1 | |
MODE_DIM_FLAG = 2 | |
MODE_UNDERLINE_FLAG = 4 | |
MODE_BLINK_FLAG = 8 | |
MODE_REVERSE_FLAG = 16 | |
MODE_HIDDEN_FLAG = 32 | |
COLORS = %w(black red green yellow blue magenta cyan light_gray dark_gray light_red light_green light_yellow light_blue light_magenta light_cyan white) | |
MODES = %w(bold bright dim underline blink reverse hidden) | |
def initialize(@object : T) | |
@fore = FORE_DEFAULT | |
@back = BACK_DEFAULT | |
@mode = 0 | |
end | |
{% for name in COLORS %} | |
def {{name.id}} | |
@fore = FORE_{{name.upcase.id}} | |
self | |
end | |
def on_{{name.id}} | |
@back = BACK_{{name.upcase.id}} | |
self | |
end | |
{% end %} | |
{% for name in MODES %} | |
def {{name.id}} | |
@mode |= MODE_{{name.upcase.id}}_FLAG | |
self | |
end | |
{% end %} | |
def fore(color : Symbol) | |
{% for name in COLORS %} | |
if color == :{{name.id}} | |
@fore = FORE_{{name.upcase.id}} | |
return self | |
end | |
{% end %} | |
raise ArgumentError.new "unknown color: #{color}" | |
end | |
def back(color : Symbol) | |
{% for name in COLORS %} | |
if color == :{{name.id}} | |
@back = BACK_{{name.upcase.id}} | |
return self | |
end | |
{% end %} | |
raise ArgumentError.new "unknown color: #{color}" | |
end | |
def mode(mode : Symbol) | |
{% for name in MODES %} | |
if mode == :{{name.id}} | |
@mode |= MODE_{{name.upcase.id}}_FLAG | |
return self | |
end | |
{% end %} | |
raise ArgumentError.new "unknown mode: #{mode}" | |
end | |
def on(color : Symbol) | |
back color | |
end | |
def to_s(io) | |
surround(io) do | |
io << @object | |
end | |
end | |
def inspect(io) | |
surround(io) do | |
@object.inspect(io) | |
end | |
end | |
def surround(io = STDOUT) | |
ifdef !windows | |
must_append_end = append_start(io) | |
end | |
yield io | |
ifdef !windows | |
append_end(io) if must_append_end | |
end | |
end | |
STACK = [] of ColorizedObject(String) | |
def push(io = STDOUT) | |
last_color = STACK.last? | |
append_start(io, !!last_color) | |
STACK.push self | |
yield io | |
STACK.pop | |
if last_color | |
last_color.append_start(io, true) | |
else | |
append_end(io) | |
end | |
end | |
protected def append_start(io, reset = false) | |
fore_is_default = @fore == FORE_DEFAULT | |
back_is_default = @back == BACK_DEFAULT | |
mode_is_default = @mode == 0 | |
if fore_is_default && back_is_default && mode_is_default && !reset | |
false | |
else | |
io << "\e[" | |
printed = false | |
if reset | |
io << MODE_DEFAULT | |
printed = true | |
end | |
unless fore_is_default | |
io << ";" if printed | |
io << @fore | |
printed = true | |
end | |
unless back_is_default | |
io << ";" if printed | |
io << @back | |
printed = true | |
end | |
unless mode_is_default | |
# Can't reuse MODES constant because it has bold/bright duplicated | |
{% for name in %w(bold dim underline blink reverse hidden) %} | |
if (@mode & MODE_{{name.upcase.id}}_FLAG) != 0 | |
io << ";" if printed | |
io << MODE_{{name.upcase.id}} | |
printed = true | |
end | |
{% end %} | |
end | |
io << "m" | |
true | |
end | |
end | |
protected def append_end(io) | |
io << "\e[0m" | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
lib LibCrystalMain | |
@[Raises] | |
fun __crystal_main(argc : Int32, argv : UInt8**) | |
end | |
module AtExitHandlers | |
@@handlers = nil | |
def self.add(handler) | |
handlers = @@handlers ||= [] of -> | |
handlers << handler | |
end | |
def self.run | |
return if @@running | |
@@running = true | |
begin | |
@@handlers.try &.each &.call | |
rescue handler_ex | |
puts "Error running at_exit handler: #{handler_ex}" | |
end | |
end | |
end | |
def at_exit(&handler) | |
AtExitHandlers.add(handler) | |
end | |
def exit(status = 0) | |
AtExitHandlers.run | |
Process.exit(status) | |
end | |
def abort(message, status = 1) | |
puts message | |
exit status | |
end | |
ifdef darwin || linux | |
macro redefine_main(name = main) | |
fun main = {{name}}(argc : Int32, argv : UInt8**) : Int32 | |
GC.init | |
{{yield LibCrystalMain.__crystal_main(argc, argv)}} | |
0 | |
rescue ex | |
puts ex | |
ex.backtrace.each do |frame| | |
puts frame | |
end | |
1 | |
ensure | |
AtExitHandlers.run | |
end | |
end | |
redefine_main do |main| | |
{{main}} | |
end | |
elsif windows | |
macro redefine_main(name = main) | |
fun main = {{name}}(argc : Int32, argv : UInt8**) : Int32 | |
{{yield LibCrystalMain.__crystal_main(argc, argv)}} | |
0 | |
end | |
end | |
redefine_main do |main| | |
{{main}} | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Spec | |
class EqualExpectation(T) | |
def initialize(@value : T) | |
end | |
def match(value) | |
@target = value | |
value == @value | |
end | |
def failure_message | |
"expected: #{@value.inspect}\n got: #{@target.inspect}" | |
end | |
def negative_failure_message | |
"expected: value != #{@value.inspect}\n got: #{@target.inspect}" | |
end | |
end | |
class BeExpectation(T) | |
def initialize(@value : T) | |
end | |
def match(value) | |
@target = value | |
value.same? @value | |
end | |
def failure_message | |
"expected: #{@value.inspect} (object_id: #{@value.object_id})\n got: #{@target.inspect} (object_id: #{@target.object_id})" | |
end | |
def negative_failure_message | |
"expected: value.same? #{@value.inspect} (object_id: #{@value.object_id})\n got: #{@target.inspect} (object_id: #{@target.object_id})" | |
end | |
end | |
class BeTruthyExpectation | |
def match(@value) | |
!!@value | |
end | |
def failure_message | |
"expected: #{@value.inspect} to be truthy" | |
end | |
def negative_failure_message | |
"expected: #{@value.inspect} not to be truthy" | |
end | |
end | |
class BeFalseyExpectation | |
def match(@value) | |
!@value | |
end | |
def failure_message | |
"expected: #{@value.inspect} to be falsey" | |
end | |
def negative_failure_message | |
"expected: #{@value.inspect} not to be falsey" | |
end | |
end | |
class CloseExpectation | |
def initialize(@expected, @delta) | |
end | |
def match(value) | |
@target = value | |
(value - @expected).abs <= @delta | |
end | |
def failure_message | |
"expected #{@target} to be within #{@delta} of #{@expected}" | |
end | |
def negative_failure_message | |
"expected #{@target} not to be within #{@delta} of #{@expected}" | |
end | |
end | |
class BeAExpectation(T) | |
def match(value) | |
@target = value | |
value.is_a?(T) | |
end | |
def failure_message | |
"expected #{@target} to be a #{T}" | |
end | |
def negative_failure_message | |
"expected #{@target} not to be a #{T}" | |
end | |
end | |
class Be(T) | |
def self.<(other) | |
Be.new(other, :"<") | |
end | |
def self.<=(other) | |
Be.new(other, :"<=") | |
end | |
def self.>(other) | |
Be.new(other, :">") | |
end | |
def self.>=(other) | |
Be.new(other, :">=") | |
end | |
def initialize(@expected : T, @op) | |
end | |
def match(value) | |
@target = value | |
case @op | |
when :"<" | |
value < @expected | |
when :"<=" | |
value <= @expected | |
when :">" | |
value > @expected | |
when :">=" | |
value >= @expected | |
else | |
false | |
end | |
end | |
def failure_message | |
"expected #{@target} to be #{@op} #{@expected}" | |
end | |
def negative_failure_message | |
"expected #{@target} not to be #{@op} #{@expected}" | |
end | |
end | |
class MatchExpectation(T) | |
def initialize(@value : T) | |
end | |
def match(value) | |
@target = value | |
@target =~ @value | |
end | |
def failure_message | |
"expected: #{@target.inspect}\nto match: #{@value.inspect}" | |
end | |
def negative_failure_message | |
"expected: value #{@target.inspect}\n to not match: #{@value.inspect}" | |
end | |
end | |
end | |
def eq(value) | |
Spec::EqualExpectation.new value | |
end | |
def be(value) | |
Spec::BeExpectation.new value | |
end | |
def be_true | |
eq true | |
end | |
def be_false | |
eq false | |
end | |
def be_truthy | |
Spec::BeTruthyExpectation.new | |
end | |
def be_falsey | |
Spec::BeFalseyExpectation.new | |
end | |
def be_nil | |
eq nil | |
end | |
def be_close(expected, delta) | |
Spec::CloseExpectation.new(expected, delta) | |
end | |
def be | |
Spec::Be | |
end | |
def match(value) | |
Spec::MatchExpectation.new(value) | |
end | |
macro be_a(type) | |
Spec::BeAExpectation({{type}}).new | |
end | |
ifdef windows | |
macro expect_raises(*args) | |
end | |
else | |
macro expect_raises | |
raised = false | |
begin | |
{{yield}} | |
rescue | |
raised = true | |
end | |
fail "expected to raise" unless raised | |
end | |
macro expect_raises(klass) | |
begin | |
{{yield}} | |
fail "expected to raise {{klass.id}}" | |
rescue {{klass.id}} | |
end | |
end | |
macro expect_raises(klass, message) | |
begin | |
{{yield}} | |
fail "expected to raise {{klass.id}}" | |
rescue _ex_ : {{klass.id}} | |
_msg_ = {{message}} | |
_ex_to_s_ = _ex_.to_s | |
case _msg_ | |
when Regex | |
unless (_ex_to_s_ =~ _msg_) | |
fail "expected {{klass.id}}'s message to match #{_msg_}, but was #{_ex_to_s_.inspect}" | |
end | |
when String | |
unless _ex_to_s_.includes?(_msg_) | |
fail "expected {{klass.id}}'s message to include #{_msg_.inspect}, but was #{_ex_to_s_.inspect}" | |
end | |
end | |
end | |
end | |
end | |
class Object | |
def should(expectation, file = __FILE__, line = __LINE__) | |
unless expectation.match self | |
fail(expectation.failure_message, file, line) | |
end | |
end | |
def should_not(expectation, file = __FILE__, line = __LINE__) | |
if expectation.match self | |
fail(expectation.negative_failure_message, file, line) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment