skydb 0.2.2 → 0.2.3

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 (54) hide show
  1. data/bin/sky +4 -0
  2. data/lib/skydb.rb +3 -2
  3. data/lib/skydb/action.rb +19 -0
  4. data/lib/skydb/client.rb +15 -5
  5. data/lib/skydb/event.rb +3 -7
  6. data/lib/skydb/import/importer.rb +236 -59
  7. data/lib/skydb/import/transforms/apache.yml +4 -0
  8. data/lib/skydb/import/transforms/sky.yml +20 -12
  9. data/lib/skydb/message.rb +1 -0
  10. data/lib/skydb/message/add_event.rb +1 -1
  11. data/lib/skydb/message/get_actions.rb +4 -0
  12. data/lib/skydb/message/get_properties.rb +4 -0
  13. data/lib/skydb/message/get_tables.rb +43 -0
  14. data/lib/skydb/message/lua/aggregate.rb +4 -0
  15. data/lib/skydb/property.rb +10 -0
  16. data/lib/skydb/query.rb +44 -59
  17. data/lib/skydb/query/after_condition.rb +104 -0
  18. data/lib/skydb/query/{after.rb → condition.rb} +37 -27
  19. data/lib/skydb/query/on_condition.rb +53 -0
  20. data/lib/skydb/query/selection.rb +131 -1
  21. data/lib/skydb/query/selection_field.rb +25 -0
  22. data/lib/skydb/query/selection_group.rb +21 -0
  23. data/lib/skydb/table.rb +7 -0
  24. data/lib/skydb/version.rb +1 -1
  25. data/test/integration/query_test.rb +102 -0
  26. data/test/test_helper.rb +42 -1
  27. data/test/{client_test.rb → unit/client_test.rb} +0 -0
  28. data/test/{event_test.rb → unit/event_test.rb} +0 -5
  29. data/test/unit/import/importer_test.rb +208 -0
  30. data/test/{import → unit/import}/translator_test.rb +0 -0
  31. data/test/{message → unit/message}/add_action_message_test.rb +0 -0
  32. data/test/{message → unit/message}/add_event_message_test.rb +2 -2
  33. data/test/{message → unit/message}/add_property_message_test.rb +0 -0
  34. data/test/{message → unit/message}/create_table_message_test.rb +0 -0
  35. data/test/{message → unit/message}/delete_table_message_test.rb +0 -0
  36. data/test/{message → unit/message}/get_action_message_test.rb +0 -0
  37. data/test/{message → unit/message}/get_actions_message_test.rb +0 -0
  38. data/test/{message → unit/message}/get_properties_message_test.rb +0 -0
  39. data/test/{message → unit/message}/get_property_message_test.rb +0 -0
  40. data/test/{message → unit/message}/get_table_message_test.rb +0 -0
  41. data/test/unit/message/get_tables_message_test.rb +18 -0
  42. data/test/{message → unit/message}/lookup_message_test.rb +0 -0
  43. data/test/{message → unit/message}/lua_aggregate_message_test.rb +0 -0
  44. data/test/{message → unit/message}/multi_message_test.rb +0 -0
  45. data/test/{message → unit/message}/next_action_message_test.rb +0 -0
  46. data/test/{message → unit/message}/ping_message_test.rb +0 -0
  47. data/test/{message_test.rb → unit/message_test.rb} +0 -0
  48. data/test/unit/query/after_test.rb +89 -0
  49. data/test/{query/after_test.rb → unit/query/on_test.rb} +10 -10
  50. data/test/{query → unit/query}/selection_test.rb +2 -2
  51. data/test/{query_test.rb → unit/query_test.rb} +32 -6
  52. data/test/{skydb_test.rb → unit/skydb_test.rb} +0 -0
  53. metadata +165 -53
  54. data/test/import/importer_test.rb +0 -42
@@ -0,0 +1,4 @@
1
+ translate: |
2
+ output[:object_id] = input[:ip_address]
3
+ output[:timestamp] = input[:timestamp]
4
+ output[:action][:name] = input[:url]
@@ -1,20 +1,28 @@
1
1
  fields:
2
- object_id: object_id:int
2
+ object_id: object_id
3
3
  timestamp: timestamp:date
4
4
 
5
5
  translate: |
