Plugins Installer API (#3352)

* Sketch out in comments the unit and functional tests for the installer
* Make a test fixture gem, v0.1.0
* Add a 0.2.0 version of the test fixture gem, this one with a dependency
* Add a fixture with a pre-installed gem
* Correct test-fixture 0.1.0 gem
* Moockup of installed inspec-test-fixture gems
* Uggh add gemspec files to mock installs
* Update gem fixtures, and add a script that does it for me
* Able to load from and list privately managed gems
# Conflicts:
#	lib/inspec/plugin/v2/loader.rb

* Expanded tests, starting on implementation of installer

# Conflicts:
#	test/unit/plugin/v2/loader_test.rb

* Install plugin from local gem file works
* Writes the plugins.json file; needs refactor
* Gem install works; no version pinning
* Install with pinned version works
* Install from path works
* update works
* Validation for uninstall
* Uninstall from path works
* Uninstaller works on gems
* Add search to installer API.


Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
Clinton Wolfe 2018-09-19 17:38:13 -04:00 committed by Jared Quick
parent 1ba3174cbd
commit 7963131670
78 changed files with 2581 additions and 62 deletions

View file

@ -6,13 +6,24 @@ module Inspec
class Exception < Inspec::Error; end
class ConfigError < Inspec::Plugin::V2::Exception; end
class LoadError < Inspec::Plugin::V2::Exception; end
class GemActionError < Inspec::Plugin::V2::Exception
attr_accessor :plugin_name
attr_accessor :version
end
class InstallError < Inspec::Plugin::V2::GemActionError; end
class UpdateError < Inspec::Plugin::V2::GemActionError
attr_accessor :from_version, :to_version
end
class UnInstallError < Inspec::Plugin::V2::GemActionError; end
class SearchError < Inspec::Plugin::V2::GemActionError; end
end
end
end
require_relative 'v2/registry'
require_relative 'v2/loader'
require_relative 'v2/plugin_base'
require 'inspec/globals'
require 'inspec/plugin/v2/registry'
require 'inspec/plugin/v2/loader'
require 'inspec/plugin/v2/plugin_base'
# Load all plugin type base classes
Dir.glob(File.join(__dir__, 'v2', 'plugin_types', '*.rb')).each { |file| require file }

View file

@ -3,14 +3,19 @@ module Inspec::Plugin::V2
:plugin_name,
:plugin_type,
:activator_name,
:activated,
:'activated?',
:exception,
:activation_proc,
:implementation_class,
) do
def initialize(*)
super
self[:activated] = false
self[:'activated?'] = false
end
def activated?(new_value = nil)
return self[:'activated?'] if new_value.nil?
self[:'activated?'] = new_value
end
end
end

View file

@ -0,0 +1,406 @@
# This file is not required by default.
require 'singleton'
require 'forwardable'
# Gem extensions for doing unusual things - not loaded by Gem default
require 'rubygems/package'
require 'rubygems/name_tuple'
require 'rubygems/uninstaller'
module Inspec::Plugin::V2
# Handles all actions modifying the user's plugin set:
# * Modifying the plugins.json file
# * Installing, updating, and removing gem-based plugins
# Loading plugins is handled by Loader.
# Listing plugins is handled by Loader.
# Searching for plugins is handled by ???
class Installer
include Singleton
extend Forwardable
Gem.configuration['verbose'] = false
attr_reader :loader, :registry
def_delegator :loader, :plugin_gem_path, :gem_path
def_delegator :loader, :plugin_conf_file_path
def_delegator :loader, :list_managed_gems
def_delegator :loader, :list_installed_plugin_gems
def initialize
@loader = Inspec::Plugin::V2::Loader.new
@registry = Inspec::Plugin::V2::Registry.instance
end
def plugin_installed?(name)
list_installed_plugin_gems.detect { |spec| spec.name == name }
end
def plugin_version_installed?(name, version)
list_installed_plugin_gems.detect { |spec| spec.name == name && spec.version == Gem::Version.new(version) }
end
# Installs a plugin. Defaults to assuming the plugin provided is a gem, and will try to install
# from whatever gemsources `rubygems` thinks it should use.
# If it's a gem, installs it and its dependencies to the `gem_path`. The gem is not activated.
# If it's a path, leaves it in place.
# Finally, updates the plugins.json file with the new information.
# No attempt is made to load the plugin.
#
# @param [String] plugin_name
# @param [Hash] opts The installation options
# @option opts [String] :gem_file Path to a local gem file to install from
# @option opts [String] :path Path to a file to be used as the entry point for a path-based plugin
# @option opts [String] :version Version constraint for remote gem installs
def install(plugin_name, opts = {})
# TODO: - check plugins.json for validity before trying anything that needs to modify it.
validate_installation_opts(plugin_name, opts)
if opts[:path]
install_from_path(plugin_name, opts)
elsif opts[:gem_file]
install_from_gem_file(plugin_name, opts)
else
install_from_remote_gems(plugin_name, opts)
end
update_plugin_config_file(plugin_name, opts.merge({ action: :install }))
end
# Updates a plugin. Most options same as install, but will not handle path installs.
# If no :version is provided, updates to the latest.
# If a version is provided, the plugin becomes pinned at that specified version.
#
# @param [String] plugin_name
# @param [Hash] opts The installation options
# @option opts [String] :gem_file Reserved for future use. No effect.
# @option opts [String] :version Version constraint for remote gem updates
def update(plugin_name, opts = {})
# TODO: - check plugins.json for validity before trying anything that needs to modify it.
validate_update_opts(plugin_name, opts)
opts[:update_mode] = true
# TODO: Handle installing from a local file
# TODO: Perform dependency checks to make sure the new solution is valid
install_from_remote_gems(plugin_name, opts)
update_plugin_config_file(plugin_name, opts.merge({ action: :update }))
end
# Uninstalls (removes) a plugin. Refers to plugin.json to determine if it
# was a gem-based or path-based install.
# If it's a gem, uninstalls it, and all other unused plugins.
# If it's a path, removes the reference from the plugins.json, but does not
# tamper with the plugin source tree.
# Either way, the plugins.json file is updated with the new information.
#
# @param [String] plugin_name
# @param [Hash] opts The uninstallation options. Currently unused.
def uninstall(plugin_name, opts = {})
# TODO: - check plugins.json for validity before trying anything that needs to modify it.
validate_uninstall_opts(plugin_name, opts)
if registry.path_based_plugin?(plugin_name)
uninstall_via_path(plugin_name, opts)
else
uninstall_via_gem(plugin_name, opts)
end
update_plugin_config_file(plugin_name, opts.merge({ action: :uninstall }))
end
# Search rubygems.org for a plugin gem.
#
# @param [String] plugin_seach_term
# @param [Hash] opts Search options
# @option opts [TrueClass, FalseClass] :exact If true, use plugin_search_term exactly. If false (default), append a wildcard.
# @return [Hash of Arrays] - Keys are String names of gems, arrays contain String versions.
def search(plugin_query, opts = {})
validate_search_opts(plugin_query, opts)
fetcher = Gem::SpecFetcher.fetcher
matched_tuples = []
if opts[:exact]
matched_tuples = fetcher.detect(:released) { |tuple| tuple.name == plugin_query }
else
regex = Regexp.new('^' + plugin_query + '.*')
matched_tuples = fetcher.detect(:released) do |tuple|
tuple.name != 'inspec-core' && tuple.name =~ regex
end
end
gem_info = {}
matched_tuples.each do |tuple|
gem_info[tuple.first.name] ||= []
gem_info[tuple.first.name] << tuple.first.version.to_s
end
gem_info
end
# Testing API. Performs a hard reset on the installer and registry, and reloads the loader.
# Not for public use.
# TODO: bad timing coupling in tests
def __reset
registry.__reset
end
def __reset_loader
@loader = Loader.new
end
private
#===================================================================#
# Validation Methods #
#===================================================================#
# rubocop: disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
# rationale for rubocop exemption: While there are many conditionals, they are all of the same form;
# its goal is to check for several subtle combinations of params, and raise an error if needed. It's
# straightforward to understand, but has to handle many cases.
def validate_installation_opts(plugin_name, opts)
unless plugin_name =~ /^(inspec|train)-/
raise InstallError, "All inspec plugins must begin with either 'inspec-' or 'train-' - refusing to install #{plugin_name}"
end
if opts.key?(:gem_file) && opts.key?(:path)
raise InstallError, 'May not specify both gem_file and a path (for installing from source)'
end
if opts.key?(:version) && (opts.key?(:gem_file) || opts.key?(:path))
raise InstallError, 'May not specify a version when installing from a gem file or source path'
end
if opts.key?(:gem_file)
unless opts[:gem_file].end_with?('.gem')
raise InstallError, "When installing from a local gem file, gem file must have '.gem' extension - saw #{opts[:gem_file]}"
end
unless File.exist?(opts[:gem_file])
raise InstallError, "Could not find local gem file to install - #{opts[:gem_file]}"
end
elsif opts.key?(:path)
unless Dir.exist?(opts[:path])
raise InstallError, "Could not find directory for install from source path - #{opts[:path]}"
end
end
if plugin_installed?(plugin_name)
if opts.key?(:version) && plugin_version_installed?(plugin_name, opts[:version])
raise InstallError, "#{plugin_name} version #{opts[:version]} is already installed."
else
raise InstallError, "#{plugin_name} is already installed. Use 'inspec plugin update' to change version."
end
end
end
# rubocop: enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
def validate_update_opts(plugin_name, opts)
# Only update plugins we know about
unless plugin_name =~ /^(inspec|train)-/
raise UpdateError, "All inspec plugins must begin with either 'inspec-' or 'train-' - refusing to update #{plugin_name}"
end
unless registry.known_plugin?(plugin_name.to_sym)
raise UpdateError, "'#{plugin_name}' is not installed - use 'inspec plugin install' to install it"
end
# No local path support for update
if registry[plugin_name.to_sym].installation_type == :path
raise UpdateError, "'inspec plugin update' will not handle path-based plugins like '#{plugin_name}'. Use 'inspec plugin uninstall' to remove the reference, then install as a gem."
end
if opts.key?(:path)
raise UpdateError, "'inspec plugin update' will not install from a path."
end
if opts.key?(:version) && plugin_version_installed?(plugin_name, opts[:version])
raise UpdateError, "#{plugin_name} version #{opts[:version]} is already installed."
end
end
def validate_uninstall_opts(plugin_name, _opts)
# Only uninstall plugins we know about
unless plugin_name =~ /^(inspec|train)-/
raise UnInstallError, "All inspec plugins must begin with either 'inspec-' or 'train-' - refusing to uninstall #{plugin_name}"
end
unless registry.known_plugin?(plugin_name.to_sym)
raise UnInstallError, "'#{plugin_name}' is not installed, refusing to uninstall."
end
end
def validate_search_opts(search_term, _opts)
unless search_term =~ /^(inspec|train)-/
raise SearchError, "All inspec plugins must begin with either 'inspec-' or 'train-'."
end
end
#===================================================================#
# Install / Upgrade Methods #
#===================================================================#
def install_from_path(requested_plugin_name, opts)
# Nothing to do here; we will later update the plugins file with the path.
end
def install_from_gem_file(requested_plugin_name, opts)
plugin_dependency = Gem::Dependency.new(requested_plugin_name)
# Make Set that encompasses just the gemfile that was provided
plugin_local_source = Gem::Source::SpecificFile.new(opts[:gem_file])
requested_local_gem_set = Gem::Resolver::InstallerSet.new(:both) # :both means local and remote; allow satisfying our gemfile's deps from rubygems.org
requested_local_gem_set.add_local(plugin_dependency.name, plugin_local_source.spec, plugin_local_source)
install_gem_to_plugins_dir(plugin_dependency, [requested_local_gem_set])
end
def install_from_remote_gems(requested_plugin_name, opts)
plugin_dependency = Gem::Dependency.new(requested_plugin_name, opts[:version] || '> 0')
# BestSet is rubygems.org API + indexing
install_gem_to_plugins_dir(plugin_dependency, [Gem::Resolver::BestSet.new], opts[:update_mode])
end
def install_gem_to_plugins_dir(new_plugin_dependency, extra_request_sets = [], update_mode = false)
# Get a list of all the gems available to us.
gem_to_force_update = update_mode ? new_plugin_dependency.name : nil
set_available_for_resolution = build_gem_request_universe(extra_request_sets, gem_to_force_update)
# Solve the dependency (that is, find a way to install the new plugin and anything it needs)
request_set = Gem::RequestSet.new(new_plugin_dependency)
begin
request_set.resolve(set_available_for_resolution)
rescue Gem::UnsatisfiableDependencyError => gem_ex
# TODO: use search facility to determine if the requested gem exists at all, vs if the constraints are impossible
ex = Inspec::Plugin::V2::InstallError.new(gem_ex.message)
ex.plugin_name = new_plugin_dependency.name
raise ex
end
# OK, perform the installation.
# Ignore deps here, because any needed deps should already be baked into new_plugin_dependency
request_set.install_into(gem_path, true, ignore_dependencies: true)
end
#===================================================================#
# UnInstall Methods #
#===================================================================#
def uninstall_via_path(requested_plugin_name, opts)
# Nothing to do here; we will later update the plugins file to remove the plugin entry.
end
def uninstall_via_gem(plugin_name_to_be_removed, _opts)
# Strategy: excluding the plugin we want to uninstall, determine a gem install solution
# based on gems we already have, then remove anything not needed. This removes 3 kinds
# of cruft:
# 1. All versions of the unwanted plugin gem
# 2. All dependencies of the unwanted plugin gem (that aren't needed by something else)
# 3. All other gems installed under the ~/.inspec/gems area that are not needed
# by a plugin gem. TODO: ideally this would be a separate 'clean' operation.
# Create a list of plugins dependencies, including any version constraints,
# excluding any that are path-or-core-based, excluding the gem to be removed
plugin_deps_we_still_must_satisfy = registry.plugin_statuses
plugin_deps_we_still_must_satisfy = plugin_deps_we_still_must_satisfy.select do |status|
status.installation_type == :gem && status.name != plugin_name_to_be_removed.to_sym
end
plugin_deps_we_still_must_satisfy = plugin_deps_we_still_must_satisfy.map do |status|
constraint = status.version || '> 0'
Gem::Dependency.new(status.name.to_s, constraint)
end
# Make a Request Set representing the still-needed deps
request_set_we_still_must_satisfy = Gem::RequestSet.new(*plugin_deps_we_still_must_satisfy)
request_set_we_still_must_satisfy.remote = false
# Find out which gems we still actually need...
names_of_gems_we_actually_need = \
request_set_we_still_must_satisfy.resolve(build_gem_request_universe)
.map(&:full_spec).map(&:full_name)
# ... vs what we currently have, which should have some cruft
cruft_gem_specs = loader.list_managed_gems.reject do |spec|
names_of_gems_we_actually_need.include?(spec.full_name)
end
# Ok, delete the unneeded gems
cruft_gem_specs.each do |cruft_spec|
Gem::Uninstaller.new(
cruft_spec.name,
version: cruft_spec.version,
install_dir: gem_path,
# Docs on this class are poor. Next 4 are reasonable, but cargo-culted.
all: true,
executables: true,
force: true,
ignore: true,
).uninstall_gem(cruft_spec)
end
end
#===================================================================#
# Utilities
#===================================================================#
# Provides a RequestSet (a set of gems representing the gems that are available to
# solve a dependency request) that represents a combination of:
# * the gems included in the system
# * the gems included in the inspec install
# * the currently installed gems in the ~/.inspec/gems directory
# * any other sets you provide
def build_gem_request_universe(extra_request_sets = [], gem_to_force_update = nil)
installed_plugins_gem_set = Gem::Resolver::VendorSet.new
loader.list_managed_gems.each do |spec|
next if spec.name == gem_to_force_update
installed_plugins_gem_set.add_vendor_gem(spec.name, spec.gem_dir)
end
# Combine the Sets, so the resolver has one composite place to look
Gem::Resolver.compose_sets(
installed_plugins_gem_set, # The gems that are in the plugin gem path directory tree
Gem::Resolver::CurrentSet.new, # The gems that are already included either with Ruby or with the InSpec install
*extra_request_sets, # Anything else our caller wanted to include
)
end
#===================================================================#
# plugins.json Maintenance Methods #
#===================================================================#
# TODO: refactor the plugin.json file to have its own class, which Installer consumes
def update_plugin_config_file(plugin_name, opts)
config = update_plugin_config_data(plugin_name, opts)
File.write(plugin_conf_file_path, JSON.pretty_generate(config))
end
# TODO: refactor the plugin.json file to have its own class, which Installer consumes
def update_plugin_config_data(plugin_name, opts)
config = read_or_init_config_data
config['plugins'].delete_if { |entry| entry['name'] == plugin_name }
return config if opts[:action] == :uninstall
entry = { 'name' => plugin_name }
# Parsing by Requirement handles lot of awkward formattoes
entry['version'] = Gem::Requirement.new(opts[:version]).to_s if opts.key?(:version)
if opts.key?(:path)
entry['installation_type'] = 'path'
entry['installation_path'] = opts[:path]
end
config['plugins'] << entry
config
end
# TODO: check for validity
# TODO: refactor the plugin.json file to have its own class, which Installer consumes
def read_or_init_config_data
if File.exist?(plugin_conf_file_path)
JSON.parse(File.read(plugin_conf_file_path))
else
{
'plugins_config_version' => '1.0.0',
'plugins' => [],
}
end
end
end
end

