class Schematrix::Generators::Base
Base class for all output generators. A generator renders the compiled record of one object schema as one file: an ERB template filled through a template helper class, then formatted with SyntaxTree. Subclasses provide the template and, optionally, override helper methods to produce different output formats.
The shared rendering lives in the Templates modules: Templates::RubyHelpers (constructor, from_h/to_h), Templates::Coercions (the coerce_ methods), Templates::SorbetHelpers and Templates::TypeComposer (type algebra for the Sorbet and RBS notations), and Templates::Base (declaration scaffolding).
Constants
- IMMUTABLE_RESERVED_METHOD_NAMES
-
Method names the immutable surface adds on top of the property readers: the copier, the catch-all keyword and its protected reader, and the digest half of the value equality. A property folding onto one takes the keyword treatment, with ORIGINAL_KEYS still recording it.
- RESERVED_METHOD_NAMES
- RUBY_KEYWORDS
-
Ruby keywords that cannot appear as generated accessor names or keyword arguments; property_name suffixes them with “_”.
Attributes
Public Class Methods
Source
# File lib/schematrix/generators/base.rb, line 148 def self.enum_generator? = false
Source
# File lib/schematrix/generators/base.rb, line 256 def self.enum_template_class raise NotImplementedError, "#{name} must implement #enum_template_class" end
Source
# File lib/schematrix/generators/base.rb, line 252 def self.enum_template_name raise NotImplementedError, "#{name} must implement #enum_template_name" end
# File lib/schematrix/generators/base.rb, line 231 def self.erb(filename = template_name) @erbs ||= {} @erbs[filename] ||= begin template_file = File.join(File.dirname(__FILE__), "templates", filename) ERB.new(File.read(template_file), trim_mode: "-") end end
The ERB object for this generator’s template file, compiled once per class.
Source
# File lib/schematrix/generators/base.rb, line 261 def self.file_extension raise NotImplementedError, "#{name} must implement #file_extension" end
File extension of this generator’s output, including the dot.
Source
# File lib/schematrix/generators/base.rb, line 153 def self.immutable_supported? = true
Whether this generator’s output honors the immutable option. The data generators do; the DSL builders answer through DslNaming.
Source
# File lib/schematrix/generators/base.rb, line 170 def self.maybe_flavor = :sorbet
Which rendering of the generated Maybe module the generator’s output needs: with Sorbet signatures, without, or as RBS signatures alone.
Source
# File lib/schematrix/generators/base.rb, line 175 def self.maybe_supported? = true
Whether the generator family accepts the maybe optionals. The DSL builders do not: absence is expressed by never calling a setter, so a Maybe slot has no builder surface to fill.
# File lib/schematrix/generators/base.rb, line 117 def initialize( output_dir, module_name, format: true, command: nil, scalar_types: {}, immutable: false, optionals: :nilable, maybe: nil, enums: nil ) assert_namespace_valid(module_name) assert_immutable_supported if immutable @output_dir = output_dir @module_name = module_name.delete_prefix("::") @format = format @command = command @scalar_types = Schematrix::BUILTIN_SCALAR_TYPES.merge(scalar_types) @immutable = immutable @optionals = self.class.partial? ? :maybe : optionals @maybe = maybe @enums = enums assert_enums_valid assert_optionals_valid assert_maybe_valid assert_coercers_valid(@scalar_types) assert_readers_valid(@scalar_types) end
output_dir is the directory files are written to (created on demand), module_name the namespace generated code is placed in. Each namespace segment must be a valid Ruby constant name. An initial :: is allowed and removed before rendering. format: false skips SyntaxTree formatting and emits the template output as-is. command names the regenerating command in the written files’ banner; nil leaves that line out. scalar_types maps [type, format] pairs to ScalarType renderings of the project’s own; it merges over BUILTIN_SCALAR_TYPES. A coercer must be a named module answering coerce(value), since the generated code calls #{coercer.name}.coerce(value): anything else fails here, at construction, before any file can be written. immutable: true renders data classes as frozen value objects: readers instead of accessors, a with copier, and value equality. It applies to the data generators only; the DSL builders refuse it. optionals: :maybe types every property not listed in required with the Maybe type instead of a nilable one, so a loaded document keeps the missing-key versus null distinction through dump. The default, :nilable, is the historical output. maybe: supplies the project’s own Maybe module; it must answer .from(value) and .empty, and its instances present? and when_present. Without it the generator needs the generated Maybe written into the output directory, which the pipeline does once per directory through the run-once machinery the auxiliary files generator established.
Source
# File lib/schematrix/generators/base.rb, line 165 def self.partial? = false
Whether the generator emits the partial class family: every slot wraps in the Maybe type, required ones included. The option is the whole point of those generators, so it is not configurable there.
Source
# File lib/schematrix/generators/base.rb, line 159 def self.run_once? = false
Whether the generator writes one file per compiled locator (the default) or one set of auxiliary files per run. The pipeline iterates locators for the former and hands the whole compiled record set once to the latter.
Source
# File lib/schematrix/generators/base.rb, line 242 def self.template_class raise NotImplementedError, "#{name} must implement #template_class" end
Name of the template helper class that fills this generator’s ERB template.
Source
# File lib/schematrix/generators/base.rb, line 248 def self.template_name raise NotImplementedError, "#{name} must implement #template_name" end
Name of the ERB file under generators/templates this generator renders through.
Public Instance Methods
Source
# File lib/schematrix/generators/base.rb, line 270 def maybe_needed? !self.class.enum_generator? && @optionals == :maybe && @maybe.nil? end
Source
# File lib/schematrix/generators/base.rb, line 268 def support_generators = []
Source
# File lib/schematrix/generators/base.rb, line 179 def target_path(locator) class_name = class_name_from_path(URI(locator)) File.join(@output_dir, target_filename(class_name)) end
The file this locator’s class writes to, without writing it: callers that batch many schemas check every target up front.
# File lib/schematrix/generators/base.rb, line 212 def transform(path, class_name, object, enums: @enums || :unions) filename = ( if object.enum_class? self.class.enum_template_name else self.class.template_name end ) code = template(path, class_name, object, enums:).render( self.class.erb(filename), ) format_code(code) end
Fills the ERB template through this generator’s template class and formats the result. Returns the file content.
# File lib/schematrix/generators/base.rb, line 184 def validate_enum_members(locator, object) uri = URI(locator) template(uri, class_name_from_path(uri), object) nil end
# File lib/schematrix/generators/base.rb, line 193 def write(locator, object, enums: @enums || :unions) uri = URI(locator) class_name = class_name_from_path(uri) code = with_banner( transform(uri, class_name, object, enums:), class_name, banner_kind, ) file_path = File.join(@output_dir, target_filename(class_name)) FileUtils.mkdir_p(File.dirname(file_path)) File.write(file_path, code) file_path end
Renders, formats, and writes the file for this locator’s class, creating the output directory when missing. The file carries the autogenerated banner. Returns the path written.