Last active
May 3, 2017 13:47
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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