Greg Karékinian de11c0d691 Set up an instance of Mastodon for Kosmos
Refs #19

Use new application cookbook, update our cookbooks
2017-04-06 21:20:51 +02:00

182 lines
6.7 KiB
Ruby

#
# Copyright 2015-2016, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise/error'
module Poise
module Utils
autoload :ResourceProviderMixin, 'poise/utils/resource_provider_mixin'
autoload :ShellOut, 'poise/utils/shell_out'
autoload :Win32, 'poise/utils/win32'
extend self
# Find the cookbook name for a given filename. The can used to find the
# cookbook that corresponds to a caller of a file.
#
# @param run_context [Chef::RunContext] Context to check.
# @param filename [String] Absolute filename to check for.
# @return [String]
# @example
# def my_thing
# caller_filename = caller.first.split(':').first
# cookbook = Poise::Utils.find_cookbook_name(run_context, caller_filename)
# # ...
# end
def find_cookbook_name(run_context, filename)
possibles = {}
Poise.debug("[Poise] Checking cookbook for #{filename.inspect}")
run_context.cookbook_collection.each do |name, ver|
# This special method is added by Halite::Gem#as_cookbook_version.
if ver.respond_to?(:halite_root)
# The join is there because ../poise-ruby/lib starts with ../poise so
# we want a trailing /.
if filename.start_with?(File.join(ver.halite_root, ''))
Poise.debug("[Poise] Found matching halite_root in #{name}: #{ver.halite_root.inspect}")
possibles[ver.halite_root] = name
end
else
Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |seg|
ver.segment_filenames(seg).each do |file|
if ::File::ALT_SEPARATOR
file = file.gsub(::File::ALT_SEPARATOR, ::File::SEPARATOR)
end
# Put this behind an environment variable because it is verbose
# even for normal debugging-level output.
Poise.debug("[Poise] Checking #{seg} in #{name}: #{file.inspect}")
if file == filename
Poise.debug("[Poise] Found matching #{seg} in #{name}: #{file.inspect}")
possibles[file] = name
end
end
end
end
end
raise Poise::Error.new("Unable to find cookbook for file #{filename.inspect}") if possibles.empty?
# Sort the items by matching path length, pick the name attached to the longest.
possibles.sort_by{|key, value| key.length }.last[1]
end
# Try to find an ancestor to call a method on.
#
# @since 2.2.3
# @since 2.3.0
# Added ignore parameter.
# @param obj [Object] Self from the caller.
# @param msg [Symbol] Method to try to call.
# @param args [Array<Object>] Method arguments.
# @param default [Object] Default return value if no valid ancestor exists.
# @param ignore [Array<Object>] Return value to ignore when scanning ancesors.
# @return [Object]
# @example
# val = @val || Poise::Utils.ancestor_send(self, :val)
def ancestor_send(obj, msg, *args, default: nil, ignore: [default])
# Class is a subclass of Module, if we get something else use its class.
obj = obj.class unless obj.is_a?(Module)
ancestors = []
if obj.respond_to?(:superclass)
# Check the superclass first if present.
ancestors << obj.superclass
end
# Make sure we don't check obj itself.
ancestors.concat(obj.ancestors.drop(1))
ancestors.each do |mod|
if mod.respond_to?(msg)
val = mod.send(msg, *args)
# If we get the default back, assume we should keep trying.
return val unless ignore.include?(val)
end
end
# Nothing valid found, use the default.
default
end
# Create a helper to invoke a module with some parameters.
#
# @since 2.3.0
# @param mod [Module] The module to wrap.
# @param block [Proc] The module to implement to parameterization.
# @return [void]
# @example
# module MyMixin
# def self.my_mixin_name(name)
# # ...
# end
# end
#
# Poise::Utils.parameterized_module(MyMixin) do |name|
# my_mixin_name(name)
# end
def parameterized_module(mod, &block)
raise Poise::Error.new("Cannot parameterize an anonymous module") unless mod.name && !mod.name.empty?
parent_name_parts = mod.name.split(/::/)
# Grab the last piece which will be the method name.
mod_name = parent_name_parts.pop
# Find the enclosing module or class object.
parent = parent_name_parts.inject(Object) {|memo, name| memo.const_get(name) }
# Object is a special case since we need #define_method instead.
method_type = if parent == Object
:define_method
else
:define_singleton_method
end
# Scoping hack.
self_ = self
# Construct the method.
parent.send(method_type, mod_name) do |*args|
self_.send(:check_block_arity!, block, args)
# Create a new anonymous module to be returned from the method.
Module.new do
# Fake the name.
define_singleton_method(:name) do
super() || mod.name
end
# When the stub module gets included, activate our behaviors.
define_singleton_method(:included) do |klass|
super(klass)
klass.send(:include, mod)
klass.instance_exec(*args, &block)
end
end
end
end
private
# Check that the given arguments match the given block. This is needed
# because Ruby will nil-pad mismatched argspecs on blocks rather than error.
#
# @since 2.3.0
# @param block [Proc] Block to check.
# @param args [Array<Object>] Arguments to check.
# @return [void]
def check_block_arity!(block, args)
# Convert the block to a lambda-style proc. You can't make this shit up.
obj = Object.new
obj.define_singleton_method(:block, &block)
block = obj.method(:block).to_proc
# Check
required_args = block.arity < 0 ? ~block.arity : block.arity
if args.length < required_args || (block.arity >= 0 && args.length > block.arity)
raise ArgumentError.new("wrong number of arguments (#{args.length} for #{required_args}#{block.arity < 0 ? '+' : ''})")
end
end
end
end