From 97441474b754e160f48311f7dc5e19faa0b2c216 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 6 Feb 2025 18:15:59 +0100 Subject: [PATCH] Refactor docs for Object getter/setter macros (#15428) --- scripts/generate_object_properties.cr | 929 ++++------------- src/object.cr | 197 ++++ src/object/properties.cr | 1365 +++---------------------- 3 files changed, 550 insertions(+), 1941 deletions(-) diff --git a/scripts/generate_object_properties.cr b/scripts/generate_object_properties.cr index 67b676868e48..73c650bd1b66 100755 --- a/scripts/generate_object_properties.cr +++ b/scripts/generate_object_properties.cr @@ -102,364 +102,12 @@ struct Generator TEXT end - def gen_getter + def gen_property_macros puts <<-TEXT - # Defines getter methods for each of the given arguments. + # Generates both `#{@macro_prefix}getter` and `#{@macro_prefix}setter` + # methods to access instance variables. # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}getter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}name - # #{@var_prefix}name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}getter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # #{@macro_prefix}getter name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String - # - # def #{@method_prefix}name : String - # #{@var_prefix}name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # #{@macro_prefix}getter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String = "John Doe" - # - # def #{@method_prefix}name : String - # #{@var_prefix}name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # #{@macro_prefix}getter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name = "John Doe" - # - # def #{@method_prefix}name : String - # #{@var_prefix}name - # end - # end - # ``` - # - # If a block is given to the macro, a getter is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # #{@macro_prefix}getter(birth_date) { Time.local } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}birth_date - # if (value = #{@var_prefix}birth_date).nil? - # #{@var_prefix}birth_date = Time.local - # else - # value - # end - # end - # end - # ``` - macro #{@macro_prefix}getter(*names, &block) - {% for name in names %} - #{def_vars} - #{def_getter} - {% end %} - end - TEXT - end - - def gen_getter? - puts <<-TEXT - # Defines query getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}getter? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}happy? - # #{@var_prefix}happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}getter? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # #{@macro_prefix}getter? happy : Bool - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}happy : Bool - # - # def #{@method_prefix}happy? : Bool - # #{@var_prefix}happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # #{@macro_prefix}getter? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}happy : Bool = true - # - # def #{@method_prefix}happy? : Bool - # #{@var_prefix}happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # #{@macro_prefix}getter? happy = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}happy = true - # - # def #{@method_prefix}happy? - # #{@var_prefix}happy - # end - # end - # ``` - # - # If a block is given to the macro, a getter is generated with a variable - # that is lazily initialized with the block's contents, for examples see - # `##{@macro_prefix}getter`. - macro #{@macro_prefix}getter?(*names, &block) - {% for name in names %} - #{def_vars} - #{def_getter "?"} - {% end %} - end - TEXT - end - - def gen_property - puts <<-TEXT - # Defines property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}property name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}name=(#{@var_prefix}name) - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}property :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # #{@macro_prefix}property name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String - # - # def #{@method_prefix}name=(#{@var_prefix}name) - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # #{@macro_prefix}property name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String = "John Doe" - # - # def #{@method_prefix}name=(#{@var_prefix}name : String) - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # #{@macro_prefix}property name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name = "John Doe" - # - # def #{@method_prefix}name=(#{@var_prefix}name : String) - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # #{@macro_prefix}property(birth_date) { Time.local } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}birth_date - # if (value = #{@var_prefix}birth_date).nil? - # #{@var_prefix}birth_date = Time.local - # else - # value - # end - # end - # - # def #{@method_prefix}birth_date=(#{@var_prefix}birth_date) - # end - # end - # ``` + # Refer to the aforementioned macros for details. macro #{@macro_prefix}property(*names, &block) {% for name in names %} #{def_vars} @@ -467,116 +115,11 @@ struct Generator #{def_setter} {% end %} end - TEXT - end - def gen_property? - puts <<-TEXT - # Defines query property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}property? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}happy=(#{@var_prefix}happy) - # end + # Generates both `#{@macro_prefix}getter?` and `#{@macro_prefix}setter` + # methods to access instance variables. # - # def #{@method_prefix}happy? - # #{@var_prefix}happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}property? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # #{@macro_prefix}property? happy : Bool - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}happy : Bool - # - # def #{@method_prefix}happy=(#{@var_prefix}happy : Bool) - # end - # - # def #{@method_prefix}happy? : Bool - # #{@var_prefix}happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # #{@macro_prefix}property? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}happy : Bool = true - # - # def #{@method_prefix}happy=(#{@var_prefix}happy : Bool) - # end - # - # def #{@method_prefix}happy? : Bool - # #{@var_prefix}happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # #{@macro_prefix}property? happy = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}happy = true - # - # def #{@method_prefix}happy=(#{@var_prefix}happy) - # end - # - # def #{@method_prefix}happy? - # #{@var_prefix}happy - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents, for examples see `##{@macro_prefix}property`. + # Refer to the aforementioned macros for details. macro #{@macro_prefix}property?(*names, &block) {% for name in names %} #{def_vars} @@ -584,140 +127,11 @@ struct Generator #{def_setter} {% end %} end - TEXT - end - def gen_getter! - puts <<-TEXT - # Defines raise-on-nil and nilable getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}getter! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}name? - # #{@var_prefix}name - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}getter! :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person - # #{@macro_prefix}getter! name : String - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String? - # - # def #{@method_prefix}name? - # #{@var_prefix}name - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name.not_nil! - # end - # end - # ``` - macro #{@macro_prefix}getter!(*names) - {% for name in names %} - #{def_vars!} - #{def_getter!} - {% end %} - end - TEXT - end - - def gen_property! - puts <<-TEXT - # Defines raise-on-nil property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}property! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}name=(#{@var_prefix}name) - # end - # - # def #{@method_prefix}name? - # #{@var_prefix}name - # end + # Generates both `#{@macro_prefix}getter!` and `#{@macro_prefix}setter` + # methods to access instance variables. # - # def #{@method_prefix}name - # #{@var_prefix}name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}property! :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person - # #{@macro_prefix}property! name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String? - # - # def #{@method_prefix}name=(#{@var_prefix}name) - # end - # - # def #{@method_prefix}name? - # #{@var_prefix}name - # end - # - # def #{@method_prefix}name - # #{@var_prefix}name.not_nil! - # end - # end - # ``` + # Refer to the aforementioned macros for details. macro #{@macro_prefix}property!(*names) {% for name in names %} #{def_vars!} @@ -727,102 +141,6 @@ struct Generator end TEXT end - - def gen_setter - puts <<-TEXT - # Defines setter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # #{@macro_prefix}setter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def #{@method_prefix}name=(#{@var_prefix}name) - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # #{@macro_prefix}setter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # #{@macro_prefix}setter name : String - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String - # - # def #{@method_prefix}name=(#{@var_prefix}name : String) - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # #{@macro_prefix}setter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name : String = "John Doe" - # - # def #{@method_prefix}name=(#{@var_prefix}name : String) - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # #{@macro_prefix}setter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # #{@var_prefix}name = "John Doe" - # - # def #{@method_prefix}name=(#{@var_prefix}name) - # end - # end - # ``` - macro #{@macro_prefix}setter(*names) - {% for name in names %} - #{def_vars_no_macro_block} - #{def_setter} - {% end %} - end - TEXT - end end directory = File.expand_path("../src/object", __DIR__) @@ -837,27 +155,226 @@ File.open(output, "w") do |f| g = Generator.new(f, "", "", "@", "#") - g.gen_getter - g.gen_getter? - g.gen_getter! + f.puts <<-TEXT + # Defines getter method(s) to access instance variable(s). + # + # Refer to [Getters](#getters) for details. + macro getter(*names, &block) + {% for name in names %} + #{g.def_vars} + #{g.def_getter} + {% end %} + end + + # Identical to `getter` but defines query methods. + # + # For example writing: + # + # ``` + # class Robot + # getter? working + # end + # ``` + # + # Is equivalent to writing: + # + # ``` + # class Robot + # def working? + # @working + # end + # end + # ``` + # + # Refer to [Getters](#getters) for general details. + macro getter?(*names, &block) + {% for name in names %} + #{g.def_vars} + #{g.def_getter "?"} + {% end %} + end - g.gen_setter + # Similar to `getter` but defines both raise-on-nil methods as well as query + # methods that return a nilable value. + # + # If a type is specified, then it will become a nilable type (union of the + # type and `Nil`). Unlike the other `getter` methods the value is always + # initialized to `nil`. There are no initial value or lazy initialization. + # + # For example writing: + # + # ``` + # class Robot + # getter! name : String + # end + # ``` + # + # Is equivalent to writing: + # + # ``` + # class Robot + # @name : String? + # + # def name? : String? + # @name + # end + # + # def name : String + # @name.not_nil!("Robot#name cannot be nil") + # end + # end + # ``` + # + # Refer to [Getters](#getters) for general details. + macro getter!(*names) + {% for name in names %} + #{g.def_vars!} + #{g.def_getter!} + {% end %} + end + + # Generates setter methods to set instance variables. + # + # Refer to [Setters](#setters) for general details. + macro setter(*names) + {% for name in names %} + #{g.def_vars_no_macro_block} + #{g.def_setter} + {% end %} + end + TEXT - g.gen_property - g.gen_property? - g.gen_property! + g.gen_property_macros g = Generator.new(f, "class_", "self.", "@@", ".") - g.gen_getter - g.gen_getter? - g.gen_getter! + f.puts <<-TEXT + # Defines getter method(s) to access class variable(s). + # + # For example writing: + # + # ``` + # class Robot + # class_getter backend + # end + # ``` + # + # Is equivalent to writing: + # + # ``` + # class Robot + # def self.backend + # @@backend + # end + # end + # ``` + # + # Refer to [Getters](#getters) for details. + macro class_getter(*names, &block) + {% for name in names %} + #{g.def_vars} + #{g.def_getter} + {% end %} + end + + # Identical to `class_getter` but defines query methods. + # + # For example writing: + # + # ``` + # class Robot + # class_getter? backend + # end + # ``` + # + # Is equivalent to writing: + # + # ``` + # class Robot + # def self.backend? + # @@backend + # end + # end + # ``` + # + # Refer to [Getters](#getters) for general details. + macro class_getter?(*names, &block) + {% for name in names %} + #{g.def_vars} + #{g.def_getter "?"} + {% end %} + end - g.gen_setter + # Similar to `class_getter` but defines both raise-on-nil methods as well as + # query methods that return a nilable value. + # + # If a type is specified, then it will become a nilable type (union of the + # type and `Nil`). Unlike with `class_getter` the value is always initialized + # to `nil`. There are no initial value or lazy initialization. + # + # For example writing: + # + # ``` + # class Robot + # class_getter! backend : String + # end + # ``` + # + # Is equivalent to writing: + # + # ``` + # class Robot + # @@backend : String? + # + # def self.backend? : String? + # @@backend + # end + # + # def backend : String + # @@backend.not_nil!("Robot.backend cannot be nil") + # end + # end + # ``` + # + # Refer to [Getters](#getters) for general details. + macro class_getter!(*names) + {% for name in names %} + #{g.def_vars!} + #{g.def_getter!} + {% end %} + end + + # Generates setter method(s) to set class variable(s). + # + # For example writing: + # + # ``` + # class Robot + # class_setter factories + # end + # ``` + # + # Is equivalent to writing: + # + # ``` + # class Robot + # @@factories + # + # def self.factories=(@@factories) + # end + # end + # ``` + # + # Refer to [Setters](#setters) for general details. + macro class_setter(*names) + {% for name in names %} + #{g.def_vars_no_macro_block} + #{g.def_setter} + {% end %} + end + TEXT - g.gen_property - g.gen_property? - g.gen_property! + g.gen_property_macros f.puts "end" end diff --git a/src/object.cr b/src/object.cr index 9482dc170892..cb7edd474b1d 100644 --- a/src/object.cr +++ b/src/object.cr @@ -1,6 +1,203 @@ require "./object/properties" # `Object` is the base type of all Crystal objects. +# +# ## Getters +# +# Multiple macros are available to easily declare, initialize and expose +# instance variables as well as class variables on an `Object` by generating +# simple accessor methods. +# +# For example writing: +# +# ``` +# class Person +# getter name +# end +# ``` +# +# Is the same as writing: +# +# ``` +# class Person +# def name +# @name +# end +# end +# ``` +# +# For class variables we'd have called `class_getter name` that would have +# generated a `def self.name` class method returning `@@name`. +# +# We can define as many variables as necessary in a single call. For example +# `getter name, age, city` will create a getter method for each of `name`, `age` +# and `city`. +# +# ### Type and initial value +# +# Instead of plain arguments, we can specify a type as well as an initial value. +# If the initial value is simple enough Crystal should be able to infer the type +# of the instance or class variable! +# +# Specifying a type will also declare the instance or class variable with said +# type and type the accessor method arguments and return type accordingly. +# +# For example writing: +# +# ``` +# class Person +# getter name : String +# getter age = 0 +# getter city : String = "unspecified" +# end +# ``` +# +# Is the same as writing: +# +# ``` +# class Person +# @name : String +# @age = 0 +# @city : String = "unspecified" +# +# def name : String +# @name +# end +# +# def age +# @age +# end +# +# def city : String +# @city +# end +# end +# ``` +# +# The initial value of an instance variable is automatically set when the object +# is constructed. The initial value of a class variable will be set when the +# program starts up. +# +# ### Lazy initialization +# +# Instead of eagerly initializing the value, we can lazily initialize it the +# first time the accessor method is called. +# +# Since the variable will be lazily initialized the type of the variable will be +# a nilable type. The generated method however will return the specified type +# only (not a nilable). +# +# For example writing: +# +# ``` +# class Person +# getter(city : City) { City.unspecified } +# end +# ``` +# +# Is equivalent to writing: +# +# ``` +# class Person +# @city : City? +# +# def city : City +# if (city == @city).nil? +# @city = City.unspecified +# else +# city +# end +# end +# end +# ``` +# +# ### Variants +# +# Please refer to the different variants to understand how they differ from the +# general overview presented above: +# +# - `getter` +# - `getter?` +# - `getter!` +# - `class_getter` +# - `class_getter?` +# - `class_getter!` +# +# ## Setters +# +# The `setter` and `class_setter` macros are the write counterparts of the +# getter macros. They declare `name=(value)` accessor methods. The arguments +# behave just as for the getter macros. Each setter can have a type as well as +# an initial value. There is no lazy initialization however since the macro +# doesn't generate a getter method. +# +# For example writing: +# +# ``` +# class Person +# setter name +# setter age = 0 +# setter city : String = "unspecified" +# end +# ``` +# +# Is the same as writing: +# +# ``` +# class Person +# @age = 0 +# @city : String = "unspecified" +# +# def name=(@name) +# end +# +# def age=(@age) +# end +# +# def city=(@city : String) : String +# end +# end +# ``` +# +# For class variables we'd have called `class_setter name` that would have +# generated a `def self.name=(@@name)` class method instead. +# +# ## Properties +# +# The property macros define both getter and setter methods at once. +# +# For example writing: +# +# ``` +# class Person +# property name +# end +# ``` +# +# Is equivalent to writing: +# +# ``` +# class Person +# getter name +# setter name +# end +# ``` +# +# Which is the same as writing: +# +# ``` +# class Person +# def name +# @name +# end +# +# def name=(@name) +# end +# end +# ``` +# +# Refer to [Getters](#getters) and [Setters](#setters) above for details. The +# macros take the exact same arguments. class Object # Returns `true` if this object is equal to *other*. # diff --git a/src/object/properties.cr b/src/object/properties.cr index a986894ab236..5d7145edcacc 100644 --- a/src/object/properties.cr +++ b/src/object/properties.cr @@ -2,119 +2,9 @@ # WARNING: DO NOT EDIT MANUALLY! class Object - # Defines getter methods for each of the given arguments. + # Defines getter method(s) to access instance variable(s). # - # Writing: - # - # ``` - # class Person - # getter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def name - # @name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # getter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # getter name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name : String - # - # def name : String - # @name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # getter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name : String = "John Doe" - # - # def name : String - # @name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # getter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name = "John Doe" - # - # def name : String - # @name - # end - # end - # ``` - # - # If a block is given to the macro, a getter is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # getter(birth_date) { Time.local } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def birth_date - # if (value = @birth_date).nil? - # @birth_date = Time.local - # else - # value - # end - # end - # end - # ``` + # Refer to [Getters](#getters) for details. macro getter(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -149,99 +39,27 @@ class Object {% end %} end - # Defines query getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # getter? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def happy? - # @happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # getter? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # getter? happy : Bool - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # @happy : Bool - # - # def happy? : Bool - # @happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # getter? happy : Bool = true - # end - # ``` + # Identical to `getter` but defines query methods. # - # Is the same as writing: + # For example writing: # # ``` - # class Person - # @happy : Bool = true - # - # def happy? : Bool - # @happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # getter? happy = true + # class Robot + # getter? working # end # ``` # - # Is the same as writing: + # Is equivalent to writing: # # ``` - # class Person - # @happy = true - # - # def happy? - # @happy + # class Robot + # def working? + # @working # end # end # ``` # - # If a block is given to the macro, a getter is generated with a variable - # that is lazily initialized with the block's contents, for examples see - # `#getter`. + # Refer to [Getters](#getters) for general details. macro getter?(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -276,62 +94,38 @@ class Object {% end %} end - # Defines raise-on-nil and nilable getter methods for each of the given arguments. + # Similar to `getter` but defines both raise-on-nil methods as well as query + # methods that return a nilable value. # - # Writing: + # If a type is specified, then it will become a nilable type (union of the + # type and `Nil`). Unlike the other `getter` methods the value is always + # initialized to `nil`. There are no initial value or lazy initialization. # - # ``` - # class Person - # getter! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def name? - # @name - # end + # For example writing: # - # def name - # @name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # getter! :name, "age" - # end # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person + # class Robot # getter! name : String # end # ``` # - # is the same as writing: + # Is equivalent to writing: # # ``` - # class Person + # class Robot # @name : String? # - # def name? + # def name? : String? # @name # end # - # def name - # @name.not_nil! + # def name : String + # @name.not_nil!("Robot#name cannot be nil") # end # end # ``` + # + # Refer to [Getters](#getters) for general details. macro getter!(*names) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -358,91 +152,9 @@ class Object {% end %} end - # Defines setter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # setter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def name=(@name) - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # setter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # setter name : String - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # @name : String - # - # def name=(@name : String) - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # setter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: + # Generates setter methods to set instance variables. # - # ``` - # class Person - # @name : String = "John Doe" - # - # def name=(@name : String) - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # setter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name = "John Doe" - # - # def name=(@name) - # end - # end - # ``` + # Refer to [Setters](#setters) for general details. macro setter(*names) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -464,134 +176,10 @@ class Object {% end %} end - # Defines property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # property name - # end - # ``` - # - # Is the same as writing: + # Generates both `getter` and `setter` + # methods to access instance variables. # - # ``` - # class Person - # def name=(@name) - # end - # - # def name - # @name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # property :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # property name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name : String - # - # def name=(@name) - # end - # - # def name - # @name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # property name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name : String = "John Doe" - # - # def name=(@name : String) - # end - # - # def name - # @name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # property name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name = "John Doe" - # - # def name=(@name : String) - # end - # - # def name - # @name - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # property(birth_date) { Time.local } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def birth_date - # if (value = @birth_date).nil? - # @birth_date = Time.local - # else - # value - # end - # end - # - # def birth_date=(@birth_date) - # end - # end - # ``` + # Refer to the aforementioned macros for details. macro property(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -629,111 +217,10 @@ class Object {% end %} end - # Defines query property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # property? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def happy=(@happy) - # end - # - # def happy? - # @happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # property? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # property? happy : Bool - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @happy : Bool - # - # def happy=(@happy : Bool) - # end - # - # def happy? : Bool - # @happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # property? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @happy : Bool = true - # - # def happy=(@happy : Bool) - # end - # - # def happy? : Bool - # @happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # property? happy = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @happy = true + # Generates both `getter?` and `setter` + # methods to access instance variables. # - # def happy=(@happy) - # end - # - # def happy? - # @happy - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents, for examples see `#property`. + # Refer to the aforementioned macros for details. macro property?(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -771,68 +258,10 @@ class Object {% end %} end - # Defines raise-on-nil property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # property! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def name=(@name) - # end - # - # def name? - # @name - # end - # - # def name - # @name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # property! :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person - # property! name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @name : String? - # - # def name=(@name) - # end - # - # def name? - # @name - # end + # Generates both `getter!` and `setter` + # methods to access instance variables. # - # def name - # @name.not_nil! - # end - # end - # ``` + # Refer to the aforementioned macros for details. macro property!(*names) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -857,252 +286,33 @@ class Object end def {{var_name}}=(@{{var_name}}{% if type %} : {{type}} {% end %}) - end - - {% end %} - end - - # Defines getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # class_getter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.name - # @@name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # class_getter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # class_getter name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name : String - # - # def self.name : String - # @@name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # class_getter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name : String = "John Doe" - # - # def self.name : String - # @@name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # class_getter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name = "John Doe" - # - # def self.name : String - # @@name - # end - # end - # ``` - # - # If a block is given to the macro, a getter is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # class_getter(birth_date) { Time.local } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.birth_date - # if (value = @@birth_date).nil? - # @@birth_date = Time.local - # else - # value - # end - # end - # end - # ``` - macro class_getter(*names, &block) - {% for name in names %} - {% if name.is_a?(TypeDeclaration) %} - {% var_name = name.var.id %} - {% type = name.type %} - {% if block %} - @@{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %} - {% else %} - @@{{name}} - {% end %} - {% elsif name.is_a?(Assign) %} - {% var_name = name.target %} - {% type = nil %} - @@{{name}} - {% else %} - {% var_name = name.id %} - {% type = nil %} - {% end %} - - def self.{{var_name}} {% if type %} : {{type}} {% end %} - {% if block %} - if (%value = @@{{var_name}}).nil? - @@{{var_name}} = {{yield}} - else - %value - end - {% else %} - @@{{var_name}} - {% end %} - end - - {% end %} - end - - # Defines query getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # class_getter? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.happy? - # @@happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # class_getter? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # class_getter? happy : Bool - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # @@happy : Bool - # - # def self.happy? : Bool - # @@happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # class_getter? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@happy : Bool = true - # - # def self.happy? : Bool - # @@happy - # end - # end - # ``` + end + + {% end %} + end + + # Defines getter method(s) to access class variable(s). # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: + # For example writing: # # ``` - # class Person - # class_getter? happy = true + # class Robot + # class_getter backend # end # ``` # - # Is the same as writing: + # Is equivalent to writing: # # ``` - # class Person - # @@happy = true - # - # def self.happy? - # @@happy + # class Robot + # def self.backend + # @@backend # end # end # ``` # - # If a block is given to the macro, a getter is generated with a variable - # that is lazily initialized with the block's contents, for examples see - # `#class_getter`. - macro class_getter?(*names, &block) + # Refer to [Getters](#getters) for details. + macro class_getter(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} {% var_name = name.var.id %} @@ -1121,7 +331,7 @@ class Object {% type = nil %} {% end %} - def self.{{var_name}}? {% if type %} : {{type}} {% end %} + def self.{{var_name}} {% if type %} : {{type}} {% end %} {% if block %} if (%value = @@{{var_name}}).nil? @@{{var_name}} = {{yield}} @@ -1136,62 +346,93 @@ class Object {% end %} end - # Defines raise-on-nil and nilable getter methods for each of the given arguments. + # Identical to `class_getter` but defines query methods. # - # Writing: + # For example writing: # # ``` - # class Person - # class_getter! name + # class Robot + # class_getter? backend # end # ``` # - # Is the same as writing: + # Is equivalent to writing: # # ``` - # class Person - # def self.name? - # @@name - # end - # - # def self.name - # @@name.not_nil! + # class Robot + # def self.backend? + # @@backend # end # end # ``` # - # The arguments can be string literals, symbol literals or plain names: + # Refer to [Getters](#getters) for general details. + macro class_getter?(*names, &block) + {% for name in names %} + {% if name.is_a?(TypeDeclaration) %} + {% var_name = name.var.id %} + {% type = name.type %} + {% if block %} + @@{{var_name}} : {{type}}? {% if name.value %} = {{name.value}} {% end %} + {% else %} + @@{{name}} + {% end %} + {% elsif name.is_a?(Assign) %} + {% var_name = name.target %} + {% type = nil %} + @@{{name}} + {% else %} + {% var_name = name.id %} + {% type = nil %} + {% end %} + + def self.{{var_name}}? {% if type %} : {{type}} {% end %} + {% if block %} + if (%value = @@{{var_name}}).nil? + @@{{var_name}} = {{yield}} + else + %value + end + {% else %} + @@{{var_name}} + {% end %} + end + + {% end %} + end + + # Similar to `class_getter` but defines both raise-on-nil methods as well as + # query methods that return a nilable value. # - # ``` - # class Person - # class_getter! :name, "age" - # end - # ``` + # If a type is specified, then it will become a nilable type (union of the + # type and `Nil`). Unlike with `class_getter` the value is always initialized + # to `nil`. There are no initial value or lazy initialization. # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. + # For example writing: # # ``` - # class Person - # class_getter! name : String + # class Robot + # class_getter! backend : String # end # ``` # - # is the same as writing: + # Is equivalent to writing: # # ``` - # class Person - # @@name : String? + # class Robot + # @@backend : String? # - # def self.name? - # @@name + # def self.backend? : String? + # @@backend # end # - # def self.name - # @@name.not_nil! + # def backend : String + # @@backend.not_nil!("Robot.backend cannot be nil") # end # end # ``` + # + # Refer to [Getters](#getters) for general details. macro class_getter!(*names) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -1218,91 +459,28 @@ class Object {% end %} end - # Defines setter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # class_setter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.name=(@@name) - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # class_setter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # class_setter name : String - # end - # ``` - # - # is the same as writing: + # Generates setter method(s) to set class variable(s). # - # ``` - # class Person - # @@name : String - # - # def self.name=(@@name : String) - # end - # end - # ``` - # - # The type declaration can also include an initial value: + # For example writing: # # ``` - # class Person - # class_setter name : String = "John Doe" + # class Robot + # class_setter factories # end # ``` # - # Is the same as writing: + # Is equivalent to writing: # # ``` - # class Person - # @@name : String = "John Doe" + # class Robot + # @@factories # - # def self.name=(@@name : String) + # def self.factories=(@@factories) # end # end # ``` # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # class_setter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name = "John Doe" - # - # def self.name=(@@name) - # end - # end - # ``` + # Refer to [Setters](#setters) for general details. macro class_setter(*names) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -1324,134 +502,10 @@ class Object {% end %} end - # Defines property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # class_property name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.name=(@@name) - # end - # - # def self.name - # @@name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # class_property :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # class_property name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name : String - # - # def self.name=(@@name) - # end - # - # def self.name - # @@name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # class_property name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name : String = "John Doe" - # - # def self.name=(@@name : String) - # end - # - # def self.name - # @@name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: - # - # ``` - # class Person - # class_property name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name = "John Doe" - # - # def self.name=(@@name : String) - # end - # - # def self.name - # @@name - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # class_property(birth_date) { Time.local } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.birth_date - # if (value = @@birth_date).nil? - # @@birth_date = Time.local - # else - # value - # end - # end + # Generates both `class_getter` and `class_setter` + # methods to access instance variables. # - # def self.birth_date=(@@birth_date) - # end - # end - # ``` + # Refer to the aforementioned macros for details. macro class_property(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -1489,111 +543,10 @@ class Object {% end %} end - # Defines query property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # class_property? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.happy=(@@happy) - # end - # - # def self.happy? - # @@happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # class_property? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # class_property? happy : Bool - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@happy : Bool - # - # def self.happy=(@@happy : Bool) - # end - # - # def self.happy? : Bool - # @@happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # class_property? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@happy : Bool = true - # - # def self.happy=(@@happy : Bool) - # end - # - # def self.happy? : Bool - # @@happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferable from the initial value: + # Generates both `class_getter?` and `class_setter` + # methods to access instance variables. # - # ``` - # class Person - # class_property? happy = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@happy = true - # - # def self.happy=(@@happy) - # end - # - # def self.happy? - # @@happy - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents, for examples see `#class_property`. + # Refer to the aforementioned macros for details. macro class_property?(*names, &block) {% for name in names %} {% if name.is_a?(TypeDeclaration) %} @@ -1631,68 +584,10 @@ class Object {% end %} end - # Defines raise-on-nil property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # class_property! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def self.name=(@@name) - # end - # - # def self.name? - # @@name - # end - # - # def self.name - # @@name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # class_property! :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person - # class_property! name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # @@name : String? - # - # def self.name=(@@name) - # end - # - # def self.name? - # @@name - # end + # Generates both `class_getter!` and `class_setter` + # methods to access instance variables. # - # def self.name - # @@name.not_nil! - # end - # end - # ``` + # Refer to the aforementioned macros for details. macro class_property!(*names) {% for name in names %} {% if name.is_a?(TypeDeclaration) %}