class Hoe
def self.add_include_dirs(*dirs)
def self.add_include_dirs(*dirs) dirs = dirs.flatten include_dirs.concat dirs $:.unshift(*dirs) s = File::PATH_SEPARATOR RUBY_FLAGS.sub!(/-I/, "-I#{dirs.join(s)}#{s}") end
def self.bad_plugins
def self.bad_plugins @bad_plugins end
def self.load_plugins plugins = Hoe.plugins
def self.load_plugins plugins = Hoe.plugins @found ||= {} @loaded ||= {} @files ||= Gem.find_files "hoe/*.rb" @files.reverse.each do |path| # reverse so first one wins @found[File.basename(path, ".rb").intern] = path end :keep_doing_this while @found.map { |name, plugin| next unless plugins.include? name next if @loaded[name] begin warn "loading #{plugin}" if $DEBUG @loaded[name] = require plugin rescue LoadError => e warn "error loading #{plugin.inspect}: #{e.message}. skipping..." end }.any? bad_plugins = plugins - @loaded.keys bad_plugins.each do |bad_plugin| plugins.delete bad_plugin end @bad_plugins.concat bad_plugins @bad_plugins.uniq! return @loaded, @found end
def self.normalize_names project # :nodoc:
def self.normalize_names project # :nodoc: project = project.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, "") klass = project.gsub(/(?:^|_)([a-z])/) { $1.upcase } klass = klass. gsub(/(?:^|-)([a-z])/) { "::#{$1.upcase}" } test_klass = klass. gsub(/(^|::)([A-Z])/) { "#{$1}Test#{$2}" } file_name = project.gsub(/-/, "/") return project, file_name, klass, test_klass end
def self.plugin *plugins
def self.plugin *plugins self.plugins.concat plugins self.plugins.uniq! end
def self.plugins
def self.plugins @@plugins end
def self.spec name, &block
def self.spec name, &block Hoe.load_plugins spec = self.new name spec.activate_plugins spec.instance_eval(&block) spec.post_initialize spec # TODO: remove? end
def activate_plugin_deps
def activate_plugin_deps Hoe.plugins.each do |plugin| msg = "activate_#{plugin}_deps" warn msg if $DEBUG send msg if self.respond_to? msg end end
def activate_plugins
def activate_plugins with_config do |config, _| config_plugins = config["plugins"] break unless config_plugins Hoe.plugins.concat config_plugins.map(&:intern) end Hoe.load_plugins Hoe.plugins names = Hoe.constants.map(&:to_s) names.reject! { |n| n =~ /^[A-Z_]+$/ } names.each do |name| next unless Hoe.plugins.include? name.downcase.intern warn "extend #{name}" if $DEBUG self.extend Hoe.const_get(name) end initialize_plugins end
def add_dependencies
def add_dependencies self.extra_deps = normalize_deps extra_deps self.extra_dev_deps = normalize_deps extra_dev_deps case name when "hoe" then # do nothing? these deps are already in the hoe spec in the Rakefile else version = VERSION.split(/\./).first(2).join(".") dependency "hoe", "~> #{version}", :development end seen = {} extra_deps.each do |dep| next if seen[dep.first] seen[dep.first] = true spec.add_dependency(*dep) end extra_dev_deps.each do |dep| next if seen[dep.first] seen[dep.first] = true spec.add_development_dependency(*dep) end end
def check_for_version # :nodoc:
def check_for_version # :nodoc: return if self.version version = nil version_re = /VERSION += +([\"\'])([\d][\w\.]+)\1/ spec.files.each do |file| next unless File.exist? file version = File.read_utf(file)[version_re, 2] rescue nil break if version end spec.version = self.version = version if version unless self.version then spec.version = self.version = "0.borked" warn "** Add 'VERSION = \"x.y.z\"' to your code," warn " add a version to your hoe spec," warn " or fix your Manifest.txt" end end
def define_spec
def define_spec self.spec = Gem::Specification.new do |s| dirs = Dir["lib"] manifest = read_manifest abort [ "Manifest is missing or couldn't be read.", "The Manifest is kind of a big deal.", "Maybe you're using a gem packaged by a linux project.", "It seems like they enjoy breaking other people's code.", ].join "\n" unless manifest s.name = name s.version = version if version s.summary = summary s.email = email s.homepage = homepage || urls["home"] || urls.values.first s.description = description s.files = manifest s.bindir = bindir || "bin" s.executables = s.files.grep(/^#{s.bindir}/) { |f| File.basename(f) } s.require_paths = dirs unless dirs.empty? s.rdoc_options = ["--main", readme_file] s.post_install_message = post_install_message s.metadata = (urls.keys & URLS_TO_META_MAP.keys).map { |name| [URLS_TO_META_MAP[name], urls[name]] }.to_h if urls missing "Manifest.txt" if s.files.empty? case author when Array s.authors = author else s.author = author end s.extra_rdoc_files += s.files.grep(/\.(txt|rdoc|md)$/) s.extra_rdoc_files.reject! { |f| f =~ %r%^(test|spec|vendor|template|data|tmp)/% } s.extra_rdoc_files += @extra_rdoc_files end check_for_version if licenses.empty? warn "Defaulting gemspec to MIT license." warn "Call license in hoe spec to change." license "MIT" end spec.licenses = licenses run_spec_extras end
def dependency name, version, type = :runtime
def dependency name, version, type = :runtime raise "Unknown dependency type: #{type}" unless [:runtime, :dev, :development, :developer].include? type ary = if type == :runtime then extra_deps else extra_dev_deps end ary << [name, version] end
def dependency_target
def dependency_target self.name == "hoe" ? extra_deps : extra_dev_deps end
def developer name, email
def developer name, email self.author << name self.email << email end
def have_gem? name
def have_gem? name Gem::Specification.find_by_name name.to_s rescue Gem::LoadError false end
def initialize name, version = nil # :nodoc:
def initialize name, version = nil # :nodoc: self.name = name self.version = version self.author = [] self.changes = nil self.description = nil self.description_sections = %w[description] self.email = [] self.extra_deps = [] self.extra_dev_deps = [] self.extra_rdoc_files = [] self.licenses = [] self.post_install_message = nil self.group_name = name.downcase self.spec = nil self.spec_extras = {} self.summary = nil self.summary_sentences = 1 self.test_globs = ["test/**/{test,spec}_*.rb", "test/**/*_{test,spec}.rb"] manifest = read_manifest if manifest then self.readme_file = manifest.grep(/^README\./).first self.history_file = manifest.grep(/^History\./).first end self.history_file ||= Dir.glob("History.{rdoc,txt,md}").first || "History.txt" self.readme_file ||= Dir.glob("README.{rdoc,txt,md}").first || "README.txt" abort "Hoe.new {...} removed. Switch to Hoe.spec." if block_given? end
def initialize_plugins
def initialize_plugins Hoe.plugins.each do |plugin| msg = "initialize_#{plugin}" warn msg if $DEBUG send msg if self.respond_to? msg end end
def intuit_values input
def intuit_values input readme = input .lines .chunk { |l| l[/^(?:=+|#+)/] || "" } .map(&:last) .each_slice(2) .to_h { |k, v| raise "No body for %p section" % [k[0].strip] \ unless v kp = k.map { |s| s.strip.chomp(":").sub(/(?:=+|#+)\s*/, '').downcase }.join("\n") [kp, v.join.strip] } unless readme.empty? then desc = readme.values_at(*description_sections).join("\n\n") summ = desc.split(/\.\s+/).first(summary_sentences).join(". ") self.urls ||= parse_urls(readme.values.first) self.description ||= desc self.summary ||= summ else missing readme_file end self.changes ||= begin h = File.read_utf(history_file) h.split(/^(={2,}|\#{2,})/)[1..2].join.strip rescue missing history_file "" end end
def license name
Call it multiple times if you are releasing under multiple licenses.
Specify a license for your gem.
#
def license name self.licenses << name.to_s end
def load_plugin_tasks
def load_plugin_tasks bad = [] $plugin_max = self.class.plugins.map { |s| s.to_s.size }.max self.class.plugins.each do |plugin| warn "define: #{plugin}" if $DEBUG old_tasks = Rake::Task.tasks.dup begin send "define_#{plugin}_tasks" rescue NoMethodError warn "warning: couldn't activate the #{plugin} plugin, skipping" bad << plugin next end (Rake::Task.tasks - old_tasks).each do |task| task.plugin = plugin end end @@plugins -= bad end
def maybe_load_yaml path # :nodoc:
def maybe_load_yaml path # :nodoc: if File.exist? path then if YAML.respond_to? :safe_load_file then YAML.safe_load_file path, permitted_classes: [Regexp, Symbol] else YAML.load_file path end else {} end end
def missing name
def missing name warn "** #{name} is missing or in the wrong format for auto-intuiting." warn " run `sow blah` and look at its text files" end
def normalize_deps deps
def normalize_deps deps deps = Array(deps) deps.each do |o| abort "ERROR: Add '~> x.y' to the '#{o}' dependency." if String === o end deps end
def paragraphs_of path, *paragraphs
def paragraphs_of path, *paragraphs File.read_utf(path).delete("\r").split(/\n\n+/).values_at(*paragraphs) end
def parse_urls text
def parse_urls text lines = text.gsub(/^\* /, "").delete("<>").split(/\n/).grep(/\S+/) if lines.first =~ /::/ then Hash[lines.map { |line| line.split(/\s*::\s*/) }] else raise "Please switch readme to hash format for urls." end end
def pluggable!
def pluggable! abort "update rubygems to >= 1.3.1" unless Gem.respond_to? :find_files require_rubygems_version ">= 1.3.1" end
def plugin? name
def plugin? name self.class.plugins.include? name end
def post_initialize
def post_initialize activate_plugin_deps unless skip_intuit_values? intuit_values File.read_utf readme_file if readme_file end validate_fields define_spec load_plugin_tasks add_dependencies end
def read_manifest
def read_manifest File.read_utf("Manifest.txt").split(/\r?\n\r?/) rescue nil end
def require_ruby_version *versions
def require_ruby_version *versions spec_extras[:required_ruby_version] = versions end
def require_rubygems_version *versions
def require_rubygems_version *versions spec_extras[:required_rubygems_version] = versions end
def ruby20!
def ruby20! require_ruby_version "~> 2.0" end
def ruby21!
def ruby21! require_ruby_version "~> 2.1" end
def ruby22!
def ruby22! require_ruby_version "~> 2.2" end
def ruby23!
def ruby23! require_ruby_version "~> 2.3" end
def run_spec_extras # :nodoc:
def run_spec_extras # :nodoc: # Do any extra stuff the user wants self.spec_extras.each do |msg, val| case val when Proc val.call spec.send(msg) else spec.send "#{msg}=", val end end end
def skip_intuit_values? # :nodoc:
def skip_intuit_values? # :nodoc: %w[summary description homepage].all? { |field| send field } end
def timebomb n, m, finis = nil, start = nil
def timebomb n, m, finis = nil, start = nil require "time" finis = Time.parse(finis || "#{Time.now.year}-12-31") start = Time.parse(start || "#{Time.now.year}-01-01") rest = (finis - Time.now) full = (finis - start) [((n - m) * rest / full).to_i + m, m].max end
def validate_fields
def validate_fields %w[email author].each do |field| value = self.send(field) abort "Hoe #{field} value not set. aborting" if value.nil? or value.empty? end end
def with_config
def with_config config = Hoe::DEFAULT_CONFIG rc = File.expand_path("~/.hoerc") homeconfig = maybe_load_yaml rc config = config.merge homeconfig localrc = File.join Dir.pwd, ".hoerc" localconfig = maybe_load_yaml(localrc) config = config.merge localconfig yield config, rc end