crygen
[!WARNING] This library is under development, it is not completely finished.
crygen is a library that allows to generate a Crystal file. It is inspired by the PHP library : nette/php-generator.
Installation
- Add the dependency to your 
shard.yml: 
dependencies:
  crygen:
    github: tamdaz/crygen
- Run 
shards install 
Examples usage
To generate code, you can find classes in the CGT module (Crygen::Types).
Method
# Create a method with one comment and a body.
method_type = CGT::Method.new("add", "Int32")
method_type.add_comment("Adds the two numbers.")
method_type.add_arg("a", "Int32")
method_type.add_arg("b", "Int32")
method_type.add_body("a + b")
puts method_type.generate
Once the method is generated, it will look like this:
# Adds the two numbers.
def add(a : Int32, b : Int32) : Int32
  a + b
end
[!TIP] You can add new lines of comments and code to the method by calling the
add_commentandadd_bodymethods several times.
Class
In addition to creating methods, you can add them to a class using the add_method method of the CGT::Class class.
# Create a method with one comment and a body.
method_full_name = CGT::Method.new("full_name", "String")
method_full_name.add_comment("Gets the person's full name.")
method_full_name.add_body("John Doe".dump)
# Create a class with one comment and a method.
class_person = CGT::Class.new("Person")
class_person.add_comment("This is a class called Person.")
class_person.add_method(method_full_name)
# Print the generated code.
puts class_person.generate
Once the code is generated, the code will look like this:
# This is a class called Person.
class Person
  # Gets the person's full name.
  def full_name : String
    "John Doe"
  end
end
You can add properties in the class, for example:
# Create a class with one comment and a method.
class_person = CGT::Class.new("Person")
class_person.add_comment("This is a class called Person.")
class_type.add_property(CGE::PropVisibility::Property, "full_name", "String")
class_type.add_property(CGE::PropVisibility::Getter, "first_name", "String")
class_type.add_property(CGE::PropVisibility::Setter, "last_name", "String")
# Print the generated code.
puts class_person.generate
Output :
class Person
  property full_name : String
  getter first_name : String
  setter last_name : String
end
Also, you can create the nullable properties.
# Create a class with one comment and a method.
class_klass = CGT::Class.new("Klass")
class_klass.add_comment("This is a class called Person.")
class_type.add_property(:nil_property, "a", "String")
class_type.add_property(:nil_getter, "b", "String")
# Print the generated code.
puts class_klass.generate
Output :
class Klass
  property? a : String
  getter? b : String
end
Instance variables
In a class, instance variables can be added.
class_person = CGT::Class.new("Person")
class_type.add_instance_var("first_name", "String", "John")
class_type.add_instance_var("last_name", "String", "Doe")
puts class_person.generate
Output:
class Person
  @first_name : String = "John"
  @last_name : String = "Doe"
end
Class variables
In addition of instance variables, class variables can also be added.
class_person = CGT::Class.new("Person")
class_type.add_class_var("count", "Int32", "0")
puts class_person.generate
Output:
class Person
  @@count : Int32 = 0
end
Abstract class
Abstract class can be generated.
class_person = CGT::Class.new("Person")
class_type.as_abstract # Set this class as abstract.
class_type.add_method(CGT::Method.new("first_name", "String"))
class_type.add_method(CGT::Method.new("last_name", "String"))
class_type.add_method(CGT::Method.new("full_name", "String"))
puts class_person.generate
Output:
abstract class Person
  abstract def first_name : String
  abstract def last_name : String
  abstract def full_name : String
end
[!NOTE] If you add code to an abstract method, only the method signature will be generated.
Enum
enum_type = CGT::Enum.new("Person")
enum_type.add_constant("Employee")
enum_type.add_constant("Student")
enum_type.add_constant("Intern")
puts enum_type.generate
Once the code is generated, the enum will look like this:
enum Person
  Employee
  Student
  Intern
end
In addition to this, you can specify the types of constants by passing the type as a second argument, as well as defining default values for any constant.
enum_type = CGT::Enum.new("Person", "Int32")
enum_type.add_constant("Employee", "1")
enum_type.add_constant("Student", "2")
enum_type.add_constant("Intern", "3")
puts enum_type.generate
Output:
enum Person : Int32
  Employee = 1
  Student = 2
  Intern = 3
