xail 0.0.1 → 0.0.2
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/README.md +47 -2
- data/examples/customfilter.xail.rb +22 -0
- data/examples/logview.xail.rb +3 -14
- data/lib/xail.rb +10 -4
- data/lib/xail/config.rb +21 -7
- data/lib/xail/filter.rb +23 -13
- data/lib/xail/version.rb +1 -1
- data/spec/filter_spec.rb +56 -5
- metadata +8 -7
data/README.md
CHANGED
@@ -38,10 +38,10 @@ and performs some action, potentially altering the stream, or terminating the fl
|
|
38
38
|
Compound filters take a stream and also a set of subfilters. These generally implement
|
39
39
|
flow control.
|
40
40
|
|
41
|
-
Stdout is the ultimate consumer of the strings, unless they've been
|
41
|
+
Stdout is the ultimate consumer of the strings, unless they've been redirected using `rest`.
|
42
42
|
|
43
43
|
### Compound Filters
|
44
|
-
* `cascade` -- stream the result of first subfilter that streams
|
44
|
+
* `cascade` -- stream the result of first subfilter that streams, a cascade filter is the container for your xail script
|
45
45
|
* `composition` -- applies each subfilter on the stream of the preceding subfilter, streaming the final result
|
46
46
|
|
47
47
|
* `and` -- streams the original if all subfilters stream
|
@@ -65,7 +65,52 @@ Stdout is the ultimate consumer of the strings, unless they've been filtered.
|
|
65
65
|
* `sample` -- samples the stream, only printing at the rate requested
|
66
66
|
* `stop` -- stops processing of this stream and continues with the next
|
67
67
|
* `count` -- [todo] computes the rate of the stream for display (need UI aspect)
|
68
|
+
* `rest` -- a special compound filter that is applied to any unmatched streams
|
68
69
|
|
69
70
|
### Custom Filters
|
70
71
|
|
71
72
|
You can easily develop your own filters. Either as an anonymous block:
|
73
|
+
|
74
|
+
filter do |stream|
|
75
|
+
if stream.includes? 'awesome'
|
76
|
+
nil # stop the stream
|
77
|
+
else
|
78
|
+
stream # keep it going
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Or, if you need to preserve state, as a filter within the xail file:
|
83
|
+
|
84
|
+
class LineNumbersFilter < AbstractFilter
|
85
|
+
def initialize
|
86
|
+
@lineno = 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def streamLine(line)
|
90
|
+
@lineno += 1
|
91
|
+
return "%5d %s" % [@lineno, line]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
linenumbers
|
96
|
+
group('fatal') {
|
97
|
+
contains 'fatal'
|
98
|
+
red
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
### Hide unfiltered lines
|
103
|
+
By default xail will print any unfiltered lines as this is a more typical case.
|
104
|
+
You can hide unfiltered lines using a rest block. For example, to only show
|
105
|
+
exceptions:
|
106
|
+
|
107
|
+
group('error') {
|
108
|
+
contains 'exception'
|
109
|
+
red
|
110
|
+
}
|
111
|
+
|
112
|
+
stop
|
113
|
+
|
114
|
+
### Fine-grained stream control
|
115
|
+
You can preform finer grained stream control using the `OR`, `AND`, and `NOT`
|
116
|
+
combinators.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env xail
|
2
|
+
|
3
|
+
class ExecuteFilter < AbstractFilter
|
4
|
+
def initialize(command)
|
5
|
+
@command = command
|
6
|
+
end
|
7
|
+
|
8
|
+
def streamLine(line)
|
9
|
+
puts "WOULD EXECUTE: #{@command} #{line}"
|
10
|
+
return line
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
group('exec') {
|
15
|
+
contains 'exec'
|
16
|
+
execute 'echo'
|
17
|
+
stop
|
18
|
+
}
|
19
|
+
|
20
|
+
rest {
|
21
|
+
stop
|
22
|
+
}
|
data/examples/logview.xail.rb
CHANGED
@@ -3,22 +3,11 @@
|
|
3
3
|
# a simple log error and warning highlighter
|
4
4
|
#
|
5
5
|
|
6
|
-
|
7
|
-
def initialize(command)
|
8
|
-
@command = command
|
9
|
-
end
|
10
|
-
|
11
|
-
def streamLine(line)
|
12
|
-
puts "EXECUTING #{@command} #{line}"
|
13
|
-
return line
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
|
6
|
+
#!/usr/bin/env xail
|
18
7
|
group('fatal') {
|
19
8
|
contains 'fatal'
|
20
9
|
red
|
21
|
-
|
10
|
+
underscore
|
22
11
|
bell
|
23
12
|
}
|
24
13
|
|
@@ -31,4 +20,4 @@ group('error') {
|
|
31
20
|
group('warning') {
|
32
21
|
contains 'warn'
|
33
22
|
yellow
|
34
|
-
}
|
23
|
+
}
|
data/lib/xail.rb
CHANGED
@@ -28,16 +28,22 @@ A Ruby utility for performing basic stream processing, directly focused on incre
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
31
|
stream = $stdin
|
33
32
|
stream.each() do |line|
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
begin
|
34
|
+
streamed = filter.streamLine(line)
|
35
|
+
if streamed and streamed.size > 0
|
36
|
+
print streamed
|
37
|
+
end
|
38
|
+
|
39
|
+
rescue StreamLineStop
|
37
40
|
end
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
41
44
|
config = IO.read(ARGV[0])
|
42
45
|
Xail.run(config)
|
46
|
+
|
47
|
+
rescue SignalException
|
48
|
+
exit
|
43
49
|
end
|
data/lib/xail/config.rb
CHANGED
@@ -16,10 +16,19 @@ module Xail
|
|
16
16
|
@filter_stack.pop
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
19
|
+
def filter(&block)
|
20
|
+
filter_in_scope << Class.new(AbstractFilter) do
|
21
|
+
def streamLine(line)
|
22
|
+
block(line)
|
23
|
+
end
|
24
|
+
end
|
21
25
|
end
|
22
26
|
|
27
|
+
# TODO add support for explicitly listing sources
|
28
|
+
#def stream(name, source = null)
|
29
|
+
# source ||= name
|
30
|
+
#end
|
31
|
+
|
23
32
|
def group(name, &filters)
|
24
33
|
# TODO intergrate with UX
|
25
34
|
filter_scope(FilterComposition.new) {
|
@@ -27,10 +36,7 @@ module Xail
|
|
27
36
|
}
|
28
37
|
end
|
29
38
|
|
30
|
-
|
31
|
-
@has_final
|
32
|
-
end
|
33
|
-
|
39
|
+
attr :has_final
|
34
40
|
def rest(&filters)
|
35
41
|
if @has_final
|
36
42
|
raise "rest may only be specified once"
|
@@ -48,10 +54,18 @@ module Xail
|
|
48
54
|
end
|
49
55
|
|
50
56
|
def method_missing(name, *args, &block)
|
57
|
+
abort "internal error #{name} #{args} #{block}" unless name
|
51
58
|
filterClass = FilterRegistry::get_filter(name.downcase)
|
52
|
-
|
59
|
+
filter_scope(filterClass.new(*args)) do
|
60
|
+
block.yield if block
|
61
|
+
end
|
53
62
|
filter_in_scope << filter
|
54
63
|
|
64
|
+
# short circuit the stream line stop exception so we can catch it
|
65
|
+
# in xail main
|
66
|
+
rescue StreamLineStop => stop
|
67
|
+
raise stop
|
68
|
+
|
55
69
|
rescue UnknownFilter => error
|
56
70
|
abort error.to_s
|
57
71
|
|
data/lib/xail/filter.rb
CHANGED
@@ -102,7 +102,7 @@ module Xail
|
|
102
102
|
if line != nil
|
103
103
|
filter.streamLine(line)
|
104
104
|
else
|
105
|
-
nil
|
105
|
+
return nil
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -127,26 +127,26 @@ module Xail
|
|
127
127
|
class OrFilter < AbstractCompoundFilter
|
128
128
|
def streamLine(line)
|
129
129
|
@filters.each do |filter|
|
130
|
-
if filter.streamLine
|
130
|
+
if filter and filter.streamLine(line)
|
131
131
|
return line
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
-
nil
|
135
|
+
return nil
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
139
|
|
140
140
|
# the not filter streams the original if none of the component filters stream
|
141
|
-
class NotFilter <
|
141
|
+
class NotFilter < AbstractCompoundFilter
|
142
142
|
def streamLine(line)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
else
|
148
|
-
line
|
143
|
+
@filters.each do |filter|
|
144
|
+
if filter.streamLine(line)
|
145
|
+
return
|
146
|
+
end
|
149
147
|
end
|
148
|
+
|
149
|
+
return line
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
@@ -157,7 +157,9 @@ module Xail
|
|
157
157
|
|
158
158
|
def streamLine(line)
|
159
159
|
@keys.each do |key|
|
160
|
-
if
|
160
|
+
if key.instance_of? Regexp and line[key]
|
161
|
+
return line
|
162
|
+
elsif key.instance_of? String and line.downcase.include? key.downcase
|
161
163
|
return line
|
162
164
|
end
|
163
165
|
end
|
@@ -182,13 +184,21 @@ module Xail
|
|
182
184
|
end
|
183
185
|
|
184
186
|
|
185
|
-
# the
|
186
|
-
class
|
187
|
+
# the sink filter never streams
|
188
|
+
class SinkFilter < AbstractFilter
|
187
189
|
def streamLine(line)
|
188
190
|
nil
|
189
191
|
end
|
190
192
|
end
|
191
193
|
|
194
|
+
# the stop filter stops all processing of this stream-line
|
195
|
+
class StreamLineStop < Exception; end
|
196
|
+
class StopFilter < AbstractFilter
|
197
|
+
def streamLine(line)
|
198
|
+
raise StreamLineStop
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
192
202
|
# the pass through filter always streams
|
193
203
|
class PassThroughFilter < AbstractFilter
|
194
204
|
def streamLine(line)
|
data/lib/xail/version.rb
CHANGED
data/spec/filter_spec.rb
CHANGED
@@ -24,10 +24,17 @@ describe PassThroughFilter do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
describe SinkFilter do
|
28
|
+
it "should sink all streams" do
|
29
|
+
f = SinkFilter.new
|
30
|
+
f.streamLine("hi").should eq(nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
27
34
|
describe StopFilter do
|
28
|
-
it "should stop all
|
35
|
+
it "should stop all processing of this stream-line" do
|
29
36
|
f = StopFilter.new
|
30
|
-
f.streamLine("hi").should
|
37
|
+
lambda { f.streamLine("hi") }.should raise_error(StreamLineStop)
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
@@ -61,10 +68,10 @@ end
|
|
61
68
|
describe FilterCascade do
|
62
69
|
it "should cascade through" do
|
63
70
|
f = FilterCascade.new
|
64
|
-
f <<
|
65
|
-
f <<
|
71
|
+
f << SinkFilter.new
|
72
|
+
f << SinkFilter.new
|
66
73
|
f << ContainsFilter.new("hi")
|
67
|
-
f <<
|
74
|
+
f << SinkFilter.new
|
68
75
|
|
69
76
|
f.streamLine("bye").should eq(nil)
|
70
77
|
f.streamLine("hi").should eq("hi")
|
@@ -80,4 +87,48 @@ describe FilterComposition do
|
|
80
87
|
f.streamLine('bye').should eq(nil)
|
81
88
|
f.streamLine('hi there alice').should eq('hi there blice')
|
82
89
|
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe OrFilter do
|
93
|
+
it "should act like logical or" do
|
94
|
+
f = OrFilter.new
|
95
|
+
f << ContainsFilter.new('a')
|
96
|
+
f << ContainsFilter.new('b')
|
97
|
+
|
98
|
+
f.streamLine('a').should eq('a')
|
99
|
+
f.streamLine('b').should eq('b')
|
100
|
+
f.streamLine('c').should eq(nil)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe AndFilter do
|
105
|
+
it "should act like logical and" do
|
106
|
+
f = AndFilter.new
|
107
|
+
f << ContainsFilter.new('a')
|
108
|
+
f << ContainsFilter.new('b')
|
109
|
+
|
110
|
+
f.streamLine('a').should eq(nil)
|
111
|
+
f.streamLine('b').should eq(nil)
|
112
|
+
f.streamLine('ab').should eq('ab')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe NotFilter do
|
117
|
+
it "should act like logical not" do
|
118
|
+
f = NotFilter.new
|
119
|
+
f << ContainsFilter.new('a')
|
120
|
+
|
121
|
+
f.streamLine('a').should eq(nil)
|
122
|
+
f.streamLine('b').should eq('b')
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should take multiple subfilters" do
|
126
|
+
f = NotFilter.new
|
127
|
+
f << ContainsFilter.new('a')
|
128
|
+
f << ContainsFilter.new('b')
|
129
|
+
|
130
|
+
f.streamLine('a').should eq(nil)
|
131
|
+
f.streamLine('b').should eq(nil)
|
132
|
+
f.streamLine('c').should eq('c')
|
133
|
+
end
|
83
134
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-02-24 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70300575161740 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70300575161740
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: trollop
|
27
|
-
requirement: &
|
27
|
+
requirement: &70300575161280 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70300575161280
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: term-ansicolor
|
38
|
-
requirement: &
|
38
|
+
requirement: &70300575160760 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70300575160760
|
47
47
|
description: A lightweight Ruby DSL for building text-stream filters
|
48
48
|
email:
|
49
49
|
- wiktor@tumblr.com
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- README.md
|
58
58
|
- Rakefile
|
59
59
|
- bin/xail
|
60
|
+
- examples/customfilter.xail.rb
|
60
61
|
- examples/logview.xail.rb
|
61
62
|
- examples/logviewtest.txt
|
62
63
|
- lib/xail.rb
|