View file

@ -14,7 +14,6 @@ module Inspec::Plugin::V2
def initialize(options = {})
@options = options
@registry = Inspec::Plugin::V2::Registry.instance
determine_plugin_conf_file
read_conf_file
unpack_conf_file
@ -28,7 +27,10 @@ module Inspec::Plugin::V2
end
def load_all
registry.each do |plugin_name, plugin_details|
# Be careful not to actually iterate directly over the registry here;
# we want to allow "sidecar loading", in which case a plugin may add an entry to the registry.
registry.plugin_names.dup.each do |plugin_name|
plugin_details = registry[plugin_name]
# We want to capture literally any possible exception here, since we are storing them.
# rubocop: disable Lint/RescueException
begin
@ -69,13 +71,26 @@ module Inspec::Plugin::V2
end
end
def activate_mentioned_cli_plugins(cli_args = ARGV)
# Get a list of CLI plugin activation hooks
registry.find_activators(plugin_type: :cli_command).each do |act|
next if act.activated?
# If there is anything in the CLI args with the same name, activate it
# If the word 'help' appears in the first position, load all CLI plugins
if cli_args.include?(act.activator_name.to_s) || cli_args[0] == 'help'
activate(:cli_command, act.activator_name)
act.implementation_class.register_with_thor
end
end
end
def activate(plugin_type, hook_name)
activator = registry.find_activators(plugin_type: plugin_type, activator_name: hook_name).first
# We want to capture literally any possible exception here, since we are storing them.
# rubocop: disable Lint/RescueException
begin
impl_class = activator.activation_proc.call
activator.activated = true
activator.activated?(true)
activator.implementation_class = impl_class
rescue Exception => ex
activator.exception = ex
@ -84,21 +99,73 @@ module Inspec::Plugin::V2
# rubocop: enable Lint/RescueException
end
def activate_mentioned_cli_plugins(cli_args = ARGV)
# Get a list of CLI plugin activation hooks
registry.find_activators(plugin_type: :cli_command).each do |act|
next if act.activated
# If there is anything in the CLI args with the same name, activate it
# If the word 'help' appears in the first position, load all CLI plugins
if cli_args.include?(act.activator_name.to_s) || cli_args[0] == 'help' || cli_args.size.zero?
activate(:cli_command, act.activator_name)
act.implementation_class.register_with_thor
end
end
def plugin_gem_path
self.class.plugin_gem_path
end
def self.plugin_gem_path
# I can't believe there isn't a simpler way of getting this
# 2.4.2.p123 => 2.4.0
ruby_abi_version = (Gem.ruby_version.segments[0, 2] << 0).join('.')
File.join(Inspec.config_dir, 'gems', ruby_abi_version)
end
# Lists all gems found in the plugin_gem_path.
# @return [Array[Gem::Specification]] Specs of all gems found.
def list_managed_gems
Dir.glob(File.join(plugin_gem_path, 'specifications', '*.gemspec')).map { |p| Gem::Specification.load(p) }
end
# Lists all plugin gems found in the plugin_gem_path.
# This is simply all gems that begin with train- or inspec-.
# @return [Array[Gem::Specification]] Specs of all gems found.
def list_installed_plugin_gems
list_managed_gems.select { |spec| spec.name.match(/^(inspec|train)-/) }
end
# TODO: refactor the plugin.json file to have its own class, which Loader consumes
def plugin_conf_file_path
self.class.plugin_conf_file_path
end
# TODO: refactor the plugin.json file to have its own class, which Loader consumes
def self.plugin_conf_file_path
File.join(Inspec.config_dir, 'plugins.json')
end
private
# 'Activating' a gem adds it to the load path, so 'require "gemname"' will work.
# Given a gem name, this activates the gem and all of its dependencies, respecting
# version pinning needs.
def activate_managed_gems_for_plugin(plugin_gem_name, version_constraint = '> 0')
# TODO: enforce first-level version pinning
plugin_deps = [Gem::Dependency.new(plugin_gem_name.to_s, version_constraint)]
managed_gem_set = Gem::Resolver::VendorSet.new
list_managed_gems.each { |spec| managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir) }
# TODO: Next two lines merge our managed gems with the other gems available
# in our "local universe" - which may be the system, or it could be in a Bundler microcosm,
# or rbenv, etc. Do we want to merge that, though?
distrib_gem_set = Gem::Resolver::CurrentSet.new
installed_gem_set = Gem::Resolver.compose_sets(managed_gem_set, distrib_gem_set)
# So, given what we need, and what we have available, what activations are needed?
resolver = Gem::Resolver.new(plugin_deps, installed_gem_set)
begin
solution = resolver.resolve
rescue Gem::UnsatisfiableDependencyError => gem_ex
# If you broke your install, or downgraded to a plugin with a bad gemspec, you could get here.
ex = Inspec::Plugin::V2::LoadError.new(gem_ex.message)
raise ex
end
solution.each do |activation_request|
next if activation_request.full_spec.activated?
activation_request.full_spec.activate
# TODO: If we are under Bundler, inform it that we loaded a gem
end
end
def annotate_status_after_loading(plugin_name)
status = registry[plugin_name]
return if status.api_generation == 2 # Gen2 have self-annotating superclasses
@ -116,7 +183,7 @@ module Inspec::Plugin::V2
status = registry[plugin_name]
status.api_generation = 0
act = Activator.new
act.activated = true
act.activated?(true)
act.plugin_type = :cli_command
act.plugin_name = plugin_name
act.activator_name = :default
@ -143,11 +210,6 @@ module Inspec::Plugin::V2
end
end
def determine_plugin_conf_file
@plugin_conf_file_path = ENV['INSPEC_CONFIG_DIR'] ? ENV['INSPEC_CONFIG_DIR'] : File.join(Dir.home, '.inspec')
@plugin_conf_file_path = File.join(@plugin_conf_file_path, 'plugins.json')
end
def detect_core_plugins
core_plugins_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'plugins'))
# These are expected to be organized as proper separate projects,
@ -165,8 +227,8 @@ module Inspec::Plugin::V2
# TODO: DRY up re: Installer read_or_init_config_file
# TODO: refactor the plugin.json file to have its own class, which Loader consumes
def read_conf_file
if File.exist?(@plugin_conf_file_path)
@plugin_file_contents = JSON.parse(File.read(@plugin_conf_file_path))
if File.exist?(plugin_conf_file_path)
@plugin_file_contents = JSON.parse(File.read(plugin_conf_file_path))
else
@plugin_file_contents = {
'plugins_config_version' => '1.0.0',
@ -174,19 +236,20 @@ module Inspec::Plugin::V2
}
end
rescue JSON::ParserError => e
raise Inspec::Plugin::V2::ConfigError, "Failed to load plugins JSON configuration from #{@plugin_conf_file_path}:\n#{e}"
raise Inspec::Plugin::V2::ConfigError, "Failed to load plugins JSON configuration from #{plugin_conf_file_path}:\n#{e}"
end
# TODO: refactor the plugin.json file to have its own class, which Loader consumes
def unpack_conf_file
validate_conf_file
@plugin_file_contents['plugins'].each do |plugin_json|
status = Inspec::Plugin::V2::Status.new
status.name = plugin_json['name'].to_sym
status.loaded = false
status.installation_type = plugin_json['installation_type'].to_sym || :gem
status.installation_type = (plugin_json['installation_type'] || :gem).to_sym
case status.installation_type
when :gem
status.entry_point = status.name
status.entry_point = status.name.to_s
status.version = plugin_json['version']
when :path
status.entry_point = plugin_json['installation_path']
@ -196,9 +259,10 @@ module Inspec::Plugin::V2
end
end
# TODO: refactor the plugin.json file to have its own class, which Loader consumes
def validate_conf_file
unless @plugin_file_contents['plugins_config_version'] == '1.0.0'
raise Inspec::Plugin::V2::ConfigError, "Unsupported plugins.json file version #{@plugin_file_contents['plugins_config_version']} at #{@plugin_conf_file_path} - currently support versions: 1.0.0"
raise Inspec::Plugin::V2::ConfigError, "Unsupported plugins.json file version #{@plugin_file_contents['plugins_config_version']} at #{plugin_conf_file_path} - currently support versions: 1.0.0"
end
plugin_entries = @plugin_file_contents['plugins']

