Module: Ast::Merge::RSpec::MergeGemRegistry
- Defined in:
- lib/ast/merge/rspec/merge_gem_registry.rb
Overview
Registry for merge gem dependency tag availability checkers
This module allows merge gems (like markly-merge, prism-merge, json-merge) to register their availability checker for RSpec dependency tags without ast-merge needing to know about them directly.
Purpose
When running RSpec tests with dependency tags (e.g., :markly_merge),
ast-merge needs to know if each merge gem is available. The MergeGemRegistry
provides a way for gems to register their availability checkers. Test
bootstraps can also register known gem metadata before gems are loaded so
RSpec filters are configured in time.
Registration
Each merge gem registers itself when loaded using MergeGemRegistry.register:
- Tag name (e.g., :markly_merge)
- Require path (e.g., "markly/merge")
- Merger class name (e.g., "Markly::Merge::SmartMerger")
- Test source code to verify the merger works
- Optional category for grouping (e.g., :markdown, :data, :code)
When a tag is registered, an availability method is automatically defined
on Ast::Merge::RSpec::DependencyTags.
Thread Safety
All operations are thread-safe using a Mutex for synchronization. Results are cached after first check for performance.
Constant Summary collapse
- CATEGORIES =
Valid categories for merge gems
%i[markdown data code config other].freeze
Class Method Summary collapse
-
.available?(tag_name) ⇒ Boolean
Check if a merge gem is available and functional.
-
.clear! ⇒ void
Clear all registrations and cache.
-
.clear_cache! ⇒ void
Clear the availability cache.
-
.force_check_availability! ⇒ void
Force availability checking for all registered gems.
-
.gems_by_category(category) ⇒ Array<Symbol>
Get gems filtered by category.
-
.info(tag_name) ⇒ Hash?
Get registration info for a gem.
- .known_gems ⇒ Object
-
.register(tag_name, require_path:, merger_class:, test_source:, category: :other, skip_instantiation: false) ⇒ void
Register a merge gem for dependency tag support.
-
.register_known_gem(tag_name, require_path:, merger_class:, test_source:, category: :other, skip_instantiation: false) ⇒ Object
Register metadata for a merge gem that may not be loaded yet.
-
.register_known_gems(*gem_names) ⇒ void
Register one or more known gems for RSpec dependency tag support.
-
.registered?(tag_name) ⇒ Boolean
Check if a tag is registered.
-
.registered_gems ⇒ Array<Symbol>
Get all explicitly registered gem tag names.
-
.reset_availability! ⇒ void
Reset memoized availability on DependencyTags.
-
.summary ⇒ Hash{Symbol => Boolean}
Get a summary of all registered gems and their availability.
Class Method Details
.available?(tag_name) ⇒ Boolean
Check if a merge gem is available and functional
This method will try to load the gem if it was registered directly or predeclared by a spec bootstrap before the gem was explicitly loaded.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 143 def available?(tag_name) tag_sym = tag_name.to_sym # Check cache first @mutex.synchronize do return @availability_cache[tag_sym] if @availability_cache.key?(tag_sym) end # Get registration info (from loaded registry or bootstrap metadata) info = @mutex.synchronize { @registry[tag_sym] } info ||= @mutex.synchronize { @known_gems[tag_sym] } return false unless info # Check if gem works result = gem_works?( info[:require_path], info[:merger_class], info[:test_source], info[:skip_instantiation] ) # Cache result @mutex.synchronize do @availability_cache[tag_sym] = result end result end |
.clear! ⇒ void
This method returns an undefined value.
Clear all registrations and cache
314 315 316 317 318 319 320 321 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 314 def clear! @mutex.synchronize do @registry.clear @known_gems.clear @availability_cache.clear end nil end |
.clear_cache! ⇒ void
This method returns an undefined value.
Clear the availability cache
304 305 306 307 308 309 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 304 def clear_cache! @mutex.synchronize do @availability_cache.clear end nil end |
.force_check_availability! ⇒ void
This method returns an undefined value.
Force availability checking for all registered gems
This method should be called AFTER SimpleCov is loaded (typically at the end of spec_helper.rb) to trigger gem loading and availability checking. Calling this ensures RSpec exclusion filters are properly configured based on which gems are actually available.
This is necessary because register_known_gems() only registers gems without checking availability. The actual availability check (which requires loading the gem) must happen AFTER coverage instrumentation is set up.
272 273 274 275 276 277 278 279 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 272 def force_check_availability! registered_gems.each do |tag| # This will trigger gem_works? which loads the gem # Results are cached, so subsequent calls are fast available?(tag) end nil end |
.gems_by_category(category) ⇒ Array<Symbol>
Get gems filtered by category
248 249 250 251 252 253 254 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 248 def gems_by_category(category) @mutex.synchronize do known = @known_gems.select { |_, info| info[:category] == category }.keys registered = @registry.select { |_, info| info[:category] == category }.keys (known + registered).uniq end end |
.info(tag_name) ⇒ Hash?
Get registration info for a gem
285 286 287 288 289 290 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 285 def info(tag_name) tag_sym = tag_name.to_sym @mutex.synchronize do @registry[tag_sym]&.dup || @known_gems[tag_sym]&.dup end end |
.known_gems ⇒ Object
238 239 240 241 242 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 238 def known_gems @mutex.synchronize do @known_gems.transform_values(&:dup) end end |
.register(tag_name, require_path:, merger_class:, test_source:, category: :other, skip_instantiation: false) ⇒ void
This method returns an undefined value.
Register a merge gem for dependency tag support
When a gem is registered, this also dynamically defines a *_available? method
on Ast::Merge::RSpec::DependencyTags if it doesn't already exist.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 90 def register(tag_name, require_path:, merger_class:, test_source:, category: :other, skip_instantiation: false) raise ArgumentError, "Invalid category: #{category}" unless CATEGORIES.include?(category) tag_sym = tag_name.to_sym @mutex.synchronize do @registry[tag_sym] = { require_path: require_path, merger_class: merger_class, test_source: test_source, category: category, skip_instantiation: skip_instantiation } # Clear cache when re-registering @availability_cache.delete(tag_sym) end # Define availability method on DependencyTags define_availability_method(tag_sym) nil end |
.register_known_gem(tag_name, require_path:, merger_class:, test_source:, category: :other, skip_instantiation: false) ⇒ Object
Register metadata for a merge gem that may not be loaded yet.
This is intended for spec/bootstrap layers that need to declare the tag universe before RSpec filters examples. Runtime provider gems should still call register when loaded.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 118 def register_known_gem(tag_name, require_path:, merger_class:, test_source:, category: :other, skip_instantiation: false) raise ArgumentError, "Invalid category: #{category}" unless CATEGORIES.include?(category) @mutex.synchronize do @known_gems[tag_name.to_sym] = { require_path: require_path, merger_class: merger_class, test_source: test_source, category: category, skip_instantiation: skip_instantiation } end define_availability_method(tag_name.to_sym) nil end |
.register_known_gems(*gem_names) ⇒ void
This method returns an undefined value.
Register one or more known gems for RSpec dependency tag support
This allows test suites to explicitly register only the merge gems they need for their tests, avoiding the overhead of registering all known gems.
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 200 def register_known_gems(*gem_names) gem_names.each do |tag_name| tag_sym = tag_name.to_sym unless known_gems.key?(tag_sym) warn("Unknown gem: #{tag_name}. Available: #{known_gems.keys.join(', ')}") next end # Skip if already registered next if registered?(tag_sym) = known_gems.fetch(tag_sym) register( tag_sym, require_path: [:require_path], merger_class: [:merger_class], test_source: [:test_source], category: [:category], skip_instantiation: [:skip_instantiation] ) end end |
.registered?(tag_name) ⇒ Boolean
Check if a tag is registered
177 178 179 180 181 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 177 def registered?(tag_name) @mutex.synchronize do @registry.key?(tag_name.to_sym) end end |
.registered_gems ⇒ Array<Symbol>
Get all explicitly registered gem tag names
This returns ONLY gems that were explicitly registered via register() or register_known_gems(), NOT every predeclared gem. This prevents premature loading of gems during RSpec tag setup, which would happen before SimpleCov and ruin coverage reporting.
232 233 234 235 236 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 232 def registered_gems @mutex.synchronize do @registry.keys end end |
.reset_availability! ⇒ void
This method returns an undefined value.
Reset memoized availability on DependencyTags
326 327 328 329 330 331 332 333 334 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 326 def reset_availability! clear_cache! return unless defined?(DependencyTags) registered_gems.each do |tag| ivar = :"@#{tag}_available" DependencyTags.remove_instance_variable(ivar) if DependencyTags.instance_variable_defined?(ivar) end end |
.summary ⇒ Hash{Symbol => Boolean}
Get a summary of all registered gems and their availability
295 296 297 298 299 |
# File 'lib/ast/merge/rspec/merge_gem_registry.rb', line 295 def summary registered_gems.each_with_object({}) do |tag, result| result[tag] = available?(tag) end end |