6
- input.each_pair do |k, v|
7
- m, prefix, field, data_type = *k.match(/^(?:(action|data)\.)?(\w+)(?:\:(\w+))?$/)
8
- next unless ["object_id", "timestamp"].index(field).nil?
6
+ def process(_input, _output)
7
+ _input.each_pair do |k, v|
8
+ if v.is_a?(Hash)
9
+ _output[k] = {}
10
+ process(v, _output[k])
11
+ else
12
+ m, prefix, field, data_type = *k.match(/^(?:(action|data)\.)?(\w+)(?:\:(\w+))?$/)
13
+ next unless ["object_id", "timestamp"].index(field).nil?
9
14
 
10
- output[prefix] ||= {} unless prefix.nil?
11
- target = prefix.nil? ? output : output[prefix]
15
+ _output[prefix] ||= {} unless prefix.nil?
16
+ target = prefix.nil? ? _output : _output[prefix]
12
17
 
13
- target[field] = case data_type.to_s.downcase
14
- when "int" then v.to_i
15
- when "float" then v.to_f
16
- when "boolean" then (v.downcase == "true" || v.downcase == "yes" || v.downcase == "y")
17
- when "date" then Chronic.parse(v)
18
- else v.to_s
18
+ target[field] = case data_type.to_s.downcase
19
+ when "int" then v.to_i
20
+ when "float" then v.to_f
21
+ when "boolean" then (v.downcase == "true" || v.downcase == "yes" || v.downcase == "y")
22
+ when "date" then Chronic.parse(v)
23
+ else v.to_s
24
+ end
25
+ end
19
26
  end
20
27
  end
28
+ process(input, output)
data/lib/skydb/message.rb CHANGED
@@ -126,6 +126,7 @@ end
126
126
  require 'skydb/message/create_table'
127
127
  require 'skydb/message/delete_table'
128
128
  require 'skydb/message/get_table'
129
+ require 'skydb/message/get_tables'
129
130
 
130
131
  require 'skydb/message/add_action'
131
132
  require 'skydb/message/get_action'
@@ -47,7 +47,7 @@ class SkyDB
47
47
  def validate!
48
48
  super
49
49
 
50
- if !(event.object_id > 0)
50
+ if event.object_id.nil?
51
51
  raise SkyDB::ObjectIdRequiredError.new('Object ID required')
52
52
  end
53
53
 
@@ -29,6 +29,10 @@ class SkyDB
29
29
  def encode_body(buffer)
30
30
  # Do nothing.
31
31
  end
32
+
33
+ def process_response(response)
34
+ return response['actions']
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -29,6 +29,10 @@ class SkyDB
29
29
  def encode_body(buffer)
30
30
  # Do nothing.
31
31
  end
32
+
33
+ def process_response(response)
34
+ return response['properties']
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -0,0 +1,43 @@
1
+ class SkyDB
2
+ class Message
3
+ class GetTables < SkyDB::Message
4
+ ########################################################################
5
+ #
6
+ # Constructor
7
+ #
8
+ ########################################################################
9
+
10
+ # Initializes the 'get_tables' message.
11
+ def initialize(options={})
12
+ super('get_tables')
13
+ end
14
+
15
+
16
+ ##########################################################################
17
+ #
18
+ # Methods
19
+ #
20
+ ##########################################################################
21
+
22
+ ##################################
23
+ # Validation
24
+ ##################################
25
+
26
+ # A flag stating if the table is required for this type of message.
27
+ def require_table?
28
+ return false
29
+ end
30
+
31
+ ####################################
32
+ # Encoding
33
+ ####################################
34
+
35
+ def process_response(response)
36
+ tables = []
37
+ response['tables'].each do |hash|
38
+ tables << SkyDB::Table.new(hash['name'])
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -53,6 +53,10 @@ class SkyDB
53
53
  :source => source
54
54
  }.to_msgpack
55
55
  end
56
+
57
+ def process_response(response)
58
+ return response['data']
59
+ end
56
60
  end
57
61
  end
58
62
  end
@@ -89,5 +89,15 @@ class SkyDB
89
89
  name:name
90
90
  }.to_msgpack
91
91
  end
92
+
93
+ # Encodes the property into JSON format.
94
+ def to_json(*a)
95
+ {
96
+ 'id' => id,
97
+ 'type' => type,
98
+ 'dataType' => data_type,
99
+ 'name' => name
100
+ }.to_json(*a)
101
+ end
92
102
  end
