Skip to content

Commit

Permalink
More documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Oct 30, 2024
1 parent c17cdb6 commit 9dec1fd
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 5 deletions.
51 changes: 50 additions & 1 deletion lib/xrb/buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,68 @@
# Copyright, 2016-2024, by Samuel Williams.

module XRB
# Represents a string buffer with a given path.
class Buffer
# Create a new buffer from a string.
# @parameter string [String] the string to use as the buffer.
# @parameter path [String] the path to use for the buffer.
def initialize(string, path: "<string>")
@string = string
@path = path
end

# @attribute [String] the path name of the buffer.
attr :path

def freeze
return self if frozen?

@string.freeze
@path.freeze

super
end

# @returns [Encoding] the encoding of the buffer.
def encoding
@string.encoding
end

# @returns [String] the content of the buffer.
def read
@string
end

# Create a buffer from the specified file.
# @parameter path [String] the path to load the file from.
# @returns [Buffer] a buffer with the contents of the specified path.
def self.load_file(path)
FileBuffer.new(path).freeze
end

# Create a buffer from the specified string.
# @parameter string [String] the string to load into the buffer.
# @returns [Buffer] a buffer with the contents of the specified string.
def self.load(string)
Buffer.new(string).freeze
end

# @returns [Buffer] itself.
def to_buffer
self
end
end

# Represents a file buffer with a given path.
class FileBuffer
# Create a new file buffer from a file path.
# @parameter path [String] the path to the file.
def initialize(path)
@path = path
@cache = nil
end

# Freeze the buffer, caching the contents of the file.
def freeze
return self if frozen?

Expand All @@ -46,27 +74,38 @@ def freeze
super
end

# @attribute [String] the path name of the buffer.
attr :path

# @returns [Encoding] the encoding of the buffer.
def encoding
read.encoding
end

# Read the contents of the file into the buffer.
# @returns [String] the content of the buffer.
def read
@cache ||= File.read(@path).freeze
end

# @returns [Buffer] a buffer with the contents of the file.
def to_buffer
Buffer.new(self.read, @path)
end
end

# Represents an IO buffer with a given path.
class IOBuffer
# Create a new IO buffer from an IO object.
# @parameter io [IO] the IO object to use as the buffer.
# @parameter path [String] the path to use for the buffer.
def initialize(io, path: io.inspect)
@io = io
@path = path
@cache = nil
end

# Freeze the buffer, caching the contents of the IO object.
def freeze
return self if frozen?

Expand All @@ -75,21 +114,31 @@ def freeze
super
end

# @attribute [String] the path name of the buffer.
attr :path

# @returns [Encoding] the encoding of the buffer.
def encoding
read.encoding
end

# Read the contents of the IO object into the buffer.
def read
@cache ||= @io.read.freeze
unless @cache
@cache = @io.read.freeze
@io.close
end

return @cache
end

# @returns [Buffer] a buffer with the contents of the IO object.
def to_buffer
Buffer.new(self.read, path: @path)
end
end

# Convert the given value to a buffer.
def self.Buffer(value)
case value
when String
Expand Down
20 changes: 18 additions & 2 deletions lib/xrb/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ module XRB
class Builder
INDENT = "\t"

# Represents a lazy fragment of markup that can be generated on demand.
class Fragment
# Initialize the fragment with a block that will be called to generate the markup.
# @parameter block [Proc] the block that will be called to generate the markup.
def initialize(block)
@block = block
@builder = nil
end

# Append the markup to the given output.
def append_markup(output)
builder = Builder.new(output)

Expand All @@ -25,19 +29,25 @@ def append_markup(output)
return builder.output
end

# Build the markup using the given builder.
def build_markup(builder)
@block.call(builder)
end

# Convert the fragment to a string.
# @returns [MarkupString] the markup generated by the fragment.
def to_s
self.append_markup(nil)
end

# Compare the fragment to another object as a string. Primarily useful for testing.
def == other
# This is a bit of a hack... but is required for existing specs to pass:
self.to_s == other.to_s
end

