AshReports.RenderContext (ash_reports v0.1.0)

Immutable context structure for managing render state and data transformation.

The RenderContext serves as the central data structure for the Phase 3.1 Renderer Interface, providing a comprehensive context for rendering operations that includes report metadata, processed data, variable values, layout information, and rendering configuration.

Key Features

  • Immutable State: All context operations return new context instances
  • Data Integration: Seamless integration with Phase 2 DataLoader results
  • Layout Management: Support for band-based layout calculations
  • Variable Resolution: Access to resolved variable values from VariableState
  • Error Context: Comprehensive error tracking and recovery
  • Type Safety: Strict type definitions for reliable rendering

Context Structure

The RenderContext contains:

  • Report Definition: The report struct with bands, elements, and metadata
  • Processed Data: Records from DataLoader with group and variable processing
  • Internationalization: Locale information, text direction, and formatting metadata
  • Layout State: Current layout calculations and positioning information
  • Render Configuration: Output format, styling, and rendering options
  • Variable Values: Resolved variable values from Phase 2 processing
  • Group State: Current group processing state and break information
  • Error State: Any errors encountered during rendering

Usage Patterns

Basic Context Creation

context = RenderContext.new(report, data_result)

With Custom Configuration

config = %RenderConfig{
  format: :html,
  page_size: {8.5, 11},
  margins: {0.5, 0.5, 0.5, 0.5}
}

context = RenderContext.new(report, data_result, config)

Context Transformation

context
|> RenderContext.set_current_band(band)
|> RenderContext.add_layout_info(layout_data)
|> RenderContext.update_variable_context(new_values)

Locale Management

# Create context with specific locale
config = %{locale: "fr", format: :html}
context = RenderContext.new(report, data_result, config)

# Change locale during rendering
context = RenderContext.set_locale(context, "de")

# Get locale information
locale = RenderContext.get_locale(context)
direction = RenderContext.get_text_direction(context)
is_rtl = RenderContext.rtl?(context)

Error Handling

case RenderContext.validate(context) do
  {:ok, validated_context} -> proceed_with_rendering(validated_context)
  {:error, validation_errors} -> handle_errors(validation_errors)
end

Integration with Phase 2

RenderContext seamlessly integrates with DataLoader results:

{:ok, data_result} = DataLoader.load_report(domain, :sales_report, params)
context = RenderContext.from_data_result(report, data_result)

Summary

Functions

Adds an error to the context.

Adds a rendered element to the context.

Adds a warning to the context.

Creates a RenderContext from a DataLoader result.

Gets the current record or a field from the current record.

Gets the current locale for the context.

Gets locale-specific metadata for formatting.

Gets the next record in the sequence.

Gets the text direction for the current locale.

Gets a variable value from the context.

Checks if there are more records to process.

Gets CSS classes appropriate for the current locale.

Creates a new RenderContext from a report and data result.

Removes an element from the pending elements list.

Resets the context for a new rendering pass.

Determines if the context uses a right-to-left locale.

Sets the current band being rendered.

Sets the current group being processed.

Sets the current record being processed.

Sets the locale for the context and updates related locale metadata.

Updates the layout state with new layout information.

Updates the locale metadata in the context.

Updates the current rendering position.

Validates the context for rendering operations.

Types

t()

@type t() :: %AshReports.RenderContext{
  config: RenderConfig.t(),
  created_at: DateTime.t(),
  current_band: AshReports.Band.t() | nil,
  current_group: AshReports.Group.t() | nil,
  current_position: %{x: number(), y: number()},
  current_record: map() | nil,
  current_record_index: non_neg_integer(),
  data_result: map(),
  errors: [map()],
  groups: %{required(term()) => map()},
  layout_state: map(),
  locale: String.t(),
  locale_metadata: map(),
  metadata: map(),
  page_dimensions: %{width: number(), height: number()},
  pending_elements: [AshReports.Element.t()],
  records: [map()],
  rendered_elements: [map()],
  report: AshReports.Report.t(),
  text_direction: String.t(),
  updated_at: DateTime.t(),
  variables: %{required(atom()) => term()},
  warnings: [map()]
}

Functions

add_error(context, error)

@spec add_error(t(), map()) :: t()

Adds an error to the context.

Examples

error = %{type: :layout_error, message: "Element overflow", element: element}
context = RenderContext.add_error(context, error)

add_rendered_element(context, rendered_element)

@spec add_rendered_element(t(), map()) :: t()

Adds a rendered element to the context.

Examples

rendered_element = %{type: :label, content: "Total", position: {100, 50}}
context = RenderContext.add_rendered_element(context, rendered_element)

add_warning(context, warning)

@spec add_warning(t(), map()) :: t()

Adds a warning to the context.

Examples

warning = %{type: :performance_warning, message: "Large dataset detected"}
context = RenderContext.add_warning(context, warning)

from_data_result(report, data_result, config \\ %{})