93
103
  end
data/lib/skydb/query.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'skydb/query/selection'
2
- require 'skydb/query/after'
2
+ require 'skydb/query/condition'
3
+ require 'skydb/query/after_condition'
4
+ require 'skydb/query/on_condition'
3
5
  require 'skydb/query/validation_error'
4
6
 
5
7
  class SkyDB
@@ -17,8 +19,7 @@ class SkyDB
17
19
 
18
20
  def initialize(options={})
19
21
  self.client = options[:client]
20
- self.selection = options[:selection] || SkyDB::Query::Selection.new()
21
- self.conditions = options[:conditions] || []
22
+ self.selection = options[:selection]
22
23
  end
23
24
 
24
25
 
@@ -32,10 +33,13 @@ class SkyDB
32
33
  attr_accessor :client
33
34
 
34
35
  # The properties that should be selected from the database.
35
- attr_accessor :selection
36
-
37
- # A list of conditions that must be fulfilled before selection can occur.
38
- attr_accessor :conditions
36
+ attr_reader :selection
37
+
38
+ def selection=(value)
39
+ @selection = value
40
+ value.query = self unless value.nil?
41
+ return value
42
+ end
39
43
 
40
44
  # The number of idle seconds that separates sessions.
41
45
  attr_accessor :session_idle_time
@@ -51,34 +55,15 @@ class SkyDB
51
55
  # Helpers
52
56
  ####################################
53
57
 
54
- # Adds a list of fields to the selection.
58
+ # Creates and appends a new selection to the query.
55
59
  #
56
60
  # @param [String] fields A list of fields to add to the selection.
57
61
  #
58
- # @return [Query] The query object is returned.
62
+ # @return [Selection] The newly created selection object is returned.
59
63
  def select(*fields)
64
+ self.selection = SkyDB::Query::Selection.new()
60
65
  selection.select(*fields)
61
- return self
62
- end
63
-
64
- # Adds one or more grouping fields to the selection of the query.
65
- #
66
- # @param [String] groups A list of groups to add to the selection.
67
- #
68
- # @return [Query] The query object is returned.
69
- def group_by(*groups)
70
- selection.group_by(*groups)
71
- return self
72
- end
73
-
74
- # Adds an 'after' condition to the query.
75
- #
76
- # @param [Hash] options The options to pass to the 'after' condition.
77
- #
78
- # @return [Query] The query object is returned.
79
- def after(options={})
80
- conditions << SkyDB::Query::After.new(options)
81
- return self
66
+ return selection
82
67
  end
83
68
 
84
69
  # Sets the session idle seconds and returns the query object.
@@ -133,43 +118,47 @@ class SkyDB
133
118
 
134
119
  # Generate selection.
135
120
  code = []
136
- code << selection.codegen_select()
137
-
138
- # Generate condition functions.
139
- conditions.each_with_index do |condition, index|
140
- condition.function_name ||= "__condition#{nextseq}"
141
- code << condition.codegen
142
- end
143
-
144
- # Generate the invocation of the conditions.
145
- conditionals = conditions.map {|condition| "#{condition.function_name}(cursor, data)"}.join(' and ')
146
- conditionals = "true" if conditions.length == 0
121
+ code << selection.codegen()
147
122
 
148
123
  # Generate aggregate() function.
149
124
  code << "function aggregate(cursor, data)"
150
125
  code << " cursor:set_session_idle(#{session_idle_time.to_i})" if session_idle_time.to_i > 0
151
- code << " while cursor:next_session() do"
152
- code << " while cursor:next() do"
153
- code << " if #{conditionals} then"
154
- code << " select(cursor, data)"
155
- code << " end"
156
- code << " end"
157
- code << " end"
126
+ code << " select_all(cursor, data)"
158
127
  code << "end"
159
128
  code << ""
160
-
161
- # Generate merge function.
162
- code << selection.codegen_merge()
163
129
 
164
130
  return code.join("\n")
165
131
  end
166
132
 
167
133
 
168
134
  ####################################
169
- # Utility
135
+ # Serialization
170
136
  ####################################
171
137
 