View file

@ -25,7 +25,7 @@ module Inspec::Plugin::V2
end
def loaded_plugin?(name)
registry.dig(name, :loaded)
registry.dig(name.to_sym, :loaded)
end
def loaded_count
@ -40,6 +40,10 @@ module Inspec::Plugin::V2
registry.values.select(&:loaded).map(&:name)
end
def path_based_plugin?(name)
known_plugin?(name.to_sym) && registry[name.to_sym].installation_type == :path
end
def find_status_by_class(klass)
registry.values.detect { |status| status.plugin_class == klass }
end

View file

@ -0,0 +1,45 @@
#!/bin/bash
# Unportable assumptions:
# 1. You use rbenv to manage ruby runtimes.
# 2. You are running from project root
eval "$(rbenv init -)"
PLUGIN_SRC_DIR=test/unit/mock/plugins/inspec-test-fixture
FIXTURE_BASE=test/unit/mock/config_dirs
FIXTURE_VERSIONS="1 2"
RUBY_VERSIONS="2.3.1,2.3.0 2.4.2,2.4.0 2.5.1,2.5.0"
# Make two fresh gems
cd $PLUGIN_SRC_DIR
sed -i -e s/0\.2\.0/0.1.0/ lib/inspec-test-fixture/version.rb
bundle exec rake build
sed -i -e s/0\.1\.0/0.2.0/ lib/inspec-test-fixture/version.rb
bundle exec rake build
sed -i -e s/0\.2\.0/0.1.0/ lib/inspec-test-fixture/version.rb
rm -f lib/inspec-test-fixture/version.rb-e
cd ../../../../../
# Purge and re-install the existing gem installs
for fver in $FIXTURE_VERSIONS; do
for info in $RUBY_VERSIONS; do
RUBY_VER=$(echo "$info" | cut -d, -f1)
RUBY_ABI=$(echo "$info" | cut -d, -f2)
GEM_DIR="$FIXTURE_BASE/test-fixture-${fver}-float/gems/$RUBY_ABI"
echo "Reinstalling inspec-test-fixture-0.${fver}.0 for ABI $RUBY_ABI"
rm -rf "$GEM_DIR"
mkdir -p "$GEM_DIR"
rbenv shell "$RUBY_VER"
gem install -N -i "$GEM_DIR $PLUGIN_SRC_DIR/pkg/inspec-test-fixture-0.${fver}.0.gem"
echo
done
done
# Fix ordinal array gemspec....
for info in $RUBY_VERSIONS; do
RUBY_ABI=$(echo $info | cut -d, -f2)
GEM_DIR="$FIXTURE_BASE/test-fixture-${fver}-float/gems/$RUBY_ABI"
cp -v "$GEM_DIR/specifications/ordinal_array-0.2.0.gemspec" "$GEM_DIR/gems/ordinal_array-0.2.0/ordinal_array.gemspec"
done
rbenv shell 2.4.2

View file

@ -0,0 +1,11 @@
This test fixture is a user config dir setup as though the inspec-test-fixture v1 was already installed, and it has no version constraint.
This was accomplished by executing (*without* bundler) the following commands:
```bash
mkdir -p test/unit/mock/config_dirs/test-fixture-1-float/gems/{2.3.0,2.4.0,2.5.0}
# Here I'm running a ruby 2.4.x binary with rbenv
gem install -l -N -i test/unit/mock/config_dirs/test-fixture-1-float/gems/2.4.0 test/unit/mock/plugins/inspec-test-fixture/pkg/inspec-test-fixture-0.1.0.gem
```
Note that we will need to add an installation tree each time we support a new minor or major version of the Ruby VM.

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.1.0"
end
end

View file

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
# stub: inspec-test-fixture 0.1.0 ruby lib
Gem::Specification.new do |s|
s.name = "inspec-test-fixture"
s.version = "0.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
s.authors = ["InSpec Engineering Team"]
s.date = "2018-08-17"
s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development."
s.email = ["hello@chef.io"]
s.homepage = "https://github.com/inspec/inspec"
s.rubygems_version = "2.5.1"
s.summary = "A simple test plugin gem for InSpec"
s.installed_by_version = "2.5.1" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>, ["~> 10.0"])
else
s.add_dependency(%q<rake>, ["~> 10.0"])
end
else
s.add_dependency(%q<rake>, ["~> 10.0"])
end
end

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.1.0"
end
end

View file

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
# stub: inspec-test-fixture 0.1.0 ruby lib
Gem::Specification.new do |s|
s.name = "inspec-test-fixture".freeze
s.version = "0.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["InSpec Engineering Team".freeze]
s.date = "2018-08-17"
s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.".freeze
s.email = ["hello@chef.io".freeze]
s.homepage = "https://github.com/inspec/inspec".freeze
s.rubygems_version = "2.6.13".freeze
s.summary = "A simple test plugin gem for InSpec".freeze
s.installed_by_version = "2.6.13" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>.freeze, ["~> 10.0"])
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
end
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
end
end

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.1.0"
end
end

View file

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
# stub: inspec-test-fixture 0.1.0 ruby lib
Gem::Specification.new do |s|
s.name = "inspec-test-fixture".freeze
s.version = "0.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["InSpec Engineering Team".freeze]
s.date = "2018-08-17"
s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.".freeze
s.email = ["hello@chef.io".freeze]
s.homepage = "https://github.com/inspec/inspec".freeze
s.rubygems_version = "2.7.6".freeze
s.summary = "A simple test plugin gem for InSpec".freeze
s.installed_by_version = "2.7.6" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>.freeze, ["~> 10.0"])
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
end
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
end
end

View file

@ -0,0 +1,8 @@
{
"plugins_config_version" : "1.0.0",
"plugins": [
{
"name": "inspec-test-fixture"
}
]
}

View file

