Skip to content

Instantly share code, notes, and snippets.

@creadone
Last active August 19, 2019 17:42
Show Gist options
  • Save creadone/f7a153550c587f0b206c75073cadd904 to your computer and use it in GitHub Desktop.
Save creadone/f7a153550c587f0b206c75073cadd904 to your computer and use it in GitHub Desktop.
Useful snippets for Crystal language
# Method alias
# ------------
macro alias_method(new_name, existing_method)
def {{new_name.id}}(*args)
{{existing_method.id}}(*args)
end
end
# Generate Union type from array of classes
# -----------------------------------------
class Test1; end
class Test2; end
class Test3; end
FULL = [
Test1,
Test2,
Test3
]
LITE = [
Test1,
Test2
]
macro finished
alias FullStack = {{ FULL.join(" | ").id }}
alias LiteStack = {{ LITE.join(" | ").id }}
end
p! FullStack # => (Test1 | Test2 | Test3)
p! LiteStack # => (Test1 | Test2)
# Simple memoization (not only for Crystal, Ruby too)
# ---------------------------------------------------
class HeavyWorker
@@time = Time.now
def time
return @@time if @@time
@@time = Time.now
end
def drop_cache!
@@time = nil
end
end
puts HeavyWorker.new.time
sleep 2
puts HeavyWorker.new.time
sleep 1
puts HeavyWorker.new.drop_cache!
sleep 1
puts HeavyWorker.new.time
# User-defined annotations
# ------------------------
annotation Custom; end
class Object
macro inherited
macro method_added(method)
\{% if method.annotation(Custom) %}
\{% pp method.name.id %}
\{% pp method.annotation(Custom)[:x] %}
\{% end %}
end
end
end
class CustomAnnotationTest
@[Custom]
def first_method(x : String)
x
end
@[Custom(x: 2)]
def second_method(x : Int32)
x
end
end
# Any type in macro
# -----------------
def foo(bar : T) forall T
{% pp! T %}
end
foo(42)
def foo(**values : **T) forall T
{% pp! T %}
end
foo(bar: 42)
# Debug class methods with annotations
# ------------------------------------
require "logger"
LOGGER = Logger.new(STDOUT)
annotation Explain; end
class Object
macro inherited
macro method_added(method)
\{% if method.annotation(Explain) %}
def \{{method.name.id}}(\{{method.args.splat}})
LOGGER.\{{method.annotation(Explain)[:severity].id}}("Method \{{method.name.id.symbolize}} defined on line \{{ method.line_number }} invoked with args \{{method.args.splat}} and return #{previous_def}")
return previous_def
end
\{% end %}
end
end
end
class DebugWithAnnotations
@[Explain(severity: "info")]
def first_method(x : Int32)
x * x
end
@[Explain(severity: "fatal")]
def second_method(x : Int32)
x + 2
end
end
DebugWithAnnotations.new.first_method(1)
DebugWithAnnotations.new.second_method(5)
# link and use own C library
# --------------------------
# file: prog.c
#include <stdio.h>
void say(const char * name){
printf("Back my many %s!\n", name);
}
# compile lib: gcc -c prog.c -o prog.o
# file: prog.cr
@[Link(ldflags: "#{__DIR__}/prog.o")]
lib LibProg
fun say(name : LibC::Char*) : Void
end
LibProg.say("now")
# build crystal release: crystal build prog.cr --release, then run ./prog
# Build chain
# --------------------------
class Transforma
property hash : String?
def build
yield self
end
def add(bla : String)
self.hash = bla
end
def show_me
p! hash
end
end
Transforma.new.build do |t|
t.add "some string"
t.show_me
end
# Convert parsed data to hash
# ------------------------------
require "json"
class Parser
include JSON::Serializable
@[JSON::Field(key: "event_date")]
property event_date : String
@[JSON::Field(key: "event_name")]
property event_name : String
macro finished
def to_hash
{% parser_methods = @type.methods.map(&.name).select { |m| !m.includes?("=") }.map(&.stringify) %}
{
{% for parser_method in parser_methods %}
{{ parser_method.id.stringify }} => {{ parser_method.id }},
{% end %}
}
end
end
end
json_string = "{\"event_date\":\"2019-01-01\", \"event_name\":\"user:click:welcome_button\"}"
result = Parser.from_json(json_string)
p result.to_hash
# => {"event_date" => "2019-01-01", "event_name" => "user:click:welcome_button"}
# Instance variable on object level (by @vladfaust)
# ---------------------------------
class User
property id : Int32?
property name : Int32?
macro finished
{% ivars = @type.methods.select { |_def| _def.body.is_a?(InstanceVar) }.map(&.body) %}
{% pp ivars %}
end
end
# Change Instanse self from outside modifier
# ------------------------------------------
class Exp
property bla : String = "bla"
def external_modifier
yield self
end
end
exp = Exp.new
p exp.bla # => "bla"
exp.external_modifier do |instance|
instance.bla = "blo"
end
p exp.bla # => "blo"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment