skydb 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/bin/sky +85 -0
  2. data/lib/ext/hash.rb +11 -0
  3. data/lib/ext/treetop.rb +19 -0
  4. data/lib/skydb.rb +10 -3
  5. data/lib/skydb/client.rb +92 -28
  6. data/lib/skydb/import.rb +7 -0
  7. data/lib/skydb/import/importer.rb +258 -0
  8. data/lib/skydb/import/transforms/sky.yml +20 -0
  9. data/lib/skydb/import/transforms/snowplow.yml +1 -0
  10. data/lib/skydb/import/translator.rb +119 -0
  11. data/lib/skydb/message.rb +17 -12
  12. data/lib/skydb/message/create_table.rb +64 -0
  13. data/lib/skydb/message/delete_table.rb +66 -0
  14. data/lib/skydb/message/get_table.rb +74 -0
  15. data/lib/skydb/message/lookup.rb +79 -0
  16. data/lib/skydb/property.rb +5 -5
  17. data/lib/skydb/query.rb +198 -0
  18. data/lib/skydb/query/after.rb +103 -0
  19. data/lib/skydb/query/ast/selection_field_syntax_node.rb +26 -0
  20. data/lib/skydb/query/ast/selection_fields_syntax_node.rb +16 -0
  21. data/lib/skydb/query/ast/selection_group_syntax_node.rb +16 -0
  22. data/lib/skydb/query/ast/selection_groups_syntax_node.rb +16 -0
  23. data/lib/skydb/query/selection.rb +268 -0
  24. data/lib/skydb/query/selection_field.rb +74 -0
  25. data/lib/skydb/query/selection_fields_grammar.treetop +46 -0
  26. data/lib/skydb/query/selection_fields_parse_error.rb +30 -0
  27. data/lib/skydb/query/selection_group.rb +57 -0
  28. data/lib/skydb/query/selection_groups_grammar.treetop +31 -0
  29. data/lib/skydb/query/selection_groups_parse_error.rb +30 -0
  30. data/lib/skydb/query/validation_error.rb +8 -0
  31. data/lib/skydb/table.rb +69 -0
  32. data/lib/skydb/version.rb +1 -1
  33. data/test/import/importer_test.rb +42 -0
  34. data/test/import/translator_test.rb +88 -0
  35. data/test/message/add_event_message_test.rb +1 -1
  36. data/test/message/add_property_message_test.rb +2 -2
  37. data/test/message/create_table_message_test.rb +34 -0
  38. data/test/message/delete_table_message_test.rb +34 -0
  39. data/test/message/get_table_message_test.rb +19 -0
  40. data/test/message/lookup_message_test.rb +27 -0
  41. data/test/message_test.rb +1 -1
  42. data/test/query/after_test.rb +71 -0
  43. data/test/query/selection_test.rb +273 -0
  44. data/test/query_test.rb +156 -0
  45. data/test/test_helper.rb +3 -0
  46. metadata +129 -3
