shades 0.15 → 0.16
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.
- 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
|