Class: ConfigMatchinator
- Defined in:
- lib/ceedling/config/config_matchinator.rb
Overview
:
:<context>:
:<optional operation>:
:<optional matcher>:
- <Value 1>
- <Value 2>
- ...
Class Method Summary collapse
-
.append_matcher_entries(config_entry, entries, matcher: :*) ⇒ Object
Append one or more entries to a config section value holding either a simple list or a matcher hash.
Instance Method Summary collapse
- #config_include?(primary:, secondary:, tertiary: nil) ⇒ Boolean
- #get_config(primary:, secondary:, tertiary: nil) ⇒ Object
-
#matches?(hash:, filepath:, section:, context:, operation: nil) ⇒ Boolean
Note: This method only relevant if hash includes test filepath matching keys.
Class Method Details
.append_matcher_entries(config_entry, entries, matcher: :*) ⇒ Object
Append one or more entries to a config section value holding either a simple list or a matcher hash. This handles the two structural forms documented in :defines and :flags sections:
- Simple list (Array): a flat list applied to all build contexts -- shortcut for the :* wildcard.
- Matcher hash (Hash): keys are test filepath matchers (:*, regex /.../, substring, wildcard string).
config_entry: Array or matcher Hash (e.g. the raw value at config[:test]). entries: String or Array of strings to append. matcher: Optional keyword arg (default :). The hash key to target. When : (the default), the top-level Array shortcut is also in scope -- a bare Array is equivalent to { :* => [...] }. For any other matcher key, only Hash processing applies (no Array shortcut).
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/ceedling/config/config_matchinator.rb', line 166 def self.append_matcher_entries(config_entry, entries, matcher: :*) # Normalize entries to an array for uniform handling below _entries = Array(entries) if matcher == :* # Simple list at the top level is the shortcut form of the :* wildcard -- # it applies to all test executables, same as { :* => [...] }. if config_entry.is_a?( Array ) config_entry.concat( _entries ) elsif config_entry.is_a?( Hash ) # If an all-tests wildcard matcher already exists... if config_entry.key?( :* ) # If the matcher maps to a list, append to it. if config_entry[:*].is_a?( Array ) config_entry[:*].concat( _entries ) # If the matcher maps to a single string, # replace it with a list containing the original string plus the new entries. else config_entry[:*] = [config_entry[:*]] + _entries end # No all-tests wildcard matcher -- create one with the new entries. else config_entry[:*] = _entries end end # Nil or other types: no-op (no else branch) else # A specific matcher key was given -- only Hash processing applies; no Array shortcut. if config_entry.is_a?( Hash ) if config_entry.key?( matcher ) # If the matcher maps to a list, append to it. if config_entry[matcher].is_a?( Array ) config_entry[matcher].concat( _entries ) # If the matcher maps to a single string, # replace it with a list containing the original string plus the new entries. else config_entry[matcher] = [config_entry[matcher]] + _entries end # Matcher key absent -- add it with the new entries. else config_entry[matcher] = _entries end end # Non-Hash with a non-:* matcher (Array, nil, etc.): no-op end end |
Instance Method Details
#config_include?(primary:, secondary:, tertiary: nil) ⇒ Boolean
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/ceedling/config/config_matchinator.rb', line 22 def config_include?(primary:, secondary:, tertiary:nil) # Create configurator accessor method accessor = (primary.to_s + '_' + secondary.to_s).to_sym # If no entry in configuration for secondary in primary, bail out return false if not @configurator.respond_to?( accessor ) # If tertiary undefined, we've progressed as far as we need and already know the config is present return true if tertiary.nil? # Get element associated with this context elem = @configurator.send( accessor ) # If [primary][secondary] is a simple array if elem.is_a?(Array) # A list instead of a hash, means [tertiary] is not present return false # If [primary][secondary] is a hash elsif elem.is_a?(Hash) return elem.include?( tertiary ) end # Otherwise, [primary][secondary] is something that cannot contain a [tertiary] sub-hash return false end |
#get_config(primary:, secondary:, tertiary: nil) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/ceedling/config/config_matchinator.rb', line 49 def get_config(primary:, secondary:, tertiary:nil) # Create configurator accessor method accessor = (primary.to_s + '_' + secondary.to_s).to_sym # If no entry in configuration for secondary in primary, bail out return nil if not @configurator.respond_to?( accessor ) # Get config element associated with this secondary elem = @configurator.send( accessor ) # If [primary][secondary] is a simple array if elem.class == Array # If no tertiary specified, then a simple array makes sense return elem if tertiary.nil? # Otherwise, if an tertiary is specified but we have an array, go boom error = ":#{primary} ↳ :#{secondary} present in project configuration but does not contain :#{tertiary}." raise CeedlingException.new( error ) # If [primary][secondary] is a hash elsif elem.class == Hash if not tertiary.nil? # Bail out if we're looking for an [tertiary] sub-hash, but it's not present return nil if not elem.include?( tertiary ) # Return array or hash at tertiary return elem[tertiary] # If tertiary is not being queried, but we have a hash, return the hash else return elem end # If [primary][secondary] is nothing we expect--something other than an array or hash else error = ":#{primary} ↳ :#{secondary} in project configuration is neither a list nor hash." raise CeedlingException.new( error ) end return nil end |
#matches?(hash:, filepath:, section:, context:, operation: nil) ⇒ Boolean
Note: This method only relevant if hash includes test filepath matching keys
92 93 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 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/ceedling/config/config_matchinator.rb', line 92 def matches?(hash:, filepath:, section:, context:, operation:nil) _values = [] # Sanity check if filepath.nil? path = generate_matcher_path(section, context, operation) error = "#{path} matching provided nil filepath" raise CeedlingException.new(error) end # Iterate through every hash touple [matcher key, values array] # In prioritized order match test filepath against each matcher key. # This order matches on special patterns first to ensure no funny business with simple substring matching # 1. All files wildcard ('*') # 2. Regex (/.../) # 3. Wildcard filepath matching (e.g. 'name*') # 4. Any filepath matching (substring matching) # # Each element of the collected _values array will be an array of values. hash.each do |matcher, values| matched = false _matcher = matcher.to_s.strip # 1. Try gross wildcard matching -- return values for all test filepaths if '*' is the matching key if ('*' == _matcher) matched = true # 2. Try regular expression matching against all values matching keys that are regexes (ignore if not a valid regex) # Note: We use logical AND here so that we get a meaningful fall-through condition. # Nesting the actual regex matching beneath validity checking improperly catches unmatched regexes elsif (regex?(_matcher)) and (!(form_regex(_matcher).match(filepath)).nil?) matched = true # 3. Try wildcard matching -- return values for any test filepath that matches with '*' expansion # Treat matcher as a regex: # 1. Escape any regex characters (e.g. '-') # 2. Convert any now escaped '\*'s into '.*' # 3. Match filepath against regex-ified matcher elsif (filepath =~ /#{Regexp.escape(matcher).gsub('\*', '.*')}/) matched = true # 4. Try filepath literal matching (including substring matching) with each matching key # Note: (3) will do this if the matcher key lacks a '*', but this is a just-in-case backup elsif (filepath.include?(_matcher)) matched = true end if matched _values += values matched_notice(section:section, context:context, operation:operation, matcher:_matcher, filepath:filepath) else # No match path = generate_matcher_path(section, context, operation) @loginator.lazy( Verbosity::DEBUG ) { "#{path} ↳ `#{matcher}` did not match #{filepath}" } end end # Flatten to handle list-nested YAML aliasing (should have already been flattened during validation) return _values.flatten end |