xail 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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 filtered.
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
+ }
@@ -3,22 +3,11 @@
3
3
  # a simple log error and warning highlighter
4
4
  #
5
5
 
6
- class ExecuteFilter < AbstractFilter
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
- onblue
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
+ }
@@ -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
- streamed = filter.streamLine(line)
35
- if streamed and streamed.size > 0
36
- printf streamed
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
@@ -16,10 +16,19 @@ module Xail
16
16
  @filter_stack.pop
17
17
  end
18
18
 
19
- def stream(name, source = null)
20
- source ||= name
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
- def has_final
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
- filter = filterClass.new(*args)
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
 
@@ -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 line
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 < AndFilter
141
+ class NotFilter < AbstractCompoundFilter
142
142
  def streamLine(line)
143
- result = super.streamLine(line)
144
-
145
- if result != nil
146
- nil
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 line.include?(key)
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 stop filter never streams
186
- class StopFilter < AbstractFilter
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)
@@ -1,3 +1,3 @@
1
1
  module Xail
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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 streams" do
35
+ it "should stop all processing of this stream-line" do
29
36
  f = StopFilter.new
30
- f.streamLine("hi").should eq(nil)
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 << StopFilter.new
65
- f << StopFilter.new
71
+ f << SinkFilter.new
72
+ f << SinkFilter.new
66
73
  f << ContainsFilter.new("hi")
67
- f << StopFilter.new
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.1
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: &70198967869060 !ruby/object:Gem::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: *70198967869060
24
+ version_requirements: *70300575161740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: trollop
27
- requirement: &70198967868420 !ruby/object:Gem::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: *70198967868420
35
+ version_requirements: *70300575161280
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: term-ansicolor
38
- requirement: &70198967867800 !ruby/object:Gem::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: *70198967867800
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