To simplify the creation of dependency trees for options, we are introducing a new class; OptionTreeGenerator. It provides a very simple interface. You can call add_option to add a new option and if you specify a parent, it will be added as a child option of that parent. At the end, you can call serialize, which returns 2 useful constructs; a dependency tree with all possible options and paths from parents to reach specified option node, both are used in the frontend to render the form.
62 lines
1.6 KiB
Ruby
62 lines
1.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class OptionTreeGenerator
|
|
def initialize
|
|
@options = []
|
|
@parents = {}
|
|
end
|
|
|
|
def add_option(name:, values: nil, parent: nil, &check)
|
|
@options << {name:, values:, parent:, check:}
|
|
end
|
|
|
|
def build_subtree(option, path)
|
|
return unless option[:values]
|
|
|
|
subtree = {}
|
|
Array(option[:values]).each do |value|
|
|
next if option[:check] && !option[:check].call(*path, value)
|
|
child_options = @options.select { |opt| opt[:parent] == option[:name] }
|
|
subtree[value] = child_options.map do |child_option|
|
|
@parents[child_option[:name]] = @parents[option[:name]] + [option[:name]]
|
|
[child_option[:name], build_subtree(child_option, path + [value])]
|
|
end.to_h
|
|
end
|
|
|
|
subtree
|
|
end
|
|
|
|
def serialize
|
|
option_tree = {}
|
|
@options.each do |option|
|
|
if option[:parent].nil?
|
|
@parents[option[:name]] = []
|
|
option_tree[option[:name]] = build_subtree(option, [])
|
|
end
|
|
end
|
|
|
|
[option_tree, @parents]
|
|
end
|
|
|
|
def self.generate_allowed_options(name, option_tree, parents)
|
|
allowed_options = []
|
|
|
|
traverse = lambda do |tree, path_to_follow, current_path|
|
|
if path_to_follow.empty?
|
|
allowed_options << current_path
|
|
else
|
|
current_node = path_to_follow.first
|
|
tree[current_node].keys.each do |option|
|
|
new_path = current_path.dup
|
|
new_path[path_to_follow.first] = option
|
|
traverse.call(tree[path_to_follow.first][option], path_to_follow[1..], new_path)
|
|
end
|
|
end
|
|
end
|
|
|
|
traverse.call(option_tree, parents[name] + [name], {})
|
|
|
|
allowed_options
|
|
end
|
|
end
|