lib/fbe/bylaws.rb



# frozen_string_literal: true

# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
# SPDX-License-Identifier: MIT

require 'liquid'
require_relative '../fbe'

# Generates policies/bylaws from Liquid templates.
#
# Using the templates stored in the +assets/bylaws+ directory, this function
# creates a hash where keys are bylaw names (derived from filenames) and values
# are the rendered formulas. Templates can use three parameters to control
# the strictness and generosity of the bylaws.
#
# @param [Integer] anger Strictness level for punishments (0-4, default: 2)
#   - 0: Very lenient, minimal punishments
#   - 2: Balanced approach (default)
#   - 4: Very strict, maximum punishments
# @param [Integer] love Generosity level for rewards (0-4, default: 2)
#   - 0: Minimal rewards
#   - 2: Balanced rewards (default)
#   - 4: Maximum rewards
# @param [Integer] paranoia Requirements threshold for rewards (1-4, default: 2)
#   - 1: Easy to earn rewards
#   - 2: Balanced requirements (default)
#   - 4: Very difficult to earn rewards
# @return [Hash<String, String>] Hash mapping bylaw names to their formulas
# @raise [RuntimeError] If parameters are out of valid ranges
# @example Generate balanced bylaws
#   bylaws = Fbe.bylaws(anger: 2, love: 2, paranoia: 2)
#   bylaws['bug-report-was-rewarded']
#   # => "award { 2 * love * paranoia }"
# @example Generate strict bylaws with minimal rewards
#   bylaws = Fbe.bylaws(anger: 4, love: 1, paranoia: 3)
#   bylaws['dud-was-punished']
#   # => "award { -16 * anger }"
def Fbe.bylaws(anger: 2, love: 2, paranoia: 2)
  raise "The 'anger' must be in the [0..4] interval: #{anger.inspect}" unless !anger.negative? && anger < 5
  raise "The 'love' must be in the [0..4] interval: #{love.inspect}" unless !love.negative? && love < 5
  raise "The 'paranoia' must be in the [1..4] interval: #{paranoia.inspect}" unless paranoia.positive? && paranoia < 5
  home = File.join(__dir__, '../../assets/bylaws')
  raise "The directory with templates is absent #{home.inspect}" unless File.exist?(home)
  Dir[File.join(home, '*.liquid')].to_h do |f|
    formula = Liquid::Template.parse(File.read(f)).render(
      'anger' => anger, 'love' => love, 'paranoia' => paranoia
    )
    [File.basename(f).gsub(/\.liquid$/, ''), formula]
  end
end