172
- private
138
+ # Serializes the query object into a JSON string.
139
+ def to_json(*a); to_hash.to_json(*a); end
140
+
141
+ # Serializes the query object into a hash.
142
+ def to_hash(*a)
143
+ hash = {}
144
+ hash['selections'] = [selection.to_hash(*a)] unless selection.nil?
145
+ hash['sessionIdleTime'] = session_idle_time.to_i
146
+ hash
147
+ end
148
+
149
+ # Deserializes the query object into a hash.
150
+ def from_hash(hash, *a)
151
+ return if hash.nil?
152
+ selection_hash, x = *hash['selections']
153
+ self.selection = SkyDB::Query::Selection.new.from_hash(selection_hash)
154
+ self.session_idle_time = hash['sessionIdleTime'].to_i
155
+ return self
156
+ end
157
+
158
+
159
+ ####################################
160
+ # Utility
161
+ ####################################
173
162
 
174
163
  # Generates a sequence number used for uniquely naming objects and
175
164
  # functions in the query.
@@ -179,13 +168,9 @@ class SkyDB
179
168
 
180
169
  # Looks up all actions and properties that are missing an identifier.
181
170
  def lookup_identifiers
182
- # Find all the actions on conditions that are missing an id.
171
+ # Find all the actions on the selection that are missing an id.
183
172
  actions = []
184
- conditions.each do |condition|
185
- if condition.action.is_a?(SkyDB::Action) && condition.action.id.to_i == 0
186
- actions << condition.action
187
- end
188
- end
173
+ actions.concat(selection.get_identifiers())
189
174
 
190
175
  # Lookup all the actions.
191
176
  if actions.length > 0
@@ -0,0 +1,104 @@
1
+ class SkyDB
2
+ class Query
3
+ # The 'after' condition filters out selection only after the condition
4
+ # has been fulfilled.
5
+ class AfterCondition < SkyDB::Query::Condition
6
+ ##########################################################################
7
+ #
8
+ # Constructor
9
+ #
10
+ ##########################################################################
11
+
12
+ def initialize(action=nil, options={})
13
+ options.merge!(action.is_a?(Hash) ? action : {:action => action})
14
+ super(options)
15
+ self.within = options[:within]
16
+ end
17
+
18
+
19
+ ##########################################################################
20
+ #
21
+ # Attributes
22
+ #
23
+ ##########################################################################
24
+
25
+ attr_accessor :within
26
+
27
+
28
+ ##########################################################################
29
+ #
30
+ # Methods
31
+ #
32
+ ##########################################################################
33
+
34
+ ##################################
35
+ # Validation
36
+ ##################################
37
+
38
+ # Validates that the object is correct before executing a codegen.
39
+ def validate!
40
+ # Do not allow the :enter action. That is reserved for the 'On'
41
+ # condition.
42
+ if action == :enter
43
+ raise SkyDB::Query::ValidationError.new("Enter actions cannot be used with an 'after' condition. Please use an 'on' condition instead.")
44
+ end
45
+
46
+ super
47
+
48
+ return nil
49
+ end
50
+
51
+
52
+ ##################################
53
+ # Codegen
54
+ ##################################
55
+
56
+ # Generates Lua code to match a given action.
57
+ def codegen(options={})
58
+ header, body, footer = "function #{function_name.to_s}(cursor, data)\n", [], "end\n"
59
+ within_unit = (within.nil? ? nil : within[:unit])
60
+
61
+ # Find the matching event and then move to the next one for selection.
62
+ body << "if cursor:eos() or cursor:eof() then return false end"
63
+ body << "remaining = #{within[:quantity].to_i}" if within_unit == 'step'
64
+ body << "repeat"
65
+ body << " if remaining <= 0 then return false end" if within_unit == 'step'
66
+ body << " if cursor.event.action_id == #{action.id.to_i} then"
67
+ body << " cursor:next()"
68
+ body << " return true"
69
+ body << " end"
70
+ body << " remaining = remaining - 1" if within_unit == 'step'
71
+ body << "until not cursor:next()"
72
+ body << "return false"
73
+
74
+ # Indent body and return.
75
+ body.map! {|line| " " + line}
76
+ return header + body.join("\n") + "\n" + footer
77
+ end
78
+
79
+
80
+ ####################################
81
+ # Serialization
82
+ ####################################
83
+
84
+ # Serializes the condition into a hash.
85
+ def to_hash(*a)
86
+ json = super(*a)
87
+ json['within'] = within.to_hash(*a) unless within.nil?
88
+ json
89
+ end
90
+
91
+ # Deserializes the condition from a hash.
92
+ def from_hash(hash, *a)
93
+ super(hash, *a)
94
+ return nil if hash.nil?
95
+
96
+ hash['within']._symbolize_keys! unless hash['within'].nil?
97
+ self.within = hash['within']
98
+
99
+ return self
100
+ end
101
+ end
102
+ end
103
+ end
104
+
@@ -1,16 +1,15 @@
1
1
  class SkyDB