@@ -0,0 +1,74 @@
1
+ class SkyDB
2
+ # The selection field is a single field in the selection part of the query.
3
+ # This can include a simple property or it can be the aggregation of a
4
+ # property. Fields can also be aliased to a different name that is returned.
5
+ # This is typically useful when naming aggregated fields.
6
+ class Query
7
+ class SelectionField
8
+ ##########################################################################
9
+ #
10
+ # Constructor
11
+ #
12
+ ##########################################################################
13
+
14
+ def initialize(options={})
15
+ self.expression = options[:expression]
16
+ self.alias_name = options[:alias_name]
17
+ self.aggregation_type = options[:aggregation_type]
18
+ end
19
+
20
+
21
+ ##########################################################################
22
+ #
23
+ # Attributes
24
+ #
25
+ ##########################################################################
26
+
27
+ # The name of the property to select.
28
+ attr_accessor :expression
29
+
30
+ # The field name that is actually returned.
31
+ attr_accessor :alias_name
32
+
33
+ # The type of the aggregation used to process the property.
34
+ attr_accessor :aggregation_type
35
+
36
+ # The final computed name of the field. It is named after the alias name
37
+ # if provided, otherwise defaults to the expression. If neither the
38
+ # expression or alias are provided then the aggregation type is used.
39
+ def target_name
40
+ return alias_name || expression || aggregation_type.to_s
41
+ end
42
+
43
+ # The string used to access the expression.
44
+ def accessor(options={})
45
+ prefix = options.delete(:prefix) || 'cursor.event.'
46
+
47
+ if ["action_id", "timestamp"].index(expression)
48
+ return "#{prefix}#{expression}"
49
+ else
50
+ return "#{prefix}#{expression}()"
51
+ end
52
+ end
53
+
54
+
55
+ ##########################################################################
56
+ #
57
+ # Methods
58
+ #
59
+ ##########################################################################
60
+
61
+ ####################################
62
+ # Validation
63
+ ####################################
64
+
65
+ # Validates that the field is valid.
66
+ def validate!
67
+ # Expression must be present unless this is a COUNT().
68
+ if expression.to_s.length == 0 && aggregation_type != :count
69
+ raise SkyDB::Query::ValidationError.new("Invalid expression for selection field: '#{expression.to_s}'")
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,46 @@
1
+ grammar SelectionFieldsGrammar
2
+ rule root
3
+ fields <SkyDB::Query::Ast::SelectionFieldsSyntaxNode>
4
+ end
5
+
6
+ # Matches all selection fields.
7
+ rule fields
8
+ ws field (ws ',' ws field)* ws
9
+ end
10
+
11
+ # Matches a single field name as well as additional options such as an alias
12
+ # or an aggregation method.
13
+ rule field
14
+ aggregation_type '(' ws ')' ws alias_name <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
15
+ /
16
+ aggregation_type '(' ws ')' <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
17
+ /
18
+ aggregation_type '(' ws expression ws ')' ws alias_name <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
19
+ /
20
+ aggregation_type '(' ws expression ws ')' <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
21
+ /
22
+ expression ws alias_name <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
23
+ /
24
+ expression <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
25
+ end
26
+
27
+ rule expression
28
+ identifier
29
+ end
30
+
31
+ rule alias_name
32
+ identifier
33
+ end
34
+
35
+ rule aggregation_type
36
+ ([a-zA-Z]+)
37
+ end
38
+
39
+ rule identifier
40
+ ([a-zA-Z_] [a-zA-Z0-9_]*)
41
+ end
42
+
43
+ rule ws
44
+ (' ' / "\t" / "\n" / "\r")*
45
+ end
46
+ end
@@ -0,0 +1,30 @@
1
+ class SkyDB
2
+ class Query
3
+ class SelectionFieldsParseError < StandardError
4
+ ##########################################################################
5
+ #
6
+ # Constructor
7
+ #
8
+ ##########################################################################
9
+
10
+ def initialize(message, options={})
11
+ super(message)
12
+ @line = options[:line].to_i
13
+ @column = options[:column].to_i
14
+ end
15
+
16
+
17
+ ##########################################################################
18
+ #
19
+ # Attributes
20
+ #
21
+ ##########################################################################
22
+
23
+ # The line number that the error occurred on.
24
+ attr_reader :line
25
+
26
+ # The column number that the error occurred on.
27
+ attr_reader :column
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,57 @@
1
+ class SkyDB
2
+ # The selection group contains an expression by which to group selected
3
+ # data.
4
+ class Query
5
+ class SelectionGroup
6
+ ##########################################################################
7
+ #
8
+ # Constructor
9
+ #
10
+ ##########################################################################
11
+
12
+ def initialize(options={})
13
+ self.expression = options[:expression]
14
+ end
15
+
16
+
17
+ ##########################################################################
18
+ #
19
+ # Attributes
20
+ #
21
+ ##########################################################################
22
+
23
+ # The name of the expression to group by.
24
+ attr_accessor :expression
25
+
26
+ # The string used to access the expression.
27
+ def accessor(options={})
28
+ prefix = options.delete(:prefix) || 'cursor.event.'
29
+
30
+ if ["action_id", "timestamp"].index(expression)
31
+ return "#{prefix}#{expression}"
32
+ else
33
+ return "#{prefix}#{expression}()"
34
+ end
35
+ end
36
+
37
+
38
+ ##########################################################################
39
+ #
40
+ # Methods
41
+ #
42
+ ##########################################################################
43
+
44
+ ####################################
45
+ # Validation
46
+ ####################################
47
+
48
+ # Validates that the field is valid.
49
+ def validate!
50
+ # Expression must be present.
51
+ if expression.to_s.length == 0
52
+ raise SkyDB::Query::ValidationError.new("Invalid expression for selection group: '#{expression.to_s}'")
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,31 @@
1
+ grammar SelectionGroupsGrammar
2
+ rule root
3
+ groups <SkyDB::Query::Ast::SelectionGroupsSyntaxNode>
4
+ end
5
+
6
+ # Matches all selection groups.
7
+ rule groups
8
+ ws group (ws ',' ws group)* ws
9
+ end
10
+
11
+ # Matches a single group name and optional alias.
12
+ rule group
13
+ expression <SkyDB::Query::Ast::SelectionGroupSyntaxNode>
14
+ end
15
+
16
+ rule expression
17
+ identifier
18
+ end
19
+
20
+ rule alias_name
21
+ identifier
22
+ end
23
+
24
+ rule identifier
25
+ ([a-zA-Z_] [a-zA-Z0-9_]*)
26
+ end
27
+
28
+ rule ws
29
+ (' ' / "\t" / "\n" / "\r")*
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ class SkyDB
2
+ class Query
3
+ class SelectionGroupsParseError < StandardError
4
+ ##########################################################################
5
+ #
6
+ # Constructor
7
+ #
8
+ ##########################################################################
9
+
10
+ def initialize(message, options={})
11
+ super(message)
12
+ @line = options[:line].to_i
13
+ @column = options[:column].to_i
14
+ end
15
+
16
+
17
+ ##########################################################################
18
+ #
19
+ # Attributes
20
+ #
21
+ ##########################################################################
22
+
23
+ # The line number that the error occurred on.
24
+ attr_reader :line
25
+
26
+ # The column number that the error occurred on.
27
+ attr_reader :column
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ class SkyDB
2
+ class Query
3
+ # A validation error indicates that some part of a query is incomplete
4
+ # and that codegen could not occur.
5
+ class ValidationError < StandardError; end
6
+ end
7
+ end
8
+
@@ -0,0 +1,69 @@
1
+ class SkyDB
2
+ class Table
3
+ ##########################################################################
4
+ #
5
+ # Constants
6
+ #
7
+ ##########################################################################
8
+
9
+ DEFAULT_TABLET_COUNT = 4
10
+
11
+
12
+ ##########################################################################
13
+ #
14
+ # Constructor
15
+ #
16
+ ##########################################################################
17
+
18
+ # Initializes the table.
19
+ def initialize(name='', options={})
20
+ self.name = name
21
+ self.tablet_count = options[:tablet_count].to_i
22
+ end
23
+
24
+
25
+ ##########################################################################
26
+ #
27
+ # Attributes
28
+ #
29
+ ##########################################################################
30
+
31
+ ##################################
32
+ # Name
33
+ ##################################
34
+
35
+ # The name of the table.
36
+ attr_reader :name
37
+
38
+ def name=(value)
39
+ @name = value.to_s
40
+ end
41
+
42
+
43
+ ##################################
44
+ # Tablet count
45
+ ##################################
46
+
47
+ # The number of tablets the table has
48
+ attr_reader :tablet_count
49
+
50
+ def tablet_count=(value)
51
+ @tablet_count = value.to_i || DEFAULT_TABLET_COUNT
52
+ end
53
+
54
+
55
+ ##########################################################################
56
+ #
57
+ # Methods
58
+ #
59
+ ##########################################################################
60
+
61
+ # Encodes the table into MsgPack format.
62
+ def to_msgpack
63
+ return {
64
+ name: name,
65
+ tablet_count: tablet_count
66
+ }.to_msgpack
67
+ end
68
+ end
69
+ end
data/lib/skydb/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class SkyDB
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ class TestImporter < MiniTest::Unit::TestCase
4
+ def setup
5
+ @importer = SkyDB::Import::Importer.new()
6
+ @input = {
7
+ 'myString' => 'Hello',
8
+ 'foo' => 100
9
+ }
10
+ @output = {}
11
+ end
12
+
13
+
14
+ ######################################
15
+ # Transform File
16
+ ######################################
17
+
18
+ def test_load_simple_transform
19
+ @importer.load_transform(
20
+ <<-BLOCK.unindent
21
+ fields:
22
+ name: myString
23
+ BLOCK
24
+ )
25
+
26
+ assert_equal "myString", @importer.translators.first.input_field
27
+ assert_equal "name", @importer.translators.first.output_field
28
+ assert_equal "string", @importer.translators.first.format
29
+ end
30
+
31
+ def test_load_simple_proc
32
+ @importer.load_transform(
33
+ <<-BLOCK.unindent
34
+ fields:
35
+ name: "{ output['foo'] * 10 }"
36
+ BLOCK
37
+ )
38
+
39
+ assert_equal "name", @importer.translators.first.output_field
40
+ assert !@importer.translators.first.translate_function.nil?
41
+ end
42
+ end
@@ -0,0 +1,88 @@
1
+ require 'test_helper'
2
+
3
+ class TestTranslator < MiniTest::Unit::TestCase
4
+ def setup
5
+ @translator = SkyDB::Import::Translator.new()
6
+
7
+ @input = {
8
+ 'myString' => 'hello world',
9
+ 'myInt' => '100',
10
+ 'myFloat' => '20.21',
11
+ 'myTrue' => 'true',
12
+ 'myFalse' => 'false',
13
+ 'myDate' => '10/26/1982 12:00 AM',
14
+ 'foo' => '1000',
15
+ }
16
+ @output = {}
17
+ end
18
+
19
+
20
+ ######################################
21
+ # Simple Translation
22
+ ######################################
23
+
24
+ def test_string_translation
25
+ SkyDB::Import::Translator.new(:input_field => 'myString', :output_field => 'data')
26
+ .translate(@input, @output)
27
+ assert_equal 'hello world', @output['data']
28
+ end
29
+
30
+ def test_int_translation
31
+ SkyDB::Import::Translator.new(:input_field => 'myInt', :output_field => 'data', :format => 'Int')
32
+ .translate(@input, @output)
33
+ assert_equal 100, @output['data']
34
+ end
35
+
36
+ def test_float_translation
37
+ SkyDB::Import::Translator.new(:input_field => 'myFloat', :output_field => 'data', :format => 'Float')
38
+ .translate(@input, @output)
39
+ assert_equal 20.21, @output['data']
40
+ end
41
+
42
+ def test_boolean_true_translation
43
+ SkyDB::Import::Translator.new(:input_field => 'myTrue', :output_field => 'data', :format => 'Boolean')
44
+ .translate(@input, @output)
45
+ assert_equal true, @output['data']
46
+ end
47
+
48
+ def test_boolean_false_translation
49
+ SkyDB::Import::Translator.new(:input_field => 'myFalse', :output_field => 'data', :format => 'Boolean')
50
+ .translate(@input, @output)
51
+ assert_equal false, @output['data']
52
+ end
53
+
54
+ def test_date_translation
55
+ SkyDB::Import::Translator.new(:input_field => 'myDate', :output_field => 'data', :format => 'Date')
56
+ .translate(@input, @output)
57
+ assert_equal Time.parse('1982-10-26'), @output['data']
58
+ end
59
+
60
+
61
+ ######################################
62
+ # Nested Translation
63
+ ######################################
64
+
65
+ def test_nested_translation
66
+ SkyDB::Import::Translator.new(:input_field => 'myString', :output_field => ['action', 'data'])
67
+ .translate(@input, @output)
68
+ assert_equal 'hello world', @output['action']['data']
69
+ end
70
+
71
+
72
+ ######################################
73
+ # Dynamic Translation
74
+ ######################################
75
+
76
+ def test_translate_function_proc
77
+ SkyDB::Import::Translator.new(:translate_function => lambda {|input, output| output['data'] = input['foo'].to_i + 10})
78
+ .translate(@input, @output)
79
+ assert_equal 1010, @output['data']
80
+ end
81
+
82
+
83
+ def test_translate_function_string
84
+ SkyDB::Import::Translator.new(:output_field => 'data', :translate_function => "input['foo'].to_i * 2")
85
+ .translate(@input, @output)
86
+ assert_equal 2000, @output['data']
87
+ end
88
+ end