@ -0,0 +1,13 @@
This test fixture is a user config dir setup as though the inspec-test-fixture v0.2.0 was already installed, and it has no version constraint.
This is interesting because (unlike 0.1.0) v0.2.0 has a gem dependency.
This was accomplished by executing (*without* bundler) the following commands:
```bash
mkdir -p test/unit/mock/config_dirs/test-fixture-2-float/gems/{2.3.0,2.4.0,2.5.0}
# Here I'm running a ruby 2.4.x binary with rbenv
gem install -N -i test/unit/mock/config_dirs/test-fixture-2-float/gems/2.4.0 test/unit/mock/plugins/inspec-test-fixture/pkg/inspec-test-fixture-0.2.0.gem
```
Note that we will need to add an installation tree each time we support a new minor or major version of the Ruby VM.

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.2.0"
end
end

View file

@ -0,0 +1,29 @@
= Ordinal array
Ordinal array is a Gem which allows you to access to a value of an array by an ordinal value. You can access to the first element of an array by the "first" method. Now you can access to the second element by "second", the third by "third" and that until the 999th elements.
Works with Ruby 1.9
Improve ordinal array speed for multiple calls on the same method on the same array: (Benchmark results: https://gist.github.com/2337544)
Exemple:
my_array = ["value1", "value2", "value3"]
puts my_array.third # print: value 3
puts my_array.third # print: value 3 (But... much faster!)
= How to use
Just add gem "ordinal_array" in your gemfile.
It provides you some methods on the basic array class
Exemple of use:
my_array = ["value1", "value2", "value3"]
puts my_array.third # print: value 3
my_array[41] = "it works fine"
puts my_array.fourty_second # print: it works fine
= Contributors
*Kevin Disneur

View file

@ -0,0 +1,65 @@
require_relative './ordinal_array/ordinal'
require_relative './ordinal_array/ordinal_constants'
class Array
include OrdinalArray::Constant
include OrdinalArray
def self.respond_to?(method_sym, include_private=false)
return true if Array.number_in_letter? method_sym
super
end
def method_missing(name, *params)
if Array.number_in_letter? name
index = index_by_number_in_letter(name, params)
self.class.send(:define_method, name) do
self[index]
end
self.send(name)
else
super
end
end
private
def self.number_in_letter?(name)
ordinal_figure = false
possible_followers = [:hundred, :decade, :ordinal]
letter_numbers = name.to_s.split('_').drop_while do |letter_number|
return false if ordinal_figure
figure = Numbers_in_letter.element_by_name(letter_number)
return false unless figure
return false unless possible_followers.include? figure.to_sym
possible_followers = figure.can_be_followed_by
ordinal_figure = !figure.kind_of?(ComposedOrdinal)
true
end
letter_numbers.empty? && ordinal_figure
end
def index_by_number_in_letter(name, *params)
partial_sum = 1
sum = name.to_s.split('_').inject(0) do |sum, letter_number|
number = Numbers_in_letter.element_by_name(letter_number).number
if partial_sum < number
partial_sum = partial_sum * number
else
sum = sum + partial_sum
partial_sum = number
end
sum
end
sum = sum + partial_sum
index = sum - 1
index > 0 ? index : nil
end
end

View file

@ -0,0 +1,71 @@
module OrdinalArray
class Ordinal
attr_accessor :number_in_letter, :number, :can_be_followed_by
def initialize(number_in_letter, number)
@number_in_letter = number_in_letter
@can_be_followed_by = nil
@number = number
end
def to_sym
:ordinal
end
end
class ComposedOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:hundred]
end
def to_sym
:ordinal
end
end
class DecadeOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = nil
end
def to_sym
:decade
end
end
class ComposedDecadeOrdinal < ComposedOrdinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:ordinal]
end
def to_sym
:decade
end
end
class HundredOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = nil
end
def to_sym
:hundred
end
end
class ComposedHundredOrdinal < ComposedDecadeOrdinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:decade, :ordinal]
end
def to_sym
:hundred
end
end
end

View file

@ -0,0 +1,84 @@
require_relative './ordinal'
module OrdinalArray
module Constant
First = Ordinal.new("first", 1)
One = ComposedOrdinal.new("one", 1)
Second = Ordinal.new("second", 2)
Two = ComposedOrdinal.new("two", 2)
Third = Ordinal.new("third", 3)
Three = ComposedOrdinal.new("three", 3)
Fourth = Ordinal.new("fourth", 4)
Four = ComposedOrdinal.new("four", 4)
Fifth = Ordinal.new("fifth", 5)
Five = ComposedOrdinal.new("five", 5)
Sixth = Ordinal.new("sixth", 6)
Six = ComposedOrdinal.new("six", 6)
Seventh = Ordinal.new("seventh", 7)
Seven = ComposedOrdinal.new("seven", 7)
Eighth = Ordinal.new("eighth", 8)
Eight = ComposedOrdinal.new("eight", 8)
Ninth = Ordinal.new("ninth", 9)
Nine = ComposedOrdinal.new("nine", 9)
Tenth = DecadeOrdinal.new("tenth", 10)
Ten = ComposedDecadeOrdinal.new("ten", 10)
Eleventh = DecadeOrdinal.new("eleventh", 11)
Eleven = ComposedDecadeOrdinal.new("eleven", 11)
Twelfth = DecadeOrdinal.new("twelfth", 12)
Twelve = ComposedDecadeOrdinal.new("twelve", 12)
Thirteenth = DecadeOrdinal.new("thirteenth", 13)
Thirteen = ComposedDecadeOrdinal.new("thirteen", 13)
Fourteenth = DecadeOrdinal.new("fourteenth", 14)
Fourteen = ComposedDecadeOrdinal.new("fourteen", 14)
Fifteenth = DecadeOrdinal.new("fifteenth", 15)
Fifteen = ComposedDecadeOrdinal.new("fifteen", 15)
Sixteenth = DecadeOrdinal.new("sixteenth", 16)
Sixteen = ComposedDecadeOrdinal.new("sixteen", 16)
Seventeenth = DecadeOrdinal.new("seventeenth", 17)
Seventeen = ComposedDecadeOrdinal.new("seventeen", 17)
Eighteenth = DecadeOrdinal.new("eighteenth", 18)
Eighteen = ComposedDecadeOrdinal.new("eighteen", 18)
Nineteenth = DecadeOrdinal.new("nineteenth", 19)
Nineteen = ComposedDecadeOrdinal.new("nineteen", 19)
Twentieth = DecadeOrdinal.new("twentieth", 20)
Twenty = ComposedDecadeOrdinal.new("twenty", 20)
Thirtieth = DecadeOrdinal.new("thirtieth", 30)
Thirty = ComposedDecadeOrdinal.new("thirty", 30)
Fortieth = DecadeOrdinal.new("fortieth", 40)
Fourty = ComposedDecadeOrdinal.new("fourty", 40)
Fiftieth = DecadeOrdinal.new("fiftieth", 50)
Fifty = ComposedDecadeOrdinal.new("fifty", 50)
Sixtieth = DecadeOrdinal.new("sixtieth", 60)
Sixty = ComposedDecadeOrdinal.new("sixty", 60)
Seventieth = DecadeOrdinal.new("seventieth", 70)
Seventy = ComposedDecadeOrdinal.new("seventy", 70)
Eightieth = DecadeOrdinal.new("eightieth", 80)
Eighty = ComposedDecadeOrdinal.new("eighty", 80)
Ninetieth = DecadeOrdinal.new("ninetieth", 90)
Ninety = ComposedDecadeOrdinal.new("ninety", 90)
Hundredth = HundredOrdinal.new("hundredth", 100)
Hundred = ComposedHundredOrdinal.new("hundred", 100)
Numbers_in_letter = [
One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen,
Twenty, Thirty, Fourty, Fifty, Sixty, Seventy, Eighty, Ninety,
Hundred,
First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth,
Tenth, Eleventh, Twelfth, Thirteenth, Fourteenth, Fifteenth, Sixteenth, Seventeenth, Eighteenth, Nineteenth,
Twentieth, Thirtieth, Fortieth, Fiftieth, Sixtieth, Seventieth, Eightieth, Ninetieth,
Hundredth
]
class << Numbers_in_letter
def element_by_name(name)
index = self.index {|n| n.number_in_letter == name }
return nil unless index
self.[](index)
end
end
end
end

View file

@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
# stub: ordinal_array 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "ordinal_array"
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
s.authors = ["Kevin Disneur"]
s.date = "2012-04-08"
s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements"
s.email = ["kevin.disneur@gmail.com"]
s.homepage = "https://github.com/kdisneur/ordinal_array"
s.rubygems_version = "2.5.1"
s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements"
s.installed_by_version = "2.5.1" if s.respond_to? :installed_by_version
end

View file

@ -0,0 +1,34 @@
# -*- encoding: utf-8 -*-
# stub: inspec-test-fixture 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "inspec-test-fixture"
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
s.authors = ["InSpec Engineering Team"]
s.date = "2018-08-17"
s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development."
s.email = ["hello@chef.io"]
s.homepage = "https://github.com/inspec/inspec"
s.rubygems_version = "2.5.1"
s.summary = "A simple test plugin gem for InSpec"
s.installed_by_version = "2.5.1" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>, ["~> 10.0"])
s.add_runtime_dependency(%q<ordinal_array>, ["~> 0.2.0"])
else
s.add_dependency(%q<rake>, ["~> 10.0"])
s.add_dependency(%q<ordinal_array>, ["~> 0.2.0"])
end
else
s.add_dependency(%q<rake>, ["~> 10.0"])
s.add_dependency(%q<ordinal_array>, ["~> 0.2.0"])
end
end

View file

@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
# stub: ordinal_array 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "ordinal_array"
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
s.authors = ["Kevin Disneur"]
s.date = "2012-04-08"
s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements"
s.email = ["kevin.disneur@gmail.com"]
s.homepage = "https://github.com/kdisneur/ordinal_array"
s.rubygems_version = "2.5.1"
s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements"
s.installed_by_version = "2.5.1" if s.respond_to? :installed_by_version
end

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.2.0"
end
end

View file

