# frozen_string_literal: true module Doing ## Handling of search and regex strings class ::String ## ## Determine whether case should be ignored for string ## ## @param search The search string ## @param case_type The case type, :smart, ## :sensitive, :ignore ## ## @return [Boolean] true if case should be ignored ## def ignore_case(search, case_type) (case_type == :smart && search !~ /[A-Z]/) || case_type == :ignore end ## ## Test if line should be ignored ## ## @return [Boolean] line is empty or comment ## def ignore? line = self line =~ /^#/ || line =~ /^\s*$/ end ## ## Determines if receiver is surrounded by slashes or starts with single quote ## ## @return [Boolean] True if regex, False otherwise. ## def rx? self =~ %r{(^/.*?/$|^')} end ## ## Convert ? and * wildcards to regular expressions. ## Uses \S (non-whitespace) instead of . (any character) ## ## @return [String] Regular expression string ## def wildcard_to_rx gsub(/\?/, '\S').gsub(/\*/, '\S*?').gsub(/\]\]/, '--') end ## ## Convert string to fuzzy regex. Characters in words ## can be separated by up to *distance* characters in ## haystack, spaces indicate unlimited distance. ## ## @example ## "this word".to_rx(3) ## # => /t.{0,3}h.{0,3}i.{0,3}s.{0,3}.*?w.{0,3}o.{0,3}r.{0,3}d/ ## ## @param distance [Integer] Allowed distance ## between characters ## @param case_type The case type ## ## @return [Regexp] Regex pattern ## def to_rx(distance: nil, case_type: nil) distance ||= Doing.config.fetch('search', 'distance', 3).to_i case_type ||= Doing.config.fetch('search', 'case', 'smart')&.normalize_case case_sensitive = case case_type when :smart self =~ /[A-Z]/ ? true : false when :sensitive true else false end pattern = case dup.strip when %r{^/.*?/$} sub(%r{/(.*?)/}, '\1') when /^'/ sub(/^'(.*?)'?$/, '\1') else split(/ +/).map do |w| w.split('').join(".{0,#{distance}}").gsub(/\+/, '\+').wildcard_to_rx end.join('.*?') end Regexp.new(pattern, !case_sensitive) end def to_phrase_query parser = PhraseParser::QueryParser.new transformer = PhraseParser::QueryTransformer.new parse_tree = parser.parse(self) transformer.apply(parse_tree).to_elasticsearch end def to_query parser = BooleanTermParser::QueryParser.new transformer = BooleanTermParser::QueryTransformer.new parse_tree = parser.parse(self) transformer.apply(parse_tree).to_elasticsearch end ## ## Test string for truthiness (0, "f", "false", "n", "no" all return false, case insensitive, otherwise true) ## ## @return [Boolean] String is truthy ## def truthy? if self =~ /^(0|f(alse)?|n(o)?)$/i false else true end end ## ## Returns a bool representation of the string. ## ## @return [Boolean] Bool representation of the object. ## def to_bool case self when /^[yt1]/i true else false end end end end