lib/planter/string.rb in planter-cli-3.0.4 vs lib/planter/string.rb in planter-cli-3.0.5

- old
+ new

@@ -136,15 +136,18 @@ # @return [String] Regular expression for matching variable modifiers MOD_RX = '(?<mod> (?:: ( - l(?:ow(?:er)?)?)?| - u(?:p(?:per)?)?| + l(?:ow(?:er(case)?)?)?)?| + d(?:own(?:case)?)?| + u(?:p(?:per(case)?)?)?|upcase| c(?:ap(?:ital(?:ize)?)?)?| t(?:itle)?| snake|camel|slug| + fl|first_letter| + fw|first_word| f(?:ile(?:name)? )? )* )' # @return [String] regular expression string for default values @@ -170,11 +173,11 @@ if !variables.key?(m['varname'].to_var) # If the variable is not present, use the default value from the match m['default'].apply_var_names else # Retrieve the default value for the variable from the configuration - vars = Planter.config[:variables].filter { |v| v[:key] == m['varname'] } + vars = Planter.config.variables.filter { |v| v[:key] == m['varname'] } default = vars.first[:default] if vars.count.positive? if default.nil? m[0] elsif variables[m['varname'].to_var] == default # If the variable's value matches the default value, use the default value from the match @@ -202,91 +205,138 @@ ## @param variables [Hash] Hash of variables to apply ## def apply_logic(variables = nil) variables = variables.nil? ? Planter.variables : variables - gsub(/%%if .*?%%.*?%%end(if)?%%/mi) do |construct| - res = false + gsub(/%%if .*?%%.*?%%end( ?if)?%%/mi) do |construct| # Get the condition and the content output = construct.match(/%%else%%(.*?)%%end/m) ? Regexp.last_match(1) : '' conditions = construct.to_enum(:scan, /%%(?<statement>(?:els(?:e )?)?if) (?<condition>.*?)%%(?<content>.*?)(?=%%)/mi).map do Regexp.last_match end - conditions.each do |condition| - variable, operator, value = condition['condition'].split(/ +/, 3) - value.strip_quotes! - variable = variable.to_var - negate = false - if operator =~ /^!/ - operator = operator[1..-1] - negate = true - end - operator = case operator - when /^={1,2}/ - :equal - when /^=~/ - :matches_regex - when /\*=/ - :contains - when /\^=/ - :starts_with - when /\$=/ - :ends_with - when />/ - :greater_than - when /</ - :less_than - when />=/ - :greater_than_or_equal - when /<=/ - :less_than_or_equal - else - :equal - end - comp = variables[variable.to_var].to_s + apply_conditions(conditions, variables, output) + end + end - res = case operator - when :equal - comp =~ /^#{value}$/i - when :matches_regex - comp =~ Regexp.new(value.gsub(%r{^/|/$}, '')) - when :contains - comp =~ /#{value}/i - when :starts_with - comp =~ /^#{value}/i - when :ends_with - comp =~ /#{value}$/i - when :greater_than - comp > value.to_f - when :less_than - comp < value.to_f - when :greater_than_or_equal - comp >= value.to_f - when :less_than_or_equal - comp <= value.to_f - else - false - end - res = !res if negate + ## Destructive version of #apply_logic + def apply_logic!(variables) + replace apply_logic(variables) + end - next unless res + ## + ## Apply operator logic to a string. Operators are defined as + ## :copy, :overwrite, :ignore, or :merge. Logic can be if/else + ## constructs or inline operators. + ## + ## @example "var = 1; if var == 1:copy; else: ignore" #=> :copy + ## @example "var = 2; copy if var == 1 else ignore" #=> :ignore + ## + ## @param variables [Hash] Hash of variables (default: Planter.variables) + ## + def apply_operator_logic(variables = nil) + variables = variables.nil? ? Planter.variables : variables + op_rx = ' *(?<content>c(?:opy)?|o(?:ver(?:write)?)?|i(?:gnore)?|m(?:erge)?)? *' - Planter.notify("Condition matched: #{comp} #{negate ? 'not ' : ''}#{operator} #{value}", :debug) - output = condition['content'] - break + strip.gsub(/^if .*?(?:end(?: ?if)?|$)/mi) do |construct| + # Get the condition and the content + output = construct.match(/else:#{op_rx}/m) ? Regexp.last_match(1) : '' + + conditions = construct.to_enum(:scan, + /(?<statement>(?:els(?:e )?)?if) +(?<condition>.*?):#{op_rx}(?=;|$)/mi).map do + Regexp.last_match end - output - end + apply_conditions(conditions, variables, output) + end.gsub(/^#{op_rx} +if .*?(end( ?if)?|$)/mi) do |construct| + # Get the condition and the content + output = construct.match(/else[; ]+(#{op_rx})/m) ? Regexp.last_match(1) : :ignore + condition = construct.match(/^#{op_rx}(?<statement>if) +(?<condition>.*?)(?=;|$)/mi) + + apply_conditions([condition], variables, output) + end.normalize_operator end - ## Destructive version of #apply_logic - def apply_logic!(variables) - replace apply_logic(variables) + ## + ## Apply conditions + ## + ## @param conditions [Array<MatchData>] Array of conditions ['statement', 'condition', 'content'] + ## @param variables [Hash] Hash of variables + ## @param output [String] Output string + ## + ## @return [String] Output string + ## + def apply_conditions(conditions, variables, output) + res = false + conditions.each do |condition| + variable, operator, value = condition['condition'].split(/ +/, 3) + value.strip_quotes! + variable = variable.to_var + negate = false + if operator =~ /^!/ + operator = operator[1..-1] + negate = true + end + operator = case operator + when /^={1,2}/ + :equal + when /^=~/ + :matches_regex + when /\*=/ + :contains + when /\^=/ + :starts_with + when /\$=/ + :ends_with + when />/ + :greater_than + when /</ + :less_than + when />=/ + :greater_than_or_equal + when /<=/ + :less_than_or_equal + else + :equal + end + + comp = variables[variable.to_var].to_s + + res = case operator + when :equal + comp =~ /^#{value}$/i + when :matches_regex + comp =~ Regexp.new(value.gsub(%r{^/|/$}, '')) + when :contains + comp =~ /#{value}/i + when :starts_with + comp =~ /^#{value}/i + when :ends_with + comp =~ /#{value}$/i + when :greater_than + comp > value.to_f + when :less_than + comp < value.to_f + when :greater_than_or_equal + comp >= value.to_f + when :less_than_or_equal + comp <= value.to_f + else + false + end + res = res ? true : false + res = !res if negate + + next unless res + + Planter.notify("Condition matched: #{comp} #{negate ? 'not ' : ''}#{operator} #{value}", :debug) + output = condition['content'] + break + end + output end ## ## Apply key/value substitutions to a string. Variables are represented as ## %%key%%, and the hash passed to the function is { key: value } @@ -324,10 +374,12 @@ m = Regexp.last_match if m['mod'] mods = m['mod']&.split(/:/) mods&.each do |mod| + next if mod.nil? || mod.empty? + v = v.apply_mod(mod.normalize_mod) end end v end @@ -367,17 +419,24 @@ end content end ## - ## Apply regex replacements from @config[:replacements] + ## Apply all logic, variables, and regexes to a string ## + def apply_all + apply_logic.apply_variables.apply_regexes + end + + ## + ## Apply regex replacements from Planter.config[:replacements] + ## ## @return [String] string with regexes applied ## def apply_regexes(regexes = nil) content = dup.clean_encode - regexes = regexes.nil? && Planter.config.key?(:replacements) ? Planter.config[:replacements] : regexes + regexes = regexes.nil? && Planter.config.key?(:replacements) ? Planter.config.replacements : regexes return self unless regexes regexes.stringify_keys.each do |pattern, replacement| pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) @@ -453,10 +512,14 @@ upcase when :snake_case snake_case when :camel_case camel_case + when :first_letter + split('')[0] + when :first_word + split(/[ !,?;:]+/)[0] else self end end @@ -479,22 +542,26 @@ ## ## @return [Symbol] symbolized modifier ## def normalize_mod case self - when /^(f|slug)/ + when /^(file|slug)/ :slug when /^cam/ :camel_case when /^s/ :snake_case when /^u/ :uppercase - when /^l/ + when /^[ld]/ :lowercase when /^[ct]/ :title_case + when /^(fl|first_letter)/ + :first_letter + when /^(fw|first_word)/ + :first_word end end ## ## Convert operator string to symbol @@ -619,8 +686,17 @@ if default gsub(/\((#{default})\)/, '{dw}({xbc}\1{dw}){xw}').gsub(/\((.)\)/, '{dw}({xbw}\1{dw}){xw}') else gsub(/\((.)\)/, '{dw}({xbw}\1{dw}){xw}') end + end + + # + # Test if a string has a parenthetical selector + # + # @return [Boolean] has selector + # + def has_selector? + self =~ /\(.\)/ ? true : false end end end