plunk 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +34 -0
- data/LICENSE +20 -0
- data/README.md +4 -0
- data/Rakefile +5 -0
- data/lib/plunk.rb +3 -0
- data/lib/plunk/elasticsearch.rb +121 -0
- data/lib/plunk/parser.rb +84 -0
- data/lib/plunk/result_set.rb +38 -0
- data/lib/plunk/transformer.rb +64 -0
- data/plunk.gemspec +16 -0
- data/spec/elasticsearch_spec.rb +15 -0
- data/spec/parser_spec.rb +101 -0
- data/spec/result_set_spec.rb +0 -0
- data/spec/transformer_spec.rb +10 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 355fabb9e6a73101c70a9990992abe5974626a76
|
4
|
+
data.tar.gz: d61e5388b448da8412899038e8b0e3a72f3a81ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 94c4b45f0018ac30b45b9922625d7ca25f7c28e8ff7506f831ad70e246179812e55b66b794611dc2f86a22e41ba0f6f7f53087f0bc8e0ddfc7d12ce81f108593
|
7
|
+
data.tar.gz: e957db51d157857e99a2b69138d678d0375ad27e98c594c30fff19e355a628d5a16d36f09d5b88b646c3c9af8d4cba687c04ab7f652ff87e827a6743f70d6ade
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
plunk (0.0.0)
|
5
|
+
json
|
6
|
+
parslet
|
7
|
+
rest-client
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
blankslate (2.1.2.4)
|
13
|
+
diff-lcs (1.2.5)
|
14
|
+
json (1.8.1)
|
15
|
+
mime-types (2.0)
|
16
|
+
parslet (1.5.0)
|
17
|
+
blankslate (~> 2.0)
|
18
|
+
rest-client (1.6.7)
|
19
|
+
mime-types (>= 1.16)
|
20
|
+
rspec (2.14.1)
|
21
|
+
rspec-core (~> 2.14.0)
|
22
|
+
rspec-expectations (~> 2.14.0)
|
23
|
+
rspec-mocks (~> 2.14.0)
|
24
|
+
rspec-core (2.14.7)
|
25
|
+
rspec-expectations (2.14.4)
|
26
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
27
|
+
rspec-mocks (2.14.4)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
plunk!
|
34
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Elbii
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/lib/plunk.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
# Wrapper for Elasticsearch API
|
5
|
+
class Plunk::Elasticsearch
|
6
|
+
attr_accessor :host, :port, :fields
|
7
|
+
|
8
|
+
def initialize(opts={})
|
9
|
+
@scheme = opts[:scheme] || "http"
|
10
|
+
@host = opts[:host] || "localhost"
|
11
|
+
@port = opts[:port] || "9200"
|
12
|
+
@size = opts[:size] || "10000"
|
13
|
+
@endpoint = "#{@scheme}://#{@host}:#{@port}"
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get list of all field mappings stored in ES
|
17
|
+
# TODO: cache response from ES
|
18
|
+
def available_fields
|
19
|
+
uri = URI.escape "#{@endpoint}/_mapping"
|
20
|
+
result = JSON.parse(RestClient.get(uri))
|
21
|
+
|
22
|
+
@fields = {}
|
23
|
+
Plunk::Elasticsearch.nested_values(result, 'properties')
|
24
|
+
|
25
|
+
@fields
|
26
|
+
end
|
27
|
+
|
28
|
+
def active_record_errors_for(query)
|
29
|
+
return " can't be blank." if query.blank?
|
30
|
+
|
31
|
+
uri = URI.escape "#{@endpoint}/_validate/query?explain=true"
|
32
|
+
response = RestClient.post(uri, build_ES_validator(query))
|
33
|
+
|
34
|
+
json = JSON.parse(response)
|
35
|
+
|
36
|
+
json['valid'] ? nil : json['explanations'].collect { |exp| exp['error'] }
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# UTILITY METHODS
|
41
|
+
#
|
42
|
+
def self.search(query)
|
43
|
+
uri = URI.escape "#{@endpoint}/_search?size=#{@size}"
|
44
|
+
|
45
|
+
RestClient.post uri, build_ES_query(query)
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns all values for all occurences of the given nested key
|
49
|
+
def self.nested_values(hash, key)
|
50
|
+
hash.each do |k, v|
|
51
|
+
if k == key
|
52
|
+
@fields.merge! v
|
53
|
+
else
|
54
|
+
nested_values(v, key) if v.is_a? Hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# nested field matcher
|
60
|
+
def self.extract_values(hash, keys)
|
61
|
+
@vals ||= []
|
62
|
+
|
63
|
+
hash.each_pair do |k, v|
|
64
|
+
if v.is_a? Hash
|
65
|
+
extract_values(v, keys)
|
66
|
+
elsif v.is_a? Array
|
67
|
+
v.flatten!
|
68
|
+
if v.first.is_a? Hash
|
69
|
+
v.each { |el| extract_values(el, keys) }
|
70
|
+
elsif keys.include? k
|
71
|
+
@vals += v
|
72
|
+
end
|
73
|
+
elsif keys.include? k
|
74
|
+
@vals << v
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
return @vals
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.build_ES_validator(query)
|
82
|
+
if valid_json? query
|
83
|
+
# Strip the top-level "query" paramter since ES doesn't expect it
|
84
|
+
JSON.parse(query)['query'].to_json
|
85
|
+
else
|
86
|
+
<<-END
|
87
|
+
{
|
88
|
+
"query_string": {
|
89
|
+
"query": "#{query}"
|
90
|
+
}
|
91
|
+
}
|
92
|
+
END
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.build_ES_query(query)
|
97
|
+
if valid_json? query
|
98
|
+
query
|
99
|
+
else
|
100
|
+
<<-END
|
101
|
+
{
|
102
|
+
"query": {
|
103
|
+
"query_string": {
|
104
|
+
"query": "#{query}"
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
END
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.valid_json?(json)
|
113
|
+
begin
|
114
|
+
JSON.parse json
|
115
|
+
true
|
116
|
+
rescue JSON::ParserError
|
117
|
+
false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
data/lib/plunk/parser.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
class Plunk::Parser < Parslet::Parser
|
4
|
+
# Single character rules
|
5
|
+
rule(:lparen) { str('(') >> space? }
|
6
|
+
rule(:rparen) { str(')') >> space? }
|
7
|
+
rule(:comma) { str(',') >> space? }
|
8
|
+
rule(:digit) { match('[0-9]') }
|
9
|
+
rule(:space) { match('\s').repeat(1) }
|
10
|
+
rule(:space?) { space.maybe }
|
11
|
+
|
12
|
+
# Numbers
|
13
|
+
rule(:integer) { str('-').maybe >> digit.repeat(1) >> space? }
|
14
|
+
rule(:float) {
|
15
|
+
str('-').maybe >> digit.repeat(1) >> str('.') >> digit.repeat(1) >> space?
|
16
|
+
}
|
17
|
+
rule(:number) { integer | float }
|
18
|
+
|
19
|
+
# Field / value
|
20
|
+
rule(:identifier) { match['_@a-zA-Z.'].repeat(1) }
|
21
|
+
rule(:wildcard) { match('[a-zA-Z0-9.*]').repeat(1) }
|
22
|
+
rule(:searchop) { match('[=]').as(:op) >> space? }
|
23
|
+
|
24
|
+
# boolean operators search
|
25
|
+
rule(:concatop) { (str('OR') | str('AND')) >> space? }
|
26
|
+
rule(:operator) { match('[|]').as(:op) >> space? }
|
27
|
+
rule(:timerange) {
|
28
|
+
integer.as(:quantity) >> match('s|m|h|d|w').as(:quantifier)
|
29
|
+
}
|
30
|
+
|
31
|
+
# Grammar parts
|
32
|
+
rule(:rhs) {
|
33
|
+
(regex | wildcard | integer | subsearch |
|
34
|
+
(lparen >> (space? >> (wildcard | integer) >>
|
35
|
+
(space >> concatop).maybe).repeat(1) >> rparen))
|
36
|
+
}
|
37
|
+
|
38
|
+
rule(:regex) {
|
39
|
+
str('/') >> match('[^/]').repeat >> str('/')
|
40
|
+
}
|
41
|
+
|
42
|
+
rule(:search) {
|
43
|
+
identifier.as(:field) >> space? >> searchop >> space? >>
|
44
|
+
rhs.as(:value) | rhs.as(:match)
|
45
|
+
}
|
46
|
+
|
47
|
+
rule(:last) {
|
48
|
+
str("last") >> space >> timerange.as(:timerange) >> space >>
|
49
|
+
search.as(:search)
|
50
|
+
}
|
51
|
+
|
52
|
+
rule(:binaryop) {
|
53
|
+
(paren | search).as(:left) >> space? >> operator >> job.as(:right)
|
54
|
+
}
|
55
|
+
|
56
|
+
rule(:subsearch) {
|
57
|
+
str('`') >> space? >> nested_search >> str('`')
|
58
|
+
}
|
59
|
+
|
60
|
+
rule(:nested_search) {
|
61
|
+
match('[^|]').repeat.as(:term) >> str('|') >> space? >>
|
62
|
+
match('[^`]').repeat.as(:extractors)
|
63
|
+
}
|
64
|
+
|
65
|
+
rule(:joined_search) {
|
66
|
+
|
67
|
+
}
|
68
|
+
|
69
|
+
rule(:paren) {
|
70
|
+
lparen >> space? >> job >> space? >> rparen
|
71
|
+
}
|
72
|
+
|
73
|
+
rule(:job) {
|
74
|
+
binaryop | paren | last | search
|
75
|
+
}
|
76
|
+
|
77
|
+
# root :job
|
78
|
+
rule(:plunk_query) {
|
79
|
+
job >> (space >> job).repeat
|
80
|
+
}
|
81
|
+
|
82
|
+
root :plunk_query
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'elasticsearch'
|
2
|
+
|
3
|
+
class Plunk::ResultSet
|
4
|
+
attr_accessor :query
|
5
|
+
|
6
|
+
def initialize(opts=nil)
|
7
|
+
if opts
|
8
|
+
@query = { query: { }}
|
9
|
+
|
10
|
+
if opts[:query_string]
|
11
|
+
@query[:query][:query_string] = { query: opts[:query_string] }
|
12
|
+
end
|
13
|
+
|
14
|
+
if opts[:start_time] and opts[:end_time]
|
15
|
+
@query[:query][:range] = {
|
16
|
+
'@timestamp' => {
|
17
|
+
gte: opts[:start_time],
|
18
|
+
lte: opts[:end_time]
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def *(rs)
|
26
|
+
self.join(rs)
|
27
|
+
end
|
28
|
+
|
29
|
+
def join(rs)
|
30
|
+
ResultSet.new("[ #{@query} * #{rs.query} ]")
|
31
|
+
end
|
32
|
+
|
33
|
+
def eval
|
34
|
+
return unless @query
|
35
|
+
Elasticsearch.search(@query.to_json)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
class Plunk::Transformer < Parslet::Transform
|
4
|
+
|
5
|
+
rule(match: simple(:value)) do
|
6
|
+
ResultSet.new(query_string: "#{value}")
|
7
|
+
end
|
8
|
+
|
9
|
+
rule(
|
10
|
+
field: simple(:field),
|
11
|
+
value: {
|
12
|
+
term: simple(:term),
|
13
|
+
extractors: simple(:extractors)
|
14
|
+
},
|
15
|
+
op: '=') do
|
16
|
+
|
17
|
+
rs = ResultSet.new(query_string: "#{field}:#{term}")
|
18
|
+
|
19
|
+
json = JSON.parse rs.eval
|
20
|
+
values = Elasticsearch.extract_values json, extractors.to_s.split(',')
|
21
|
+
|
22
|
+
if values.empty?
|
23
|
+
ResultSet.new
|
24
|
+
else
|
25
|
+
ResultSet.new(query_string: "(#{values.uniq.join(' OR ')})")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
rule(field: simple(:field), value: simple(:value), op: '=') do
|
30
|
+
ResultSet.new(query_string: "#{field}:#{value}")
|
31
|
+
end
|
32
|
+
|
33
|
+
rule(
|
34
|
+
search: simple(:query),
|
35
|
+
timerange: {
|
36
|
+
quantity: simple(:quantity),
|
37
|
+
quantifier: simple(:quantifier)
|
38
|
+
}) do
|
39
|
+
|
40
|
+
int_quantity = quantity.to_s.to_i
|
41
|
+
|
42
|
+
start_time =
|
43
|
+
case quantifier
|
44
|
+
when 's'
|
45
|
+
int_quantity.seconds.ago
|
46
|
+
when 'm'
|
47
|
+
int_quantity.minutes.ago
|
48
|
+
when 'h'
|
49
|
+
int_quantity.hours.ago
|
50
|
+
when 'd'
|
51
|
+
int_quantity.days.ago
|
52
|
+
when 'w'
|
53
|
+
int_quantity.weeks.ago
|
54
|
+
end
|
55
|
+
|
56
|
+
end_time = Time.now.utc.to_datetime
|
57
|
+
|
58
|
+
ResultSet.new(
|
59
|
+
query_string: query,
|
60
|
+
start_time: start_time,
|
61
|
+
end_time: end_time)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/plunk.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "plunk"
|
3
|
+
s.version = "0.0.0"
|
4
|
+
s.date = "2013-11-26"
|
5
|
+
s.add_runtime_dependency "json"
|
6
|
+
s.add_runtime_dependency "parslet"
|
7
|
+
s.add_runtime_dependency "rest-client"
|
8
|
+
s.add_development_dependency "rspec"
|
9
|
+
s.summary = "Elasticsearch query language"
|
10
|
+
s.description = "Human-friendly query language for Elasticsearch"
|
11
|
+
s.authors = ["Ram Mehta", "Jamil Bou Kheir"]
|
12
|
+
s.email = ["jamil@elbii.com", "ram.mehta@gmail.com"]
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.homepage = "https://github.com/elbii/plunk"
|
15
|
+
s.license = "MIT"
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'plunk'
|
2
|
+
require 'plunk/elasticsearch'
|
3
|
+
|
4
|
+
describe Plunk::Elasticsearch do
|
5
|
+
before :all do
|
6
|
+
@elasticsearch = Plunk::Elasticsearch.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'test field mapping' do
|
10
|
+
it 'should successfully list all fields' do
|
11
|
+
fields = @elasticsearch.available_fields
|
12
|
+
expect(fields).to be_a Hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'plunk'
|
2
|
+
require 'plunk/parser'
|
3
|
+
|
4
|
+
# Print ascii_tree when exception occurs
|
5
|
+
class Plunk::ParserWrapper < Plunk::Parser
|
6
|
+
def parse(query)
|
7
|
+
begin
|
8
|
+
super(query)
|
9
|
+
rescue Parslet::ParseFailed => failure
|
10
|
+
puts failure.cause.ascii_tree
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Plunk::Parser do
|
16
|
+
before :all do
|
17
|
+
@parser = Plunk::ParserWrapper.new
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'single-query searches' do
|
21
|
+
it 'should parse a single keyword' do
|
22
|
+
@parsed = @parser.parse 'bar'
|
23
|
+
expect(@parsed[:match].to_s).to eq 'bar'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should parse a single field/value combo' do
|
27
|
+
@parsed = @parser.parse 'tshark.http.@src_ip=bar'
|
28
|
+
expect(@parsed[:field].to_s).to eq 'tshark.http.@src_ip'
|
29
|
+
expect(@parsed[:value].to_s).to eq 'bar'
|
30
|
+
expect(@parsed[:op].to_s).to eq '='
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should parse a single boolean expression' do
|
34
|
+
@parsed = @parser.parse '(bar OR car)'
|
35
|
+
expect(@parsed[:match].to_s).to eq '(bar OR car)'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should parse a single field / value complex boolean expression' do
|
39
|
+
@parsed = @parser.parse 'ids.attackers=(bar OR car)'
|
40
|
+
expect(@parsed[:field].to_s).to eq 'ids.attackers'
|
41
|
+
expect(@parsed[:value].to_s).to eq '(bar OR car)'
|
42
|
+
expect(@parsed[:op].to_s).to eq '='
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should parse a single field / parenthesized value' do
|
46
|
+
@parsed = @parser.parse 'ids.attacker=(10.150.44.195)'
|
47
|
+
expect(@parsed[:field].to_s).to eq 'ids.attacker'
|
48
|
+
expect(@parsed[:value].to_s).to eq '(10.150.44.195)'
|
49
|
+
expect(@parsed[:op].to_s).to eq '='
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should parse a basic regex search' do
|
53
|
+
@parsed = @parser.parse 'foo=/blah foo/'
|
54
|
+
expect(@parsed[:field].to_s).to eq 'foo'
|
55
|
+
expect(@parsed[:value].to_s).to eq '/blah foo/'
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'the last command' do
|
59
|
+
it 'should parse last command with a search' do
|
60
|
+
@parsed = @parser.parse 'last 24w tshark.@src_ip = bar'
|
61
|
+
expect(@parsed[:timerange][:quantity].to_s).to eq '24'
|
62
|
+
expect(@parsed[:timerange][:quantifier].to_s).to eq 'w'
|
63
|
+
expect(@parsed[:search][:field].to_s).to eq 'tshark.@src_ip'
|
64
|
+
expect(@parsed[:search][:value].to_s).to eq 'bar'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'chained searches' do
|
69
|
+
it 'should parse last command with a regex' do
|
70
|
+
@parsed = @parser.parse 'last 24w foo=/blah/'
|
71
|
+
expect(@parsed[:timerange][:quantity].to_s).to eq '24'
|
72
|
+
expect(@parsed[:timerange][:quantifier].to_s).to eq 'w'
|
73
|
+
expect(@parsed[:search][:field].to_s).to eq 'foo'
|
74
|
+
expect(@parsed[:search][:value].to_s).to eq '/blah/'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should parse last command with boolean' do
|
79
|
+
@parsed = @parser.parse 'last 1h (foo OR bar)'
|
80
|
+
expect(@parsed[:search][:match].to_s).to eq '(foo OR bar)'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should parse key/value with regex' do
|
84
|
+
@parsed = @parser.parse 'foo=bar fe.ip=/whodunnit/'
|
85
|
+
expect(@parsed[0][:field].to_s).to eq 'foo'
|
86
|
+
expect(@parsed[0][:value].to_s).to eq 'bar'
|
87
|
+
expect(@parsed[1][:field].to_s).to eq 'fe.ip'
|
88
|
+
expect(@parsed[1][:value].to_s).to eq '/whodunnit/'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'nested search' do
|
93
|
+
it 'should parse the nested search' do
|
94
|
+
@parsed = @parser.parse 'tshark.len = ` 226 | tshark.frame.time_epoch,tshark.ip.src`'
|
95
|
+
expect(@parsed[:field].to_s).to eq 'tshark.len'
|
96
|
+
expect(@parsed[:op].to_s).to eq '='
|
97
|
+
expect(@parsed[:value][:term].to_s).to eq '226 '
|
98
|
+
expect(@parsed[:value][:extractors].to_s).to eq 'tshark.frame.time_epoch,tshark.ip.src'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plunk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ram Mehta
|
8
|
+
- Jamil Bou Kheir
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: parslet
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rest-client
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: Human-friendly query language for Elasticsearch
|
71
|
+
email:
|
72
|
+
- jamil@elbii.com
|
73
|
+
- ram.mehta@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- LICENSE
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- lib/plunk.rb
|
85
|
+
- lib/plunk/elasticsearch.rb
|
86
|
+
- lib/plunk/parser.rb
|
87
|
+
- lib/plunk/result_set.rb
|
88
|
+
- lib/plunk/transformer.rb
|
89
|
+
- plunk.gemspec
|
90
|
+
- spec/elasticsearch_spec.rb
|
91
|
+
- spec/parser_spec.rb
|
92
|
+
- spec/result_set_spec.rb
|
93
|
+
- spec/transformer_spec.rb
|
94
|
+
homepage: https://github.com/elbii/plunk
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.1.11
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Elasticsearch query language
|
118
|
+
test_files: []
|