Class: Ukiryu::Definition::DefinitionComposer
- Inherits:
-
Object
- Object
- Ukiryu::Definition::DefinitionComposer
- Defined in:
- lib/ukiryu/definition/definition_composer.rb
Overview
Compose tool definitions from multiple sources
This class handles merging and composing tool definitions, allowing for profile inheritance and command mixins.
Constant Summary collapse
- MERGE_STRATEGIES =
Merge strategies
%i[override replace prepend append].freeze
Class Method Summary collapse
-
.compose(definition, loader: nil) ⇒ Models::ToolDefinition
Compose a definition with its includes/inherits.
-
.find_profile(profiles, name) ⇒ Object?
Find a profile by name.
-
.load_base_definition(spec) ⇒ Models::ToolDefinition?
Load a base definition from a spec.
-
.merge_commands_by_name(base_commands, addition_commands) ⇒ Array
Merge commands by name.
-
.merge_definitions(base, addition, strategy: :override) ⇒ Models::ToolDefinition
Merge two definitions.
-
.merge_profile_content(base, addition) ⇒ Object
Merge the content of two profiles.
-
.merge_profiles_by_name(base_profiles, addition_profiles) ⇒ Array
Merge profiles by name.
-
.process_includes(definition) ⇒ Models::ToolDefinition
Process includes clauses.
-
.process_inherits(definition) ⇒ Models::ToolDefinition
Process inherits clauses.
-
.validate_composable(definition) ⇒ Array<String>
Validate that a definition can be composed.
Class Method Details
.compose(definition, loader: nil) ⇒ Models::ToolDefinition
Compose a definition with its includes/inherits
18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 18 def self.compose(definition, loader: nil) @loader = loader || Loader # Process inherits first (base definitions) definition = process_inherits(definition) if definition.respond_to?(:inherits) && definition.inherits # Process includes (additions) definition = process_includes(definition) if definition.respond_to?(:includes) && definition.includes definition end |
.find_profile(profiles, name) ⇒ Object?
Find a profile by name
222 223 224 225 226 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 222 def self.find_profile(profiles, name) return nil unless profiles profiles.find { |p| p.name == name } end |
.load_base_definition(spec) ⇒ Models::ToolDefinition?
Load a base definition from a spec
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 68 def self.load_base_definition(spec) case spec when String # Just a tool name, find latest version = Discovery.find(spec) &.load_definition when Hash tool = spec[:tool] || spec['tool'] version = spec[:version] || spec['version'] = if version Discovery.find(tool, version) else Discovery.find(tool) end &.load_definition end end |
.merge_commands_by_name(base_commands, addition_commands) ⇒ Array
Merge commands by name
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 199 def self.merge_commands_by_name(base_commands, addition_commands) merged = base_commands.dup addition_commands.each do |addition_command| existing_index = merged.find_index { |c| c.name == addition_command.name } if existing_index # Override existing command merged[existing_index] = addition_command.dup else # Add new command merged << addition_command.dup end end merged end |
.merge_definitions(base, addition, strategy: :override) ⇒ Models::ToolDefinition
Merge two definitions
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 94 def self.merge_definitions(base, addition, strategy: :override) # Create a merged definition by deep copying and merging merged = base.dup # Merge profiles if addition.profiles merged.profiles ||= [] case strategy when :replace merged.profiles = addition.profiles.dup when :override # Merge by profile name, addition overrides base merged.profiles = merge_profiles_by_name(base.profiles || [], addition.profiles) when :append # Append addition profiles merged.profiles = (base.profiles || []) + addition.profiles when :prepend # Prepend addition profiles merged.profiles = addition.profiles + (base.profiles || []) end end # Merge commands if profiles have them merged.profiles&.each do |profile| # Find corresponding profile in addition addition_profile = find_profile(addition.profiles, profile.name) next unless addition_profile # Merge commands next unless addition_profile.commands profile.commands ||= [] case strategy when :replace profile.commands = addition_profile.commands.dup when :override profile.commands = merge_commands_by_name(profile.commands, addition_profile.commands) when :append profile.commands = profile.commands + addition_profile.commands when :prepend profile.commands = addition_profile.commands + profile.commands end end merged end |
.merge_profile_content(base, addition) ⇒ Object
Merge the content of two profiles
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 171 def self.merge_profile_content(base, addition) # Create a new profile that merges both merged = base.dup # Merge commands merged.commands = merge_commands_by_name(base.commands || [], addition.commands) if addition.commands # Merge other arrays if they exist %i[environment options flags arguments].each do |attr| if addition.respond_to?(attr) && addition.send(attr) base_items = base.respond_to?(attr) ? base.send(attr) : [] merged.send("#{attr}=", base_items + addition.send(attr)) end end # Override scalars %i[description option_style].each do |attr| merged.send("#{attr}=", addition.send(attr)) if addition.respond_to?(attr) && !addition.send(attr).nil? end merged end |
.merge_profiles_by_name(base_profiles, addition_profiles) ⇒ Array
Merge profiles by name
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 147 def self.merge_profiles_by_name(base_profiles, addition_profiles) merged = base_profiles.dup addition_profiles.each do |addition_profile| existing_index = merged.find_index { |p| p.name == addition_profile.name } if existing_index # Merge the profile content existing = merged[existing_index] merged[existing_index] = merge_profile_content(existing, addition_profile) else # Add new profile merged << addition_profile.dup end end merged end |
.process_includes(definition) ⇒ Models::ToolDefinition
Process includes clauses
51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 51 def self.process_includes(definition) includes = definition.includes || [] return definition if includes.empty? # Load included definitions and merge them includes.reduce(definition) do |current, include_spec| include_def = load_base_definition(include_spec) next current unless include_def merge_definitions(current, include_def, strategy: :append) end end |
.process_inherits(definition) ⇒ Models::ToolDefinition
Process inherits clauses
34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 34 def self.process_inherits(definition) inherits = definition.inherits || [] return definition if inherits.empty? # Load base definitions and merge them inherits.reverse.reduce(definition) do |current, inherit_spec| base_def = load_base_definition(inherit_spec) next current unless base_def merge_definitions(base_def, current, strategy: :replace) end end |
.validate_composable(definition) ⇒ Array<String>
Validate that a definition can be composed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/ukiryu/definition/definition_composer.rb', line 232 def self.validate_composable(definition) errors = [] # Check inherits if definition.respond_to?(:inherits) && definition.inherits definition.inherits.each do |inherit_spec| base_def = load_base_definition(inherit_spec) errors << "Cannot inherit from '#{inherit_spec}': definition not found" unless base_def end end # Check includes if definition.respond_to?(:includes) && definition.includes definition.includes.each do |include_spec| include_def = load_base_definition(include_spec) errors << "Cannot include '#{include_spec}': definition not found" unless include_def end end errors end |