Class: Unravel::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/unravel.rb

Defined Under Namespace

Classes: DefaultConfig, FixableError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = DefaultConfig.new) ⇒ Session



121
122
123
124
# File 'lib/unravel.rb', line 121

def initialize(config = DefaultConfig.new)
  @config = config
  @registry = Registry.new
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



107
108
109
# File 'lib/unravel.rb', line 107

def config
  @config
end

#registryObject (readonly)

Returns the value of attribute registry.



106
107
108
# File 'lib/unravel.rb', line 106

def registry
  @registry
end

Instance Method Details

#achieve(name, *args) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# 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) ⇒ Object



197
198
199
200
# 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

#errorObject



202
203
204
# File 'lib/unravel.rb', line 202

def error
  registry.errors
end

#fix_for(*args, &block) ⇒ Object

TODO: move logic to registry



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# 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 = {}) ⇒ Object

Shorthand for easy-to-fix and name problems



234
235
236
237
238
239
240
241
242
# 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) ⇒ Object



206
207
208
209
# File 'lib/unravel.rb', line 206

def root_cause_for(mapping)
  symptom, root_cause = *mapping.first
  registry.add_symptom(symptom, root_cause)
end