end
Annotation
annotation_type = CGT::Annotation.new("MyAnnotation")
puts annotation_type.generate
Output:
@[MyAnnotation]
With the annotation, you can add it to the method or class to add the metadata.
class_type = CGT::Class.new("Person")
class_type.add_annotation(CGT::Annotation.new("Experimental"))
puts class_type.generate
method_type = CGT::Method.new("full_name", "String")
method_type.add_annotation(CGT::Annotation.new("MyAnnotation"))
puts method_type.generate
class_type.add_method(method_type)
puts class_type.generate
Output:
# Annotation on class
@[Experimental]
class Person
end
# Annotation on method
@[Experimental]
def full_name : String
  "John Doe"
end
# Annotation on class and method.
@[Experimental]
class Person
  @[MyAnnotation]
  def full_name : String
    "John Doe"
  end
end
[!TIP] You can add many annotations as you want thanks to the
add_annotationmethod.
Struct
method_first_name = CGT::Method.new("first_name", "String")
method_first_name.add_body("John".dump)
method_last_name = CGT::Method.new("last_name", "String")
method_last_name.add_body("Doe".dump)
struct_type = CGT::Struct.new("Point")
struct_type.add_method(method_first_name)
struct_type.add_method(method_last_name)
puts struct_type.generate
Output:
struct Point
  def first_name : String
    "John"
  end
  def last_name : String
    "Doe"
  end
end
Module
enum_type = Crygen::Types::Enum.new("Role", "Int8")
enum_type.add_constant("Member", "1")
enum_type.add_constant("Moderator", "2")
enum_type.add_constant("Administrator", "3")
module_type = Crygen::Types::Module.new("Folder::Sub::Folder")
module_type.add_object(enum_type)
puts module_type
Output:
module Folder::Sub::Folder
  enum Role : Int8
    Member        = 1
    Moderator     = 2
    Administrator = 3
  end
end
[!TIP] You can add many objects as you want into that module, thanks to
add_objectmethod.
Lib C-binding
libc_type = CGT::LibC.new("C")
libc_type.add_function("getch", "Int32", [{"arg", "Int32"}])
libc_type.add_function("getpid", "Int32")
libc_type.add_struct("TimeZone", [
  {"field_one", "Int32"},
  {"field_two", "Int32"},
])
libc_type.add_union("IntOrFloat", [
  {"some_int", "Int32"},
  {"some_float", "Float64"},
])
puts libc_type.generate
Output:
lib C
  struct TimeZone
    field_one : Int32
    field_two : Int32
  end
  union IntOrFloat
    some_int : Int32
    some_float : Float64
  end
  fun getch(arg : Int32) : Int32
  fun getpid : Int32
end
Macro
macro_type = Crygen::Types::Macro.new("example")
macro_type.add_arg("name")
macro_type.add_arg("value")
macro_type.body = <<-CRYSTAL
{% for i in 1..10 %}
  puts {{ name }}
  puts {{ value }}
  puts {{ "Hello world" }}
{% end %}
CRYSTAL
puts macro_type.generate
Output:
macro example(name, value)
  {% for i in 1..10 %}
    puts {{ name }}
    puts {{ value }}
    puts {{ "Hello world" }}
  {% end %}
end
Alias
alias_type = CGT::Alias.new("MyAlias", %w[Foo Bar])
puts alias_type.generate
Output:
alias MyAlias = Foo | Bar
Mixin
class_type = CGT::Class.new("Person")
class_type.add_include("FirstModule")
class_type.add_include("SecondModule")
class_type.add_extend("MyExtension")
class_type.add_extend("AnotherExtension")
puts class_type
Output:
class Person
  include FirstModule
  include SecondModule
  extend MyExtension
  extend AnotherExtension
end
Usage
This library can be used to save time. In particular, the frameworks have features for generating code more easily, without having to rewrite everything by hand.
Check out the references : https://crystal-lang.org/reference/1.16/syntax_and_semantics/index.html
If there's something missing from the todo, don't hesitate to add it.
Contributing
- Fork it (https://github.com/tamdaz/crygen/fork)
 - Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
 
Contributors
- tamdaz - creator and maintainer