Skip to content

Instantly share code, notes, and snippets.

@asterite
Created March 25, 2015 21:32
Show Gist options
  • Save asterite/00d485071980f302daca to your computer and use it in GitHub Desktop.
Save asterite/00d485071980f302daca to your computer and use it in GitHub Desktop.
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
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
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