Skip to content

Instantly share code, notes, and snippets.

@ziprandom
Last active May 3, 2017 13:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ziprandom/fefd80f8ca46b4371f540666b82759d0 to your computer and use it in GitHub Desktop.
Save ziprandom/fefd80f8ca46b4371f540666b82759d0 to your computer and use it in GitHub Desktop.
Nesting & Reusing Crystal Macros to Build Up Class Constans Along the Type Hierarchy
require "spec"
macro on_all_child_classes(&block)
macro injection
{{block && block.body}}
end
macro inject
injection
macro inherited
injection
end
end
inject
end
class TextClass
on_all_child_classes do
FIELDS = [] of String
macro field(name)
\{% FIELDS << name %}
end
end
end
class Text < TextClass
field "Field1"
field "Field2"
end
class Text2 < Text
field "Field3"
field "Field4"
end
class Text3 < Text2
field "Field5"
field "Field6"
end
describe "macro on_all_child_classes" do
it "should work on the first child" do
Text::FIELDS.should eq ["Field1", "Field2"]
end
it "should work on the second child" do
Text2::FIELDS.should eq ["Field3", "Field4"]
end
it "should work on the third child" do
Text3::FIELDS.should eq ["Field5", "Field6"]
end
end
macro reduce_through_class_hierarchy(constant, value, function_name, &block)
on_all_child_classes do
macro inherited
{{constant.id}} = {{value}}
end
end
on_all_child_classes do
def self.{{function_name.id}}
{{block.body}}
end
end
def self.{{function_name.id}}
{{value}}
end
end
class RecTestClass
# define a constant and it's start value in
# the uppermost part of the class hierarchy.
# give the accumulation class function a name (fields)
# and define a reduce function (the block)
# to recursively build up the functions return
# value using super
reduce_through_class_hierarchy(
FIELDS, [] of Tuple(Symbol, String, String), fields
) do
super.concat(FIELDS)
end
# define a field macro to manipulate the class
# Constant for every class in the hierarchy
macro field(name, type, description)
{% FIELDS << {name, type, description} %}
end
# we can also accumulate this back into another
# class constant that also will be set for every
# class in the hierarchy
macro inherited
ALL_FIELDS = self.fields
end
end
class RecTest < RecTestClass
field :field_1, "Text", "the description of the field"
field :field_2, "Text", "the description of the field"
end
class RecTest2 < RecTest
field :field_3, "Text", "the description of the field"
field :field_4, "Text", "the description of the field"
end
class RecTest3 < RecTest2
field :field_5, "Text", "the description of the field"
field :field_6, "Text", "the description of the field"
end
describe "macro on_all_child_classes" do
it "should work on the first child" do
RecTest::FIELDS.should eq [
{:field_1, "Text", "the description of the field"},
{:field_2, "Text", "the description of the field"}
]
RecTest.fields.should eq [
{:field_1, "Text", "the description of the field"},
{:field_2, "Text", "the description of the field"}
]
RecTest::ALL_FIELDS.should eq RecTest.fields
end
it "should work on the second child" do
RecTest2::FIELDS.should eq [
{:field_3, "Text", "the description of the field"},
{:field_4, "Text", "the description of the field"}
]
RecTest2.fields.should eq [
{:field_1, "Text", "the description of the field"},
{:field_2, "Text", "the description of the field"},
{:field_3, "Text", "the description of the field"},
{:field_4, "Text", "the description of the field"}
]
RecTest2::ALL_FIELDS.should eq RecTest2.fields
end
it "should work on the third child" do
RecTest3::FIELDS.should eq [
{:field_5, "Text", "the description of the field"},
{:field_6, "Text", "the description of the field"}
]
RecTest3.fields.should eq [
{:field_1, "Text", "the description of the field"},
{:field_2, "Text", "the description of the field"},
{:field_3, "Text", "the description of the field"},
{:field_4, "Text", "the description of the field"},
{:field_5, "Text", "the description of the field"},
{:field_6, "Text", "the description of the field"}
]
RecTest3::ALL_FIELDS.should eq RecTest3.fields
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment