Class: RakeTaskRegistry
- Defined in:
- lib/ceedling/rake_app/rake_task_registry.rb
Overview
Tracks which Rake task namespaces are associated with which semantic tags (:test, :release, :build) so bin/ CLI code and other components can ask, e.g. "does invoking this task run tests?" without waiting for Rake to load .rake files and build real task objects.
The unit of registration is the root namespace (e.g. 'test', 'gcov'), not the full task name. This handles all Rake task name forms uniformly:
- Explicitly named tasks (test:all)
- Rule-synthesized tasks (test:foo_file)
- Bare top-level aliases (test)
All share the same root namespace and are therefore all covered once that namespace is registered.
Namespaces are discovered by scanning .rake files as text for marker regexes (see register_tasks) rather than via Rake's task inspection API, because Rake tasks don't exist as objects until their .rake files are loaded. Namespace identification is needed before loading .rake files, so bin/ CLI code and other components can determine whether the user's requested tasks include tests or other build domains.
Registration happens in two passes over the Ceedling lifecycle:
Pass 1 — Early, from bin/cli_helper.rb before .rake files are loaded.
constants.rb is already required, so stock constants (TEST_SYM, RELEASE_SYM,
UTILS_SYM) resolve correctly via Object.const_get. Plugin-specific constants
(GCOV_SYM, VALGRIND_SYM, BULLSEYE_SYM, etc.) are defined in plugin lib/ files
that have not been loaded yet, causing Object.const_get to raise NameError.
Pass 2 — After .rake files are loaded in rakefile.rb.
All plugin lib/ paths have been added to $LOAD_PATH and all constants are
defined. Object.const_get succeeds. The registry is cleared and rebuilt.
Unresolvable constants (NameError in Pass 1) are handled by the _SYM convention fallback in find_enclosing_namespace.
Constant Summary collapse
- TAG_TEST =
Semantic tag constants — used as keys in the namespace_tags registry
:test- TAG_RELEASE =
:release- TAG_BUILD =
:build- TAGS_TEST =
Composite tag sets — applied as a group when registering a domain
[TAG_TEST, TAG_BUILD].freeze
- TAGS_RELEASE =
[TAG_RELEASE, TAG_BUILD].freeze
- MARKER_TEST_TASKS_SETUP_AND_INVOKE =
Marker regexes — a line matching any marker signals the enclosing namespace belongs to the associated domain.
MARKER_TEST_TASKS_SETUP_AND_INVOKE matches any call of the form:
@ceedling[:test_invoker].setup_and_invoke(MARKER_RELEASE_TASKS matches any call of the form:
@ceedling[:release_invoker].setup_and_invoke_objects( /\[\s*:test_invoker\s*\]\.setup_and_invoke/- MARKER_RELEASE_TASKS =
/\[\s*:release_invoker\s*\]\.setup_and_invoke_objects/
Instance Method Summary collapse
-
#initialize ⇒ RakeTaskRegistry
constructor
A new instance of RakeTaskRegistry.
-
#namespaces_for_tag(tag) ⇒ Object
Returns all registered namespace strings that carry the given tag.
-
#register_namespace(namespace, *tags) ⇒ Object
Directly register a namespace string with one or more tags.
-
#register_release_tasks(rakefile_paths) ⇒ Object
Clears all release-tagged namespaces and re-scans for release pipeline invocations.
-
#register_tasks(rakefile_paths, markers:, tags:) ⇒ Object
Scans .rake files as text to identify Rake namespaces that call certain marker code, registering each namespace's root with the given tags.
-
#register_test_tasks(rakefile_paths) ⇒ Object
Clears all test-tagged namespaces and re-scans for test pipeline invocations.
-
#tags_for(task_name) ⇒ Object
Returns all tags associated with a task name.
-
#task_is?(task_name, tag) ⇒ Boolean
Returns true if the task is associated with the given tag.
Constructor Details
#initialize ⇒ RakeTaskRegistry
Returns a new instance of RakeTaskRegistry.
66 67 68 69 70 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 66 def initialize # Maps root namespace string → Array<Symbol> of semantic tags # e.g. { 'test' => [:test, :build], 'gcov' => [:test, :build], 'release' => [:release, :build] } @namespace_tags = {} end |
Instance Method Details
#namespaces_for_tag(tag) ⇒ Object
Returns all registered namespace strings that carry the given tag. Used by RakeInvocationTracker to build dynamic regex patterns for invocation checks.
90 91 92 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 90 def namespaces_for_tag(tag) @namespace_tags.select { |_, | .include?( tag ) }.keys end |
#register_namespace(namespace, *tags) ⇒ Object
Directly register a namespace string with one or more tags. Merges with any existing tags — does not overwrite prior registrations. Example: register_namespace( 'plugin', *TAGS_TEST )
97 98 99 100 101 102 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 97 def register_namespace(namespace, *) @namespace_tags[namespace] ||= [] .each do |tag| @namespace_tags[namespace] << tag unless @namespace_tags[namespace].include?( tag ) end end |
#register_release_tasks(rakefile_paths) ⇒ Object
Clears all release-tagged namespaces and re-scans for release pipeline invocations.
160 161 162 163 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 160 def register_release_tasks(rakefile_paths) @namespace_tags.delete_if { |_, | .include?( TAG_RELEASE ) } register_tasks( rakefile_paths, markers: [MARKER_RELEASE_TASKS], tags: TAGS_RELEASE ) end |
#register_tasks(rakefile_paths, markers:, tags:) ⇒ Object
Scans .rake files as text to identify Rake namespaces that call certain marker code, registering each namespace's root with the given tags.
Algorithm per file:
1. Skip the file if no marker regex matches the full content (fast pre-check).
2. For each line matching any marker, search backwards to find the
enclosing `namespace` declaration.
3. Resolve the namespace identifier to a string (see find_enclosing_namespace).
4. Register the namespace string with the provided tags.
Called once per pass (see class comment) — Pass 2 clears and rebuilds the registry so it doesn't accumulate stale Pass 1 entries.
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 141 142 143 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 116 def register_tasks(rakefile_paths, markers:, tags:) rakefile_paths.each do |filepath| begin # Force valid UTF-8: transcode BOM-prefixed files correctly and replace # any invalid/undefined byte sequences so regex matching below can't # raise on files containing arbitrary unicode content. content = File.read( filepath, encoding: 'bom|utf-8', invalid: :replace, undef: :replace, replace: '' ) rescue next # Skip unreadable files without failing end next unless markers.any? { |m| content.match?( m ) } lines = content.lines lines.each_with_index do |line, idx| next unless markers.any? { |m| line.match?( m ) } namespace_name = find_enclosing_namespace( lines, idx ) next if namespace_name.nil? @namespace_tags[namespace_name] ||= [] .each do |tag| @namespace_tags[namespace_name] << tag unless @namespace_tags[namespace_name].include?( tag ) end end end end |
#register_test_tasks(rakefile_paths) ⇒ Object
Clears all test-tagged namespaces and re-scans for test pipeline invocations. The delete_if ensures Pass 2 replaces Pass 1 results cleanly without disturbing release-tagged entries registered independently.
148 149 150 151 152 153 154 155 156 157 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 148 def register_test_tasks(rakefile_paths) @namespace_tags.delete_if { |_, | .include?( TAG_TEST ) } register_tasks( rakefile_paths, markers: [ MARKER_TEST_TASKS_SETUP_AND_INVOKE, ], tags: TAGS_TEST ) end |
#tags_for(task_name) ⇒ Object
Returns all tags associated with a task name. Any task form maps to its root namespace:
'test:all' → root 'test'
'test:foo_file' → root 'test' (rule-synthesized tasks handled for free)
'test' → root 'test' (bare alias handled for free)
'gcov:all' → root 'gcov'
78 79 80 81 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 78 def (task_name) root = task_name.split(':').first @namespace_tags[root] || [] end |
#task_is?(task_name, tag) ⇒ Boolean
Returns true if the task is associated with the given tag.
84 85 86 |
# File 'lib/ceedling/rake_app/rake_task_registry.rb', line 84 def task_is?(task_name, tag) ( task_name ).include?( tag ) end |