lib/planter/string.rb in planter-cli-0.0.3 vs lib/planter/string.rb in planter-cli-0.0.4

- old
+ new

@@ -21,11 +21,11 @@ ## @example "planter-string".to_class_name #=> PlanterString ## ## @return Class name representation of the object. ## def to_class_name - strip.no_ext.split(/[-_ ]/).map(&:capitalize).join('').gsub(/[^a-z0-9]/i, '') + strip.no_ext.title_case.gsub(/[^a-z0-9]/i, '').sub(/^\S/, &:upcase) end ## ## Convert a class name to a file slug ## @@ -64,11 +64,11 @@ ## @example "A title string".snake_case #=> a-title-string ## ## @return [String] Snake-cased version of string ## def snake_case - strip.gsub(/\S[A-Z]/) { |pair| pair.split('').join('_') } + strip.gsub(/\S(?=[A-Z])/, '\0_') .gsub(/[ -]+/, '_') .gsub(/[^a-z0-9_]+/i, '') .gsub(/_+/, '_') .gsub(/(^_|_$)/, '').downcase end @@ -80,11 +80,11 @@ ## @example "A title string".camel_case #=> aTitleString ## ## @return [String] Snake-cased version of string ## def camel_case - strip.gsub(/[ _]+(\S)/) { Regexp.last_match(1).upcase } + strip.gsub(/(?<=[^a-z0-9])(\S)/) { Regexp.last_match(1).upcase } .gsub(/[^a-z0-9]+/i, '') .sub(/^(\w)/) { Regexp.last_match(1).downcase } end ## @@ -94,24 +94,15 @@ ## @example "a title string".title_case #=> A Title String ## ## @return [String] title cased string ## def title_case - gsub(/\b(\w)/) { Regexp.last_match(1).upcase } + split(/\b(\w+)/).map(&:capitalize).join('') end - ## - ## Apply key/value substitutions to a string. Variables are represented as - ## %%key%%, and the hash passed to the function is { key: value } - ## - ## @param last_only [Boolean] Only replace the last instance of %%key%% - ## - ## @return [String] string with variables substituted - ## - def apply_variables(last_only: false) - content = dup.clean_encode - mod_rx = '(?<mod> + # @return [String] Regular expression for matching variable modifiers + MOD_RX = '(?<mod> (?:: ( l(?:ow(?:er)?)?)?| u(?:p(?:per)?)?| c(?:ap(?:ital(?:ize)?)?)?| @@ -119,52 +110,151 @@ snake|camel|slug| f(?:ile(?:name)? )? )* )' + # @return [String] regular expression string for default values + DEFAULT_RX = '(?:%(?<default>[^%]+))?' - Planter.variables.each do |k, v| + # + # Apply default values to a string + # + # Default values are applied to variables that are not present in the variables hash, + # or whose value matches the default value + # + # @param variables [Hash] Hash of variable values + # + # @return [String] string with default values applied + # + def apply_defaults(variables) + # Perform an in-place substitution on the content string for default values + gsub(/%%(?<varname>[^%:]+)(?<mods>(?::[^%]+)*)%(?<default>[^%]+)%%/) do + # Capture the last match object + m = Regexp.last_match + + # Check if the variable is not present in the variables hash + 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'] } + 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 + m['default'].apply_var_names + else + m[0] + end + end + end + end + + # + # Destructive version of #apply_defaults + # + # @param variables [Hash] hash of variables to apply + # + # @return [String] string with defaults applied + # + def apply_defaults!(variables) + replace apply_defaults(variables) + end + + ## + ## Apply key/value substitutions to a string. Variables are represented as + ## %%key%%, and the hash passed to the function is { key: value } + ## + ## @param last_only [Boolean] Only replace the last instance of %%key%% + ## + ## @return [String] string with variables substituted + ## + def apply_variables(variables: nil, last_only: false) + variables = variables.nil? ? Planter.variables : variables + + content = dup.clean_encode + + content = content.apply_defaults(variables) + + variables.each do |k, v| if last_only pattern = "%%#{k.to_var}" - content = content.reverse.sub(/(?mix)%%(?:(?<mod>.*?):)*(?<key>#{pattern.reverse})/) do + content = content.reverse.sub(/(?mix)%%(?:(?<mod>.*?):)*(?<key>#{pattern.reverse})/i) do m = Regexp.last_match if m['mod'] m['mod'].reverse.split(/:/).each do |mod| v = v.apply_mod(mod.normalize_mod) end end v.reverse end.reverse else - rx = /(?mix)%%(?<key>#{k.to_var})#{mod_rx}%%/ + rx = /(?mix)%%(?<key>#{k.to_var})#{MOD_RX}#{DEFAULT_RX}%%/ content.gsub!(rx) do m = Regexp.last_match - mods = m['mod']&.split(/:/) - mods&.each do |mod| - v = v.apply_mod(mod.normalize_mod) + if m['mod'] + mods = m['mod']&.split(/:/) + mods&.each do |mod| + v = v.apply_mod(mod.normalize_mod) + end end v end end end content end + # + # Handle $varname and ${varname} variable substitutions + # + # @return [String] String with variables substituted + # + def apply_var_names + sub(/\$\{?(?<varname>\w+)(?<mods>(?::\w+)+)?\}?/) do + m = Regexp.last_match + if Planter.variables.key?(m['varname'].to_var) + Planter.variables[m['varname'].to_var].apply_mods(m['mods']) + else + m + end + end + end + + # + # Apply modifiers to a string + # + # @param mods [String] Colon separated list of modifiers to apply + # + # @return [String] string with modifiers applied + # + def apply_mods(mods) + content = dup + mods.split(/:/).each do |mod| + content.apply_mod!(mod.normalize_mod) + end + content + end + ## ## Apply regex replacements from @config[:replacements] ## ## @return [String] string with regexes applied ## - def apply_regexes + def apply_regexes(regexes = nil) content = dup.clean_encode - return self unless Planter.config.key?(:replacements) + regexes = regexes.nil? && Planter.config.key?(:replacements) ? Planter.config[:replacements] : regexes - Planter.config[:replacements].stringify_keys.each do |pattern, replacement| + return self unless regexes + + regexes.stringify_keys.each do |pattern, replacement| pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) replacement = replacement.gsub(/\$(\d)/, '\\\1').apply_variables content.gsub!(pattern, replacement) end content @@ -175,21 +265,21 @@ ## ## @param last_only [Boolean] Only replace the last instance of %%key%% ## ## @return [String] string with variables substituted ## - def apply_variables!(last_only: false) - replace apply_variables(last_only: last_only) + def apply_variables!(variables: nil, last_only: false) + replace apply_variables(variables: variables, last_only: last_only) end ## ## Destructive version of #apply_regexes ## ## @return [String] string with variables substituted ## - def apply_regexes! - replace apply_regexes + def apply_regexes!(regexes = nil) + replace apply_regexes(regexes) end ## ## Remove any file extension ## @@ -220,10 +310,12 @@ ## ## Apply a modification to string ## ## @param mod [Symbol] The modifier to apply ## + ## @return [String] modified string + ## def apply_mod(mod) case mod when :slug to_slug when :title_case @@ -239,10 +331,21 @@ else self end end + # + # Destructive version of #apply_mod + # + # @param mod [String] modified string + # + # @return [<Type>] <description> + # + def apply_mod!(mod) + replace apply_mod(mod) + end + ## ## Convert mod string to symbol ## ## @example "snake" => :snake_case ## @example "cap" => :title_case @@ -332,12 +435,14 @@ ## ## @param type [Symbol] The type ## ## def coerce(type) + type = type.normalize_type + case type when :date - Chronic.parse(self) + Chronic.parse(self).strftime('%Y-%m-%d %H:%M') when :integer || :number to_i when :float to_f when :class || :module