class Unravel::Session
Attributes
config[R]
registry[R]
Public Class Methods
new(config = DefaultConfig.new)
click to toggle source
# File lib/unravel.rb, line 121 def initialize(config = DefaultConfig.new) @config = config @registry = Registry.new end
Public Instance Methods
achieve(name, *args)
click to toggle source
# File lib/unravel.rb, line 126 def achieve(name, *args) check # TODO: overhead? prev_causes = Set.new max_retries = config.max_retries retries_left = max_retries begin Unravel.logger.info("Achieving (attempt: #{max_retries - retries_left + 1}/#{max_retries}): #{name.inspect}") block = registry.get_achievement(name) if block.arity >= 0 unless block.arity == args.size fail ArgumentError, "expected #{block.arity} args for #{name.inspect}, got: #{args.inspect}" end end error_contexts = registry.error_contexts_for_achievement(name) begin res = return_wrap(*args, &block) rescue *error_contexts.keys ex = $! econtext = error_contexts[ex.class] unless econtext # TODO: not tested fail NoErrorHandler.new(name, ex) end econtext.each do |fix_name| error = $!.message fix! fix_name, error end fail end if res == true config.after_achievement_success(name) return true end fail NotImplementedError, "#{name} unexpectedly returned #{res.inspect} (expected true or exception)" rescue FixableError => error Unravel.logger.info("#{name}: Symptom: #{error.symptom.inspect}") #Unravel.logger.debug(" -> failed: #{name.inspect}: #{error.symptom.inspect}\n") cause = get_root_cause_for(error.symptom) fail NoKnownRootCause, "Can't find root cause for: #{error.symptom}, #{error.message}" unless cause Unravel.logger.info("#{name}: Cause: #{cause.inspect}") # Since causes can be generic (parametized), they're unique based on error matchers unique_context = error.extracted_info unique_cause = [cause, unique_context] if prev_causes.include? unique_cause fail SameCauseReoccurringCause, "#{cause.to_s} (with #{unique_context.inspect}) wasn't ultimately fixed (it occured again)" end prev_causes << unique_cause fix = registry.get_fix_for(cause) fix.call(error) retries_left -= 1 retry if retries_left > 0 fail end end
achievement(name, error_contexts, &block)
click to toggle source
# File lib/unravel.rb, line 197 def achievement(name, error_contexts, &block) registry.add_achievement(name, &block) registry.add_error_contexts(name, error_contexts) end
error()
click to toggle source
# File lib/unravel.rb, line 202 def error registry.errors end
fix_for(*args, &block)
click to toggle source
TODO: move logic to registry
# File lib/unravel.rb, line 212 def fix_for(*args, &block) name, achievement = *args if block_given? if args.size > 1 fail ArgumentError, "#{args[1..-1].inspect} ignored because of block" end registry.add_fix(name, block) else if name.is_a?(Hash) name, achievement = *name.first end # TODO: this recursively calls self (just to provied block), though - # is the block_given check needed? unless block_given? fix_for(name) do |error| achieve(achievement, *error.extracted_info) end end end end
quickfix(error_name, regexp, fix_name, handlers={})
click to toggle source
Shorthand for easy-to-fix and name problems
# File lib/unravel.rb, line 234 def quickfix(error_name, regexp, fix_name, handlers={}) root_cause_name = "no_#{fix_name}".to_sym error[error_name] = regexp root_cause_for error_name => root_cause_name unless registry.has_fix_for?(root_cause_name) fix_for root_cause_name => fix_name end achievement fix_name, handlers, &method(fix_name) end
root_cause_for(mapping)
click to toggle source
# File lib/unravel.rb, line 206 def root_cause_for(mapping) symptom, root_cause = *mapping.first registry.add_symptom(symptom, root_cause) end
Private Instance Methods
check()
click to toggle source
# File lib/unravel.rb, line 254 def check logger = Unravel.logger res = registry.fixes.keys - registry.symptoms.values logger.warn "Unused: #{res.inspect}" unless res.empty? res = registry.symptoms.values - registry.fixes.keys logger.warn "Unhandled: #{res.inspect}" unless res.empty? errors = registry.contexts.values.map(&:values).flatten(2) res = errors - registry.symptoms.keys logger.warn "Unknown contexts: #{res.inspect}" unless res.empty? res = registry.symptoms.keys - errors logger.warn "Unused errors: #{res.inspect}" unless res.empty? end
fix!(name, error)
click to toggle source
# File lib/unravel.rb, line 271 def fix!(name, error) regexp = registry.fixable_error(name) unless regexp fail HumanInterventionNeeded, "Unregistered error: #{name} to match #{error.inspect}" end # TODO: encoding not tested match = regexp.match(error.force_encoding(Encoding::ASCII_8BIT)) fail FixableError.new(name, match) if match end
get_root_cause_for(symptom)
click to toggle source
# File lib/unravel.rb, line 282 def get_root_cause_for(symptom) registry.get_root_cause(symptom) end
return_wrap(*args, &block)
click to toggle source
# File lib/unravel.rb, line 246 def return_wrap(*args, &block) Thread.new do return block.yield(*args) end.join rescue LocalJumpError => ex ex.exit_value end