2
2
  class Query
3
- # The 'after' condition filters out selection only after the condition
4
- # has been fulfilled.
5
- class After
3
+ # The Condition class is the base class for all query classes that limit
4
+ # the selection.
5
+ class Condition
6
6
  ##########################################################################
7
7
  #
8
8
  # Constructor
9
9
  #
10
10
  ##########################################################################
11
11
 
12
- def initialize(action=nil, options={})
13
- options.merge!(action.is_a?(Hash) ? action : {:action => action})
12
+ def initialize(options={})
14
13
  self.action = options[:action]
15
14
  self.function_name = options[:function_name]
16
15
  end
@@ -30,13 +29,11 @@ class SkyDB
30
29
  attr_reader :action
31
30
 
32
31
  def action=(value)
33
- if value.is_a?(Symbol)
34
- @action = :enter
35
- elsif value.is_a?(String)
32
+ if value.is_a?(String)
36
33
  @action = SkyDB::Action.new(:name => value)
37
34
  elsif value.is_a?(Fixnum)
38
35
  @action = SkyDB::Action.new(:id => value)
39
- elsif value.is_a?(SkyDB::Action)
36
+ elsif value.is_a?(SkyDB::Action) || value == :enter
40
37
  @action = value
41
38
  else
42
39
  @action = nil
@@ -57,7 +54,7 @@ class SkyDB
57
54
  # Validates that the object is correct before executing a codegen.
58
55
  def validate!
59
56
  # Require the action identifier.
60
- if action.nil? || action.id.to_i == 0
57
+ if action.nil? || action == :enter || action.id.to_i == 0
61
58
  raise SkyDB::Query::ValidationError.new("Action with non-zero identifier required.")
62
59
  end
63
60
 
@@ -77,25 +74,38 @@ class SkyDB
77
74
 
78
75
  # Generates Lua code to match a given action.
79
76
  def codegen(options={})
80
- header, body, footer = "function #{function_name.to_s}(cursor, data)\n", [], "end\n"
81
-
82
- # If the action is :enter then just check for the beginning of a session.
83
- if action == :enter
84
- body << "return (cursor.session_event_index == 0)"
85
- else
86
- # Only move to the next event if directed to by the options.
87
- body << "repeat"
88
- body << " if cursor.event.action_id == #{action.id.to_i} then"
89
- body << " cursor:next()"
90
- body << " return true"
91
- body << " end"
92
- body << "until not cursor:next()"
93
- body << "return false"
77
+ return "function #{function_name.to_s}(cursor, data) return false end\n"
78
+ end
79
+
80
+
81
+ ####################################
82
+ # Serialization
83
+ ####################################
84
+
85
+ # Serializes the condition into a JSON string.
86
+ def to_json(*a); to_hash.to_json(*a); end
87
+
88
+ # Serializes the condition into a hash.
89
+ def to_hash(*a)
90
+ json = {}
91
+ json['type'] = self.class.to_s.split("::").last.gsub('Condition', '').downcase
92
+ if action.is_a?(SkyDB::Action)
93
+ json['action'] = action.to_hash(*a)
94
+ elsif action.is_a?(Symbol)
95
+ json['action'] = action
94
96
  end
97
+ json
98
+ end
95
99
 
96
- # Indent body and return.
97
- body.map! {|line| " " + line}
98
- return header + body.join("\n") + "\n" + footer
100
+ # Deserializes the condition from a hash.
101
+ def from_hash(hash, *a)
102
+ return nil if hash.nil?
103
+ if hash['action'] == "enter"
104
+ self.action = hash['action'].to_sym
105
+ else
106
+ self.action = SkyDB::Action.new.from_hash(hash['action'], *a)
107
+ end
108
+ return self
99
109
  end
100
110
  end
101
111
  end