skydb 0.2.1 → 0.2.2

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.
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