warningshot 0.9.4 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +8 -0
- data/CONTRIBUTORS +2 -1
- data/README +86 -6
- data/Rakefile +4 -3
- data/TODO +1 -147
- data/bin/warningshot +11 -7
- data/lib/resolvers/core_lib_resolver.rb +2 -3
- data/lib/resolvers/directory_resolver.rb +1 -1
- data/lib/resolvers/file_resolver.rb +35 -17
- data/lib/resolvers/gem_resolver.rb +81 -60
- data/lib/resolvers/integrity_resolver.rb +10 -11
- data/lib/resolvers/manual_resolver.rb +15 -3
- data/lib/resolvers/permission_resolver.rb +6 -8
- data/lib/resolvers/symlink_resolver.rb +13 -8
- data/lib/resolvers/url_resolver.rb +28 -41
- data/lib/warningshot.rb +10 -7
- data/lib/warningshot/config.rb +254 -0
- data/lib/{warning_shot → warningshot}/dependency_resolver.rb +38 -17
- data/lib/{warning_shot → warningshot}/growl.rb +2 -0
- data/lib/{warning_shot → warningshot}/logger.rb +2 -0
- data/lib/{warning_shot → warningshot}/resolver.rb +177 -89
- data/lib/warningshot/suite.rb +4 -0
- data/lib/{warning_shot → warningshot}/template_generator.rb +4 -1
- data/lib/{warning_shot → warningshot}/version.rb +3 -1
- data/lib/warningshot/warning_shot.rb +187 -0
- data/tasks/gemspec.rb +3 -16
- data/tasks/yard.rb +1 -1
- data/templates/gems.yml +1 -0
- data/test/data/faux_test.yml +5 -0
- data/test/data/faux_test_resolver.rb +21 -0
- data/test/data/mock_resolver.rb +11 -5
- data/test/log/warningshot.log +3051 -532
- data/test/spec/unit/resolvers/core_lib_resolver_spec.rb +1 -1
- data/test/spec/unit/resolvers/directory_resolver_spec.rb +1 -1
- data/test/spec/unit/resolvers/file_resolver_spec.rb +9 -13
- data/test/spec/unit/resolvers/gem_resolver_spec.rb +108 -32
- data/test/spec/unit/resolvers/integrity_resolver_spec.rb +6 -6
- data/test/spec/unit/resolvers/permission_resolver_spec.rb +2 -2
- data/test/spec/unit/resolvers/symlink_resolver_spec.rb +8 -1
- data/test/spec/unit/resolvers/url_resolver_spec.rb +10 -10
- data/test/spec/unit/warningshot/config_spec.rb +57 -0
- data/test/spec/unit/{warning_shot → warningshot}/dependency_resolver_spec.rb +18 -9
- data/test/spec/unit/{warning_shot → warningshot}/resolver_spec.rb +54 -79
- data/test/spec/unit/{warning_shot → warningshot}/template_generator_spec.rb +1 -1
- data/test/spec/unit/{warning_shot → warningshot}/version_spec.rb +0 -0
- data/test/spec/unit/{warning_shot → warningshot}/warning_shot_spec.rb +0 -0
- metadata +24 -26
- data/lib/warning_shot/config.rb +0 -132
- data/lib/warning_shot/warning_shot.rb +0 -130
- data/test/spec/spec.opts.zoiks +0 -0
- data/test/spec/unit/warning_shot/config_spec.rb +0 -35
@@ -0,0 +1,254 @@
|
|
1
|
+
require File.dirname(__FILE__) / 'warning_shot'
|
2
|
+
require File.dirname(__FILE__) / 'template_generator'
|
3
|
+
|
4
|
+
# This is a factory class for creating WarningShot hash configurations.
|
5
|
+
# Configurations can be created by passing a hash or block to
|
6
|
+
# WarningShot::Config.create
|
7
|
+
# OR
|
8
|
+
# By calling WarningShot::Config.parse_args (for command line, uses ARGV)
|
9
|
+
#
|
10
|
+
# It also provies an interface for plugins to extend the CLI
|
11
|
+
# Config.cli => Provides access to add stuff to command line
|
12
|
+
# Config.cli_options => Provides a hash to store values into from within the OptionParser block
|
13
|
+
#
|
14
|
+
# The resolver class also provides functions of the same name that shortcut to WarningShot::Config
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# class WarningShot::MyResolver
|
18
|
+
# cli("-s", "--someflag=VALUE", String, "Add some feature") do |value|
|
19
|
+
# options[:some_value] = value
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
module WarningShot
|
23
|
+
module Config
|
24
|
+
attr_reader :configuration
|
25
|
+
PARSER = OptionParser.new
|
26
|
+
|
27
|
+
DEFAULTS = {
|
28
|
+
:pload => [],
|
29
|
+
:oload => [],
|
30
|
+
:environment => 'development',
|
31
|
+
:resolve => false,
|
32
|
+
:config_paths => ['.' / 'config' / 'warningshot', '~' / '.warningshot'],
|
33
|
+
:application => '.',
|
34
|
+
:log_path => '.' / 'log' / 'warningshot.log',
|
35
|
+
:log_level => :info,
|
36
|
+
:growl => false,
|
37
|
+
:verbose => false,
|
38
|
+
:colorize => true,
|
39
|
+
:resolvers => ['~' / '.warningshot' / '*.rb']
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
class << self
|
43
|
+
|
44
|
+
# Add command line flags to tail of CLI
|
45
|
+
# shortcut to WarningSHot::Config::PARSER.on_tail
|
46
|
+
#
|
47
|
+
# @see OptionParser
|
48
|
+
#
|
49
|
+
# @param *opts [Array]
|
50
|
+
#
|
51
|
+
# @param &block [Proc]
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
def cli(*opts,&block)
|
55
|
+
PARSER.on_tail(*opts,&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
# access to a hash for command line options use for Plugin to extend interface
|
59
|
+
#
|
60
|
+
# @return [Hash]
|
61
|
+
def cli_options
|
62
|
+
@@cli_options ||={}
|
63
|
+
@@cli_options
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Initialize a new WarningShot config
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# Setting config with a hash
|
71
|
+
# conf = WarningShot::Config.create({:environment=>"staging",:chickens=>true})
|
72
|
+
#
|
73
|
+
# Setting config with a block
|
74
|
+
# conf = WarningShot::Config.create do |c|
|
75
|
+
# c[:environment] = "production"
|
76
|
+
# c[:cool_feature] = true
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# Just using default config
|
80
|
+
# conf = WarningShot::Config.create
|
81
|
+
#
|
82
|
+
# Using a hash and a block, block wins
|
83
|
+
# conf = WarningShot::Config.create({:environment=>"hash",:something=>true}) do |c|
|
84
|
+
# c[:environment] = "blk"
|
85
|
+
# c[:else] = true
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
def create(config={})
|
89
|
+
opt_config = config
|
90
|
+
if block_given?
|
91
|
+
blk_config = {}
|
92
|
+
yield(blk_config)
|
93
|
+
opt_config = opt_config.merge(blk_config)
|
94
|
+
end
|
95
|
+
WarningShot::Config::DEFAULTS.clone.merge(opt_config)
|
96
|
+
end
|
97
|
+
|
98
|
+
def parse_args(argv = ARGV)
|
99
|
+
@@cli_options = {}
|
100
|
+
@@cli_options[:environment] = ENV["WARNING_SHOT_ENV"] if ENV["WARNING_SHOT_ENV"]
|
101
|
+
|
102
|
+
WarningShot::Config::PARSER.banner = WarningShot.header
|
103
|
+
WarningShot::Config::PARSER.banner += "\n"
|
104
|
+
WarningShot::Config::PARSER.banner += "Dependency Resolution Framework\n\n"
|
105
|
+
WarningShot::Config::PARSER.banner += "Usage: warningshot [options]"
|
106
|
+
|
107
|
+
|
108
|
+
WarningShot::Config::PARSER.separator "Standard Flags".center(80,'-')
|
109
|
+
WarningShot::Config::PARSER.on("-e=STRING", "--environment=STRING", String, "Environment to test in","Default: #{DEFAULTS[:environment]}") do |env|
|
110
|
+
@@cli_options[:environment] = env
|
111
|
+
end
|
112
|
+
WarningShot::Config::PARSER.on("--resolve","Resolve missing dependencies (probably need sudo)") do |resolve|
|
113
|
+
@@cli_options[:resolve] = resolve
|
114
|
+
end
|
115
|
+
WarningShot::Config::PARSER.on("-a=PATH","--app=PATH", String, "Path to application", "Default: #{DEFAULTS[:application]}") do |app|
|
116
|
+
@@cli_options[:application] = app
|
117
|
+
end
|
118
|
+
WarningShot::Config::PARSER.on("-c=PATH","--configs=PATH", String,"Path to config directories (':' seperated)","Default: #{DEFAULTS[:config_paths].join(':')}") do |config|
|
119
|
+
@@cli_options[:config_paths] = config.split(':')
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
WarningShot::Config::PARSER.separator "Resolver Loading Flags".center(80,'-')
|
124
|
+
WarningShot::Config::PARSER.on("-r=PATH","--resolvers=PATH", String,"Globs to add'l resolvers (':' seperated)","Default: #{DEFAULTS[:resolvers].join(':')}") do |config|
|
125
|
+
@@cli_options[:resolvers] = config.split(':')
|
126
|
+
end
|
127
|
+
WarningShot::Config::PARSER.on("--oload=LIST", String, "Only load specified resolvers (Command seperated)") do |oload|
|
128
|
+
@@cli_options[:oload] = oload.split(',')
|
129
|
+
WarningShot.only_load *@@cli_options[:oload]
|
130
|
+
end
|
131
|
+
WarningShot::Config::PARSER.on("--pload=LIST", String, "Load specified resolvers only, setting sequential priority (Command seperated)") do |pload|
|
132
|
+
@@cli_options[:pload] = pload.split(',')
|
133
|
+
WarningShot.only_load *@@cli_options[:pload]
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
WarningShot::Config::PARSER.separator "Output Flags".center(80,'-')
|
138
|
+
WarningShot::Config::PARSER.on("-l=LOG","--log=LOG", String, "Path to log file", "Default: #{DEFAULTS[:log_path]}") do |log_path|
|
139
|
+
@@cli_options[:log_path] = log_path
|
140
|
+
end
|
141
|
+
WarningShot::Config::PARSER.on("--loglevel=LEVEL",[:debug, :info, :warn, :error, :fatal], "Default: #{DEFAULTS[:log_level]}") do |log_level|
|
142
|
+
@@cli_options[:log_level] = log_level
|
143
|
+
end
|
144
|
+
WarningShot::Config::PARSER.on("-g", "--growl", "Output results via growl (Requires growlnotify)") do |growl|
|
145
|
+
@@cli_options[:growl] = growl
|
146
|
+
end
|
147
|
+
WarningShot::Config::PARSER.on("-p", "--[no-]prettycolors", "Colorize output") do |colorize|
|
148
|
+
@@cli_options[:colorize] = colorize
|
149
|
+
end
|
150
|
+
WarningShot::Config::PARSER.on("-v", "--verbose", "Output verbose information") do |verbose|
|
151
|
+
@@cli_options[:verbose] = verbose
|
152
|
+
end
|
153
|
+
WarningShot::Config::PARSER.on("--very-verbose", "Outputs debugging information, same as --loglevel=DEBUG") do |verbose|
|
154
|
+
@@cli_options[:verbose] = true
|
155
|
+
@@cli_options[:log_level] = :debug
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
WarningShot::Config::PARSER.separator "Prestaging Flags".center(80,'-')
|
160
|
+
WarningShot::Config::PARSER.on("--build-deps", "Installs gems that WarningShot resolvers depend on into standard RubyGems path (probably need sudo)") do |deps|
|
161
|
+
build_deps_config = WarningShot::Config.create
|
162
|
+
WarningShot.load_addl_resolvers build_deps_config[:resolvers]
|
163
|
+
|
164
|
+
warningshot_gem_recipe = []
|
165
|
+
Resolver.descendants(false).each do |klass|
|
166
|
+
klass.depends_on[:gem].each{ |gem_dep| warningshot_gem_recipe.push(gem_dep) }
|
167
|
+
end
|
168
|
+
|
169
|
+
gem_resolver = WarningShot::GemResolver.new build_deps_config, *warningshot_gem_recipe
|
170
|
+
gem_resolver.test!
|
171
|
+
|
172
|
+
if gem_resolver.failed.length == 0
|
173
|
+
puts 'WarningShot is A-OK!!!'
|
174
|
+
else
|
175
|
+
gem_resolver.resolve!
|
176
|
+
gem_resolver.resolved.each{|res_gem| puts "[SUCCESS]\t\t#{res_gem.name}" }
|
177
|
+
|
178
|
+
gem_resolver.unresolved.each{|unres_gem| puts "[FAILURE]\t\t#{unres_gem.name}" }
|
179
|
+
end
|
180
|
+
|
181
|
+
exit
|
182
|
+
end
|
183
|
+
WarningShot::Config::PARSER.on("--list-deps", "List all core libs and gems that each resolver is dependent on") do |deps|
|
184
|
+
puts WarningShot.header
|
185
|
+
puts "Resolvers' dependencies:"
|
186
|
+
|
187
|
+
Resolver.descendants(false).each do |klass|
|
188
|
+
puts "\n#{klass}"
|
189
|
+
puts " Core Lib Dependencies:"
|
190
|
+
klass.depends_on[:core].each do |core|
|
191
|
+
puts " [#{core[:installed] ? 'INSTALLED' : 'MISSING'}]\t\t#{core[:name]}"
|
192
|
+
end
|
193
|
+
|
194
|
+
puts " Gem Dependencies:"
|
195
|
+
klass.depends_on[:gem].each do |gem|
|
196
|
+
puts " [#{gem[:installed] ? 'INSTALLED' : 'MISSING'}]\t\t#{gem[:name]}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
exit
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
WarningShot::Config::PARSER.separator "Help, Info, & Etc. Flags".center(80,'-')
|
205
|
+
WarningShot::Config::PARSER.on("-t[PATH]","--templates[PATH]", String, "Generate template files", "Default: .") do |template_path|
|
206
|
+
template_path = @@cli_options[:config_paths].first if template_path.nil? || template_path.empty?
|
207
|
+
WarningShot::TemplateGenerator.create(template_path)
|
208
|
+
exit
|
209
|
+
end
|
210
|
+
WarningShot::Config::PARSER.on("--version", "Show version"){
|
211
|
+
WarningShot::Config::PARSER.parse!(argv)
|
212
|
+
conf = WarningShot::Config.create(@@cli_options)
|
213
|
+
|
214
|
+
WarningShot.load_app(conf[:application])
|
215
|
+
WarningShot.load_addl_resolvers(conf[:resolvers])
|
216
|
+
|
217
|
+
puts WarningShot.header
|
218
|
+
puts "Installed resolvers:"
|
219
|
+
Resolver.descendants(false).each { |klass|
|
220
|
+
puts "\n#{klass}"
|
221
|
+
puts " Tests: #{klass.tests.length}, Resolutions: #{klass.resolutions.length} [#{klass.resolutions.empty? ? 'irresolvable' : 'resolvable'}]"
|
222
|
+
puts " #{klass.description}"
|
223
|
+
}
|
224
|
+
exit
|
225
|
+
}
|
226
|
+
WarningShot::Config::PARSER.on("-h", "--help","Show this help message") { puts WarningShot::Config::PARSER; exit }
|
227
|
+
WarningShot::Config::PARSER.on("--debugger","Enable debugging") do
|
228
|
+
begin
|
229
|
+
require "ruby-debug"
|
230
|
+
Debugger.start
|
231
|
+
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
|
232
|
+
puts "Debugger enabled"
|
233
|
+
rescue LoadError => ex
|
234
|
+
puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
|
235
|
+
exit
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
WarningShot::Config::PARSER.separator "Resolver Specific Flags".center(80,'-')
|
241
|
+
|
242
|
+
WarningShot::Config::PARSER.parse!(argv)
|
243
|
+
|
244
|
+
@curr_config = @@cli_options.clone
|
245
|
+
@@cli_options = {}
|
246
|
+
return WarningShot::Config.create(@curr_config)
|
247
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument,OptionParser::NeedlessArgument => op
|
248
|
+
puts op
|
249
|
+
puts WarningShot::Config::PARSER;
|
250
|
+
end
|
251
|
+
|
252
|
+
end #End self
|
253
|
+
end #End Config
|
254
|
+
end#End WarningShot
|
@@ -1,3 +1,8 @@
|
|
1
|
+
require File.dirname(__FILE__) / 'warning_shot'
|
2
|
+
require File.dirname(__FILE__) / 'resolver'
|
3
|
+
require File.dirname(__FILE__) / 'config'
|
4
|
+
require File.dirname(__FILE__) / 'logger'
|
5
|
+
|
1
6
|
module WarningShot
|
2
7
|
class DependencyResolver
|
3
8
|
|
@@ -8,13 +13,19 @@ module WarningShot
|
|
8
13
|
@dependency_tree = {}
|
9
14
|
@resolvers = []
|
10
15
|
|
11
|
-
init_logger
|
16
|
+
self.init_logger
|
17
|
+
WarningShot.load_app(self[:application])
|
18
|
+
WarningShot.load_addl_resolvers(self[:resolvers])
|
12
19
|
|
13
20
|
# Parsed yml files
|
14
21
|
self.load_configs
|
15
22
|
@dependency_tree.symbolize_keys!
|
16
23
|
end
|
17
|
-
|
24
|
+
|
25
|
+
def [](k)
|
26
|
+
@config[k]
|
27
|
+
end
|
28
|
+
|
18
29
|
# gets stats of all resolvers
|
19
30
|
# @return [Hash]
|
20
31
|
# :passed, :failed, :resolved, :unresolved
|
@@ -39,15 +50,15 @@ module WarningShot
|
|
39
50
|
#
|
40
51
|
# @api private
|
41
52
|
def init_logger
|
42
|
-
FileUtils.mkdir_p(File.dirname(File.expand_path(
|
53
|
+
FileUtils.mkdir_p(File.dirname(File.expand_path(self[:log_path]))) unless self[:verbose]
|
43
54
|
|
44
55
|
@logger = Logger.new(
|
45
|
-
|
56
|
+
self[:verbose] ? STDOUT : self[:log_path], 10, 1024000
|
46
57
|
)
|
47
|
-
_log_level = (
|
58
|
+
_log_level = (self[:log_level] || :info).to_s.upcase
|
48
59
|
|
49
60
|
_formatter = WarningShot::LoggerFormatter.new
|
50
|
-
_formatter.colorize =
|
61
|
+
_formatter.colorize = self[:colorize]
|
51
62
|
|
52
63
|
@logger.formatter = _formatter
|
53
64
|
@logger.level = Object.class_eval("Logger::#{_log_level}")
|
@@ -58,21 +69,26 @@ module WarningShot
|
|
58
69
|
# @api private
|
59
70
|
def run
|
60
71
|
@logger.info "WarningShot v. #{WarningShot::VERSION}"
|
61
|
-
@logger.info "Environment: #{
|
72
|
+
@logger.info "Environment: #{self.environment}; Application: #{WarningShot.application_type}"
|
62
73
|
@logger.info "On host: #{WarningShot.hostname}"
|
63
74
|
|
64
|
-
WarningShot::Resolver.
|
75
|
+
WarningShot::Resolver.descendants.each do |klass|
|
76
|
+
next if klass.disabled?
|
77
|
+
|
65
78
|
@logger.info "\n#{'-'*60}"
|
66
79
|
|
67
80
|
branch = @dependency_tree[klass.branch.to_sym]
|
68
81
|
|
69
82
|
if branch.nil?
|
70
|
-
@logger.info "
|
83
|
+
@logger.info "[SKIPPING] #{klass}, #{klass.branch}; No machine recipes was registered"
|
84
|
+
next
|
85
|
+
elsif branch.empty?
|
86
|
+
@logger.info "[SKIPPING] #{klass}, #{klass.branch}; No dependencies in machine recipe"
|
71
87
|
next
|
72
88
|
end
|
73
89
|
|
74
90
|
klass.logger = @logger
|
75
|
-
resolver = klass.new(
|
91
|
+
resolver = klass.new(@config,*branch)
|
76
92
|
|
77
93
|
@resolvers << resolver
|
78
94
|
|
@@ -85,10 +101,10 @@ module WarningShot
|
|
85
101
|
|
86
102
|
@logger.info "Passed: #{resolver.passed.size} / Failed: #{resolver.failed.size}"
|
87
103
|
|
88
|
-
if
|
104
|
+
if self[:resolve] && !klass.resolutions.empty?
|
89
105
|
@logger.info "#{resolver.class}; branch: #{klass.branch} [RESOLVING]"
|
90
106
|
|
91
|
-
klass.before_filters(:resolution).each{|p| p.call}
|
107
|
+
klass.before_filters(:resolution).each{|p| p.call}
|
92
108
|
resolver.resolve!
|
93
109
|
klass.after_filters(:resolution).each{|p| p.call}
|
94
110
|
|
@@ -105,10 +121,10 @@ module WarningShot
|
|
105
121
|
#
|
106
122
|
# @api protected
|
107
123
|
def load_configs
|
108
|
-
|
124
|
+
self[:config_paths].each do |config_path|
|
109
125
|
#Parse the global/running env configs out of the YAML files.
|
110
|
-
Dir[config_path / WarningShot::
|
111
|
-
# Use WarningShot::
|
126
|
+
Dir[config_path / WarningShot::RecipeExt].each do |config_file|
|
127
|
+
# Use WarningShot::RecipeExt & regexp on extension to make supporting add'l
|
112
128
|
# file types easier in the future
|
113
129
|
case File.extname(config_file)
|
114
130
|
when /.y(a)?ml/
|
@@ -132,8 +148,13 @@ module WarningShot
|
|
132
148
|
def parse_yml(file)
|
133
149
|
#if only on branch is specified in a yaml file it may not come back as an array
|
134
150
|
branches = YAML::load(File.open(file,'r'))
|
135
|
-
branches = [branches] unless branches.is_a? Array
|
136
151
|
|
152
|
+
if branches === false
|
153
|
+
@logger.error "Skipping malformed Yaml file: #{file}"
|
154
|
+
return
|
155
|
+
end
|
156
|
+
|
157
|
+
branches = [branches] unless branches.is_a? Array
|
137
158
|
branches.each do |branch|
|
138
159
|
branch_name = branch[:branch]
|
139
160
|
dependency_tree[branch_name] ||= []
|
@@ -150,6 +171,6 @@ module WarningShot
|
|
150
171
|
@dependency_tree[branch_name].delete(nil)
|
151
172
|
end
|
152
173
|
end
|
153
|
-
|
174
|
+
|
154
175
|
end
|
155
176
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require File.dirname(__FILE__) / 'warning_shot'
|
2
|
+
|
1
3
|
# API access for resolvers
|
2
4
|
#
|
3
5
|
# @example
|
@@ -10,46 +12,78 @@
|
|
10
12
|
module WarningShot
|
11
13
|
module Resolver
|
12
14
|
module ClassMethods
|
13
|
-
attr_reader :raw_cli_ext
|
14
15
|
|
15
|
-
#
|
16
|
+
# creates a list of gems that the resolver is dependent on for different features
|
17
|
+
# The goal of this is that WarningShot will not need a bunch of libraries installed unless
|
18
|
+
# the end users needs that specific functionality. If a corelib/gem is missing the logger will
|
19
|
+
# receive warnings if a particular functionality that needs that library is enabled and its missing
|
20
|
+
# The missing GEMS can be installed with: warningshot --build-deps
|
21
|
+
# All resolvers' dependencies can be viewed with: warningshot --list-deps
|
22
|
+
#
|
23
|
+
# @param type [Symbol[:core|:gem]]
|
24
|
+
# the type of library it depends on, core lib or gem
|
25
|
+
#
|
26
|
+
# @param req_name [String]
|
27
|
+
# What would normally go in 'require'
|
28
|
+
#
|
29
|
+
# @param dep_opts [Hash]
|
30
|
+
# :disable [Boolean] Default: true
|
31
|
+
# Should the resolver be disabled if the gem is missing
|
32
|
+
# :unregister [Array[Symbol]]
|
33
|
+
# Test / Resolutions to unregister
|
34
|
+
# :name [String]
|
35
|
+
# Alternate name to use to install missing gem with warningshot --build-deps
|
36
|
+
# Example: require 'net/scp' would need gem install net-scp
|
37
|
+
# :version [String]
|
38
|
+
# The version to install
|
39
|
+
# :source [String]
|
40
|
+
# Alternate gem source
|
41
|
+
#
|
42
|
+
def add_dependency(type,req_name,dep_opts={})
|
43
|
+
require req_name
|
44
|
+
dep_opts[:installed] = true
|
45
|
+
rescue LoadError => ex
|
46
|
+
self.disable! unless dep_opts[:disable] === false
|
47
|
+
dep_opts[:installed] = false
|
48
|
+
ensure
|
49
|
+
@dependent_libs ||= {:core => [], :gem => []}
|
50
|
+
|
51
|
+
#if an alternate name isn't specified refer to it by the req_name
|
52
|
+
dep_opts[:name] ||= req_name
|
53
|
+
@dependent_libs[type].push dep_opts
|
54
|
+
end
|
55
|
+
|
56
|
+
# list the gems the resolver relies on
|
57
|
+
#
|
58
|
+
# @return [Hash]
|
59
|
+
def depends_on
|
60
|
+
@dependent_libs ||= {:core => [], :gem => []}
|
61
|
+
end
|
62
|
+
|
63
|
+
# provides shortcut to WarningShot::Config.cli_options
|
64
|
+
#
|
65
|
+
# @see WarningShot::Config
|
66
|
+
#
|
67
|
+
# @return [Hash]
|
68
|
+
#
|
69
|
+
def options
|
70
|
+
WarningShot::Config.cli_options
|
71
|
+
end
|
72
|
+
|
73
|
+
# provides shortcut to WarningShot::Config.cli
|
74
|
+
#
|
75
|
+
# @see OptionParser
|
76
|
+
# @see WarningShot::Config
|
16
77
|
#
|
17
|
-
# @param opts [
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# :default => "my_value",
|
22
|
-
# :description => "Command line description",
|
23
|
-
# :name => "keyname", #required
|
24
|
-
# :type => String #[:list, :of, :available, :values]
|
25
|
-
# :default_desc => "Default: my_value"
|
78
|
+
# @param *opts [Array]
|
79
|
+
#
|
80
|
+
# @param &block [Proc]
|
81
|
+
#
|
26
82
|
# @api public
|
27
|
-
def cli(opts)
|
28
|
-
@raw_cli_ext ||= []
|
29
|
-
#Do not extend the interface if the class is being
|
83
|
+
def cli(*opts,&block)
|
30
84
|
return if self.disabled?
|
31
85
|
|
32
|
-
|
33
|
-
return if opts[:name].nil?
|
34
|
-
@raw_cli_ext << opts
|
35
|
-
|
36
|
-
clean_opts = [
|
37
|
-
opts[:short],
|
38
|
-
opts[:long],
|
39
|
-
opts[:type],
|
40
|
-
opts[:description],
|
41
|
-
opts[:default_desc]
|
42
|
-
]
|
43
|
-
clean_opts.delete(nil)
|
44
|
-
|
45
|
-
#Set the default value if it was given
|
46
|
-
opt_name = opts[:name].intern
|
47
|
-
WarningShot::Config[opt_name] = opts[:default]
|
48
|
-
|
49
|
-
WarningShot.parser.on_tail(*clean_opts) do |val|
|
50
|
-
WarningShot::Config[opt_name] = val
|
51
|
-
end
|
52
|
-
|
86
|
+
WarningShot::Config.cli(*opts, &block)
|
53
87
|
end
|
54
88
|
|
55
89
|
# Setter/Getter for resolver branch,
|
@@ -128,6 +162,23 @@ module WarningShot
|
|
128
162
|
@disabled = true
|
129
163
|
end
|
130
164
|
|
165
|
+
# enables a resolver (enabled by default)
|
166
|
+
#
|
167
|
+
# @example
|
168
|
+
# class MyResolver
|
169
|
+
# include WarningShot::Resolver
|
170
|
+
# disable!
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# # Maybe you only want to enable it under some conditions
|
174
|
+
# MyResolver.enabled! if my_merb_or_rails_env == 'production'
|
175
|
+
# MyResolver.enabled! if @pickles.count == @chickens.count
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
def enable!
|
179
|
+
@disabled = false
|
180
|
+
end
|
181
|
+
|
131
182
|
# Determines if resolver is disabled
|
132
183
|
#
|
133
184
|
# @return [Boolean]
|
@@ -164,7 +215,7 @@ module WarningShot
|
|
164
215
|
# How to cast the data to an object
|
165
216
|
#
|
166
217
|
# @api public
|
167
|
-
def
|
218
|
+
def typecast(klass=nil,&block)
|
168
219
|
if klass.nil?
|
169
220
|
klass = :default
|
170
221
|
else
|
@@ -172,8 +223,8 @@ module WarningShot
|
|
172
223
|
end
|
173
224
|
(@yaml_to_object_methods||={})[klass] = block
|
174
225
|
end
|
175
|
-
|
176
|
-
# calls the block defined by Resolver#
|
226
|
+
|
227
|
+
# calls the block defined by Resolver#typecast to convert
|
177
228
|
# the yaml data to an object
|
178
229
|
#
|
179
230
|
# @param data [~YAML::load]
|
@@ -240,8 +291,7 @@ module WarningShot
|
|
240
291
|
#
|
241
292
|
# register :resolution, :if => lambda{|dependency|
|
242
293
|
# #This will determin if resolution should be attempted
|
243
|
-
#
|
244
|
-
# WarningShot.environment == 'production'
|
294
|
+
# my_special_instance == true
|
245
295
|
# } do |dependency|
|
246
296
|
# my_method_that_would_resolve dependency
|
247
297
|
# end
|
@@ -302,6 +352,14 @@ module WarningShot
|
|
302
352
|
end
|
303
353
|
end
|
304
354
|
|
355
|
+
# removes all test/resolutions from a resolver
|
356
|
+
#
|
357
|
+
# @api public
|
358
|
+
def flush!
|
359
|
+
flush_tests!
|
360
|
+
flush_resolutions!
|
361
|
+
end
|
362
|
+
|
305
363
|
# Removes all tests from a resolver
|
306
364
|
#
|
307
365
|
# @api public
|
@@ -379,7 +437,7 @@ module WarningShot
|
|
379
437
|
#
|
380
438
|
# @api private
|
381
439
|
def test!
|
382
|
-
dependencies.each do |dep|
|
440
|
+
dependencies.each do |dep|
|
383
441
|
self.class.tests.each{ |test_meta|
|
384
442
|
dep.met = process_block :test, dep, test_meta
|
385
443
|
break if dep.met
|
@@ -392,10 +450,12 @@ module WarningShot
|
|
392
450
|
# @api private
|
393
451
|
def resolve!
|
394
452
|
dependencies.each do |dep|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
453
|
+
unless dep.met
|
454
|
+
self.class.resolutions.each{ |resolution_meta|
|
455
|
+
dep.resolved = process_block :resolution, dep, resolution_meta
|
456
|
+
break if dep.resolved
|
457
|
+
}
|
458
|
+
end
|
399
459
|
end
|
400
460
|
end
|
401
461
|
|
@@ -407,11 +467,7 @@ module WarningShot
|
|
407
467
|
# @api private
|
408
468
|
def unresolved
|
409
469
|
dependencies.inject([]){ |list,dep|
|
410
|
-
|
411
|
-
list << dep
|
412
|
-
else
|
413
|
-
list
|
414
|
-
end
|
470
|
+
(!dep.met && !dep.resolved) ? (list << dep) : (list)
|
415
471
|
}
|
416
472
|
end
|
417
473
|
|
@@ -423,25 +479,18 @@ module WarningShot
|
|
423
479
|
# @api private
|
424
480
|
def failed
|
425
481
|
dependencies.inject([]){ |list,dep|
|
426
|
-
|
427
|
-
list << dep
|
428
|
-
else
|
429
|
-
list
|
430
|
-
end
|
482
|
+
dep.met ? (list) : (list << dep)
|
431
483
|
}
|
432
484
|
end
|
433
485
|
|
434
486
|
# list of successful dependencies
|
435
487
|
#
|
436
488
|
# @return [Array<Objects>]
|
489
|
+
#
|
437
490
|
# @api private
|
438
491
|
def passed
|
439
492
|
dependencies.inject([]){ |list,dep|
|
440
|
-
|
441
|
-
list << dep
|
442
|
-
else
|
443
|
-
list
|
444
|
-
end
|
493
|
+
dep.met ? (list << dep) : (list)
|
445
494
|
}
|
446
495
|
end
|
447
496
|
|
@@ -453,15 +502,15 @@ module WarningShot
|
|
453
502
|
# @api private
|
454
503
|
def resolved
|
455
504
|
dependencies.inject([]){ |list,dep|
|
456
|
-
|
457
|
-
list << dep
|
458
|
-
else
|
459
|
-
list
|
460
|
-
end
|
505
|
+
(!dep.met && dep.resolved) ? (list << dep) : (list)
|
461
506
|
}
|
462
507
|
end
|
463
508
|
|
464
|
-
#
|
509
|
+
# setups up a resolver with config and dependencies
|
510
|
+
#
|
511
|
+
# @param config [WarningShot::Config]
|
512
|
+
# Configuration to use
|
513
|
+
#
|
465
514
|
# @param *deps [Array]
|
466
515
|
# Dependencies from YAML file
|
467
516
|
#
|
@@ -472,8 +521,10 @@ module WarningShot
|
|
472
521
|
# resolved [Boolean] Was teh dependency resolved
|
473
522
|
#
|
474
523
|
# @api semi-public
|
475
|
-
def initialize(
|
524
|
+
def initialize(config,*deps)
|
525
|
+
@config = config
|
476
526
|
@dependencies = Set.new
|
527
|
+
|
477
528
|
deps.each do |dep|
|
478
529
|
# Cast YAML data as described in resolver.
|
479
530
|
dep = self.class.yaml_to_object(dep)
|
@@ -482,10 +533,7 @@ module WarningShot
|
|
482
533
|
@dependencies.add dep
|
483
534
|
end
|
484
535
|
end
|
485
|
-
|
486
|
-
attr_accessor :logger
|
487
|
-
attr_accessor :dependencies
|
488
|
-
|
536
|
+
|
489
537
|
protected
|
490
538
|
# processes a test or resolution block
|
491
539
|
#
|
@@ -494,7 +542,6 @@ module WarningShot
|
|
494
542
|
#
|
495
543
|
# @param dep <Hash>
|
496
544
|
# Dependency parsed from yaml configs (Currently Hash)
|
497
|
-
# TODO; once Dependencies are an object besides Hash, this may need to be changed
|
498
545
|
#
|
499
546
|
# @param block_info <Hash>
|
500
547
|
# The block details and proc
|
@@ -502,33 +549,72 @@ module WarningShot
|
|
502
549
|
# @return <Boolean>
|
503
550
|
# Was the block successful; meaning conditions passed and block returned true
|
504
551
|
#
|
552
|
+
# @note
|
553
|
+
# If anyone knows a better way then doing the arity check go for it. I'd
|
554
|
+
# like the register api to 'just work' and not have the writer worry about
|
555
|
+
# a dependency or a configuration unless they need it. I know that a Proc
|
556
|
+
# works no matter how many arguments are passed to it, but a lambda checks
|
557
|
+
# to make sure its the correct number, and apparently "do;end;" and "{ }"
|
558
|
+
# check for the number of arguments.
|
559
|
+
#
|
560
|
+
# Also note for this, :unless, :if and the block all need a different set of
|
561
|
+
# params because someone may have registered a :if condition that has a different
|
562
|
+
# signature than the test/resolution block
|
563
|
+
#
|
564
|
+
# register(:test,:if=>lambda{WarningShot.hostname=="boogertron"}) do |dep,config|
|
565
|
+
# puts "A test that needs dep & config"
|
566
|
+
# end
|
567
|
+
#
|
505
568
|
# @api private
|
506
569
|
def process_block(type, dep, block_info)
|
570
|
+
block_params = case block_info[type].arity
|
571
|
+
when 1
|
572
|
+
dep
|
573
|
+
when 2
|
574
|
+
[dep,self.config]
|
575
|
+
end
|
576
|
+
|
577
|
+
if_params = case
|
578
|
+
when 1
|
579
|
+
dep
|
580
|
+
when 2
|
581
|
+
[dep,self.config]
|
582
|
+
end if block_info[:if]
|
583
|
+
|
584
|
+
unless_params = case
|
585
|
+
when 1
|
586
|
+
dep
|
587
|
+
when 2
|
588
|
+
[dep,self.config]
|
589
|
+
end if block_info[:unless]
|
590
|
+
|
507
591
|
if !block_info[:if] && !block_info[:unless]
|
508
|
-
# no
|
509
|
-
return block_info[type].call(
|
510
|
-
elsif block_info[:if] && block_info[:if].call(
|
511
|
-
#if Condition given and it applies, run block
|
512
|
-
return block_info[type].call(
|
513
|
-
elsif block_info[:unless] && !block_info[:unless].call(
|
514
|
-
#unless Condition given and it applies, run block
|
515
|
-
return block_info[type].call(
|
592
|
+
#if no conditions, run block
|
593
|
+
return block_info[type].call(block_params)
|
594
|
+
elsif block_info[:if] && block_info[:if].call(if_params)
|
595
|
+
#elsif 'if' Condition given and it applies, run block
|
596
|
+
return block_info[type].call(block_params)
|
597
|
+
elsif block_info[:unless] && !block_info[:unless].call(unless_params)
|
598
|
+
#elsif 'unless' Condition given and it applies, run block
|
599
|
+
return block_info[type].call(block_params)
|
516
600
|
end
|
517
601
|
|
518
602
|
return false
|
519
603
|
end
|
520
604
|
end
|
521
605
|
|
522
|
-
@@
|
523
|
-
def self.
|
524
|
-
#Filter out
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
606
|
+
@@descendants = []
|
607
|
+
def self.descendants(filter_disabled=true)
|
608
|
+
#Filter out descendants that are disabled
|
609
|
+
temp_descendants = []
|
610
|
+
|
611
|
+
|
612
|
+
@@descendants.each{ |klass|
|
613
|
+
temp_descendants.push(klass) unless filter_disabled && klass.disabled?
|
614
|
+
}
|
529
615
|
|
530
616
|
#Sort by order
|
531
|
-
|
617
|
+
temp_descendants.sort_by{|desc| desc.order}
|
532
618
|
end
|
533
619
|
|
534
620
|
|
@@ -536,7 +622,9 @@ module WarningShot
|
|
536
622
|
def self.included(subclass)
|
537
623
|
subclass.extend ClassMethods
|
538
624
|
subclass.send :include, InstanceMethods
|
539
|
-
|
625
|
+
subclass.send :attr_reader, :config
|
626
|
+
subclass.send :attr_accessor, :dependencies
|
627
|
+
@@descendants.push subclass
|
540
628
|
end
|
541
629
|
end
|
542
630
|
end
|