# Assuming the fragment is generated in the context of a template being rendered, append the fragment to the output buffer.
# @parameter block [Proc] the block which is within the scope of the template being rendered.
def >> block
if block
Template.buffer(block.binding) << self
Expand Down Expand Up @@ -84,12 +94,15 @@ def initialize(output = nil, indent: true, encoding: Encoding::UTF_8)
@children = [0]
end

# Append the markup to the given output.
def build_markup(builder)
builder.append(@output)
end

# @attribute [Object] the output buffer.
attr :output

# @returns [Encoding] the encoding of the output.
def encoding
@output.encoding
end
Expand All @@ -113,16 +126,17 @@ def indentation
end
end

# Append a doctype declaration to the output.
def doctype(attributes = "html")
@output << "<!doctype #{attributes}>\n"
end

# Begin a block tag.
# Append a tag to the output.
def tag(name, attributes = {}, &block)
full_tag(name, attributes, @indent, @indent, &block)
end

# Begin an inline tag.
# Append a tag to the output without indentation or whitespace.
def inline_tag(name, attributes = {}, &block)
original_indent = @indent

Expand All @@ -145,6 +159,7 @@ def inline!
@indent = original_indent
end

# Append text to the output, escaping it if necessary.
def text(content)
return unless content

Expand All @@ -164,6 +179,7 @@ def raw(content)
@output << content
end

# Append content to the output.
def <<(content)
content&.build_markup(self)
end
Expand Down
22 changes: 20 additions & 2 deletions lib/xrb/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def self.capture(*arguments, output: nil, &block)
return output.to_str
end

# Returns the buffer used for capturing output.
# @returns [Object] the buffer used for capturing output.
def self.buffer(binding)
binding.local_variable_get(OUT)
end
Expand Down Expand Up @@ -72,15 +72,19 @@ def expression(code)
end
end

# Load a template from a file.
def self.load_file(path, **options)
self.new(FileBuffer.new(path), **options).freeze
end

# Load a template from a string.
def self.load(string, *arguments, **options)
self.new(Buffer.new(string), **options).freeze
end

# @param binding [Binding] The binding in which the template is compiled. e.g. `TOPLEVEL_BINDING`.
# Initialize a new template.
# @parameter buffer [Buffer] The buffer containing the template.
# @parameter binding [Binding] The binding in which the template is compiled. e.g. `TOPLEVEL_BINDING`.
def initialize(buffer, binding: BINDING)
@buffer = buffer
@binding = binding
Expand All @@ -96,14 +100,23 @@ def freeze
super
end

# The compiled code for the template.
# @returns [String] the compiled Ruby code.
def code
@code ||= compile!
end

# The compiled template as a proc.
# @parameter scope [Object] The scope in which the template will be compiled.
# @returns [Proc] a proc that can be called with an output object.
def compiled(scope = @binding.dup)
@compiled ||= eval("\# frozen_string_literal: true\nproc{|#{OUT}|;#{code}}", scope, @buffer.path, 0).freeze
end

# Renders the template to a string.
#
# @parameter scope [Object] The scope in which the template will be evaluated.
# @parameter output [String | Nil] The output string to append to.
def to_string(scope = Object.new, output = nil)
builder = Builder.new(output, encoding: code.encoding)

Expand All @@ -112,10 +125,15 @@ def to_string(scope = Object.new, output = nil)
return builder.output
end

# Renders the template to a buffer.
def to_buffer(scope)
Buffer.new(to_string(scope), path: @buffer.path)
end

# Convert the template to a proc that can be called with an output object. The proc renders the template using `to_string` and writes the output to the given output object. The output should implement `<<` and `close_write(error = nil)` methods.
#
# @parameter scope [Object] The scope in which the template will be evaluated.
# @returns [Proc] a proc that can be called with an output object.
def to_proc(scope = @binding.dup)
proc do |output|
to_string(scope, output)
Expand Down

0 comments on commit 9dec1fd

Please sign in to comment.