shades 0.15 → 0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/Rakefile +4 -0
- data/lib/shades.rb +7 -7
- data/lib/{cube.rb → shades/cube.rb} +0 -0
- data/lib/{formatter.rb → shades/formatter.rb} +0 -0
- data/lib/{histo.rb → shades/histo.rb} +0 -0
- data/lib/{model.rb → shades/model.rb} +0 -0
- data/lib/shades/query.treetop +34 -0
- data/lib/shades/queryparser.rb +96 -0
- data/lib/shades/shades.rb +10 -0
- data/lib/{stats.rb → shades/stats.rb} +0 -0
- data/lib/{streamparser.rb → shades/streamparser.rb} +0 -0
- data/shades.gemspec +3 -1
- data/spec/queryparser_spec.rb +48 -0
- data/spec/spec_helper.rb +9 -0
- metadata +38 -17
- data/lib/queryparser.rb +0 -167
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MzlmYmQ3MTc0MTQ5NWM3MjNlMDBhNzU5MGIzMDM2ODk4NmQ0OTcyOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OWM3ZjJjNDE5NzI4OGU0MWM3NmRhNzAzOWI5NGVjMmNjYTQ3ZmEwOQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MGVkM2YxOWEwNzgyMWM2NzBlNTViOGY0ZjQ5YjEyMDg2OTY2ZWI0YmFjNWI3
|
10
|
+
YTNiOTM2OGI1YjllNmQ3YzM0NjM4ZTA0NDIzYjQzOGI3NDU2ZDQzODdmOWI3
|
11
|
+
M2IwMjUxMzJkNmRjODkzMjFmZTk5ODRmNWUwZTYyNzVjNzFmNjA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTc0YzY2NzA1MDgzZGFkNGVhNTY4Njc0ODBmYjY3MzczOTQzN2RhZTdlNjJj
|
14
|
+
ZTZhMDZlMmZjMWNlYTJiZWQ4ODY4YjczYjFmZTUwOGM2NmIzZjBmNWI4ZDQw
|
15
|
+
NjI0NmZhMGM1OGI1Nzg4ZDIyZjUyZTIwNTI2YmViY2Q0YjllY2M=
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Rakefile
CHANGED
data/lib/shades.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'model'
|
2
|
-
require 'cube'
|
3
|
-
require 'histo'
|
4
|
-
require 'stats'
|
5
|
-
require 'streamparser'
|
6
|
-
require 'queryparser'
|
7
|
-
require 'formatter'
|
1
|
+
require 'shades/model'
|
2
|
+
require 'shades/cube'
|
3
|
+
require 'shades/histo'
|
4
|
+
require 'shades/stats'
|
5
|
+
require 'shades/streamparser'
|
6
|
+
require 'shades/queryparser'
|
7
|
+
require 'shades/formatter'
|
8
8
|
|
9
9
|
module Shades
|
10
10
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Shades
|
2
|
+
grammar QueryGrammar
|
3
|
+
rule query
|
4
|
+
space* rollup_list
|
5
|
+
space* 'by' space* categorization_list:identifier_list
|
6
|
+
optional_sorting:( space* 'order by' space* sorting_list:identifier_list )?
|
7
|
+
space* <QueryNode>
|
8
|
+
end
|
9
|
+
|
10
|
+
rule space
|
11
|
+
[\s]+
|
12
|
+
end
|
13
|
+
|
14
|
+
rule rollup_list
|
15
|
+
rollup rest_rollups:( ',' space* rollup )* <RollupListNode>
|
16
|
+
end
|
17
|
+
|
18
|
+
rule rollup
|
19
|
+
stat_type '(' measures:identifier_list ')' <RollupNode>
|
20
|
+
end
|
21
|
+
|
22
|
+
rule stat_type
|
23
|
+
[\w]+ <StatTypeNode>
|
24
|
+
end
|
25
|
+
|
26
|
+
rule identifier_list
|
27
|
+
identifier rest_identifiers:( ',' space* identifier )* <IdentifierListNode>
|
28
|
+
end
|
29
|
+
|
30
|
+
rule identifier
|
31
|
+
[\w]+ <IdentifierNode>
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "treetop"
|
2
|
+
|
3
|
+
module Shades
|
4
|
+
module QueryGrammar
|
5
|
+
class QueryNode < Treetop::Runtime::SyntaxNode
|
6
|
+
def rollup_nodes
|
7
|
+
rollup_list.to_a
|
8
|
+
end
|
9
|
+
|
10
|
+
def categorization_nodes
|
11
|
+
categorization_list.to_a
|
12
|
+
end
|
13
|
+
|
14
|
+
def sorting_nodes
|
15
|
+
if optional_sorting.respond_to?(:sorting_list)
|
16
|
+
optional_sorting.sorting_list.to_a
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class RollupListNode < Treetop::Runtime::SyntaxNode
|
24
|
+
def to_a
|
25
|
+
[rollup] + rest_rollups.elements.map do |comma_and_rollup|
|
26
|
+
comma_and_rollup.rollup
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class IdentifierListNode < Treetop::Runtime::SyntaxNode
|
32
|
+
def to_a
|
33
|
+
[identifier] + rest_identifiers.elements.map do |comma_and_identifier|
|
34
|
+
comma_and_identifier.identifier
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class RollupNode < Treetop::Runtime::SyntaxNode
|
40
|
+
def stat_type_name
|
41
|
+
stat_type.text_value
|
42
|
+
end
|
43
|
+
|
44
|
+
def measure_names
|
45
|
+
measures.to_a.map(&:text_value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class StatTypeNode < Treetop::Runtime::SyntaxNode
|
50
|
+
end
|
51
|
+
|
52
|
+
class IdentifierNode < Treetop::Runtime::SyntaxNode
|
53
|
+
def name
|
54
|
+
text_value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class QueryParser
|
60
|
+
def self.parse(qs, query_factory = Query)
|
61
|
+
@parser_class ||= Treetop.load(File.join(File.dirname(__FILE__), "query.treetop"))
|
62
|
+
|
63
|
+
parser = @parser_class.new
|
64
|
+
unless query_node = parser.parse(qs)
|
65
|
+
raise ArgumentError, "Cannot parse query at character #{parser.index}"
|
66
|
+
end
|
67
|
+
|
68
|
+
query_factory.new(
|
69
|
+
:rollups => rollups_from_nodes(query_node.rollup_nodes),
|
70
|
+
:categorizations => categorizations_from_nodes(query_node.categorization_nodes),
|
71
|
+
:sorting => sorting_from_nodes(query_node.sorting_nodes)
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.rollups_from_nodes(nodes)
|
76
|
+
nodes.flat_map { |node|
|
77
|
+
stat = Stats::StatsType.get(node.stat_type_name)
|
78
|
+
node.measure_names.map { |measure_name|
|
79
|
+
{ :stat => stat, :measure => measure_name }
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.categorizations_from_nodes(nodes)
|
85
|
+
nodes.map { |categorization|
|
86
|
+
categorization.name
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.sorting_from_nodes(nodes)
|
91
|
+
nodes.map { |sorting|
|
92
|
+
{ :key => sorting.name, :asc => true }
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
File without changes
|
File without changes
|
data/shades.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'shades'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '0.16'
|
4
4
|
|
5
5
|
s.summary = "Get a new perspective on your data. In-memory data cubing of event data for Ruby."
|
6
6
|
s.description = <<-EOF
|
@@ -12,6 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.require_paths = %w[lib]
|
13
13
|
s.executables = ["shades", "histo"]
|
14
14
|
|
15
|
+
s.add_dependency 'treetop', '~> 1.4.14'
|
16
|
+
|
15
17
|
s.add_development_dependency 'rake-compiler'
|
16
18
|
s.add_development_dependency 'rspec'
|
17
19
|
s.add_development_dependency 'rdoc'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Shades
|
5
|
+
describe QueryParser do
|
6
|
+
it "parses rollups out of the query expression" do
|
7
|
+
query = QueryParser.parse("sum(amount) by transactionid", OpenStruct)
|
8
|
+
|
9
|
+
expect(query.rollups.length).to eq(1)
|
10
|
+
expect(query.rollups[0][:measure]).to eq("amount")
|
11
|
+
expect(query.rollups[0][:stat]).to eq(Stats::SUM)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "parses multiple rollups out of the query expression" do
|
15
|
+
query = QueryParser.parse("sum(amount), max(quantity) by transactionid", OpenStruct)
|
16
|
+
|
17
|
+
expect(query.rollups.length).to eq(2)
|
18
|
+
expect(query.rollups[0][:measure]).to eq("amount")
|
19
|
+
expect(query.rollups[1][:measure]).to eq("quantity")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "parses categorizations out of the query expression" do
|
23
|
+
query = QueryParser.parse("sum(amount) by transactionid", OpenStruct)
|
24
|
+
expect(query.categorizations).to eq(["transactionid"])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "parses multiple categorizations out of the query expression" do
|
28
|
+
query = QueryParser.parse("sum(amount) by customer, item", OpenStruct)
|
29
|
+
expect(query.categorizations).to eq(["customer", "item"])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "parses sort descriptions out of the query expression" do
|
33
|
+
query = QueryParser.parse("sum(amount) by transactionid order by amount", OpenStruct)
|
34
|
+
|
35
|
+
expect(query.sorting.length).to eq(1)
|
36
|
+
expect(query.sorting[0][:key]).to eq("amount")
|
37
|
+
expect(query.sorting[0][:asc]).to eq(true)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "parses multiple sort descriptions out of the query expression" do
|
41
|
+
query = QueryParser.parse("sum(amount) by transactionid order by amount, customerid", OpenStruct)
|
42
|
+
|
43
|
+
expect(query.sorting.length).to eq(2)
|
44
|
+
expect(query.sorting[0][:key]).to eq("amount")
|
45
|
+
expect(query.sorting[1][:key]).to eq("customerid")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shades
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.16'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dietrich Featherston
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: treetop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.14
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.14
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rake-compiler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -63,31 +77,38 @@ executables:
|
|
63
77
|
extensions: []
|
64
78
|
extra_rdoc_files:
|
65
79
|
- README.md
|
66
|
-
- lib/cube.rb
|
67
|
-
- lib/formatter.rb
|
68
|
-
- lib/histo.rb
|
69
|
-
- lib/model.rb
|
70
|
-
- lib/queryparser.rb
|
80
|
+
- lib/shades/cube.rb
|
81
|
+
- lib/shades/formatter.rb
|
82
|
+
- lib/shades/histo.rb
|
83
|
+
- lib/shades/model.rb
|
84
|
+
- lib/shades/queryparser.rb
|
85
|
+
- lib/shades/shades.rb
|
86
|
+
- lib/shades/stats.rb
|
87
|
+
- lib/shades/streamparser.rb
|
71
88
|
- lib/shades.rb
|
72
|
-
- lib/stats.rb
|
73
|
-
- lib/streamparser.rb
|
74
89
|
files:
|
75
90
|
- .gitignore
|
91
|
+
- .rspec
|
92
|
+
- Gemfile
|
76
93
|
- LICENSE
|
77
94
|
- README.md
|
78
95
|
- Rakefile
|
79
96
|
- bin/histo
|
80
97
|
- bin/shades
|
81
|
-
- lib/cube.rb
|
82
|
-
- lib/formatter.rb
|
83
|
-
- lib/histo.rb
|
84
|
-
- lib/model.rb
|
85
|
-
- lib/queryparser.rb
|
86
98
|
- lib/shades.rb
|
87
|
-
- lib/
|
88
|
-
- lib/
|
99
|
+
- lib/shades/cube.rb
|
100
|
+
- lib/shades/formatter.rb
|
101
|
+
- lib/shades/histo.rb
|
102
|
+
- lib/shades/model.rb
|
103
|
+
- lib/shades/query.treetop
|
104
|
+
- lib/shades/queryparser.rb
|
105
|
+
- lib/shades/shades.rb
|
106
|
+
- lib/shades/stats.rb
|
107
|
+
- lib/shades/streamparser.rb
|
89
108
|
- script/build-ctags
|
90
109
|
- shades.gemspec
|
110
|
+
- spec/queryparser_spec.rb
|
111
|
+
- spec/spec_helper.rb
|
91
112
|
- transactions.txt
|
92
113
|
homepage: https://github.com/d2fn/shades-rb
|
93
114
|
licenses:
|
@@ -115,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
136
|
version: '0'
|
116
137
|
requirements: []
|
117
138
|
rubyforge_project: shades
|
118
|
-
rubygems_version: 2.0.
|
139
|
+
rubygems_version: 2.0.7
|
119
140
|
signing_key:
|
120
141
|
specification_version: 4
|
121
142
|
summary: Get a new perspective on your data. In-memory data cubing of event data for
|
data/lib/queryparser.rb
DELETED
@@ -1,167 +0,0 @@
|
|
1
|
-
module Shades
|
2
|
-
|
3
|
-
## queries are of the form:
|
4
|
-
## <stat-type> <measure>[, [<stat-type>] <measure>]* by <dimension>[, <dimension>] order by <dimension|measure>[, <dimension|measure>]*
|
5
|
-
## for example, to get the mean load1, and load5 measures by unique combination of host role and kernel version:
|
6
|
-
## mean load1, load5 by role, kernelversion
|
7
|
-
class QueryParser
|
8
|
-
|
9
|
-
def self.parse(qs)
|
10
|
-
parts = qs.scan(/[\w\.]+/)
|
11
|
-
tokens = []
|
12
|
-
t = BeginRollupToken.new
|
13
|
-
parts.each do |p|
|
14
|
-
t = t.emit(p)
|
15
|
-
tokens << t
|
16
|
-
end
|
17
|
-
rollups = rollups_pass(tokens)
|
18
|
-
categorizations = categorizations_pass(tokens)
|
19
|
-
sorting = sorting_pass(tokens)
|
20
|
-
Query.new(:categorizations => categorizations, :rollups => rollups, :sorting => sorting)
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.rollups_pass(tokens)
|
24
|
-
stat = nil
|
25
|
-
r = []
|
26
|
-
tokens.each do |t|
|
27
|
-
if t.kind_of? StatTypeToken
|
28
|
-
stat = t.stat
|
29
|
-
elsif t.kind_of? MeasureRefToken
|
30
|
-
r << { :measure => t.text, :stat => stat }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
r
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.categorizations_pass(tokens)
|
37
|
-
d = []
|
38
|
-
tokens.each do |t|
|
39
|
-
if t.kind_of? DimensionRefToken
|
40
|
-
d << t.text
|
41
|
-
end
|
42
|
-
end
|
43
|
-
d
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.sorting_pass(tokens)
|
47
|
-
s = []
|
48
|
-
tokens.each do |t|
|
49
|
-
if t.kind_of? SortKeyToken
|
50
|
-
s << { :key => t.text, :asc => true }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
s
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class Token
|
58
|
-
attr_accessor :text
|
59
|
-
def initialize(s)
|
60
|
-
@text = s
|
61
|
-
end
|
62
|
-
def to_s
|
63
|
-
@text
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
class BeginRollupToken < Token
|
68
|
-
def initialize
|
69
|
-
super("<begin>")
|
70
|
-
end
|
71
|
-
def emit(s)
|
72
|
-
StatTypeToken::parse(s)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
class StatTypeToken < Token
|
77
|
-
attr_accessor :stat
|
78
|
-
def initialize(name, stat)
|
79
|
-
super(name)
|
80
|
-
@stat = stat
|
81
|
-
end
|
82
|
-
|
83
|
-
## context free parsing of the next token to be called by the prior token
|
84
|
-
def self.parse(s)
|
85
|
-
stat = Stats::StatsType::get(s)
|
86
|
-
if stat.nil?
|
87
|
-
nil
|
88
|
-
else
|
89
|
-
StatTypeToken.new(s, stat)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
## given the next string, parse and return the next token
|
94
|
-
def emit(s)
|
95
|
-
# a measure must always follow a stat
|
96
|
-
MeasureRefToken::parse(s)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
class MeasureRefToken < Token
|
101
|
-
def self.parse(s)
|
102
|
-
MeasureRefToken.new(s)
|
103
|
-
end
|
104
|
-
def emit(s)
|
105
|
-
if s.downcase.eql?("by")
|
106
|
-
BeginCategorizationToken::parse(s)
|
107
|
-
else
|
108
|
-
t = StatTypeToken::parse(s)
|
109
|
-
if !t.nil?
|
110
|
-
t
|
111
|
-
else
|
112
|
-
MeasureRefToken::parse(s)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
class BeginCategorizationToken < Token
|
119
|
-
def self.parse(s)
|
120
|
-
BeginCategorizationToken.new(s)
|
121
|
-
end
|
122
|
-
def emit(s)
|
123
|
-
DimensionRefToken::parse(s)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
class DimensionRefToken < Token
|
128
|
-
def self.parse(s)
|
129
|
-
DimensionRefToken.new(s)
|
130
|
-
end
|
131
|
-
def emit(s)
|
132
|
-
if s.downcase.eql?("order")
|
133
|
-
OrderToken::parse(s)
|
134
|
-
else
|
135
|
-
DimensionRefToken::parse(s)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
class OrderToken < Token
|
141
|
-
def self.parse(s)
|
142
|
-
OrderToken.new(s)
|
143
|
-
end
|
144
|
-
def emit(s)
|
145
|
-
# by
|
146
|
-
BeginSortingToken::parse(s)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
class BeginSortingToken < Token
|
151
|
-
def self.parse(s)
|
152
|
-
BeginSortingToken.new(s)
|
153
|
-
end
|
154
|
-
def emit(s)
|
155
|
-
SortKeyToken::parse(s)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
class SortKeyToken < Token
|
160
|
-
def self.parse(s)
|
161
|
-
SortKeyToken.new(s)
|
162
|
-
end
|
163
|
-
def emit(s)
|
164
|
-
SortKeyToken::parse(s)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|