@ -0,0 +1,29 @@
= Ordinal array
Ordinal array is a Gem which allows you to access to a value of an array by an ordinal value. You can access to the first element of an array by the "first" method. Now you can access to the second element by "second", the third by "third" and that until the 999th elements.
Works with Ruby 1.9
Improve ordinal array speed for multiple calls on the same method on the same array: (Benchmark results: https://gist.github.com/2337544)
Exemple:
my_array = ["value1", "value2", "value3"]
puts my_array.third # print: value 3
puts my_array.third # print: value 3 (But... much faster!)
= How to use
Just add gem "ordinal_array" in your gemfile.
It provides you some methods on the basic array class
Exemple of use:
my_array = ["value1", "value2", "value3"]
puts my_array.third # print: value 3
my_array[41] = "it works fine"
puts my_array.fourty_second # print: it works fine
= Contributors
*Kevin Disneur

View file

@ -0,0 +1,65 @@
require_relative './ordinal_array/ordinal'
require_relative './ordinal_array/ordinal_constants'
class Array
include OrdinalArray::Constant
include OrdinalArray
def self.respond_to?(method_sym, include_private=false)
return true if Array.number_in_letter? method_sym
super
end
def method_missing(name, *params)
if Array.number_in_letter? name
index = index_by_number_in_letter(name, params)
self.class.send(:define_method, name) do
self[index]
end
self.send(name)
else
super
end
end
private
def self.number_in_letter?(name)
ordinal_figure = false
possible_followers = [:hundred, :decade, :ordinal]
letter_numbers = name.to_s.split('_').drop_while do |letter_number|
return false if ordinal_figure
figure = Numbers_in_letter.element_by_name(letter_number)
return false unless figure
return false unless possible_followers.include? figure.to_sym
possible_followers = figure.can_be_followed_by
ordinal_figure = !figure.kind_of?(ComposedOrdinal)
true
end
letter_numbers.empty? && ordinal_figure
end
def index_by_number_in_letter(name, *params)
partial_sum = 1
sum = name.to_s.split('_').inject(0) do |sum, letter_number|
number = Numbers_in_letter.element_by_name(letter_number).number
if partial_sum < number
partial_sum = partial_sum * number
else
sum = sum + partial_sum
partial_sum = number
end
sum
end
sum = sum + partial_sum
index = sum - 1
index > 0 ? index : nil
end
end

View file

@ -0,0 +1,71 @@
module OrdinalArray
class Ordinal
attr_accessor :number_in_letter, :number, :can_be_followed_by
def initialize(number_in_letter, number)
@number_in_letter = number_in_letter
@can_be_followed_by = nil
@number = number
end
def to_sym
:ordinal
end
end
class ComposedOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:hundred]
end
def to_sym
:ordinal
end
end
class DecadeOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = nil
end
def to_sym
:decade
end
end
class ComposedDecadeOrdinal < ComposedOrdinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:ordinal]
end
def to_sym
:decade
end
end
class HundredOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = nil
end
def to_sym
:hundred
end
end
class ComposedHundredOrdinal < ComposedDecadeOrdinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:decade, :ordinal]
end
def to_sym
:hundred
end
end
end

View file

@ -0,0 +1,84 @@
require_relative './ordinal'
module OrdinalArray
module Constant
First = Ordinal.new("first", 1)
One = ComposedOrdinal.new("one", 1)
Second = Ordinal.new("second", 2)
Two = ComposedOrdinal.new("two", 2)
Third = Ordinal.new("third", 3)
Three = ComposedOrdinal.new("three", 3)
Fourth = Ordinal.new("fourth", 4)
Four = ComposedOrdinal.new("four", 4)
Fifth = Ordinal.new("fifth", 5)
Five = ComposedOrdinal.new("five", 5)
Sixth = Ordinal.new("sixth", 6)
Six = ComposedOrdinal.new("six", 6)
Seventh = Ordinal.new("seventh", 7)
Seven = ComposedOrdinal.new("seven", 7)
Eighth = Ordinal.new("eighth", 8)
Eight = ComposedOrdinal.new("eight", 8)
Ninth = Ordinal.new("ninth", 9)
Nine = ComposedOrdinal.new("nine", 9)
Tenth = DecadeOrdinal.new("tenth", 10)
Ten = ComposedDecadeOrdinal.new("ten", 10)
Eleventh = DecadeOrdinal.new("eleventh", 11)
Eleven = ComposedDecadeOrdinal.new("eleven", 11)
Twelfth = DecadeOrdinal.new("twelfth", 12)
Twelve = ComposedDecadeOrdinal.new("twelve", 12)
Thirteenth = DecadeOrdinal.new("thirteenth", 13)
Thirteen = ComposedDecadeOrdinal.new("thirteen", 13)
Fourteenth = DecadeOrdinal.new("fourteenth", 14)
Fourteen = ComposedDecadeOrdinal.new("fourteen", 14)
Fifteenth = DecadeOrdinal.new("fifteenth", 15)
Fifteen = ComposedDecadeOrdinal.new("fifteen", 15)
Sixteenth = DecadeOrdinal.new("sixteenth", 16)
Sixteen = ComposedDecadeOrdinal.new("sixteen", 16)
Seventeenth = DecadeOrdinal.new("seventeenth", 17)
Seventeen = ComposedDecadeOrdinal.new("seventeen", 17)
Eighteenth = DecadeOrdinal.new("eighteenth", 18)
Eighteen = ComposedDecadeOrdinal.new("eighteen", 18)
Nineteenth = DecadeOrdinal.new("nineteenth", 19)
Nineteen = ComposedDecadeOrdinal.new("nineteen", 19)
Twentieth = DecadeOrdinal.new("twentieth", 20)
Twenty = ComposedDecadeOrdinal.new("twenty", 20)
Thirtieth = DecadeOrdinal.new("thirtieth", 30)
Thirty = ComposedDecadeOrdinal.new("thirty", 30)
Fortieth = DecadeOrdinal.new("fortieth", 40)
Fourty = ComposedDecadeOrdinal.new("fourty", 40)
Fiftieth = DecadeOrdinal.new("fiftieth", 50)
Fifty = ComposedDecadeOrdinal.new("fifty", 50)
Sixtieth = DecadeOrdinal.new("sixtieth", 60)
Sixty = ComposedDecadeOrdinal.new("sixty", 60)
Seventieth = DecadeOrdinal.new("seventieth", 70)
Seventy = ComposedDecadeOrdinal.new("seventy", 70)
Eightieth = DecadeOrdinal.new("eightieth", 80)
Eighty = ComposedDecadeOrdinal.new("eighty", 80)
Ninetieth = DecadeOrdinal.new("ninetieth", 90)
Ninety = ComposedDecadeOrdinal.new("ninety", 90)
Hundredth = HundredOrdinal.new("hundredth", 100)
Hundred = ComposedHundredOrdinal.new("hundred", 100)
Numbers_in_letter = [
One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen,
Twenty, Thirty, Fourty, Fifty, Sixty, Seventy, Eighty, Ninety,
Hundred,
First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth,
Tenth, Eleventh, Twelfth, Thirteenth, Fourteenth, Fifteenth, Sixteenth, Seventeenth, Eighteenth, Nineteenth,
Twentieth, Thirtieth, Fortieth, Fiftieth, Sixtieth, Seventieth, Eightieth, Ninetieth,
Hundredth
]
class << Numbers_in_letter
def element_by_name(name)
index = self.index {|n| n.number_in_letter == name }
return nil unless index
self.[](index)
end
end
end
end

View file

@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
# stub: ordinal_array 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "ordinal_array".freeze
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["Kevin Disneur".freeze]
s.date = "2012-04-08"
s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.email = ["kevin.disneur@gmail.com".freeze]
s.homepage = "https://github.com/kdisneur/ordinal_array".freeze
s.rubygems_version = "2.6.13".freeze
s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.installed_by_version = "2.6.13" if s.respond_to? :installed_by_version
end

View file

@ -0,0 +1,34 @@
# -*- encoding: utf-8 -*-
# stub: inspec-test-fixture 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "inspec-test-fixture".freeze
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["InSpec Engineering Team".freeze]
s.date = "2018-08-17"
s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.".freeze
s.email = ["hello@chef.io".freeze]
s.homepage = "https://github.com/inspec/inspec".freeze
s.rubygems_version = "2.6.13".freeze
s.summary = "A simple test plugin gem for InSpec".freeze
s.installed_by_version = "2.6.13" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>.freeze, ["~> 10.0"])
s.add_runtime_dependency(%q<ordinal_array>.freeze, ["~> 0.2.0"])
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
s.add_dependency(%q<ordinal_array>.freeze, ["~> 0.2.0"])
end
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
s.add_dependency(%q<ordinal_array>.freeze, ["~> 0.2.0"])
end
end

View file

@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
# stub: ordinal_array 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "ordinal_array".freeze
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["Kevin Disneur".freeze]
s.date = "2012-04-08"
s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.email = ["kevin.disneur@gmail.com".freeze]
s.homepage = "https://github.com/kdisneur/ordinal_array".freeze
s.rubygems_version = "2.6.13".freeze
s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.installed_by_version = "2.6.13" if s.respond_to? :installed_by_version
end

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.2.0"
end
end

View file

@ -0,0 +1,29 @@
= Ordinal array
Ordinal array is a Gem which allows you to access to a value of an array by an ordinal value. You can access to the first element of an array by the "first" method. Now you can access to the second element by "second", the third by "third" and that until the 999th elements.
Works with Ruby 1.9
Improve ordinal array speed for multiple calls on the same method on the same array: (Benchmark results: https://gist.github.com/2337544)
Exemple:
my_array = ["value1", "value2", "value3"]
puts my_array.third # print: value 3
puts my_array.third # print: value 3 (But... much faster!)
= How to use
Just add gem "ordinal_array" in your gemfile.
It provides you some methods on the basic array class
Exemple of use:
my_array = ["value1", "value2", "value3"]
puts my_array.third # print: value 3
my_array[41] = "it works fine"
puts my_array.fourty_second # print: it works fine
= Contributors
*Kevin Disneur

View file

@ -0,0 +1,65 @@
require_relative './ordinal_array/ordinal'
require_relative './ordinal_array/ordinal_constants'
class Array
include OrdinalArray::Constant
include OrdinalArray
def self.respond_to?(method_sym, include_private=false)
return true if Array.number_in_letter? method_sym
super
end
def method_missing(name, *params)
if Array.number_in_letter? name
index = index_by_number_in_letter(name, params)
self.class.send(:define_method, name) do
self[index]
end
self.send(name)
else
super
end
end
private
def self.number_in_letter?(name)
ordinal_figure = false
possible_followers = [:hundred, :decade, :ordinal]
letter_numbers = name.to_s.split('_').drop_while do |letter_number|
return false if ordinal_figure
figure = Numbers_in_letter.element_by_name(letter_number)
return false unless figure
return false unless possible_followers.include? figure.to_sym
possible_followers = figure.can_be_followed_by
ordinal_figure = !figure.kind_of?(ComposedOrdinal)
true
end
letter_numbers.empty? && ordinal_figure
end
def index_by_number_in_letter(name, *params)
partial_sum = 1
sum = name.to_s.split('_').inject(0) do |sum, letter_number|
number = Numbers_in_letter.element_by_name(letter_number).number
if partial_sum < number
partial_sum = partial_sum * number
else
sum = sum + partial_sum
partial_sum = number
end
sum
end
sum = sum + partial_sum
index = sum - 1
index > 0 ? index : nil
end
end

View file

@ -0,0 +1,71 @@
module OrdinalArray
class Ordinal
attr_accessor :number_in_letter, :number, :can_be_followed_by
def initialize(number_in_letter, number)
@number_in_letter = number_in_letter
@can_be_followed_by = nil
@number = number
end
def to_sym
:ordinal
end
end
class ComposedOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:hundred]
end
def to_sym
:ordinal
end
end
class DecadeOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = nil
end
def to_sym
:decade
end
end
class ComposedDecadeOrdinal < ComposedOrdinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:ordinal]
end
def to_sym
:decade
end
end
class HundredOrdinal < Ordinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = nil
end
def to_sym
:hundred
end
end
class ComposedHundredOrdinal < ComposedDecadeOrdinal
def initialize(number_in_letter, number)
super(number_in_letter, number)
@can_be_followed_by = [:decade, :ordinal]
end
def to_sym
:hundred
end
end
end

