Module: Shields::Badge

Defined in:
lib/shields/badge.rb,
lib/shields/badge/base.rb,
lib/shields/badge/version.rb,
lib/shields/badge/code_coverage/coveralls.rb,
lib/shields/badge/social/github_repo_stars.rb,
lib/shields/badge/downloads/gem_download_rank.rb,
lib/shields/badge/downloads/gem_total_downloads.rb,
lib/shields/badge/build/github_branch_check_runs.rb,
lib/shields/badge/activity/github_commits_since_latest_release.rb

Defined Under Namespace

Modules: Activity, Build, CodeCoverage, Downloads, Social, Version Classes: Base

Constant Summary collapse

SAFE_TO_CLASSIFY =
/\A[\p{LOWERCASE-LETTER}\p{DECIMAL-NUMBER}_]{1,511}\Z/
SAFE_TO_UNDERSCORE =
/\A[\p{UPPERCASE-LETTER}\p{LOWERCASE-LETTER}\p{DECIMAL-NUMBER}]{1,256}\Z/
SUBBER_UNDER =
/(\p{UPPERCASE-LETTER})/
INITIAL_UNDERSCORE =
/^_/

Class Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, **kwargs, &block) ⇒ Object (private)



113
114
115
116
117
# File 'lib/shields/badge.rb', line 113

def method_missing(method_name, **kwargs, &block)
  super unless register_by_method_name(method_name:)

  send(method_name, **kwargs)
end

Class Method Details

.classy_to_underscore(string) ⇒ Object

Raises:



142
143
144
145
146
147
148
149
# File 'lib/shields/badge.rb', line 142

def classy_to_underscore(string)
  safe = string[SAFE_TO_UNDERSCORE]
  raise Errors::Error, "Invalid badge class must match #{SAFE_TO_UNDERSCORE}: #{safe} (#{safe.class}) != #{string[0..255]} (#{string.class})" unless safe == string.to_s

  underscored = safe.gsub(SUBBER_UNDER) { "_#{$1}" }
  shifted_leading_underscore = underscored.sub(INITIAL_UNDERSCORE, "")
  shifted_leading_underscore.downcase
end

.clean_method_name(method_name) ⇒ Object



128
129
130
131
# File 'lib/shields/badge.rb', line 128

def clean_method_name(method_name)
  safe_classy = underscore_to_classy(method_name)
  [safe_classy, classy_to_underscore(safe_classy)]
end

.method_missing(method_name, **kwargs, &block) ⇒ Object



113
114
115
116
117
# File 'lib/shields/badge.rb', line 113

def method_missing(method_name, **kwargs, &block)
  super unless register_by_method_name(method_name:)

  send(method_name, **kwargs)
end

.register(klass:) ⇒ Object

Badges will be registered lazily, so that they can be used without loading the entire set.

Raises:



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/shields/badge.rb', line 57

def register(klass:)
  raise Errors::Error, "Badge class must be a class, but is #{klass.class}" unless klass.is_a?(Class)

  klass_name = klass.name.split("::").last
  method_name = classy_to_underscore(klass_name)
  define_singleton_method(method_name) do |**kwargs|
    as = kwargs.delete(:as) || :markdown
    formatter_module = case as.to_sym
    # Please write an rSt formatter and submit a PR!
    # Please write an AsciiDoc formatter and submit a PR!
    # Please write an HTML formatter and submit a PR!
    when :image_src_url, :markdown
      Shields::Formatters.const_get(underscore_to_classy(as))
    else
      raise Errors::Error, "Unknown formatter: #{as}"
    end
    badge = klass.new(**kwargs)
    badge.format(formatter_module)
  end
end

.register_allObject



51
52
53
# File 'lib/shields/badge.rb', line 51

def register_all
  require_relative "register_all"
end

.register_by_method_name(method_name:, category: nil) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/shields/badge.rb', line 79

def register_by_method_name(method_name:, category: nil)
  # send it through the conversion to ensure it is clean
  # IMPORTANT: use the same logic as respond_to_missing?
  klass_name, clean_method = clean_method_name(method_name)
  # This code should be unreachable, but it is here for completeness
  # :nocov:
  return false unless klass_name && (clean_method == method_name.to_s)
  # :nocov:

  category ||= Categories[clean_method]
  # method_name must also match the file name
  file_parts = ["badge", category, clean_method].compact
  file_extension = File.join(*file_parts)
  begin
    require_relative(file_extension)

    klass = Kernel.const_get("Shields::Badge::#{underscore_to_classy(category)}::#{klass_name}")

    return register(klass:)
  rescue LoadError => error
    if error.message.include?(file_extension)
      # rescue is so method_missing, which likely called this method, can do its thing.
      warn("Could not load #{file_extension} for #{klass_name} in #{__FILE__}:#{__LINE__}.\n")
    else
      # This code should be unreachable, but it is here for completeness
      # :nocov:
      raise error
      # :nocov:
    end
  end
  false
end

.respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
123
# File 'lib/shields/badge.rb', line 120

def respond_to_missing?(method_name, include_private = false)
  klass_name, clean_method = clean_method_name(method_name)
  klass_name && (clean_method == method_name.to_s) || super
end

.underscore_to_classy(underscored) ⇒ Object

Raises:



134
135
136
137
138
139
# File 'lib/shields/badge.rb', line 134

def underscore_to_classy(underscored)
  safe = underscored[SAFE_TO_CLASSIFY]
  raise Errors::Error, "Invalid badge name must match #{SAFE_TO_CLASSIFY}: #{safe} (#{safe.class}) != #{underscored[0..510]} (#{underscored.class})" unless safe == underscored.to_s

  safe.to_s.split("_").map(&:capitalize).join("")
end