class Primer::Classify::Utilities

Handler for PrimerCSS utility classes loaded from utilities.rake

Constants

BREAKPOINTS

rubocop:enable Security/YAMLLoad

REPLACEMENT_KEYS

Replacements for some classnames that end up being a different argument key

UTILITIES

Load the utilities.yml file. Disabling because we want to load symbols, strings, and integers from the .yml file rubocop:disable Security/YAMLLoad

Public Class Methods

classes_to_args(classes) click to toggle source
# File lib/primer/classify/utilities.rb, line 117
def classes_to_args(classes)
  hash_to_args(classes_to_hash(classes))
end
classes_to_hash(classes) click to toggle source

Extract hash from classes ie. “mr-1 mb-2 foo” => { mr: 1, mb: 2, classes: “foo” }

# File lib/primer/classify/utilities.rb, line 83
def classes_to_hash(classes)
  # This method is too slow to run in production
  return { classes: classes } if ENV["RAILS_ENV"] == "production"

  obj = {}
  classes = classes.split
  # Loop through all classes supplied and reject ones we find a match for
  # So when we're at the end of the loop we have classes left with any non-system classes.
  classes.reject! do |classname|
    key, value, index = find_selector(classname)
    next false if key.nil?

    # Create array if nil
    obj[key] = Array.new(5, nil) if obj[key].nil?
    # Place the arguments in the responsive array based on index mr: [nil, 2]
    obj[key][index] = value
    next true
  end

  # Transform responsive arrays into arrays without trailing nil, so `mr: [1, nil, nil, nil, nil]` becomes `mr: 1`
  obj.transform_values! do |value|
    value = value.reverse.drop_while(&:nil?).reverse
    if value.count == 1
      value.first
    else
      value
    end
  end

  # Add back the non-system classes
  obj[:classes] = classes.join(" ") if classes.any?
  obj
end
classname(key, val, breakpoint = "") click to toggle source
# File lib/primer/classify/utilities.rb, line 33
def classname(key, val, breakpoint = "")
  if (valid = validate(key, val, breakpoint))
    valid
  else
    # Get selector
    UTILITIES[key][val][BREAKPOINTS.index(breakpoint)]
  end
end
hash_to_args(hash) click to toggle source
# File lib/primer/classify/utilities.rb, line 121
def hash_to_args(hash)
  hash.map do |key, value|
    val = case value
          when Symbol
            ":#{value}"
          when String
            value.to_json
          else
            value
          end

    "#{key}: #{val}"
  end.join(", ")
end
mappings(key) click to toggle source

Get the options for the given key

returns Array or nil if key not supported

# File lib/primer/classify/utilities.rb, line 76
def mappings(key)
  return unless supported_key?(key)

  UTILITIES[key].keys
end
responsive?(key, val) click to toggle source

Is the key and value responsive

returns Boolean

# File lib/primer/classify/utilities.rb, line 69
def responsive?(key, val)
  supported_value?(key, val) && UTILITIES[key][val].count > 1
end
supported_key?(key) click to toggle source

Does the Utilitiy class support the given key

returns Boolean

# File lib/primer/classify/utilities.rb, line 45
def supported_key?(key)
  UTILITIES[key].present?
end
supported_selector?(selector) click to toggle source

Does the given selector exist in the utilities file

returns Boolean

# File lib/primer/classify/utilities.rb, line 59
def supported_selector?(selector)
  # This method is too slow to run in production
  return false if ENV["RAILS_ENV"] == "production"

  find_selector(selector).present?
end
supported_value?(key, val) click to toggle source

Does the Utilitiy class support the given key and value

returns Boolean

# File lib/primer/classify/utilities.rb, line 52
def supported_value?(key, val)
  supported_key?(key) && UTILITIES[key][val].present?
end

Private Class Methods

find_selector(selector) click to toggle source
# File lib/primer/classify/utilities.rb, line 138
def find_selector(selector)
  key = infer_selector_key(selector)
  value_hash = UTILITIES[key]

  return nil if value_hash.blank?

  # Each value hash will also contain an array of classnames for breakpoints
  # Key argument `0`, classes `[ "mr-0", "mr-sm-0", "mr-md-0", "mr-lg-0", "mr-xl-0" ]`
  value_hash.each do |key_argument, classnames|
    # Skip each value hash until we get one with the selector
    next unless classnames.include?(selector)

    # Return [:mr, 0, 1]
    # has index of classname, so we can match it up with responsvie array `mr: [nil, 0]`
    return [key, key_argument, classnames.index(selector)]
  end

  nil
end
infer_selector_key(selector) click to toggle source
# File lib/primer/classify/utilities.rb, line 158
def infer_selector_key(selector)
  REPLACEMENT_KEYS.each do |k, v|
    return v.to_sym if selector.match?(Regexp.new(k))
  end

  selector.split("-").first.to_sym
end
validate(key, val, breakpoint) click to toggle source
# File lib/primer/classify/utilities.rb, line 166
def validate(key, val, breakpoint)
  unless supported_key?(key)
    raise ArgumentError, "#{key} is not a valid Primer utility key" unless ENV["RAILS_ENV"] == "production"

    return ""
  end

  unless breakpoint.empty? || responsive?(key, val)
    raise ArgumentError, "#{key} does not support responsive values" unless ENV["RAILS_ENV"] == "production"

    return ""
  end

  unless supported_value?(key, val)
    raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" unless ENV["RAILS_ENV"] == "production"

    return "#{key.to_s.dasherize}-#{val.to_s.dasherize}"
  end

  nil
end