Skip to content

Commit

Permalink
Merge pull request #10 from bitjourney/timeout_and_errors
Browse files Browse the repository at this point in the history
add timeout and rescue options
  • Loading branch information
gfx authored Sep 26, 2017
2 parents 6404537 + f8bf117 commit 06cd0e8
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 24 deletions.
16 changes: 11 additions & 5 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ Lint/AmbiguousBlockAssociation:
Lint/ScriptPermission:
Enabled: false

Lint/RescueWithoutErrorClass:
Enabled: false

Layout/MultilineMethodCallIndentation:
Enabled: false

Layout/EmptyLinesAroundClassBody:
Enabled: false

Metrics/AbcSize:
Max: 32
Max: 35

Metrics/PerceivedComplexity:
Max: 10
Expand Down Expand Up @@ -57,7 +60,7 @@ Style/CommentAnnotation:
Style/Documentation:
Enabled: false

Style/FileName:
Naming/FileName:
Enabled: false

Style/GuardClause:
Expand Down Expand Up @@ -114,13 +117,13 @@ Style/AsciiComments:
Style/EmptyMethod:
EnforcedStyle: expanded

Style/VariableNumber:
Naming/VariableNumber:
Enabled: false

Style/PredicateName:
Naming/PredicateName:
Enabled: false

Style/AccessorMethodName:
Naming/AccessorMethodName:
Enabled: false

Style/YodaCondition:
Expand All @@ -135,5 +138,8 @@ Style/MultipleComparison:
Style/StructInheritance:
Enabled: false

Style/NonNilCheck:
Enabled: false

Performance/RedundantBlockCall:
Enabled: false
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ end
mato.process("Hello!").render_html # "<p>HELLO!</p>\n"
```

### Timeout

There is `timeout` option to kill filters in a specified time in seconds:

```ruby
mato = Mato.define do |config|
config.append_html_filter(FooFilter, timeout: timeout_in_sec, on_timeout: callback)
end
```

If you set `on_error` callback, you can omit `on_timeout` callback.

### Errors in Filters

There is `on_error` callback to rescue errors in filters:

```ruby
mato = Mato.define do |config|
config.append_html_filter(FooFilter, on_error: callback)
end
```

## Installation

Add this line to your application's Gemfile:
Expand Down
2 changes: 2 additions & 0 deletions lib/mato.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
require_relative "./mato/config"
require_relative "./mato/processor"
require_relative "./mato/converter"
require_relative "./mato/rescue"
require_relative "./mato/timeout"

# filter classes
require_relative "./mato/html_filters/token_link"
Expand Down
37 changes: 18 additions & 19 deletions lib/mato/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,34 +75,33 @@ def configure(&block)
block.call(self)
end

def append_text_filter(text_filter)
def append_text_filter(text_filter, timeout: nil, on_timeout: nil, on_error: nil)
raise "text_filter must respond to call()" unless text_filter.respond_to?(:call)
text_filters.push(text_filter)
text_filters.push(wrap(text_filter, timeout: timeout, on_timeout: on_timeout, on_error: on_error))
end

def prepend_text_filter(text_filter)
raise "text_filter must respond to call()" unless text_filter.respond_to?(:call)
text_filters.unshift(text_filter)
end

def append_markdown_filter(markdown_filter)
raise "markdown_filter must respond to call()" unless markdown_filter.respond_to?(:call)
markdown_filters.push(markdown_filter)
end

def prepend_markdown_filter(markdown_filter)
def append_markdown_filter(markdown_filter, timeout: nil, on_timeout: nil, on_error: nil)
raise "markdown_filter must respond to call()" unless markdown_filter.respond_to?(:call)
markdown_filters.unshift(markdown_filter)
markdown_filters.push(wrap(markdown_filter, timeout: timeout, on_timeout: on_timeout, on_error: on_error))
end

def append_html_filter(html_filter)
def append_html_filter(html_filter, timeout: nil, on_timeout: nil, on_error: nil)
raise "html_filter must respond to call()" unless html_filter.respond_to?(:call)
html_filters.push(html_filter)
html_filters.push(wrap(html_filter, timeout: timeout, on_timeout: on_timeout, on_error: on_error))
end

def prepend_html_filter(html_filter)
raise "html_filter must respond to call()" unless html_filter.respond_to?(:call)
html_filters.unshift(html_filter)
private

def wrap(filter, timeout:, on_timeout:, on_error:)
if timeout
filter = Mato::Timeout.new(filter, timeout: timeout, on_timeout: on_timeout || on_error)
end

if on_error
filter = Mato::Rescue.new(filter, on_error: on_error)
end

filter
end
end
end
20 changes: 20 additions & 0 deletions lib/mato/rescue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module Mato
class Rescue
attr_reader :filter
attr_reader :on_error

def initialize(filter, on_error:)
@filter = filter
@on_error = on_error
end

def call(content)
filter.call(content)
rescue => e
on_error.call(e)
content
end
end
end
30 changes: 30 additions & 0 deletions lib/mato/timeout.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require 'timeout'

module Mato
class Timeout
attr_reader :filter
attr_reader :duration_sec
attr_reader :on_timeout

def initialize(filter, timeout:, on_timeout:)
@filter = filter
@duration_sec = timeout
@on_timeout = on_timeout

unless on_timeout
raise ArgumentError, "Missing on_timeout callback"
end
end

def call(content)
::Timeout.timeout(duration_sec) do
filter.call(content)
end
rescue ::Timeout::Error => e
on_timeout.call(e)
content
end
end
end
24 changes: 24 additions & 0 deletions test/mato_rescue_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

require_relative './test_helper'

class MatoRescueTest < MyTest
def test_text_filters
error = nil
mato = Mato.define do |config|
config.append_text_filter(->(text) {
raise 'Hello!'
}, on_error: ->(e) { error = e })
end

doc = mato.process('Hello, world!')

assert do
doc.render_html == "<p>Hello, world!</p>\n"
end

assert do
error.message == 'Hello!'
end
end
end
43 changes: 43 additions & 0 deletions test/mato_timeout_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

require_relative './test_helper'

class MatoTimeoutTest < MyTest
def test_text_filters
timeout = nil
mato = Mato.define do |config|
config.append_text_filter(->(text) {
sleep
}, timeout: 0.1, on_timeout: ->(e) { timeout = e })
end

doc = mato.process('Hello, world!')

assert do
doc.render_html == "<p>Hello, world!</p>\n"
end

assert do
timeout != nil
end
end

def test_text_filters_with_on_error
timeout = nil
mato = Mato.define do |config|
config.append_text_filter(->(text) {
sleep
}, timeout: 0.1, on_error: ->(e) { timeout = e })
end

doc = mato.process('Hello, world!')

assert do
doc.render_html == "<p>Hello, world!</p>\n"
end

assert do
timeout != nil
end
end
end

0 comments on commit 06cd0e8

Please sign in to comment.