warningshot 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|