@spec from_data_result(AshReports.Report.t(), map(), map()) :: t()

Creates a RenderContext from a DataLoader result.

Convenience function for creating context from Phase 2 DataLoader output.

Examples

{:ok, data_result} = DataLoader.load_report(domain, :report_name, params)
context = RenderContext.from_data_result(report, data_result)

get_current_record(context, field \\ nil, default \\ nil)

@spec get_current_record(t(), atom() | nil, term()) :: term()

Gets the current record or a field from the current record.

Examples

record = RenderContext.get_current_record(context)
name = RenderContext.get_current_record(context, :customer_name)

get_locale(render_context)

@spec get_locale(t()) :: String.t()

Gets the current locale for the context.

Examples

locale = RenderContext.get_locale(context)

get_locale_metadata(render_context)

@spec get_locale_metadata(t()) :: map()

Gets locale-specific metadata for formatting.

Examples

metadata = RenderContext.get_locale_metadata(context)
# => %{decimal_separator: ".", thousands_separator: ",", ...}

get_next_record(context)

@spec get_next_record(t()) :: {:ok, map(), t()} | {:error, :no_more_records}

Gets the next record in the sequence.

Examples

case RenderContext.get_next_record(context) do
  {:ok, next_record, new_context} -> process_record(next_record, new_context)
  {:error, :no_more_records} -> finish_rendering()
end

get_text_direction(render_context)

@spec get_text_direction(t()) :: String.t()

Gets the text direction for the current locale.

Examples

direction = RenderContext.get_text_direction(context)
# => "ltr" or "rtl"

get_variable(context, variable_name, default \\ nil)

@spec get_variable(t(), atom(), term()) :: term()

Gets a variable value from the context.

Examples

total = RenderContext.get_variable(context, :total_amount)
count = RenderContext.get_variable(context, :record_count, 0)

has_more_records?(context)

@spec has_more_records?(t()) :: boolean()

Checks if there are more records to process.

Examples

if RenderContext.has_more_records?(context) do
  process_next_record(context)
end

locale_css_classes(context)

@spec locale_css_classes(t()) :: [String.t()]

Gets CSS classes appropriate for the current locale.

Examples

classes = RenderContext.locale_css_classes(context)
# => ["dir-ltr", "locale-en"]

new(report, data_result, config \\ %{})

@spec new(AshReports.Report.t(), map(), map()) :: t()

Creates a new RenderContext from a report and data result.

Examples

context = RenderContext.new(report, data_result)
context = RenderContext.new(report, data_result, config)

remove_pending_element(context, element)

@spec remove_pending_element(t(), AshReports.Element.t()) :: t()

Removes an element from the pending elements list.

Examples

context = RenderContext.remove_pending_element(context, element)

reset_for_new_pass(context)

@spec reset_for_new_pass(t()) :: t()

Resets the context for a new rendering pass.

Examples

fresh_context = RenderContext.reset_for_new_pass(context)

rtl?(render_context)

@spec rtl?(t()) :: boolean()

Determines if the context uses a right-to-left locale.

Examples

RenderContext.rtl?(context)
# => true or false

set_current_band(context, band)

@spec set_current_band(t(), AshReports.Band.t()) :: t()

Sets the current band being rendered.

Examples

context = RenderContext.set_current_band(context, band)

set_current_group(context, group)

@spec set_current_group(t(), AshReports.Group.t()) :: t()

Sets the current group being processed.

Examples

context = RenderContext.set_current_group(context, group)

set_current_record(context, record, index)

@spec set_current_record(t(), map(), non_neg_integer()) :: t()

Sets the current record being processed.

Examples

context = RenderContext.set_current_record(context, record, index)

set_locale(context, locale)

@spec set_locale(t(), String.t()) :: t()

Sets the locale for the context and updates related locale metadata.

Examples

context = RenderContext.set_locale(context, "fr")

update_layout_state(context, layout_info)

@spec update_layout_state(t(), map()) :: t()

Updates the layout state with new layout information.

Examples

layout_info = %{band_height: 50, element_positions: positions}
context = RenderContext.update_layout_state(context, layout_info)

update_locale_metadata(context, new_metadata)

@spec update_locale_metadata(t(), map()) :: t()

Updates the locale metadata in the context.

Examples

metadata = %{custom_format: "yyyy-MM-dd"}
context = RenderContext.update_locale_metadata(context, metadata)

update_position(context, position)

@spec update_position(t(), %{x: number(), y: number()}) :: t()

Updates the current rendering position.

Examples

context = RenderContext.update_position(context, %{x: 100, y: 200})

validate(context)

@spec validate(t()) :: {:ok, t()} | {:error, [map()]}

Validates the context for rendering operations.

Checks that all required data is present and valid for rendering.

Examples

case RenderContext.validate(context) do
  {:ok, context} -> proceed_with_rendering(context)
  {:error, errors} -> handle_validation_errors(errors)
end