This notebook is a work-in-progress. As it stands, it's a collection of my jots, notes and bookmarks about Crystal programming language.
Once complete, I intend to edit the entire notebook so that everything is accessible and useful to readers beside myself.
Printing
Let’s start with a brief word about printing values. Throughout the notebook, we’ll be printing values out to demonstrate what a certain piece of code does and there a few different ways we can do this:
puts1 writes toSTDOUTand adds a newline character unless we are printing aStringthat ends with a newline.p2 writes toSTDOUTusinginspect.pp3 writes toSTDOUTusinginspectandprettyprint.p!4 writes the value and the expression toSTDOUT.
See printing in action:
name = "John"
puts name # => John
p name # => "John"
pp name # => "John"
p! name # => name # => "John"
Another useful method is the typeof method which returns the type of the object we pass to it.
We’ll use this method in our print calls to see the type we’re working with like so:
name = "John"
p! name, typeof(name)
#OUTPUT
name # => "John"
typeof(name) # => String
Variables
You can declare and assign value to variables with the following syntax:
# Assign variables:
first_name = "John"
last_name = "Doe"
age = 42
driver_license_type = 'A'
# Print values:
p first_name # => "John"
p last_name # => "Doe"
p age # => 42
p driver_license_type # => 'A'
The types of the variables are inferred by the compiler:
first_name = "John"
last_name = "Doe"
age = 42
driver_license_type = 'A'
# Print types:
p typeof(first_name) # => String
p typeof(last_name) # => String
p typeof(age) # => Int32
p typeof(driver_license_type) # => Char
It's possible to be explicit about types:
## Type declaration and assignment:
first_name : String = "John"
## Type declaration:
last_name : String
## Assignment
last_name = "Doe"
p first_name # => "John"
p last_name # => "Doe"
We can use the multiple assignment syntax to declare more than one variable in one statement or to reassign values:
x, y = 20, 30
p x # => 20
p y # => 30
x, y = y, x
p x # => 30
p y # => 20
It's also possible to have multiple statements on the same line by using a semicolon in between statements:
first_name = "John"; last_name = "Doe";
Constants
Constant names start with uppercase letters, though as per convention you should keep all characters uppercase.
PHI = 1.61803
# PHI += 42 # => Cannot reassign to constant.
You can declare constants at the top level or inside other types, however the value of a constant cannot be mutated.
Arithmetic Operations
Let’s have a look at some basic arithmetic operations we can perform:
add = 40 + 2
subtract = 102 - 60
multiply = 14 * 3
divide = 672 / 16 # Result type: Float64
divide_int = 9 // 4 # Result type: Int32 (Rounded down)
power = 2 ** 10
mod = 42 % 24
p! add, subtract,multiply, divide
p! divide_int, power, mod
#OUTPUT:
add # => 42
subtract # => 42
multiply # => 42
divide # => 42.0
divide_int # => 2
power # => 1024
mod # => 18
It’s important to note that it’s possible to mix floats and integers. Have a look at the following result types:
result_one = 672.5 / 16
result_two = 672 / 16
result_three = 21 * 2.0
p! result_one, typeof(result_one)
p! result_two, typeof(result_two)
p! result_three, typeof(result_three)
#OUTPUT:
result_one # => 42.03125
typeof(result_one) # => Float64
result_two # => 42.0
typeof(result_two) # => Float64
result_three # => 42.0
typeof(result_three) # => Float64
Control Flow
Here's a simple if statement:
age = 21
if age < 21
puts "You cannot purchase that item."
end
You can chain operators to check within a range:
x = 3
if 2 < x < 5
puts x
end
# => 3
You can define the previous example with logical operators:
x = 3
if 2 < x && x < 5
puts x
end
# = > 3
For more on chaining operators, take a look at the docs.
More complex statement with if/elseif/else:
x = 4
if x < 5
puts "smaller than 5"
elsif x < 8
puts "bigger than 5, smaller than 8"
else
puts "bigger than 8"
end
# => "smaller than 5"
You can assign an if statement to a variable:
x = 7
result = if x < 5
"smaller than 5"
elsif x < 8
"bigger than 5, smaller than 8"
else
"bigger than 8"
end
p result # => "bigger than 5, smaller than 8"
You can use an if statement as a suffix:
x = 3
output = "smaller than 5" if x < 5
p output # => "smaller than 5"
You can use an unless statement for the inverse:
x = 3
output = "smaller than 5" unless x > 5
p output # => "smaller than 5"
Case/When Statements
Use case/when statements for longer and complex expressions where you test the same value against different conditions:
x = 1
output = case x
when 3
"x equals 3"
when 5
"x equals 5"
when 9
"x equals 9"
else
"x is not 3/5/9"
end
p output # => "x is not 3/5/9"
An example of a case/when statement with comparisons:
x = 6
output = case
when x <= 5
"5 or smaller"
when 5 < x < 8
"bigger than 5, smaller than 8"
else
"bigger than 8"
end
p output # => bigger than 5, smaller than 8
Ternary Operator
Crystal has a ternary operator for concise if statements:
user_age = 18
registered = false
output = user_age >= 18 && registered ? "User can vote" : "User cannot vote"
p output # => User cannot vote
Loops
Loop n times
Take a look at the following syntax examples of running a block of
codes n times.
Using times method on the Int type:
5.times do
p "hi!"
end
Using a block in curly brackets:
t.times {
p "hi!"
}
Iterating over Array and Range types
Example 1:
friends = ["John", "Martha", "Paige", "Cooper", "Herbert"]
friends.each do |friend|
puts friend # => John, Martha, Paige, Cooper, Herbert
end
Example 2:
friends = ["John", "Martha", "Paige", "Cooper", "Herbert"]
(1...3).each do |friend|
puts friend # => Martha, Paige
end
Using loop do
You can express a custom logic with loop do:
x = 1
loop do
puts "x = #{x}"
x += 1
break if x == 3
end
Using a classic while loop
x = 1
while (x += 1) < 10
p j # => 2, 3, 4, 5, 6, 7, 8, 9
end
Within loop constructs, you can use next and break statements:
x = 1
while (x += 1) < 10
if x == 3
next # jump to next iteration
elsif j > 6
break # break loop here
end
puts x # => 2, 4, 5, 6
end
Use unless for the inverse
x = 5
until (z -= 1) == 0
p z # => 4, 3, 2, 1
end
Methods
Methods (functions) are an important building block. Some methods are available on
the top-level scope, few common examples are typeof, puts, gets. Whereas other
methods are available on certain objects, such as the size method which is available
on the String object.
A simple method in Crystal looks like this:
def double(num)
num * 2
end
p double 6 # => 12
Note that, methods return their last expression implicitly. Though you can use return
keyword explicitly.
What happens if we call our double method on a String?
p double "66" # => "6666"
Because the multiplication operator works on the String type as well, we get "6666" as a result.
That's where static typing and explicitly restricting types come into play:
def triple(num: Int32)
num * 3
end
p triple 2 # => 6
p triple "2" # => Error: No overload matches triple with type string
It's possible to be flexible and overload a method to accept another type parameter:
def triple(str : String)
str * 3
p "You just tripled a string. Weird."
end
Default values
You can define default values for parameters of your methods:
def random_number(base, max =10)
base + rand(0..max)
end
p random_number 5 # => Random number between 5 to 15
p random_number 5, 5 # => Random number between 5 to 10
Note that the parameter that has a default value has become an optional argument for the caller site.
Named parameters
You can explicitly use named parameters on the caller site:
p random_number 5, max: 5
p random_number base: 5, max: 5
It is also possible to force the use of named parameters on the caller site. In the following example, all parameters on the right side of * will have to be named parameters:
def greeter(*, first_name, last_name, emphatic = false)
if emphatic
p "Greetings #{first_name} #{last_name}!!!!"
else
p "Greetings #{first_name} #{last_name}."
end
end
greeter first_name: "John", last_name: "Doe" # => Greetings, John Doe
greeter first_name: "John", last_name: "Doe", emphatic: true # => Greetings, John Doe!!!!
Naming parameters externally/internally
See the following example:
def multiply(value, *, by num)
value * num
end
p multiply(3, by: 5)
Let's inspect each parameter of the method multiply:
valueis a regular positional parameter, caller provides a value.*indicates all the following parameters will have to be called by their name.by numrepresent the same parameter,byis inteded for the external use,numis intended for internal use.
Passing blocks to methods
You can pass blocks of code to a method, and these blocks can be invoked within the
method with the use of the yield keyword.
See the example:
def perform_op
p "We're in the method perform_op"
yield
p "We've executed the passed code block"
yield
p "We've executed the passed code block again"
p "Leaving perform_op method scope"
end
# You can pass a code block in two ways:
perform_op do
puts "this is the passed code block"
end
perform_op {
puts "this is the passed code block"
}
Passing blocks with context (arguments and return values)
def perform_upcase(str)
str = str.upcase
yield str
end
def perform_exclaim(str)
str += "!!!!!"
end
sample = "hello john"
result = perform_upcase sample do |s|
perform_exclaim s
end
Using next keyword within a passed code block
You can use the next keyword to stop the execution of the passed
code block, and return a value to the yield statement that invoked it.
If a value is passed to the next keyword, yield will receive it.
def greeting_generator
person_one = yield "John"
person_two = yield "Jane"
person_three = yield "Paige"
"#{person_one}, #{person_two}, #{person_three}"
end
# Let's pass a code block to get a different type of greeting for John
result = greeting_generator do |prs|
if prs == "John"
next "Greetings #{prs}"
end
"Hi #{prs}"
end
p result # => Greetings John, Hi Jane, Hi Paige
Using the break keyword within a passed code block
You can use the break keyword to stop the method that is invoking
the inner block, similar to encountering a return.
def greeting_generator
person_one = yield "John"
person_two = yield "Jane"
person_three = yield "Paige"
"#{person_one} , #{person_two}, #{person_three}"
end
result = greeting_generator do |prs|
if prs == "John"
# This will be returned on break, and the inner method will stop.
break "Greetings #{prs}"
end
"Hi #{prs}"
end
p result # => Greetings John
Using a return statement within a passed code block
The return statement will stop the execution of the passed code block similar to break,
however it will also return from the method where the passed code block was written.
Tip: Return always finalizes the execution of the method that it resides in.
See the following example:
def greeting_generator
person_one = yield "John"
person_two = yield "Jane"
person_three = yield "Paige"
"#{person_one} , #{person_two}, #{person_three}"
end
def run
greeting_generator do |prs|
if prs == "John"
return "Greetings #{prs}" # => The method `run` will also exit at this point.
end
"Hi #{prs}"
end
# -> if there was a statement here, this would not be executed due to return.
end
p run # => "Greetings John"
Using Splat parameters
It's possible to define a method that accepts an arbitrary number of
arguments by prefixing the argument with the * symbol:
def greet(*people)
p "We will greet #{people}" # => We will greet {{\"John\", \"Jane\", \"Paige\", \"Herbert\"}}"
p typeof(people) # => Tuple(String, String, String, String)
people.each do |prs|
p "Hi, #{prs}!"
end
end
greet("John", "Jane", "Paige", "Herbert")
# =>
# "Hi, John"
# "Hi, Jane"
# "Hi, Paige"
# "Hi, Herbert"
As you can see in the type check of the splat parameters, it's a tuple. Splat parameters are alwoys referred to as a Tuple of zero or more arguments.
Types
Boolean
The boolean type can have the value of either true or false:
t = true
f = false
p! t, typeof(t)
p! f, typeof(f)
#OUTPUT:
t # => true
typeof(t) # => Bool
f # => false
typeof(f) # => Bool
Nil
nil represents a lack of value. It’s type is Nil:
val = nil
p! val, typeof(val)
#OUTPUT:
val # => nil
typeof(val) # => Nil
Compound Types
Arrays
Initializing Arrays
You can initialize arrays in Crystal as such:
friends = [] of String
peers = Array(String).new
Note that you cannot use initialize empty array with the following syntax:
friends = [] # => Error: for empty arrays use [] of ElementType
We can however use the following syntax to create an array whose type will be inferred by the compiler:
friends = ["John", "Edward", "Ariel"]
p typeof(friends) # => Array(String)
peers = %w(Bill Marucio)
p typeof(peers) # => Array(String)
It's also possible to create Arrays of a union type. See what the compiler infers:
items = ["John", 'C', 42]
p typeof(items) # => Array(Char|Int32|String)
When union types are used, the compiler will check if all the methods that are called on these types exist or not.
Adding Items
Let's add some items of the String type to the arrays:
friends << "Jane"
peers << "Brian"
You cannot add another type to an array of strings:
friends << "42" # => Error
Accessing Items
You can access elements via indexing. You can also use ranges:
friends = ["John", "Edward", "Ariel", "Alex", "Chael"]
p friends[0] # => "John"
# A sub-array using ranges:
p friends[1, 3] # => ["Edward", "Ariel", "Alex"]
p friends[1..3] # => ["Edward", "Ariel", "Alex"] (1 to 3, 3 inclusive)
p friends[1...3] # => ["Edward", "Ariel"] (1 to 3, 3 exclusive)
It's a good idea to check if a certain index is within bounds, otherwise
you can get a runtime error. If you use the []? syntax, you'll get a nil
instead of a runtime error.
friends = ["John", "Edward", "Ariel", "Alex", "Chael"]
p friends[5] # => Exception: Index out of bounds
# Making sure index 5 exists:
p friends[5]? # => nil
Checking if an item exists in array by it's value:
friends = ["John", "Edward", "Ariel", "Alex", "Chael"]
p friends.includes? "John" # => true
p friends.includes? "Bob" # => false
Removing items
You can use shift and pop methods to remove items at the
first and the last indices.
friends = ["John", "Edward", "Ariel", "Alex", "Chael"]
p friends.shift # => "John"
p friends.pop # => "Chael"
puts friends # => ["Edward", "Ariel", "Alex"]
You can also use the delete method
friends = ["John", "Edward", "Ariel", "Alex", "Chael"]
friends.delete "John"
puts friends # => ["Edward", "Ariel", "Alex", "Chael"]
Iteration
You can iterate over the elements with the each do block:
friends = ["John", "Edward", "Ariel", "Alex", "Chael"]
friends.each do |i|
puts i
end
Recipes to display contents of array
display = [1, 2, 3, 4, 5, 6, 7, 8, 'A', "hello"]
print display # => [1, 2, 3, 4, 5, 6, 7, 'A', "hello"] (no newline)
puts display # => [1, 2, 3, 4, 5, 6, 7, 'A', "hello"] (with newline)
pp display # => [1, 2, 3, 4, 5, 6, 7, 'A', "hello"] (with newline)
p display.inspect # =>"[1, 2, 3, 4, 5, 6, 7, 8, 'A', \"hello\"]" (with newline)
printf("%s", display[1]) # => 2
p sprintf("%d", display[1]) # => "2"
Other methods
Crystal offers us different ways to manipulate data stored in Arrays, the complete list of these methods are in Crystal API docs.
Hashes
Hashes are the ideal container type for dealing with key/value pairs.
Creating a hash is simple. In the following example, we'll be creating a Hash with the type Hash(String, Int32)
friends_age = {
"John" => 42,
"Jane" => 31,
"George" => 50,
"Max" => 28,
}
p typeof(friends_age) # => Hash(String, Int32)
Accessing values
You can read a value using keys:
p friends_age["Jane"] # => 31
Trying to acces a value which doesn't exist will result in an exception:
p friends_age["Patrick"] # => Error: Unhandled exception, missing hash key "Patrick"
Therefore, we need to handle the outcome where a key might not exist with ?:
p friends_age["Jane"]? # => 31
p friends_age["Patrick"]? # => nil
Another way to handle this would be to use the predicate function has_key?
p friends_age.has_key? "Jane" # => true
p friends_age.has_key? "Patrick" # => false
Updating Values
We can update a value quite simply:
friends["John"] = 43
Creating Hashes
Just like arrays, declaring an empty hash with no values and no type information is not possible:
my_hash = {} # => Error: for empty hashes use {} of KeyType => ValueType
Instead we need to provide the type information:
my_hash = {} of String => Int32
my_hash_two = Hash(String, Int32).new
If you want to check if the hash is empty, you can use the empty? predicate:
p my_hash.empty? # => false
Examples of Different Syntax
There are different ways to declare key value pairs, have a look at the following declarations and their inferred types:
hash_a = {"a" => 1, "b" => 2, "c" => 3} # => Hash(String, Int32)
hash_b = {:a => 1, :b => 2, :c => 3} # => Hash(Symbol, Int32)
hash_b2 = {a: 1, b: 2, c: 3} # => NamedTuple(a: Int32, b: Int32, c: Int32)
hash_c = {"a": 1, "b": 2, "c": 3} # => NamedTuple(a: Int32, b: Int32, c: Int32)
If a hash uses symbols as keys, you can access them like so:
fruits = {:watermelon => 4.99, :banana => 2.00}
p fruits[:watermelon] # => 4.99
p "Keys: #{fruits.keys} Values: #{fruits.values}" # => "Key: [:watermelon, :banana] Value: [4.99, 2.0]"
Tuples
Tuples are immutable lists, and all the types of the elements are known at compile time.
See:
a_tuple = {42, 84, "John", 'C'}
p typeof(a_tuple) # => Tuple(Int32, Int32, String, Char)
Ranges
The range type can be used for representing an interval of values. The intervals could be last item inclusive or last item exclusive:
inclusive = 2..7
exclusive = 2...7
Examples
A range can be many different things, alphabet, time intervals etc. Here's the alphabet example:
a_to_z = 'a'..'z' # => All letters between A to Z
aa_to_zz = "aa".."zz" # => All two letter combinations between A to Z
Enumerable and Iterable
The range type implements Enurable and Iterable. These implementations provide useful methods that will let us treat ranges as data collections:
inclusive = 2..7
exclusive = 2...7
# Predicate methods:
p inclusive.includes? 3 # => true
p inclusive.covers? 7 # => true
p 3.in? inclusive # => true
# Method for summing all the values:
p inclusive.sum # => 27
# A random element within the range:
p inclusive.sample # => 4
Object-Oriented Programming
Classes and Objects
Object-Oriented programming in Crystal is very similar to other OOP languages.
- Objects are associated with data and behaviour defined by instance variables and methods.
- Classes are blueprints that the objects are created from.
In Crystal, everything is an object and every object is an instance of some class.
You can get the class of an object by using .class method. See below:
p 'C'.class # => Char : Class
p "John".class # => String : Class
p false.class # => Bool : Class
p nil.class # => Nil : Class
p ["John", 42].class # => Array(String | Int32) : Class
All the primitives being objects comes with the benefit of having
useful methods attached to them. One common example would be the
size method that's defined on the String class.
Creating Classes and Objects
In Crystal, we use CamelCase to name our classes. When you create a class you are also defining a new type.
# Creating a class:
class Bird
end
# Creating an object:
b = Bird.new
p typeof(b) # => Bird
Using A Constructor
The initialize method is used as a constructor:
class Dog
def initialize(name : String, age : Int64)
@name = name
@age = age
end
end
rex = Dog.new "Rex", 2
p typeof(rex) # => Dog
You can use a shorter syntax for instance variables. By prefixing the
constructor parameters with @, we can make the compiler create instance
variables of the same name automatically:
class Dog
def initialize(@name : String, @age : Int64)
end
end
Reading & Updating Instance Variables
We can use getters and setters to enable read/write behaviour on our instance variables. Both of these are macros that save us from the boilerplate of writing manual getters and setters.
class Bird
getter name : String
getter age : Int32
setter age
def initialize(name, age)
@name = name
@age = age
end
def describe
"This bird's name is #{name} and it is #{age} years old"
end
def to_string
self.to_s
end
end
jax = Bird.new "Jax", 3
p jax.name # => Jax
p jax.age # => 3
jax.name = "John" # => We cannot mutate this value, because there's no setter defined
jax.age += 1
p jax.age # => 4
# Instance methods that are accessible from outside:
p jax.describe # => This bird's name is Jax and it is 4 years old.
p jax.to_string # => "#<Bird:0x41412f>"
There's one more shorthand for situations where we use the macros getter
and setter. Instead of using these two, we could use property. This will
make it so that the instance variable will have both a getter and a setter:
class Elephant
getter name : String
property age : Int32
def initialize(name, age)
@name = name
@age = age
end
end
e = Elephant.new "Marcus", 19
p e.name # => "Marcus"
p e.age # => 19
e.age += 1
p e.age # => 20
A Few Useful Methods
Take a look at the methods below:
class Person
end
p1 = Person.new
p2 = Person.new
p p1 # => #<Person:0x4342>
p p1.to_s # => "#<Person:0x4342>"
p p1 == p2 # => false # This compares the reference in memory
p p1.same? p2 # => false # Same as above
p p1.nil? # => false
p p1.is_a? Person # => true # p1 is an instance of Person
Behind The Scenes For new And initialize
First let's create a class, notice the shorthand syntax compared to the examples before:
class Person
getter first_name, last_name, age
def initialize(@name : String, @last_name : String, age : Int32)
end
end
john = Person.new("John", "Doe", 42)
pp john # => #<Person:0x34234 ...>
pp john.object_id # => 4312425842
When new is called on class, we allocate memory for the class and then run initialize
automatically. After initialization, the object is created and placed on the heap.
Calling the object_id method on the object returns it's memory address. This reference
will be used to pass this object around.
Generic Types:
It's possible to define a class with a generic type to allow the initializer to accept different types. For this, use the following syntax:
class Person(T)
getter name
def initialize(@name : T)
end
end
It's possible to define a class with the generic T type, and initia
Variable prefixes: @ and @@
For instance variables use the prefix
@.
For class variables use the prefix
@@.
Calling Methods On The Class Itself
Use the
selfprefix.
Copying Objects:
Shallow Copy: Use the
dupmethod.
Shallow copy provides a copy that is different in memory but has the same fields as the original object.
Deep Copy: Use the
clonemethod.
Deep copy on the other hand is for cloning.
Have a look at the following example for how copying works:
class Person
property name
property age
def initialize(@name : String, @age : Int32)
end
def clone
self
end
end
john = Person.new("John", 42)
shallow_copy = john.dup
deep_copy = john.clone
pp john # => #<Person:0x7fd1d3604ea0 @age=42, @name="John">
pp john.object_id # => 140539171196576
pp shallow_copy # => #<Person:0x7fd1d3604900 @age=42, @name="John">
pp shallow_copy.object_id # => 140539171195136
pp deep_copy # => #<Person:0x7fd1d3604ea0 @age=42, @name="John">
pp deep_copy.object_id # => 140539171196576
pp john == shallow_copy # => false
pp john.class == shallow_copy.class # => true
pp john == deep_copy # => true
pp john.class == shallow_copy.class # => true
The finalize Method
The finalize method is essentially a hook for the garbage collector, it will be run when the object is GC'ed.
Inheritance
Inheritance is an important piece of OOP patterns. In simple terms, a class inheriting from another class means it'll get the parent classes methods, instance variables and it'll be able to add/overwrite existing ones.
Here's a basic example of inheritance:
class Animal
property name : String
property age : Int32
def initialize(@name, @age)
end
end
class Dog < Animal
property domesticated = true
property dexterity = 10
end
tex = Animal.new "Tex", 12
rex = Dog.new "Rex", 3
rex.domesticated = true
rex.dexterity = 8
# You can see below that the child class satisfies
# both the Parent and Child when is_a method is raised:
p tex.is_a? Animal # => true
p tex.is_a? Dog # => false
p rex.is_a? Animal # => true
p rex.is_a? Dog # => true
Inheritance Continued: Overriding
Overriding works very much the same way as it works with other OOP languages.
Basic example:
class Person
property name : String
def initialize(@name)
end
end
class Employee < Person
property salary = 0
end
tt = Employee.new "TT"
Reopening A Class
You might want to reopen a class for different reasons, Crystal allows this and the additions you make with the reopened class will be combined into a single class.
Have a look at the following example:
class Employee < Person
def yearly_salary
12 * @salary
end
end
class SalesEmployee < Employee
property bonus = 0
def yearly_salary
12 * @salary + @bonus
end
end
john = Person.new "John"
jane = Employee.new "Jane"
rich = SalesEmployee.new "Rich"
jane.salary = 2000
p jane.yearly_salary # => 24000
rich.salary = 2000
rich.bonus = 1000
p rich.yearly_salary # => 25000
This example can be further enhanced by the usage of the
super method where by calling super we get to use
the super class functionality:
class HrEmployee < Employee
property bonus = 0
def yearly_salary
super + @bonus
end
end
herbert = HrEmployee.new "Herbert"
herbert.salary = 2000
herbert.bonus = 2000
p herbert.yearly_salary # => 26000
Abstract Classes
A great use for abstract classes are where you want to have a class but don't want to create objects based on the class. By defining an abstract class we can create a blueprint for subclasses.
Following example demonstrates inheriting from an abstract class but having different constructors:
abstract class Shape
end
# Circle inherits from Shape
class Circle < Shape
def initialize(@radius : Float64)
end
end
class Rectangle < Shape
def initialize(@width : Float64, @height : Float64)
end
end
crc = Circle.new(4)
rec = Rectangle.new(2, 4)
The following example is a more common pattern where we define an abstract method on the abstract class, thus making sure that the classes which implement from the abstract, implement the abstract methods. If they don't, you'll get a compilation error:
abstract class Shape
abstract def area : Number
end
# Circle inherits from Shape
class Circle < Shape
def initialize(@radius : Float64)
end
def area : Number
MATH::PI * radius * 2
end
end
crc = Circle.new(12)
p crc.area # => 323......
# The following class won't compile because it doesn't implement
# area method:
class Rectangle < Shape
def initialize(@width : Float64, @height : Float64)
end
end
Absract Classes
A great use for abstract classes are where you want to have a class but don't want to create objects based on the class. By defining an abstract class we can create a blueprint for subclasses.
Following example demonstrates inheriting from an abstract class but having different constructors:
abstract class Shape
end
# Circle inherits from Shape
class Circle < Shape
def initialize(@radius : Float64)
end
end
class Rectangle < Shape
def initialize(@width : Float64, @height : Float64)
end
end
crc = Circle.new(4)
rec = Rectangle.new(2, 4)
The following example is a more common pattern where we define an abstract method on the abstract class, thus making sure that the classes which implement from the abstract, implement the abstract methods. If they don't, you'll get a compilation error:
abstract class Shape
abstract def area : Number
end
# Circle inherits from Shape
class Circle < Shape
def initialize(@radius : Float64)
end
def area : Number
MATH::PI * radius * 2
end
end
crc = Circle.new(12)
p crc.area # => 323......
# The following class won't compile because it doesn't implement
# area method:
class Rectangle < Shape
def initialize(@width : Float64, @height : Float64)
end
end
Visibility
It's possible to restrict visibility and encapsulate code in the OOP tools Crystal provide.
The default behavior of an object is that it's visible under the namespace
it's defined, but not to the outside. Unless you restrict the visibility
of a method, it's public by default. You can use private or protected
keywords to restrict this visibility.
An example of using a private method:
class Person
def initialize(@name : String)
end
private def greet(message : String)
p message
end
def greeter
greet "Hi, #{@name}"
end
end
class Employee < Person
def greeter
super
p "Welcome to the office."
end
end
p = Person.new("John")
p.greeter # => "Hi, John"
e = Employee.new("Mike")
e.greeter # => "Hi, Mike" "Welcome to the office"
An example of using a protected method:
class Person
def initialize(@name : String)
end
protected def greet(message : String)
p message
end
def greeter
greet "Hi, #{@name}"
self.greet "Hi, #{@name}"
end
end
class Employee < Person
def greeter
person = Person.new("Jan")
person.greet("Hi from the employee class") # => Works, because Employee is a person
end
end
p = Person.new("John")
p.greeter # => "Hi, John"
e = Employee.new("Mike")
e.greeter # => "Hi, Mike" "Welcome to the office"
class Machine
def greeter
emp = Employee.new("David")
emp.greet("Hi from the machine") # => Error: protected method 'greet' called for Person
end
end
Modules
Modules are great for grouping methods and classes that are related under a namespace. You can extend modules with a class, and the methods of module will be available on the class that extends it.
module Stats
def data
{
"strength" => 10,
"dexterity" => 5,
"intelligence" => 3,
}
end
def stats
data[self.stat_name]
end
end
class CharacterStats
# Include Stats as a mixin:
include Stats
getter stat_name : String
def initialize(@stat_name)
end
end
npc = CharacterStats.new "dexterity"
p npc.stats # => 5
Working with nilable types
Since Crystal supports union types, it’s possible to have methods whose returns are nilable.
Here’s a method with a return signature of (String | Nil) or in short String?.
def maybe_string : String?
condition = true
if condition
"A string..."
else
nil
end
end
And say we want to upcase the returned String:
val = maybe_string
p! val.upcase # => Error: undefined method 'upcase' for Nil (compile-time type is (String | Nil))
The result would be an error because the compiler wouldn’t be able to guarantee that the return type is a String and
not Nil. If the return type is in fact Nil, then it would not have an upcase method available.
To avoid this error, we can check for nil with the tools Crystal provides. Here’s at least three different ways we could do this:
def maybe_string : String?
condition = true
if condition
"A string..."
else
nil
end
end
val = maybe_string
# Method one - Check if `val` is truthy:
p! val.upcase if val
# Method two - Check if `val` is nil:
p! val.upcase unless val.nil?
# Method three - Check if `val` is nil by usinkg `try`:
p! val.try &.upcase
#OUTPUT:
val.upcase # => "A STRING..."
val.upcase # => "A STRING..."
val.try(&.upcase) # => "A STRING..."
We managed to call upcase in all three cases because maybe_string actually returned String and we satisfied the
compiler by branching in a way that our code was called only when maybe_string returned a String.
- In the first case, we just checked if
valwas truthy, because it wouldn’t be truthy ifvalwasNil. - In the second case we used a method provided by the compiler,
.nil?1, this would returntrueifvalwasNil. - And finally on the third case, we’ve used another built-in method
try2.tryonly executes the passed code block if val is notNil.
Exception Handling
In Crystal, the keyword raise is used to raise exceptions. When raised, the exception
will unwind the call stack and it'll hopefully be caught by the programmer somewhere
higher up in the method chain. If it's not caught, the program will exit with an unhandled
exception error.
If you bubble up an exception with raise and catch it, the regular execution
of the program will continue.
Handling exceptions with begin - rescue blocks:
arr = [] of Int8
puts "Enter an Int8 value:"
while number = gets
number = number.strip
if number == "" || number == "stop"
break
end
# Catching exceptions in the begin block:
begin
arr << number.to_i8
rescue ex
# Reacting to the exception:
p ex.message
puts "The integer you've entered is bigger than Int8 capacity"
exit
else
# In case where everything is good:
p "Int8 added to the array."
p arr
ensure
# Ensure block executes no matter what:
p "Clean up with ensure block..."
end
end
It's also possible to use a shorter syntax alternative:
def add_i8(arr, number)
arr << number.to_i8
rescue
p ex.message
puts "The integer you've entered is bigger than Int8 capacity"
end
Shorter variant with the addition of ensure:
def add_i8(arr, number)
arr << number.to_i8
rescue
p ex.message
puts "The integer you've entered is bigger than Int8 capacity"
ensure
p "clean up happened."
end
A note on performance:
Using predicates like
to_i?orto_f?with if blocks is much faster and less resource intensive compared to exception handling because exception handling allocates more memory. If you choose to usebegin-rescueblocks, try to keep the amount of code betweenbeginandrescueminimal.
Idiomatic Patterns
An idiomatic pattern is to return nil from an inner function, and catch an outer
function which raises an exception when it detects the nil return. This exception
then can be caught by the caller site.
Here's an example:
# Private implementation
def say_hi(name : String) : String | Nil
name.size > 2 ? "Hi, #{name}" : nil
end
# Public implementation
def say_hi_public(name : String)
msg = say_hi name
unless msg
raise "say_hi error: Name too short"
else
msg
end
end
# Caller site
begin
p say_hi_public "Jo"
rescue error
p error
end
Let's rewrite the previous example using shorter syntax where possible:
# Private implementation
def say_hi(name : String) : String?
name.size > 2 ? "Hi, #{name}" : nil
end
# Public implementation:
def say_hi_public(name : String)
raise "say_hi error: Name too short" unless say_hi name
end
# Caller site:
def caller_site
say_hi_public("Jo") # <Exception:say_hi error: Name too short>
rescue ex
p ex
end
caller_site
Recursion
A common example of recursion is the factorial calculation. You can see that in action below:
def factorial(n)
n == 0 ? 1 : n * factorial(n - 1)
end
p factorial(6) # => 720
The same recursive function can be made type safe, and can include guards for negative integers:
def factorial(n : Int32) : Int32
if n < 0
raise "Error, n cannot be negative"
end
n == 0 ? 1 : n * factorial(n - 1)
end
begin
p factorial(6) # => 720
p factorial(-6) # => "Error, n cannot be negative"
rescue ex
p ex.message
end
As discussed in the exception handling section, the begin-rescue
call is rather expensive. For a better performance, you can just guard against the problem
input by a plain if statement and an exit:
def factorial(n : Int32) : Int32
if n < 0
p "Error, n cannot be negative!"
exit
end
n == 0 ? 1 : n * factorial(n - 1)
end
p factorial(6) # => 720
p factorial(-6) # => "Error, n cannot be negative"
Of course, most of the time the exit statement will be an overkill, so you can just simply
return from a problemlamitc input in a recursive function, preventing cases like infinite
recursion.
Crystal CLI
Crystal Play Command
You can run a local instance of Crystal playground to run code on the browser.
Run crystal play and visit localhost:8080 on your browser.
The Playground
In the playground, you can write your code on the left-hand side and the code will automatically (800ms delay by default) compile, producing line-by-line results and type information.
The Workbook
At the root of your project (where you ran crystal play) you can create a
directory named playground. All the Markdown and Crystal files in this
directory will be listed at localhost:8080/workbook. On this page, you'll
be able to interact with code snippets that are fenced as playground in
Markdown files. You can also view and interact with .cr files.
Notes:
- I had to install
libssl-devandlibz-devpackages forcrystal playto work. - Tagging code fences as "playground" instead of Crystal is not a great solution. There might be a workaround.
Recipes
Getting User Input
Getting user input from the terminal is super simple:
puts "Enter a number:"
num = gets
puts "You've entered #{num}"
Example: Add user input to an array
arr = [] of Int8
p "Enter a number between -128 to 127:"
num = gets
# Let's imagine the user entered 48
# The type of the variable num will be:
p typeof(num) # => Compile-time type: (String | Nil)
p num.class # => Run-time type: String
# Check to make sure num is not nil
if num
arr << num.to_i8
end
p arr
Multiple numbers:
arr = [] of Int8
p "Enter a number between -128 to 127 and hit enter:"
num = gets
while num = gets
arr << num.to_i8
end
p arr
A Basic Web Application
In this recipe, we’ll take a look at how we can get started with a simple web application with zero dependencies. The tools in the standard library covers most of the important parts of a web application other than a router. For that, we’ll have to write very rudimentary and naive router.
Let’s start with an ECR1 template and write a basic home page:
<!-- file: home.ecr -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Home</title>
</head>
<body>
<h1>Welcome to the Home page!</h1>
</body>
</html>
And an about page:
<!-- file: about.ecr -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>About</title>
</head>
<body>
<h1>About Us</h1>
<p>We use Crystal at work.</p>
</body>
</html>
Let’s define our handlers as classes that include HTTP::Handler2:
# file: handlers.cr
require "http/server"
# HomeHandler is the controller for the route:
# `GET: /`
class HomeHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
context.response.content_type = "text/html"
context.response.status_code = 200
# Respond with an ECR template:
ECR.embed "home.ecr", context.response
end
end
# AboutHandler is the controller for the route:
# `GET: /about`
class AboutHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
context.response.content_type = "text/html"
context.response.status_code = 200
# Respond with an ECR template:
ECR.embed "about.ecr", context.response
end
end
# ErrorNotFoundHandler is the controller for the case:
# `Error: 404 Page Not Found`
class ErrorNotFoundHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
context.response.content_type = "text/html"
context.response.status_code = 404
# Respond with a plain string:
context.response.print "No such route as #{context.request.path}"
end
end
# Router is a simple router that matches a handler based
# on the request path.
class Router
include HTTP::Handler
def route(path : String) : HTTP::Handler
case path
when "/"
HomeHandler.new
when "/about"
AboutHandler.new
else
ErrorNotFoundHandler.new
end
end
def call(context : HTTP::Server::Context)
handler = route(context.request.path)
handler.call(context)
end
end
And finally, let’s create an array of handlers and add a few HTTP handlers from the standard library and our custom router and run the server:
# file: server.cr
require "http/server"
require "./handlers"
HOST = "127.0.0.1"
PORT = 3000
handlers = [] of HTTP::Handler
handlers << HTTP::LogHandler.new
handlers << HTTP::ErrorHandler.new
handlers << HTTP::CompressHandler.new
handlers << Router.new
server = HTTP::Server.new(handlers)
server.bind_tcp HOST, PORT
p "Server starting to listen on http://#{HOST}:#{PORT}"
server.listen
A tiny Makefile to run our example:
# file: Makefile
.PHONY: run
run:
crystal run server.cr
You can now run the project with make run and your server should be listening. We can then
try the following:
curl localhost:3000
#OUTPUT:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Home</title>
</head>
<body>
<h1>Welcome to the Home page!</h1>
</body>
</html>
curl localhost:3000/about
#OUTPUT:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>About</title>
</head>
<body>
<h1>About Us</h1>
<p>We use Crystal at work.</p>
</body>
</html>
curl localhost:3000/gibberish
#OUTPUT:
No such route as /gibberish
Ecosystem
Awesome Crystal
- Awesome Crystal
- Algorithms and Data structures
- Blockchain
- C Bindings
- Caching
- CLI Builders
- CLI Utils
- Code Analysis and Metrics
- Compression
- Configuration
- Converters
- Cryptography
- Data Formats
- Data Generators
- Database Drivers/Clients
- Database Tools
- Debugging
- Dependency Injection
- Environment Management
- Examples and funny stuff
- Framework Components
- Game Development
- GUI Development
- HTML Builders
- HTML/XML parsing
- HTTP
- Image Processing
- Implementations/Compilers
- Internationalization
- Logging and monitoring
- Machine Learning
- Markdown/Text Processors
- Misc
- Network Protocols
- Networking
- ORM/ODM Extensions
- Package Management
- Processes and Threads
- Project Generators
- Queues and Messaging
- Routing
- Scheduling
- Science and Data analysis
- Search
- Serverless Computing
- System
- Task management
- Template Engine
- Testing
- Third-party APIs
- Validation
- Web Frameworks
- Community
- Resources
- Services and Apps
- Tools
Algorithms and Data structures
- bisect - Inserting values into a sorted array
- blurhash.cr - BlurHash implementation
- crie - Compile-time Trie
- CrOTP - HOTP and TOTP implementation for two factor authentication
- crystal-linked-list - Implementation of Linked List
- crystaledge - A pure Vector Math library
- crystalg - A Generic Algorithm Library
- crystalline - A collection of containers and algorithms
- csuuid - A Chronologically Sortable UUID
- edits.cr - Collection of edit distance algorithms
- fzy - A Crystal port of awesome Fzy project fuzzy finder algorithm
- Goban - A fast and efficient QR Code implementation
- graphlb - Collection of graph datastructure and algorithms
- haversine - An Implementation of the Haversine formula
- kd_tree - An implementation of "K-Dimensional Tree" and "N-Nearest Neighbors"
- ksuid.cr - K-Sortable Globally Unique IDs
- markov - Build Markov Chains and run Markov Processes
- multiset.cr - Implementation of a multiset
- qr-code - QR Code generator
- radix - Radix Tree implementation
- s2_cells - S2 Geometry for spatial indexing
- secure-remote-password - SRP-6a protocol for authentication over an insecure network
- splay_tree_map - Splay Tree implementation that conforms to the Hash ducktype
Blockchain
- Axentro - A custom blockchain platform
- Cocol - A minimal blockchain testbed
- secp256k1.cr - Elliptic curve used in the public-private-key cryptography
C bindings
- augeas.cr - Bindings for Augeas
- clang.cr - Libclang bindings
- crt.cr - Bindings for libncursesw and crt
- crystal-gsl - Bindings for GNU Scientific Library
- crystal-hunspell - Bindings for Hunspell
- duktape.cr - Bindings for the Duktape javascript engine
- fftw.cr - Bindings for FFTW library
- gphoto2.cr - Bindings for the libgphoto2 library
- icu.cr - Bindings for the ICU library
- libnotify.cr - Bindings for Libnotify
- nlopt.cr - Bindings for NLOpt
- pcap.cr - Bindings for libpcap
- pledge.cr - Bindings for OpenBSD's
pledge(2) - ssh2.cr - Bindings for libssh2 library
- syslog.cr - Bindings for
syslog - v4l2.cr - Bindings for Video4Linux2
- wasmer-crystal - Bindings for the
wasmerWebAssembly runtime - win32cr - Bindings for Win32 API
- x_do.cr - Bindings for libxdo (
xdotool)
Caching
- crystal-memcached - Implementation of a memcached client
CLI Builders
- admiral - A robust DSL for writing command line interfaces
- Athena Console - Allows for the creation of CLI based commands
- clicr - A simple declarative command line interface builder
- clim - Slim command line interface builder
- Cling - A modular, non-macro-based command line interface library
- commander - Command-line interface builder
- Keimeno - A lightweight text user interface library in Crystal
- OptionParser - command-line options processing (Crystal stdlib)
- Phreak - A highly flexible Crystal CLI builder in the style of OptionParser
CLI Utils
- climate - Tiny tool to make your CLI output 🌈 coloured
- coin - Command-line application that performs currency conversion via the Fixer API
- cride - A light CLI text editor/IDE
- git-repository - A git cli wrapper querying and cloning remote repositories with minimal data transfer
- hetzner-k3s - A CLI tool to quickly create and manage Kubernetes clusters in Hetzner Cloud
- lff - Simple and straightforward large files finder utility in command line
- meet - Start a jitsi meeting quickly from the comfort of your command line
- oq - A performant, and portable jq wrapper to facilitate the consumption and output of formats other than JSON; using jq filters to transform the data
- progress_bar.cr - A simple and customizable progress bar
- tablo - A flexible terminal table generator
- tallboy - Generate ASCII character tables with support for spanning cells over multiple columns
Code Analysis and Metrics
- ameba - A static code analysis tool
- linguist.cr - Using multiple ways to find programming language used in files, based on Github's Linguist
Compression
- Crystar - Readers and writers of Tar archive format
- Gzip - readers and writers of gzip format (Crystal stdlib)
- polylines.cr — compression of series of coordinates
- snappy - Snappy compression format reader/writer for Crystal
- Zip - readers and writers of zip format (Crystal stdlib)
- Zlib - readers and writers of zlib format (Crystal stdlib)
- zstd.cr - Bindings for Zstandard compression library
Configuration
- cr-dotenv - Loads .env file
- Envy - Load environment variables from YAML
- envyable - A simple YAML to ENV config loader
- habitat - Type safe configuration for your classes and modules
- totem - Load and parse a configuration in JSON, YAML, dotenv formats
Converters
- base62.cr - Base62 encoder/decoder, well suited for url-shortening
- crunits - Tool for converting units of measure (miles to kilometers, celsius to fahrenheit etc)
- money - Handling money and currency conversion with ease (almost complete port of RubyMoney)
- sass.cr - Compile SASS/SCSS to CSS (libsass binding)
Cryptography
- cmac - Crystal implementation of Cipher-based Message Authentication Code (CMAC)
- ed25519 - the Ed25519 elliptic curve public-key signature system described in [RFC 8032]
- monocypher.cr - Crystal wrapper for the Monocypher crypto library
- sodium.cr - Crystal wrapper for the libsodium crypto API
Data Formats
- BinData - Binary data parser helper with an ASN.1 parser
- config.cr - Easy to use configuration format parser
- crinder - Class based json renderer
- Crystalizer - (De)serialize any Crystal object; supporting JSON, YAML, and Byte formats out of the box
- CSV - parsing and generating for comma-separated values (Crystal stdlib)
- front_matter.cr - Separates a files front matter from its content
- geoip2.cr - GeoIP2 reader
- HAR - HAR (HTTP Archive) parser
- INI - INI file parser (Crystal stdlib)
- JSON - parsing and generating JSON documents (Crystal stdlib)
- json-schema - convert JSON serializable classes into a JSON Schema representation
- JSON::OnSteroids - handle and mutate JSON document easily
- maxminddb.cr - MaxMindDB reader
- toml.cr - TOML parser
- XML - parsing and generating XML documents (Crystal stdlib)
- YAML - parsing and generating YAML documents (Crystal stdlib)
Data Generators
- faker - A library for generating fake data
- hashids.cr - A library to generate YouTube-like ids from one or many numbers
- prime - A prime number generator
Database Drivers/Clients
- couchdb.cr - CouchDB client
- cryomongo - MongoDB driver
- crystal-db - Common db api
- crystal-ldap - LDAP client
- crystal-mysql - MySQL connector for Crystal
- crystal-pg - A Postgres driver
- crystal-redis - Full featured Redis client
- crystal-rethinkdb - Driver for RethinkDB / RebirthDB
- crystal-sqlite3 - SQLite3 bindings
- leveldb - Crystal bindings for LevelDB
- rocksdb.cr - RocksDB client
- surrealdb.cr - Unoffical SurrealDB HTTP & Websocket Client
Database Tools
- migrate - A simpler database migration tool with transactions
Debugging
- backtracer.cr - Shard aiming to assist with parsing backtraces into a structured form
- debug.cr -
debug!(…)macro forpp-style debugging
Dependency Injection
- Athena Dependency Injection - Robust dependency injection service container framework
- Crystal-DI - Lightweight DI Container
- HardWire - A compile-time non-intrusive dependency injection system
- syringe - A simple and basic dependency injection shard for crystal
- carbon - Fun, testable, and adapter-based email library
- crystal-email - Simple e-mail sending library
- CrystalEmail - A RFC compliant Email validator
- sendgrid.cr - Simple Sendgrid Client
Environment Management
- asdf-crystal - Plugin for asdf version manager
- crenv - Crystal version manager
- rcm.cr - Redis Cluster Manager
Examples and funny stuff
- blackjack-cr - Console Blackjack
- crystal-patterns - Examples of GOF patters
- crystalworld - realworld.io back-end API implementation
- exercism-crystal - Exercism exercises
- try.cr - Try monad
Framework Components
- Athena Event Dispatcher - A Mediator and Observer pattern event library
- Athena Negotiation - Framework agnostic content negotiation library
- device_detector - Shard for detect device by user agent string
- Exception Page - An exceptional exception page for Crystal web libraries and frameworks
- graphql - Type-safe GraphQL server implementation
- graphql-crystal - GraphQL implementation
- kemal-session - Session handler for Kemal
- mochi - Authentication shard inspired by Devise supporting: Authenticable, Confirmable, Invitable & more
- motion.cr - Object oriented frontend library for Amber
- multi-auth - Standardized multi-provider OAuth2 authentication (inspired by omniauth)
- praetorian - Minimalist authorization library inspired by Pundit
- Shield - Comprehensive security for Lucky framework
- shrine.cr - File Attachment toolkit for Crystal applications. Heavily inspired by Shrine for Ruby
- tourmaline - Telegram bot framework with an API loosely based on telegraf.js
Game Development
- CrSFML - Bindings to SFML multimedia/game library
- crystal-chipmunk - Bindings to Chipmunk, a fast and lightweight 2D game physics library
- crystal-imgui-sfml - Bindings to integrate Dear ImGui into an SFML project
- entitas.cr - A Entity Component System Framework for Crystal
- MyECS - A Sparse Entity Component System Framework for Crystal
- Raylib-cr - Direct bindings to Raylib, which supports Linux, Windows, and Mac
- SDL-Crystal-Bindings - Direct (unsafe) bindings to SDL2, intended for writing own game libraries
GUI Development
- crystal-imgui - Bindings to Dear ImGui, an immediate-mode graphical UI library
- GTK4.cr - Bindings for GTK4 with Crystalized API
- Iu - UI framework based on the Fusion/libui.cr library, with custom elements and modified bindings from hedron-crystal/hedron
- Ultimate GTK4 Crystal Guide - Learn how to create premium GTK4 apps in Crystal
HTML Builders
- blueprint - Write reusable and testable HTML templates in plain Crystal
- form_builder.cr - Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap
- to_html - The fastest HTML builder engine for Crystal
- Water - A library for writing HTML in plain Crystal
HTML/XML Parsing
- docx_cr_converter - parse DOCX Word
- myhtml - Fast HTML5 Parser that includes CSS selectors
HTTP
- Cable - An ActionCable "port" to Crystal, framework agnostic, 100% compatible with the ActionCable JS Client
- cossack - Simple flexible HTTP client
- crest - Simple HTTP and REST client, inspired by the Ruby's RestClient gem
- crul - Command line HTTP client
- digest-auth - Digest authentication
- halite - Crystal HTTP Requests with a chainable REST API, built-in sessions and loggers
- http-multiserver.cr - Mounting multiple servers via routes (a.k.a. URL mapping)
- http-params-serializable - HTTP params (de)serialization, applicable to URL queries and URL-encoded forms
- http-protection - Protection against typical web attacks
- http2 - HTTP/2 Protocol Implementation
- HTTP::Client - HTTP client (Crystal stdlib)
- HTTP::Server - HTTP server (Crystal stdlib)
- HTTP::WebSocket - HTTP WebSocket client (Crystal stdlib)
- link-header - HTTP Link Header Parser
- ntlm - NTLM authentication
- proxy-fetcher.cr - Proxy lists fetching & validating library
- sse.cr - Server-Sent Events client
Image processing
- celestine - Create SVG images using a DSL
- ffmpeg - FFmpeg bindings that works with StumpyPNG to extract frames
- Pluto - A fast and convenient image processing library
- stumpy_png - Read and write PNG images
Implementations/Compilers
- charly - Charly Programming Language
- cltk - A crystal port of the Ruby Language Toolkit
- crisp - Lisp dialect implemented with Crystal
- LinCAS-lang - A programming language for scientific computation
- mint-lang - A refreshing programming language for the front-end web
- myst-lang - A practical, dynamic language designed to be written and understood as easily and efficiently as possible
- novika - A free-form, moldable, interpreted programming language
- runic-lang - In-design toy language
Internationalization
- crystal-i18n - An internationalization library inspired by Ruby-I18n
- i18n.cr - Internationalization shard
- Lens - A multiformat internationalization (i18n) shard for Crystal. Supports Gettext, Ruby YAML, etc.
- Rosetta - A blazing fast internationalization (i18n) library with compile-time key lookup supporting YAML and JSON formats
Logging and monitoring
- crafana - A Grafana library to help autogenerate dashboards
- fiber_metrics.cr - Track run time, wait time, or memory allocations per
Fiber, method or block - Log - logging utility (Crystal stdlib)
- statsd.cr - Statsd client library
Machine Learning
- ai4cr - Artificial Intelligence (based on https://github.com/SergioFierens/ai4r)
- Cadmium - NLP library based heavily on natural
- crystal-fann - FANN (Fast Artifical Neural Network) binding
- mxnet.cr - Bindings for MXNet
- shainet - SHAInet (Neural Network in pure crystal)
Markdown/Text Processors
- markd - Yet another markdown parser built for speed, Compliant to CommonMark specification
Misc
- aasm.cr - Easy to use finite state machine for Crystal classes
- any_hash.cr - Recursive Hash with better JSON::Any included
- anyolite - Full mruby interpreter with simple bindings, allowing for easy scripting support in projects
- burocracia.cr - burocracia.cr the dependecyless shard to validate, generate and format Brazilian burocracias such as CPF, CNPJ and CEP
- callbacks - Expressive callbacks module
- circuit_breaker - Implementation of the circuit breaker pattern
- CrSignals - Signals/slots notification library
- crystal-binary_parser - Binary parser
- crystal-web-framework-stars - Web frameworks for Crystal, most starred on Github
- crz - Functional programming library
- defined - macros for conditional compilation based on constant definitions, version requirements, or environment variable settings
- emoji.cr - Emoji library
- gphoto2-web.cr - Web API for libgphoto2
- immutable - Implementation of thread-safe, persistent, immutable collections
- iterm2 - Display images within the terminal using the ITerm2 Inline Images Protocol
- monads - Monad implementation
- observable - Implementation of the observer pattern
- pinger - Ping IP addresses and DNS entries without requiring sudo
- port_midi - Crystal C bindings for the PortMIDI cross-platform MIDI I/O library
- retriable.cr - Simple DSL to retry failed code blocks
- serf-handler.cr - Framework for building Serf handlers, with a suite of useful builtin capabilities
- simple_retry - Simple tool for retrying failed code blocks
- sslscan.cr - Crystal shard wrapping the rbsec/sslscan utility
- version_tools - Version-dependent behaviour, specified at compile-time
- wafalyzer - Web Application Firewall (WAF) Detector - shard + cli
- zaru_crystal - Filename sanitization
Network Protocols
- amqp-client.cr - AMQP 0-9.1, a messaging protocol, implemented by eg. RabbitMQ
- connect-proxy - Connect method style of HTTP tunnelling / HTTP proxy
- cr-xmpp - XMPP/Jabber Library
- Crirc - IRC protocol implementation (Client, Server, Bots)
- crystal-bacnet - BACnet protocol implementation with BACnet/IP client
- crystal-dns - DNS protocol implementation and resolver
- crystal-json-socket - JSON-socket client & server implementation. Inspired by and compatible with node-json-socket and ruby-json-socket
- crystal-mqtt - A MQTT client
- crystal-snmp - An SNMP implementation with version 1, 2c and 3 support
- fast_irc.cr - Fast IRC parser/generator
- jwt - Implementation of JWT (JSON Web Token)
- mDNS - DNS Service Discovery and multicast DNS
- mqtt-client.cr - A fast and lightweight MQTT client
- msgpack-crystal - MessagePack library
- OAuth - OAuth consumer (Crystal stdlib)
- OAuth2 - OAuth2 client (Crystal stdlib)
- OpenSSL - bindings to libssl (Crystal stdlib)
- simple_rpc - RPC Server and Client for Crystal. Implements msgpack-rpc protocol
- stomp - STOMP protocol
- telnet.cr - Telnet protocol
- transfer_more - Clone of transfer.sh to uploads files
Networking
- ipaddress.cr - Library to handle IPv4 and IPv6 addresses
- mac-address - Library for working with MAC addresses
ORM/ODM Extensions
- avram - A database wrapper for reading, writing, and migrating Postgres databases
- clear - ORM specialized to PostgreSQL only but with advanced features
- crecto - Database wrapper, based on Ecto
- granite - ORM for Postgres, Mysql, Sqlite
- jennifer.cr - Active Record pattern implementation with flexible query chainable builder and migration system
- rethinkdb-orm - ORM for RethinkDB / RebirthDB
Package Management
- shards - Dependency manager for the Crystal
Processes and Threads
- await_async - Add keywords await & async in Crystal Lang
- concurrent.cr - Simplified concurrency using streams/pipelines, waitgroups, semaphores, smores and more
- neph - A modern command line job processor that can execute jobs concurrently
- promise - A Promise implementation with type inference
- werk - Dead simple task runner with concurrent support, ideal for local CI
Project Generators
- crystal_lib - Automatic binding generator for native libraries
- fez - A Kemal application generator
- libgen - Automatic bindings generator configured using JSON/YAML files
Queues and Messaging
- mosquito - Redis backed periodic and ad hoc job processing
- NATS.io - NATS client
- sidekiq.cr - Simple, efficient job processing
Routing
- orion - A minimal, rails-esque routing library
- router.cr - Minimum but powerful http router for HTTP::Server
Scheduling
- crystime - Advanced time, calendar, schedule, and remind library
- schedule.cr - Run periodic tasks
- tasker - A high precision scheduler including timezone aware cron jobs
Science and Data analysis
- alea - Repeatable sampling, CDF and other utilities to work with probability distributions
- ishi - Graph plotting package with a small API and sensible defaults powered by gnuplot
- linalg - Linear algebra library inspired by MATLAB and SciPy.linalg
- num.cr - Numerical computing library supporting N-Dimensional data
- predict.cr - Satellite prediction library using the sgp4 model
- quartz - Modeling and simulation framework
Search
- hermes - Data Mapper pattern implementation for ElastiSearch
Serverless Computing
- crystal_openfaas - Template to enable crystal as first class citizens in OpenFaaS
- secrets-env - Extends ENV module to read values injected by docker / kubernetes secrets and other orchestration tools
System
- baked_file_system - Virtual file system implementation
- hardware - Get CPU, Memory and Network informations of the running OS and its processes
Task management
- cake - Production-ready Make-like utility tool
- sam - Another one Rake-like task manager with namespacing and arguments system
Template Engine
- crinja - An implementation of the Jinja2 template engine
- crustache - {{Mustache}} for Crystal
- ECR (Embedded Crystal) - compile time template language which uses plain crystal expressions (Crystal stdlib)
- Jbuilder - Generate JSON objects with a Builder-style DSL, inspired by jbuilder
- Kilt - Abstraction layer for template engines
- Slang - Lightweight, terse, templating language inspired by Ruby's Slim
- teeplate - A library for rendering multiple template files
Testing
- Athena Spec - Common/helpful Spec compliant testing utilities
- crotest - A tiny and simple test framework
- crytic - Mutation testing framework
- hashr - A tiny class makes test on JSON response easier
- LuckyFlow - Automated browser tests similar to Capybara
- mass-spec - Web API testing library
- microtest - Small opinionated testing library focusing on power asserts
- minitest.cr - Library for unit tests and assertions
- mocks.cr - Mocking library for Crystal
- Spec - spec framework (Crystal stdlib)
- spectator - Feature rich spec framework that uses the modern expect syntax
- timecop.cr - Library for mocking with
Time.now. Inspired by the timecop ruby gem - vcr - A HTTP capture and replay implementation for crystal
- webdriver_pump - Page Object library. Inspired by Ruby's WatirPump
- webmock.cr - Library for stubbing
HTTP::Clientrequests
Third-party APIs
- amazonite - An unofficial SDK supporting popular AWS APIs
- aws-signer.cr - This library signs your HTTP requests using AWS v4
- awscr-s3 - AWS S3 interface
- awscr-signer - Sign HTTP::Request objects and generate presigned post forms
- crystal-consul - Consul API client
- crystal-darksky - Wrapper for the Dark Sky API
- crystal-swapi - Star Wars API (SWAPI) wrapper
- crystal_slack - A tool that parses Slack slash commands or send incoming web hooks
- GDAX - GDAX REST and WebSocket API Wrapper with request signing
- gitlab.cr - GitLab API wrapper
- google - Google API wrapper
- host_meta - A Web Host Metadata (https://tools.ietf.org/html/rfc6415) client
- kube-client.cr - Kubernetes API Client
- mixpanel-crystal - A library for sending events to Mixpanel
- mollie.cr - Mollie Payments API wrapper (Creditcard, PayPal, Apple Pay, Sofort, Klarna, ...)
- office365 - Microsoft Graph API wrapper
- pinboard.cr - Pinboard API
- raven.cr - Raven is a client for Sentry
- stripe.cr - Stripe api wrapper
- tmdb.cr - The Movie DB (TMDb) api wrapper
- twitter-crystal - A library to access the Twitter API
- web_finger - A WebFinger (https://tools.ietf.org/html/rfc7033) client
- ynab.cr - A library to interact with your YNAB data
Validation
- accord - Shareable validation library for Crystal Objects
- Athena Validator - Robust & flexible validation framework
- validations - Validations mixin
- validator - Data check and validation
Web Frameworks
- amber - Open source efficient and cohesive web application framework
- Athena - A web framework comprised of reusable, independent components
- grip - The microframework for writing powerful web applications
- kemal - Lightning Fast, Super Simple web framework. Inspired by Sinatra
- lucky - Catch bugs early, forget about most performance issues, and spend more time on code instead of debugging and writing tests
- marten - A web framework that makes building web applications easy, productive, and fun
- runcobo - An api framework with simple, intuitive and consistent DSL, using jbuilder to render json
- Shivneri - Component based MVC web framework for crystal targeting good code structures, modularity & performance
- spider-gazelle - A Rails esque web framework with a focus on speed and extensibility
Community
- Chicago Crystal Podcast
- Chicago Crystal YouTube
- Crystal Forum
- Crystal newsletters
- Gitter
- Google Group
- IRC - #crystal-lang on Freenode
- Stackoverflow
Unofficial
- Chinese-speaking Telegram Group - 来吧!TG 中文圈的朋友们!
- Crystal Programming Discord Server - Unofficial Discord server dedicated to the Crystal Programming Language
- Portuguese-speaking Telegram Group - Bem vindos ao Crystal Brasil!
- Russian-speaking Telegram Group - Добро пожаловать, товарищ!
Resources
- Crystal for Rubyists - Free book to bootstrap your Crystal journey
- Crystal Shards for Ruby Gems - A list of Ruby Gems and their Crystal Shards equivalents
- crystal-koans - Learn Crystal by writing unit tests
- crystal-lang.org - Official language site
- devdocs.io - API Documentation Browser with Crystal support
- Programming Crystal - PragProg book to start your Crystal journey
Official Documentation Translations
- br.crystal-lang.org - Brazilian
- ja.crystal-lang.org - Japanese
- kr.crystal-lang.org - Korean
- ru.crystal-lang.org - Russian
- tw.crystal-lang.org - Chinese Traditional
Services and Apps
- carc.in - A web service that runs your code and displays the result
- Crank - A Procfile-based application manager (like Foreman)
- cry - Ability to execute crystal code in a fashion similar to Ruby's pry edit
- Crystal [ANN] - Announce new project, blog post, version update or any other Crystal work
- DeBot - IRC bot written in Crystal
- icr - Interactive console for Crystal (like IRB for Ruby)
- Invidious - Invidious is an alternative front-end to YouTube
- mpngin - A URL shortener with simple stats
- procodile - Run processes in the background (and foreground) on Mac & Linux from a Procfile (for production and/or development environments)
- quicktype - Generate models and serializers from JSON, JSON Schema, GraphQL, and TypeScript
- shards.info - Web service that lists all repositories on GitHub that have Crystal code in them. The sources are available on GitHub
Tools
- ast_helper - Helper tool to debug parser and formatter
- crystal-base - CentOS base docker image for Crystal development
- crystal-dash-docset - Dash docset generator
- port_ruby_to_crystal - A regex replace ruby script for port ruby code to crystal easier, reduce friction
- public_suffix - A small library designed to make the Public Suffix List (https://publicsuffix.org/) easier to use
DevOps
- ansible-crystal - Ansible playbook for installing crystal
- DPPM - An easy, universal way to install and manage applications as packages (mostly Linux)
Editor Plugins
- Acme:
- acmecrystal - Reformats crystal code in acme
- Atom
- crystal-tools - Enables built in tools in Crystal compiler
- language-crystal-actual - Crystal language support in Atom
- Emacs
- crystal-mode - Crystal language support for Emacs (crystal-lang-tools/emacs-crystal-mode)
- Geany
- geany-crystal - Crystal support for the Geany editor
- IntelliJ IDEA
- intellij-crystal-lang - Crystal support for the JetBrains IDEs
- Lite-XL
- lite-plugin-crystal - Crystal support for the Lite-XL editor
- Spacemacs
- crystal-spacemacs-layer - Spacemacs contribution layer for Crystal
- Sublime
- sublime-crystal - Crystal syntax highlighting for sublime Text
- TextMate
- Crystal.tmbundle - Crystal syntax highlighting, compile, format command, snippets
- Vim
- vim-crystal - Vim filetype support for Crystal
- vim-slang - Vim filetype support for Slang Templating Engine
- Visual Studio Code
- vscode-crystal-lang - Formatter, linter and syntax highlighting for
crandecrfiles
- vscode-crystal-lang - Formatter, linter and syntax highlighting for
LSP Language Server Protocol Implementations
- crystalline - Crystalline is an implementation of the Language Server Protocol written in and for the Crystal Language
- scry - Code analysis server for Crystal implementing the Language Server Protocol
Shell plugins
- crun - Crystal Run : shebang wrapper for Crystal
- crystal-zsh - .oh-my-zsh plugin
License
All of the original content that I've authored is licensed CC-BY-SA 4.0. Other sections might be copied and remixed from other open source documentation, they might be licensed differently, see Credits section.
You can find a copy of the CC-BY-SA 4.0 license below:
Attribution-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.