View file

@ -0,0 +1,84 @@
require_relative './ordinal'
module OrdinalArray
module Constant
First = Ordinal.new("first", 1)
One = ComposedOrdinal.new("one", 1)
Second = Ordinal.new("second", 2)
Two = ComposedOrdinal.new("two", 2)
Third = Ordinal.new("third", 3)
Three = ComposedOrdinal.new("three", 3)
Fourth = Ordinal.new("fourth", 4)
Four = ComposedOrdinal.new("four", 4)
Fifth = Ordinal.new("fifth", 5)
Five = ComposedOrdinal.new("five", 5)
Sixth = Ordinal.new("sixth", 6)
Six = ComposedOrdinal.new("six", 6)
Seventh = Ordinal.new("seventh", 7)
Seven = ComposedOrdinal.new("seven", 7)
Eighth = Ordinal.new("eighth", 8)
Eight = ComposedOrdinal.new("eight", 8)
Ninth = Ordinal.new("ninth", 9)
Nine = ComposedOrdinal.new("nine", 9)
Tenth = DecadeOrdinal.new("tenth", 10)
Ten = ComposedDecadeOrdinal.new("ten", 10)
Eleventh = DecadeOrdinal.new("eleventh", 11)
Eleven = ComposedDecadeOrdinal.new("eleven", 11)
Twelfth = DecadeOrdinal.new("twelfth", 12)
Twelve = ComposedDecadeOrdinal.new("twelve", 12)
Thirteenth = DecadeOrdinal.new("thirteenth", 13)
Thirteen = ComposedDecadeOrdinal.new("thirteen", 13)
Fourteenth = DecadeOrdinal.new("fourteenth", 14)
Fourteen = ComposedDecadeOrdinal.new("fourteen", 14)
Fifteenth = DecadeOrdinal.new("fifteenth", 15)
Fifteen = ComposedDecadeOrdinal.new("fifteen", 15)
Sixteenth = DecadeOrdinal.new("sixteenth", 16)
Sixteen = ComposedDecadeOrdinal.new("sixteen", 16)
Seventeenth = DecadeOrdinal.new("seventeenth", 17)
Seventeen = ComposedDecadeOrdinal.new("seventeen", 17)
Eighteenth = DecadeOrdinal.new("eighteenth", 18)
Eighteen = ComposedDecadeOrdinal.new("eighteen", 18)
Nineteenth = DecadeOrdinal.new("nineteenth", 19)
Nineteen = ComposedDecadeOrdinal.new("nineteen", 19)
Twentieth = DecadeOrdinal.new("twentieth", 20)
Twenty = ComposedDecadeOrdinal.new("twenty", 20)
Thirtieth = DecadeOrdinal.new("thirtieth", 30)
Thirty = ComposedDecadeOrdinal.new("thirty", 30)
Fortieth = DecadeOrdinal.new("fortieth", 40)
Fourty = ComposedDecadeOrdinal.new("fourty", 40)
Fiftieth = DecadeOrdinal.new("fiftieth", 50)
Fifty = ComposedDecadeOrdinal.new("fifty", 50)
Sixtieth = DecadeOrdinal.new("sixtieth", 60)
Sixty = ComposedDecadeOrdinal.new("sixty", 60)
Seventieth = DecadeOrdinal.new("seventieth", 70)
Seventy = ComposedDecadeOrdinal.new("seventy", 70)
Eightieth = DecadeOrdinal.new("eightieth", 80)
Eighty = ComposedDecadeOrdinal.new("eighty", 80)
Ninetieth = DecadeOrdinal.new("ninetieth", 90)
Ninety = ComposedDecadeOrdinal.new("ninety", 90)
Hundredth = HundredOrdinal.new("hundredth", 100)
Hundred = ComposedHundredOrdinal.new("hundred", 100)
Numbers_in_letter = [
One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen,
Twenty, Thirty, Fourty, Fifty, Sixty, Seventy, Eighty, Ninety,
Hundred,
First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth,
Tenth, Eleventh, Twelfth, Thirteenth, Fourteenth, Fifteenth, Sixteenth, Seventeenth, Eighteenth, Nineteenth,
Twentieth, Thirtieth, Fortieth, Fiftieth, Sixtieth, Seventieth, Eightieth, Ninetieth,
Hundredth
]
class << Numbers_in_letter
def element_by_name(name)
index = self.index {|n| n.number_in_letter == name }
return nil unless index
self.[](index)
end
end
end
end

View file

@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
# stub: ordinal_array 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "ordinal_array".freeze
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["Kevin Disneur".freeze]
s.date = "2012-04-08"
s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.email = ["kevin.disneur@gmail.com".freeze]
s.homepage = "https://github.com/kdisneur/ordinal_array".freeze
s.rubygems_version = "2.7.6".freeze
s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.installed_by_version = "2.7.6" if s.respond_to? :installed_by_version
end

View file

@ -0,0 +1,34 @@
# -*- encoding: utf-8 -*-
# stub: inspec-test-fixture 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "inspec-test-fixture".freeze
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["InSpec Engineering Team".freeze]
s.date = "2018-08-17"
s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.".freeze
s.email = ["hello@chef.io".freeze]
s.homepage = "https://github.com/inspec/inspec".freeze
s.rubygems_version = "2.7.6".freeze
s.summary = "A simple test plugin gem for InSpec".freeze
s.installed_by_version = "2.7.6" if s.respond_to? :installed_by_version
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<rake>.freeze, ["~> 10.0"])
s.add_runtime_dependency(%q<ordinal_array>.freeze, ["~> 0.2.0"])
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
s.add_dependency(%q<ordinal_array>.freeze, ["~> 0.2.0"])
end
else
s.add_dependency(%q<rake>.freeze, ["~> 10.0"])
s.add_dependency(%q<ordinal_array>.freeze, ["~> 0.2.0"])
end
end

View file

@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
# stub: ordinal_array 0.2.0 ruby lib
Gem::Specification.new do |s|
s.name = "ordinal_array".freeze
s.version = "0.2.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["Kevin Disneur".freeze]
s.date = "2012-04-08"
s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.email = ["kevin.disneur@gmail.com".freeze]
s.homepage = "https://github.com/kdisneur/ordinal_array".freeze
s.rubygems_version = "2.7.6".freeze
s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze
s.installed_by_version = "2.7.6" if s.respond_to? :installed_by_version
end

View file

@ -0,0 +1,8 @@
{
"plugins_config_version" : "1.0.0",
"plugins": [
{
"name": "inspec-test-fixture"
}
]
}

View file

@ -0,0 +1,6 @@
This directory is the source code for the RubyGem inspec-test-fixture, which is published on rubygems.org for testing.
In version 0.1.0, it has no dependencies.
In version 0.2.0, it depends on ordinal_array.
In both versions, it implements a mock_plugin, whose execute() method checks to see if Array responds to :third.

View file

@ -0,0 +1 @@
require "bundler/gem_tasks"

View file

@ -0,0 +1,30 @@
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "inspec-test-fixture/version"
Gem::Specification.new do |spec|
spec.name = "inspec-test-fixture"
spec.version = InspecPlugins::TestFixture::VERSION
spec.authors = ["InSpec Engineering Team"]
spec.email = ["hello@chef.io"]
spec.summary = %q{A simple test plugin gem for InSpec}
spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.}
spec.homepage = "https://github.com/inspec/inspec"
spec.files = [
'inspec-test-fixture.gemspec',
'lib/inspec-test-fixture.rb',
'lib/inspec-test-fixture/plugin.rb',
'lib/inspec-test-fixture/mock_plugin.rb',
'lib/inspec-test-fixture/version.rb',
]
spec.executables = []
spec.require_paths = ["lib"]
spec.add_development_dependency "rake", "~> 10.0"
if InspecPlugins::TestFixture::VERSION == '0.2.0'
spec.add_dependency "ordinal_array", "~> 0.2.0"
end
end

View file

@ -0,0 +1,2 @@
require 'inspec-test-fixture/version'
require 'inspec-test-fixture/plugin'

View file

@ -0,0 +1,13 @@
require 'inspec-test-fixture/version'
if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0')
require "ordinal_array"
end
module InspecPlugins::TextFixture
class MockPlugin < Inspec.plugin(2, :mock_plugin_type)
def execute(opts = {})
# Check to see if Array responds to 'third'
Array.respond_to?(:third)
end
end
end

View file

@ -0,0 +1,13 @@
module InspecPlugins
module TestFixture
class Plugin < Inspec.plugin(2)
plugin_name :'inspec-test-fixture'
mock_plugin_type :'inspec-test-fixture' do
require 'mock_plugin'
InspecPlugins::TestFixture
end
end
end
end

View file

@ -0,0 +1,5 @@
module InspecPlugins
module TestFixture
VERSION = "0.1.0"
end
end

View file

@ -1,13 +0,0 @@
module InspecPlugins
module MeaningOfLife
class Cli < Inspec.plugin(2, :cli)
# Do cli-ish things
def execute(opts)
puts 'The answer to life, the universe, and everything:'
puts '42'
end
end
end
end

View file

@ -1,13 +0,0 @@
module InspecPlugins
module MeaningOfLife
class Plugin < Inspec.plugin(2)
plugin_name :meaning_of_life
# cli 'meaning-of-life-the-universe-and-everything' do
# require_relative './cli'
# InspecPlugins::MeaningOfLife::Cli
# end
end
end
end

View file

