Class: Conductor::Condition
- Inherits:
-
Object
- Object
- Conductor::Condition
- Defined in:
- lib/conductor/condition.rb
Overview
Condition class
Instance Attribute Summary collapse
-
#condition ⇒ Object
R/W condition.
Instance Method Summary collapse
-
#initialize(condition) ⇒ Condition
constructor
Initializes the given condition.
-
#operator_to_symbol(operator) ⇒ Symbol
Convert an operator string to a symbol.
-
#parse_condition ⇒ Boolean
Parse a condition, handling parens and booleans.
-
#split_booleans(condition) ⇒ Boolean
Splits booleans and tests components.
-
#split_condition(condition) ⇒ Array
Splits a natural language condition.
- #test_condition(condition) ⇒ Object
-
#test_env(value, key, operator) ⇒ Boolean
Test for environment variable.
-
#test_includes(includes, val, operator) ⇒ Object
Test for includes.
-
#test_meta(content, value, key, operator) ⇒ Boolean
Test for MultiMarkdown metadata, optionally key and value.
-
#test_operator(value1, value2, operator) ⇒ Boolean
Test operators.
-
#test_pandoc(content, operator) ⇒ Object
Test for Pandoc headers.
-
#test_string(val1, val2, operator) ⇒ Boolean
Compare a string based on operator.
-
#test_tree(origin, value, operator) ⇒ Boolean
Test for the existince of a file/directory in the parent tree.
-
#test_truthy(value1, value2, operator) ⇒ Boolean
Test “truthiness”.
-
#test_type(val1, val2, operator) ⇒ Object
Test for type of value.
-
#test_yaml(content, value, key, operator) ⇒ Boolean
Test for presence of yaml, optionall for a key, optionally for a key’s value.
-
#true? ⇒ Boolean
Tests condition.
Constructor Details
Instance Attribute Details
#condition ⇒ Object
R/W condition
7 8 9 |
# File 'lib/conductor/condition.rb', line 7 def condition @condition end |
Instance Method Details
#operator_to_symbol(operator) ⇒ Symbol
Convert an operator string to a symbol
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/conductor/condition.rb', line 456 def operator_to_symbol(operator) return operator if operator.nil? case operator when /(gt|greater( than)?|>|(?:is )?after)/i :gt when /(lt|less( than)?|<|(?:is )?before)/i :lt when /not (ha(?:s|ve)|contains?|includes?) +file/i :not_includes_file when /not (ha(?:s|ve)|contains?|includes?) +path/i :not_includes_path when /(ha(?:s|ve)|contains?|includes?|\*=) +file/i :includes_file when /(ha(?:s|ve)|contains?|includes?|\*=) +path/i :includes_path when /not (ha(?:s|ve)|contains|includes|match(es)?)/i :not_contains when /(ha(?:s|ve)|contains|includes|match(es)?|\*=)/i :contains when /not (suffix|ends? with)/i :not_ends_with when /not (prefix|(starts?|begins?) with)/i :not_starts_with when /(suffix|ends with|\$=)/i :ends_with when /(prefix|(starts?|begins?) with|\^=)/i :starts_with when /is not (an?|type( of)?)/i :not_type_of when /is (an?|type( of)?)/i :type_of when /((?:(?:is|does) )?not(?: equals?)?|!==?)/i :not_equal when /(is|==?|equals?)/i :equal end end |
#parse_condition ⇒ Boolean
Parse a condition, handling parens and booleans
500 501 502 503 504 505 506 507 |
# File 'lib/conductor/condition.rb', line 500 def parse_condition cond = @condition.to_s.gsub(/\((.*?)\)/) do condition = Regexp.last_match(1) split_booleans(condition) end split_booleans(cond) end |
#split_booleans(condition) ⇒ Boolean
Splits booleans and tests components.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/conductor/condition.rb', line 35 def split_booleans(condition) split = condition.split(/ ((?:AND )?NOT|AND|OR) /) if split.count == 1 test_condition(split[0]) else res = nil bool = nil prev = false split.each do |cond| if /((?:AND )?NOT|AND|OR|&&|\|\||!!)/.match?(cond) bool = cond.bool_to_symbol next end r = split_booleans(cond) if bool == :and && (!r || !prev) res = false elsif bool == :or && (r || prev) return true elsif bool == :not && (r || prev) res = false else res = r end prev = res end res end end |
#split_condition(condition) ⇒ Array
Splits a natural language condition.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/conductor/condition.rb', line 104 def split_condition(condition) if condition.match(/(?:((?:does )?not)?(?:ha(?:s|ve)|contains?|includes?) +)?(yaml|headers|frontmatter|mmd|meta(?:data)?|pandoc)(:[\S_ ]+)?/i) m = Regexp.last_match op = m[1].nil? ? :contains : :not_contains type = case m[2] when /^m/i "mmd" when /^p/i "pandoc" else "yaml" end return ["#{type}#{m[3]}", nil, op] end res = condition.match(/(?i)^(?<val1>.*?)(?:(?:\s+(?<bool>(?:is|do(?:es)?)?\s*(?:not)?\s*)(?<op>(?:an?|type(?:\sof)?|equals?(?:\sto))?|[!*$]?==?|[gl]t|(?:greater|less)(?:\sthan)?|<|>|(?:starts|ends) with|(?:ha(?:s|ve)\s)?(?:prefix|suffix)|(?:contains?|includes?)\s+(?:file|path)|has|contains?|includes?|match(?:es)?)\s+)(?<val2>.*?))?$/) operator = res["bool"] ? "#{res["bool"]}#{res["op"]}" : res["op"] [res["val1"], res["val2"], operator_to_symbol(operator)] end |
#test_condition(condition) ⇒ Object
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/conductor/condition.rb', line 406 def test_condition(condition) type, value, operator = split_condition(condition) if operator.nil? return case type when /^(true|any|all|else|\*+|catch(all)?)$/i true else false end end case type when /^env(?:ironment)?(?:[:\[\()](.*?)[\]\)]?)?$/i key = Regexp.last_match(1) || nil test_env(value, key, operator) when /^include/i test_includes(@env[:includes], value, operator) ? true : false when /^ext/i test_string(@env[:ext], value, operator) ? true : false when /^tree/i test_tree(@env[:origin], value, operator) when /^(path|dir)/i test_string(@env[:filepath], value, operator) ? true : false when /^(file)?name/i test_string(@env[:filename], value, operator) ? true : false when /^phase/i test_string(@env[:phase], value, :starts_with) ? true : false when /^text/i test_string(Conductor.stdin, value, operator) ? true : false when /^(?:yaml|headers|frontmatter)(?::(.*?))?$/i key = Regexp.last_match(1) || nil Conductor.stdin.yaml? ? test_yaml(Conductor.stdin, value, key, operator) : false when /^(?:mmd|meta(?:data)?)(?::(.*?))?$/i key = Regexp.last_match(1) || nil Conductor.stdin. ? (Conductor.stdin, value, key, operator) : false when /^pandoc/ test_pandoc(Conductor.stdin, operator) else false end end |
#test_env(value, key, operator) ⇒ Boolean
Test for environment variable
294 295 296 297 298 299 300 301 302 |
# File 'lib/conductor/condition.rb', line 294 def test_env(value, key, operator) env = Env.env.merge(ENV) if key.nil? res = !env[value].nil? return operator == :not_contains ? !res : res end return test_string(env[key], value, operator) end |
#test_includes(includes, val, operator) ⇒ Object
Test for includes
156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/conductor/condition.rb', line 156 def test_includes(includes, val, operator) case operator when :not_includes_file !includes.includes_file?(val) when :not_includes_path !includes.includes_frag?(val) when :includes_file includes.includes_file?(val) when :includes_path includes.includes_frag?(val) else false end end |
#test_meta(content, value, key, operator) ⇒ Boolean
Test for MultiMarkdown metadata,
optionally key and value
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/conductor/condition.rb', line 363 def (content, value, key, operator) headers = [] content.split("\n").each do |line| break if line == /^ *\n$/ || line !~ /\w+: *\S/ headers << line end return operator == :not_equal if headers.empty? return operator != :not_equal if value.nil? = {} headers.each do |h| parts = h.split(/ *: */) k = parts[0].strip.downcase.gsub(/ +/, "") v = parts[1..].join(":").strip [k] = v end if key if %i[type_of not_type_of].include?(operator) return test_type([key], value, operator) end test_string([key], value, operator) else res = value ? .key?(value) : true operator == :not_equal ? !res : res end end |
#test_operator(value1, value2, operator) ⇒ Boolean
Test operators
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/conductor/condition.rb', line 77 def test_operator(value1, value2, operator) case operator when :gt value1.to_f > value2.to_f when :lt value1.to_f < value2.to_f when :not_contains value1.to_s !~ /#{value2}/i when :contains value1.to_s =~ /#{value2}/i when :starts_with value1.to_s =~ /^#{value2}/i when :ends_with value1.to_s =~ /#{value2}$/i when :not_equal value1 != value2 when :equal value1 == value2 end end |
#test_pandoc(content, operator) ⇒ Object
Test for Pandoc headers
401 402 403 404 |
# File 'lib/conductor/condition.rb', line 401 def test_pandoc(content, operator) res = content. == :pandoc %i[not_contains not_equal].include?(operator) ? !res.nil? : res.nil? end |
#test_string(val1, val2, operator) ⇒ Boolean
Compare a string based on operator
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 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/conductor/condition.rb', line 180 def test_string(val1, val2, operator) return operator == :not_equal ? val1.nil? : !val1.nil? if val2.nil? return operator == :not_equal if val1.nil? val2 = val2.to_s.dup.force_encoding("utf-8") if val2.bool? res = val2.to_bool == val1.to_bool return operator === :not_equal ? !res : res end if val1.date? if val2.time? date1 = val1.to_date date2 = val2.to_date else date1 = operator == :gt ? val1.to_day(:end) : val1.to_day date2 = operator == :gt ? val2.to_day(:end) : val2.to_day end res = case operator when :gt date1 > date2 when :lt date1 < date2 when :equal date1 == date2 when :not_equal date1 != date2 end return res unless res.nil? end val2 = if %r{^/.*?/$}.match?(val2.strip) val2.gsub(%r{(^/|/$)}, "") else Regexp.escape(val2) end val1 = val1.dup.to_s.force_encoding("utf-8") case operator when :contains val1 =~ /#{val2}/i ? true : false when :not_starts_with val1 !~ /^#{val2}/i ? true : false when :not_ends_with val1 !~ /#{val2}$/i ? true : false when :starts_with val1 =~ /^#{val2}/i ? true : false when :ends_with val1 =~ /#{val2}$/i ? true : false when :equal val1 =~ /^#{val2}$/i ? true : false when :not_equal val1 !~ /^#{val2}$/i ? true : false else false end end |
#test_tree(origin, value, operator) ⇒ Boolean
Test for the existince of a
file/directory in the parent tree
251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/conductor/condition.rb', line 251 def test_tree(origin, value, operator) return true if File.exist?(File.join(origin, value)) dir = File.dirname(origin) if Dir.exist?(File.join(dir, value)) true elsif [Dir.home, "/"].include?(dir) false else test_tree(dir, value, operator) end end |
#test_truthy(value1, value2, operator) ⇒ Boolean
Test “truthiness”
274 275 276 277 278 279 280 281 282 |
# File 'lib/conductor/condition.rb', line 274 def test_truthy(value1, value2, operator) return false unless value2&.bool? value2 = value2.to_bool if value2.is_a?(String) res = value1 == value2 operator == :not_equal ? !res : res end |
#test_type(val1, val2, operator) ⇒ Object
Test for type of value
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/conductor/condition.rb', line 131 def test_type(val1, val2, operator) res = case val2 when /number/ val1.is_a?(Numeric) when /int(eger)?/ val1.is_a?(Integer) when /(float|decimal)/ val1.is_a?(Float) when /array/i val1.is_a?(Array) when /(string|text)/i val1.is_a?(String) when /date/i val1.date? end operator == :type_of ? res : !res end |
#test_yaml(content, value, key, operator) ⇒ Boolean
Test for presence of yaml, optionall for
a key, optionally for a key's value
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/conductor/condition.rb', line 315 def test_yaml(content, value, key, operator) begin yaml = YAML.load(content.split(/^(?:---|\.\.\.)/)[1]) rescue StandardError return false end return operator == :not_equal unless yaml if key value1 = yaml[key] return operator == :not_equal if value1.nil? if value.nil? has_key = !value1.nil? return operator == :not_equal ? !has_key : has_key end if %i[type_of not_type_of].include?(operator) return test_type(value1, value, operator) end value1 = value1.join(",") if value1.is_a?(Array) if value1.to_s.bool? test_truthy(value1, value, operator) elsif value1.to_s.number? && value.to_s.number? && %i[gt lt equal not_equal].include?(operator) test_operator(value1, value, operator) else test_string(value1, value, operator) end else res = value ? yaml.key?(value) : true (operator == :not_equal) ? !res : res end end |
#true? ⇒ Boolean
Tests condition
24 25 26 |
# File 'lib/conductor/condition.rb', line 24 def true? parse_condition end |