@ -0,0 +1,438 @@
require 'minitest/autorun'
require 'minitest/test'
# Other unit tests include the webmock framework, which is process-wide.
# We need to disable it, or else mock many, many rubygems API calls.
require 'webmock/minitest'
require 'fileutils'
require 'json'
require_relative '../../../../lib/inspec/plugin/v2'
require_relative '../../../../lib/inspec/plugin/v2/installer'
require 'byebug'
module InstallerTestHelpers
def reset_globals
ENV['HOME'] = @orig_home
ENV['INSPEC_CONFIG_DIR'] = nil
@installer.__reset
end
def copy_in_config_dir(fixture_name)
src = Dir.glob(File.join(@config_dir_path, fixture_name, '*'))
dest = File.join(@config_dir_path, 'empty')
src.each { |path| FileUtils.cp_r(path, dest) }
end
def setup
@orig_home = Dir.home
repo_path = File.expand_path(File.join( __FILE__, '..', '..', '..', '..', '..'))
mock_path = File.join(repo_path, 'test', 'unit', 'mock')
@config_dir_path = File.join(mock_path, 'config_dirs')
@plugin_fixture_src_path = File.join(mock_path, 'plugins', 'inspec-test-fixture')
@plugin_fixture_pkg_path = File.join(@plugin_fixture_src_path, 'pkg')
# This is unstable under CI; see https://github.com/inspec/inspec/issues/3355
# @ruby_abi_version = (RUBY_VERSION.split('.')[0,2] << '0').join('.')
@ruby_abi_version = (Gem.ruby_version.segments[0, 2] << 0).join('.')
@installer = Inspec::Plugin::V2::Installer.instance
reset_globals
WebMock.disable_net_connect!(allow: 'api.rubygems.org')
end
def teardown
reset_globals
# We use the 'empty' config dir for exercising a lot of installs.
# Purge it after every test.
unless ENV['INSPEC_TEST_PRESERVE_PLUGIN']
Dir.glob(File.join(@config_dir_path, 'empty', '*')).each do |path|
next if path.end_with? '.gitkeep'
FileUtils.rm_rf(path)
end
end
# TODO: may need to edit the $LOAD_PATH, if it turns out that we need to "deactivate" gems after installation
end
end
#-----------------------------------------------------------------------#
# basics
#-----------------------------------------------------------------------#
class PluginInstallerBasicTests < MiniTest::Test
include InstallerTestHelpers
# it's a singleton
def test_it_should_be_a_singleton
klass = Inspec::Plugin::V2::Installer
assert_equal klass.instance, klass.instance, "Calling instance on the Installer should always return the same object"
assert_kind_of Inspec::Plugin::V2::Installer, klass.instance, 'Calling instance on the INstaller should return the right class'
assert_raises(NoMethodError, 'Installer should have a private constructor') { klass.new }
end
# it should know its gem path
def test_it_should_know_its_gem_path_with_a_default_location
ENV['HOME'] = File.join(@config_dir_path, 'fakehome')
expected = File.join(ENV['HOME'], '.inspec', 'gems', @ruby_abi_version)
assert_equal expected, @installer.gem_path
end
def test_it_should_know_its_gem_path_with_a_custom_config_dir_from_env
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
expected = File.join(ENV['INSPEC_CONFIG_DIR'], 'gems', @ruby_abi_version)
assert_equal expected, @installer.gem_path
end
end
#-----------------------------------------------------------------------#
# Installing
#-----------------------------------------------------------------------#
class PluginInstallerInstallationTests < MiniTest::Test
include InstallerTestHelpers
# While this is a negative test case on the prefix checking, there are
# several positive test cases following.
def test_refuse_to_install_gems_with_wrong_name_prefix
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
# Here, ordinal_array is the name of a simple, small gem available on rubygems.org
# There is no significance in choosing that gem over any other.
# Main point here is that its name does not begin with 'inspec-'.
assert_raises(Inspec::Plugin::V2::InstallError) { @installer.install('ordinal_array')}
end
def test_install_a_gem_from_local_file
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
gem_file = File.join(@plugin_fixture_pkg_path, 'inspec-test-fixture-0.1.0.gem')
@installer.install('inspec-test-fixture', gem_file: gem_file)
# Because no exception was thrown, this is a positive test case for prefix-checking.
# Installing a gem places it under the config dir gem area
# Two things should happen: a copy of the gemspec should be left there...
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.1.0.gemspec')
assert File.exist?(spec_path), 'After installation from a gem file, the gemspec should be installed to the gem path'
# ... and the actual library code should be decompressed into a directory tree.
installed_gem_base = File.join(@installer.gem_path, 'gems', 'inspec-test-fixture-0.1.0')
assert Dir.exist?(installed_gem_base), 'After installation from a gem file, the gem tree should be installed to the gem path'
# Installation != gem activation
spec = Gem::Specification.load(spec_path)
refute spec.activated?, 'Installing a gem should not cause the gem to activate'
end
def test_install_a_gem_from_missing_local_file
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
gem_file = File.join(@plugin_fixture_pkg_path, 'inspec-test-fixture-nonesuch-0.0.0.gem')
refute File.exist?(gem_file), "The nonexistant gem should not exist prior to install attempt"
ex = assert_raises(Inspec::Plugin::V2::InstallError) { @installer.install('inspec-test-fixture-nonesuch', gem_file: gem_file)}
assert_includes ex.message, 'Could not find local gem file'
end
def test_install_a_gem_from_local_file_creates_plugin_json
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
gem_file = File.join(@plugin_fixture_pkg_path, 'inspec-test-fixture-0.1.0.gem')
@installer.install('inspec-test-fixture', gem_file: gem_file)
# Should now be present in plugin.json
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
assert File.exist?(plugin_json_path), 'plugins.json should now exist'
plugin_json_data = JSON.parse(File.read(plugin_json_path))
assert_includes plugin_json_data.keys, 'plugins_config_version'
assert_equal '1.0.0', plugin_json_data['plugins_config_version'], 'Plugin config version should ve initted to 1.0.0'
assert_includes plugin_json_data.keys, 'plugins'
assert_kind_of Array, plugin_json_data['plugins']
assert_equal 1, plugin_json_data['plugins'].count, 'plugins.json should have one entry'
entry = plugin_json_data['plugins'].first
assert_kind_of Hash, entry
assert_includes entry.keys, 'name'
assert_equal 'inspec-test-fixture', entry['name']
# TODO: any other fields to check? gem version?
end
def test_install_a_gem_from_rubygems_org
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.install('inspec-test-fixture')
# Because no exception was thrown, this is a positive test case for prefix-checking.
# Installing a gem places it under the config dir gem area
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.2.0.gemspec')
assert File.exist?(spec_path), 'After installation from rubygems.org, the gemspec should be installed to the gem path'
installed_gem_base = File.join(@installer.gem_path, 'gems', 'inspec-test-fixture-0.2.0')
assert Dir.exist?(installed_gem_base), 'After installation from rubygems.org, the gem tree should be installed to the gem path'
# installing a gem with dependencies should result in the deps being installed under the config dir
spec_path = File.join(@installer.gem_path, 'specifications', 'ordinal_array-0.2.0.gemspec')
assert File.exist?(spec_path), 'After installation from a gem file, the gemspec should be installed to the gem path'
installed_gem_base = File.join(@installer.gem_path, 'gems', 'inspec-test-fixture-0.2.0')
assert Dir.exist?(installed_gem_base), 'After installation from a gem file, the gem tree should be installed to the gem path'
# Installation != gem activation
spec = Gem::Specification.load(spec_path)
refute spec.activated?, 'Installing a gem should not cause the gem to activate'
end
def test_handle_no_such_gem
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
assert_raises(Inspec::Plugin::V2::InstallError) { @installer.install('inspec-test-fixture-nonesuch') }
end
# Should be able to install a plugin while pinning the version
def test_install_a_pinned_gem_from_rubygems_org
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.install('inspec-test-fixture', version: '= 0.1.0')
# Installing a gem places it under the config dir gem area
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.1.0.gemspec')
assert File.exist?(spec_path), 'After pinned installation from rubygems.org, the gemspec should be installed to the gem path'
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.2.0.gemspec')
refute File.exist?(spec_path), 'After pinned installation from rubygems.org, the wrong gemspec version should be absent'
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
plugin_json_data = JSON.parse(File.read(plugin_json_path))
entry = plugin_json_data['plugins'].detect { |e| e["name"] == 'inspec-test-fixture'}
assert_includes entry.keys, 'version', 'plugins.json should include version pinning key'
assert_equal '= 0.1.0', entry['version'], 'plugins.json should include version pinning value'
end
def test_install_a_plugin_from_a_path
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.install('inspec-test-fixture', path: @plugin_fixture_src_path)
# No gemspec should exist in the plugins area
specs = Dir.glob(File.join(@installer.gem_path, 'specifications', '*.gemspec'))
assert_empty specs, 'After install-from-path, no gemspecs should be installed'
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
plugin_json_data = JSON.parse(File.read(plugin_json_path))
entry = plugin_json_data['plugins'].detect { |e| e["name"] == 'inspec-test-fixture'}
assert_includes entry.keys, 'installation_type', 'plugins.json should include installation_type key'
assert_equal 'path', entry['installation_type'], 'plugins.json should include path installation_type'
assert_includes entry.keys, 'installation_path', 'plugins.json should include installation_path key'
assert_equal @plugin_fixture_src_path, entry['installation_path'], 'plugins.json should include correct value for installation path'
end
end
#-----------------------------------------------------------------------#
# Updating
#-----------------------------------------------------------------------#
class PluginInstallerUpdaterTests < MiniTest::Test
include InstallerTestHelpers
def test_update_using_path_not_allowed
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
assert_raises(Inspec::Plugin::V2::UpdateError) do
@installer.update('inspec-test-fixture', path: @plugin_fixture_src_path)
end
end
def test_update_existing_plugin_at_same_version_not_allowed
copy_in_config_dir('test-fixture-1-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
assert_raises(Inspec::Plugin::V2::UpdateError) do
@installer.update('inspec-test-fixture', version: '0.1.0')
end
end
def test_install_plugin_at_existing_version_not_allowed
copy_in_config_dir('test-fixture-1-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
assert_raises(Inspec::Plugin::V2::InstallError) do
@installer.install('inspec-test-fixture', version: '0.1.0')
end
end
def test_install_existing_plugin_not_allowed
copy_in_config_dir('test-fixture-1-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
ex = assert_raises(Inspec::Plugin::V2::InstallError) do
@installer.install('inspec-test-fixture')
end
assert_includes ex.message, "Use 'inspec plugin update'"
end
def test_update_to_latest_version
copy_in_config_dir('test-fixture-1-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.__reset_loader
@installer.update('inspec-test-fixture')
# Verify presence of gemspecs
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.2.0.gemspec')
assert File.exist?(spec_path), 'After update, the 0.2.0 gemspec should be installed to the gem path'
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.1.0.gemspec')
assert File.exist?(spec_path), 'After update, the 0.1.0 gemspec should remain'
# Plugins file entry should not be version pinned
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
plugin_json_data = JSON.parse(File.read(plugin_json_path))
entry = plugin_json_data['plugins'].detect { |e| e["name"] == 'inspec-test-fixture'}
refute_includes entry.keys, 'version', 'plugins.json should NOT include version pinning key'
end
def test_update_to_specified_later_version
copy_in_config_dir('test-fixture-1-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.__reset_loader
# Update to specific (but later) version
@installer.update('inspec-test-fixture', version: '0.2.0')
# Verify presence of gemspecs
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.2.0.gemspec')
assert File.exist?(spec_path), 'After update, the 0.2.0 gemspec should be installed to the gem path'
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.1.0.gemspec')
assert File.exist?(spec_path), 'After update, the 0.1.0 gemspec should remain'
# Plugins file entry should be version pinned
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
plugin_json_data = JSON.parse(File.read(plugin_json_path))
entry = plugin_json_data['plugins'].detect { |e| e["name"] == 'inspec-test-fixture'}
assert_includes entry.keys, 'version', 'plugins.json should include version pinning key'
assert_equal '= 0.2.0', entry['version'], 'plugins.json should include version pinning value'
end
# TODO: Prevent updating a gem if it will lead to unsolveable dependencies
# TODO: allow updating a gem that will lead to unsolveable dependencies if :force is provided
# TODO: Prevent downgrading a gem if it will lead to unsolveable dependencies
# TODO: allow downgrading a gem that will lead to unsolveable dependencies if :force is provided
# TODO: update all
# TODO: downgrade a plugin
# TODO: Trying to do a gemfile install with an update is an error if the file version matches the installed version
end
#-----------------------------------------------------------------------#
# Uninstalling
#-----------------------------------------------------------------------#
class PluginInstallerUninstallTests < MiniTest::Test
include InstallerTestHelpers
def test_uninstalling_a_nonexistant_plugin_is_an_error
# Try a mythical one
ex = assert_raises(Inspec::Plugin::V2::UnInstallError) do
@installer.uninstall('inspec-test-fixture-nonesuch')
end
assert_includes ex.message, "'inspec-test-fixture-nonesuch' is not installed, refusing to uninstall."
# Try a real plugin that is not installed
ex = assert_raises(Inspec::Plugin::V2::UnInstallError) do
@installer.uninstall('inspec-test-fixture')
end
assert_includes ex.message, "'inspec-test-fixture' is not installed, refusing to uninstall."
end
def test_uninstalling_a_path_based_plugin_works
copy_in_config_dir('meaning_by_path')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.__reset_loader
@installer.uninstall('inspec-meaning-of-life')
# Plugins file entry should be removed
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
plugin_json_data = JSON.parse(File.read(plugin_json_path))
entries = plugin_json_data['plugins'].select { |e| e["name"] == 'inspec-meaning-of-life'}
assert_empty entries, "After path-based uninstall, plugin name should be removed from plugins.json"
end
def test_uninstall_a_gem_plugin
copy_in_config_dir('test-fixture-1-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.__reset_loader
@installer.uninstall('inspec-test-fixture')
# UnInstalling a gem physically removes the gemspec and the gem library code
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.1.0.gemspec')
refute File.exist?(spec_path), 'After uninstallation of a gem plugin, the gemspec should be removed.'
installed_gem_base = File.join(@installer.gem_path, 'gems', 'inspec-test-fixture-0.1.0')
refute Dir.exist?(installed_gem_base), 'After uninstallation of a gem plugin, the gem tree should be removed.'
# Rubygems' idea of what we have installed should be changed.
# It should no longer be able to satisfy a request for the formerly installed gem.
universe_set = @installer.send(:build_gem_request_universe) # private method
request_set = Gem::RequestSet.new(Gem::Dependency.new('inspec-test-fixture'))
assert_raises(Gem::UnsatisfiableDependencyError) { request_set.resolve(universe_set) }
# Plugins file entry should be removed
plugin_json_path = File.join(ENV['INSPEC_CONFIG_DIR'], 'plugins.json')
plugin_json_data = JSON.parse(File.read(plugin_json_path))
entries = plugin_json_data['plugins'].select { |e| e["name"] == 'inspec-test-fixture'}
assert_empty entries, "After gem-based uninstall, plugin name should be removed from plugins.json"
end
def test_uninstall_a_gem_plugin_removes_deps
copy_in_config_dir('test-fixture-2-float')
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'empty')
@installer.__reset_loader
@installer.uninstall('inspec-test-fixture')
# UnInstalling a gem removes the gemspec and the gem library code
spec_path = File.join(@installer.gem_path, 'specifications', 'inspec-test-fixture-0.2.0.gemspec')
refute File.exist?(spec_path), 'After uninstallation of a gem plugin with deps, the gemspec should be removed.'
installed_gem_base = File.join(@installer.gem_path, 'gems', 'inspec-test-fixture-0.2.0')
refute Dir.exist?(installed_gem_base), 'After uninstallation of a gem plugin with deps, the gem tree should be removed.'
# UnInstalling a gem with dependencies should result in the deps being removed
spec_path = File.join(@installer.gem_path, 'specifications', 'ordinal_array-0.2.0.gemspec')
refute File.exist?(spec_path), 'After uninstallation of a gem plugin with deps, the dep gemspec should be removed.'
installed_gem_base = File.join(@installer.gem_path, 'gems', 'ordinal_array-0.2.0')
refute Dir.exist?(installed_gem_base), 'After installation a gem plugin with deps, the gem tree should be removed.'
# Rubygems' idea of what we have installed should be changed.
# It should no longer be able to satisfy a request for the formerly installed *dependency*
universe_set = @installer.send(:build_gem_request_universe) # private method
request_set = Gem::RequestSet.new(Gem::Dependency.new('ordinal_array'))
assert_raises(Gem::UnsatisfiableDependencyError) { request_set.resolve(universe_set) }
end
# TODO: Able to uninstall a specific version of a gem plugin
# TODO: Prevent removing a gem if it will lead to unsolveable dependencies
# TODO: Allow removing a gem that will lead to unsolveable dependencies if :force is provided
end
#-----------------------------------------------------------------------#
# Searching
#-----------------------------------------------------------------------#
class PluginInstallerSearchTests < MiniTest::Test
include InstallerTestHelpers
def test_search_for_plugin_by_exact_name
results = @installer.search('inspec-test-fixture', exact: true)
assert_kind_of Hash, results, 'Results from searching should be a Hash'
assert results.key?('inspec-test-fixture'), 'Search results should have a key for the sought plugin'
assert_equal 1, results.count, 'There should be exactly one search result'
version_list = results['inspec-test-fixture']
assert_includes version_list, '0.1.0', 'Version list should contain 0.1.0'
assert_includes version_list, '0.2.0', 'Version list should contain 0.2.0'
end
def test_search_for_plugin_that_does_not_exist
results = @installer.search('inspec-test-fixture-nonesuch', exact: true)
assert_empty results
end
def test_search_for_plugin_by_wildard
results = @installer.search('inspec-test-')
assert_kind_of Hash, results, 'Results from searching should be a Hash'
assert results.key?('inspec-test-fixture'), 'Search results should have a key for at least one plugin'
version_list = results['inspec-test-fixture']
assert_includes version_list, '0.1.0', 'Version list should contain 0.1.0'
assert_includes version_list, '0.2.0', 'Version list should contain 0.2.0'
end
end

View file

@ -152,6 +152,35 @@ class PluginLoaderTests < MiniTest::Test
assert reg.loaded_plugin?(plugin_name), "\n#{plugin_name} should be loaded"
end
def test_list_managed_gems
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'test-fixture-2-float')
loader = Inspec::Plugin::V2::Loader.new(omit_bundles: true)
gemspecs = loader.list_managed_gems
gem = gemspecs.detect { |spec| spec.name == 'ordinal_array' }
refute_nil gem, 'loader.list_managed_gems should find ordinal_array'
assert_equal Gem::Version.new('0.2.0'), gem.version
end
def test_list_installed_plugin_gems
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'test-fixture-1-float')
loader = Inspec::Plugin::V2::Loader.new(omit_bundles: true)
gemspecs = loader.list_installed_plugin_gems
gem = gemspecs.detect { |spec| spec.name == 'inspec-test-fixture' }
refute_nil gem, 'loader.list_installed_plugin_gems should find inspec-test-fixture'
assert_equal Gem::Version.new('0.1.0'), gem.version
end
def test_load_mock_plugin_by_gem
ENV['INSPEC_CONFIG_DIR'] = File.join(@config_dir_path, 'test-fixture-1-float')
reg = Inspec::Plugin::V2::Registry.instance
plugin_name = :'inspec-test-fixture'
loader = Inspec::Plugin::V2::Loader.new(omit_bundles: true)
assert reg.known_plugin?(plugin_name), "\n#{plugin_name} should be a known plugin"
refute reg.loaded_plugin?(plugin_name), "\n#{plugin_name} should not be loaded yet"
loader.load_all
assert reg.loaded_plugin?(plugin_name), "\n#{plugin_name} should be loaded"
end
#====================================================================#
# activation #
#====================================================================#
@ -174,12 +203,12 @@ class PluginLoaderTests < MiniTest::Test
assert_equal 'Inspec::Plugin::V2::Activator', registry.find_activators()[0].class.name, 'find_activators should return an array of Activators'
activator = registry.find_activators(plugin_type: :mock_plugin_type, name: :'meaning-of-life-the-universe-and-everything')[0]
refute_nil activator, 'find_activators should find the test activator'
[ :plugin_name, :plugin_type, :activator_name, :activated, :exception, :activation_proc, :implementation_class ].each do |method_name|
[ :plugin_name, :plugin_type, :activator_name, :'activated?', :exception, :activation_proc, :implementation_class ].each do |method_name|
assert_respond_to activator, method_name
end
# Activation preconditions
refute activator.activated, 'Test activator should start out unactivated'
refute activator.activated?, 'Test activator should start out unactivated'
assert_nil activator.exception, 'Test activator should have no exception prior to activation'
assert_nil activator.implementation_class, 'Test activator should not know implementation class prior to activation'
refute InspecPlugins::MeaningOfLife.const_defined?(:MockPlugin), 'impl_class should not be defined prior to activation'
@ -187,7 +216,7 @@ class PluginLoaderTests < MiniTest::Test
loader.activate(:mock_plugin_type, :'meaning-of-life-the-universe-and-everything')
# Activation postconditions
assert activator.activated, 'Test activator should be activated after activate'
assert activator.activated?, 'Test activator should be activated after activate'
assert_nil activator.exception, 'Test activator should have no exception after activation'
# facts about the implementation class