skydb 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/README.md +4 -0
  2. data/lib/ext/string.rb +11 -0
  3. data/lib/skydb.rb +88 -0
  4. data/lib/skydb/action.rb +57 -0
  5. data/lib/skydb/client.rb +216 -0
  6. data/lib/skydb/event.rb +114 -0
  7. data/lib/skydb/message.rb +134 -0
  8. data/lib/skydb/message/add_action.rb +53 -0
  9. data/lib/skydb/message/add_event.rb +72 -0
  10. data/lib/skydb/message/add_property.rb +55 -0
  11. data/lib/skydb/message/get_action.rb +55 -0
  12. data/lib/skydb/message/get_actions.rb +34 -0
  13. data/lib/skydb/message/get_properties.rb +34 -0
  14. data/lib/skydb/message/get_property.rb +55 -0
  15. data/lib/skydb/message/lua/map_reduce.rb +59 -0
  16. data/lib/skydb/message/multi.rb +57 -0
  17. data/lib/skydb/message/next_actions.rb +55 -0
  18. data/lib/skydb/property.rb +93 -0
  19. data/lib/skydb/property/type.rb +40 -0
  20. data/lib/skydb/timestamp.rb +22 -0
  21. data/lib/skydb/version.rb +3 -0
  22. data/test/client_test.rb +71 -0
  23. data/test/event_test.rb +37 -0
  24. data/test/message/add_action_message_test.rb +34 -0
  25. data/test/message/add_event_message_test.rb +35 -0
  26. data/test/message/add_property_message_test.rb +41 -0
  27. data/test/message/get_action_message_test.rb +34 -0
  28. data/test/message/get_actions_message_test.rb +18 -0
  29. data/test/message/get_properties_message_test.rb +18 -0
  30. data/test/message/get_property_message_test.rb +34 -0
  31. data/test/message/lua_map_reduce_message_test.rb +19 -0
  32. data/test/message/multi_message_test.rb +22 -0
  33. data/test/message/next_action_message_test.rb +34 -0
  34. data/test/message_test.rb +15 -0
  35. data/test/skydb_test.rb +20 -0
  36. data/test/test_helper.rb +12 -0
  37. metadata +166 -0
@@ -0,0 +1,4 @@
1
+ sky.rb
2
+ ======
3
+
4
+ The Ruby client for the Sky database.
@@ -0,0 +1,11 @@
1
+ class String
2
+ def to_hex
3
+ bytes.map do |ch|
4
+ if ch.chr.index(/^[a-z_]$/i)
5
+ ch.chr
6
+ else
7
+ '\x%02x' % ch
8
+ end
9
+ end.join('')
10
+ end
11
+ end
@@ -0,0 +1,88 @@
1
+ require 'date'
2
+ require 'msgpack'
3
+ require 'socket'
4
+
5
+ require 'skydb/action'
6
+ require 'skydb/client'
7
+ require 'skydb/event'
8
+ require 'skydb/message'
9
+ require 'skydb/property'
10
+ require 'skydb/timestamp'
11
+ require 'skydb/version'
12
+
13
+ require 'ext/string'
14
+
15
+ class SkyDB
16
+ ############################################################################
17
+ #
18
+ # Errors
19
+ #
20
+ ############################################################################
21
+
22
+ class TableRequiredError < StandardError; end
23
+
24
+ class ObjectIdRequiredError < StandardError; end
25
+ class TimestampRequiredError < StandardError; end
26
+
27
+ ############################################################################
28
+ #
29
+ # Constants
30
+ #
31
+ ############################################################################
32
+
33
+ CLIENT_PASSTHROUGH = [
34
+ :host, :host=, :port, :port=,
35
+ :table, :table=,
36
+ :multi,
37
+ :add_event,
38
+ :add_action, :get_action, :get_actions,
39
+ :add_property, :get_property, :get_properties,
40
+ :next_actions,
41
+ :map_reduce
42
+ ]
43
+
44
+
45
+ ############################################################################
46
+ #
47
+ # Static Attributes
48
+ #
49
+ ############################################################################
50
+
51
+ ######################################
52
+ # Debugging
53
+ ######################################
54
+
55
+ class << self
56
+ attr_accessor :debug
57
+ end
58
+
59
+ ######################################
60
+ # Default Client
61
+ ######################################
62
+
63
+ # The default Sky client.
64
+ def self.client
65
+ @client ||= SkyDB::Client.new()
66
+ return @client
67
+ end
68
+
69
+ def self.client=(value)
70
+ @client = value
71
+ end
72
+
73
+
74
+ ############################################################################
75
+ #
76
+ # Static Methods
77
+ #
78
+ ############################################################################
79
+
80
+ def self.method_missing(method, *args, &block)
81
+ method = method
82
+ if CLIENT_PASSTHROUGH.include?(method.to_sym)
83
+ client.__send__(method.to_sym, *args, &block)
84
+ else
85
+ raise NoMethodError.new("Message type not available: #{method}")
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,57 @@
1
+ class SkyDB
2
+ class Action
3
+ ##########################################################################
4
+ #
5
+ # Constructor
6
+ #
7
+ ##########################################################################
8
+
9
+ # Initializes the action.
10
+ def initialize(options={})
11
+ self.id = options[:id]
12
+ self.name = options[:name]
13
+ end
14
+
15
+
16
+ ##########################################################################
17
+ #
18
+ # Attributes
19
+ #
20
+ ##########################################################################
21
+
22
+ ##################################
23
+ # ID
24
+ ##################################
25
+
26
+ # The action identifier.
27
+ attr_reader :id
28
+
29
+ def id=(value)
30
+ @id = value.to_i
31
+ end
32
+
33
+
34
+ ##################################
35
+ # Name
36
+ ##################################
37
+
38
+ # The name of the action.
39
+ attr_reader :name
40
+
41
+ def name=(value)
42
+ @name = value.to_s
43
+ end
44
+
45
+
46
+ ##########################################################################
47
+ #
48
+ # Methods
49
+ #
50
+ ##########################################################################
51
+
52
+ # Encodes the action into MsgPack format.
53
+ def to_msgpack
54
+ return {id:id, name:name}.to_msgpack
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,216 @@
1
+ class SkyDB
2
+ class Client
3
+ ##########################################################################
4
+ #
5
+ # Constants
6
+ #
7
+ ##########################################################################
8
+
9
+ # The default host to connect to if one is not specified.
10
+ DEFAULT_HOST = 'localhost'
11
+
12
+ # The default port to connect to if one is not specified.
13
+ DEFAULT_PORT = 8585
14
+
15
+
16
+ ##########################################################################
17
+ #
18
+ # Constructor
19
+ #
20
+ ##########################################################################
21
+
22
+ # Initializes the client.
23
+ def initialize(options={})
24
+ self.host = options[:host] || DEFAULT_HOST
25
+ self.port = options[:port] || DEFAULT_PORT
26
+ end
27
+
28
+
29
+ ##########################################################################
30
+ #
31
+ # Attributes
32
+ #
33
+ ##########################################################################
34
+
35
+ # The name of the host to conect to.
36
+ attr_accessor :host
37
+
38
+ # The port on the host to connect to.
39
+ attr_accessor :port
40
+
41
+ # The table to connect to.
42
+ attr_accessor :table
43
+
44
+
45
+ ##########################################################################
46
+ #
47
+ # Methods
48
+ #
49
+ ##########################################################################
50
+
51
+ ####################################
52
+ # Action Messages
53
+ ####################################
54
+
55
+ # Adds an action to the server.
56
+ #
57
+ # @param [Action] action the action to add.
58
+ def add_action(action, options={})
59
+ return send_message(SkyDB::Message::AddAction.new(action, options))
60
+ end
61
+
62
+ # Retrieves an individual action from the server.
63
+ #
64
+ # @param [Fixnum] action_id the identifier of the action to retrieve.
65
+ def get_action(action_id, options={})
66
+ return send_message(SkyDB::Message::GetAction.new(action_id, options))
67
+ end
68
+
69
+ # Retrieves a list of all actions from the server.
70
+ def get_actions(options={})
71
+ return send_message(SkyDB::Message::GetActions.new(options))
72
+ end
73
+
74
+
75
+ ####################################
76
+ # Property Messages
77
+ ####################################
78
+
79
+ # Adds a property to the server.
80
+ #
81
+ # @param [Property] property the property to add.
82
+ def add_property(property, options={})
83
+ return send_message(SkyDB::Message::AddProperty.new(property, options))
84
+ end
85
+
86
+ # Retrieves an individual property from the server.
87
+ #
88
+ # @param [Fixnum] property_id the identifier of the property to retrieve.
89
+ def get_property(property_id, options={})
90
+ return send_message(SkyDB::Message::GetProperty.new(property_id, options))
91
+ end
92
+
93
+ # Retrieves a list of all properties from the server.
94
+ def get_properties(options={})
95
+ return send_message(SkyDB::Message::GetProperties.new(options))
96
+ end
97
+
98
+
99
+ ####################################
100
+ # Event Messages
101
+ ####################################
102
+
103
+ # Adds an event to the server.
104
+ #
105
+ # @param [Event] event the event to add.
106
+ def add_event(event, options={})
107
+ return send_message(SkyDB::Message::AddEvent.new(event, options))
108
+ end
109
+
110
+
111
+ ####################################
112
+ # Path Messages
113
+ ####################################
114
+
115
+ # Finds a count of the action that occurs immediately after a set of
116
+ # actions.
117
+ #
118
+ # @param [Array] prior_action_ids the prior action ids to match on.
119
+ def next_actions(prior_action_ids, options={})
120
+ return send_message(SkyDB::Message::NextActions.new(prior_action_ids, options))
121
+ end
122
+
123
+
124
+ ####################################
125
+ # Lua Messages
126
+ ####################################
127
+
128
+ # Executes a Lua map/reduce job on the server and returns the results.
129
+ #
130
+ # @param [String] source the Lua source code to execute
131
+ def map_reduce(source, options={})
132
+ return send_message(SkyDB::Message::Lua::MapReduce.new(source, options))
133
+ end
134
+
135
+
136
+ ####################################
137
+ # Multi message
138
+ ####################################
139
+
140
+ # Executes multiple messages in one call.
141
+ def multi(options={})
142
+ raise "Already in a multi-message block" unless @multi_message.nil?
143
+
144
+ # Create multi-message.
145
+ @multi_message = SkyDB::Message::Multi.new(options)
146
+
147
+ # Execute the block normally and send the message.
148
+ begin
149
+ yield
150
+
151
+ # Clear multi message so it doesn't add to itself.
152
+ tmp = @multi_message
153
+ @multi_message = nil
154
+
155
+ # Send all messages at once.
156
+ send_message(tmp)
157
+
158
+ ensure
159
+ @multi_message = nil
160
+ end
161
+
162
+ return nil
163
+ end
164
+
165
+
166
+ ####################################
167
+ # Send
168
+ ####################################
169
+
170
+ # Sends a message to the server.
171
+ #
172
+ # @param [SkyDB::Message] message the message to send.
173
+ # @return [Object] the object returned by the server.
174
+ def send_message(message)
175
+ # Set the table if they're not set.
176
+ message.table = table if message.table.nil? || message.table.empty?
177
+
178
+ # Validate message before sending.
179
+ message.validate!
180
+
181
+ # If this is part of a multi message then simply append the message for
182
+ # later sending.
183
+ if !@multi_message.nil?
184
+ @multi_message.messages << message
185
+ return nil
186
+
187
+ # Otherwise send the message immediately.
188
+ else
189
+ # Connect to the server.
190
+ socket = TCPSocket.new(host, port.to_i)
191
+
192
+ # Encode and send message request.
193
+ message.encode(socket)
194
+
195
+ # Decode msgpack response. There should only be one return object.
196
+ response = nil
197
+ unpacker = MessagePack::Unpacker.new(socket)
198
+ unpacker.each do |obj|
199
+ response = obj
200
+ break
201
+ end
202
+
203
+ # Close socket.
204
+ socket.close()
205
+
206
+ # TODO: Exception processing.
207
+
208
+ # Process response back through the message.
209
+ response = message.process_response(response)
210
+
211
+ # Return response.
212
+ return response
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,114 @@
1
+ class SkyDB
2
+ class Event
3
+ ##########################################################################
4
+ #
5
+ # Constructor
6
+ #
7
+ ##########################################################################
8
+
9
+ # Initializes an event object.
10
+ def initialize(options={})
11
+ self.object_id = options[:object_id]
12
+ self.timestamp = options[:timestamp]
13
+ self.action = options[:action]
14
+ self.data = options[:data]
15
+ end
16
+
17
+
18
+ ##########################################################################
19
+ #
20
+ # Attributes
21
+ #
22
+ ##########################################################################
23
+
24
+ ##################################
25
+ # Object ID
26
+ ##################################
27
+
28
+ # The numeric identifier of the object that the event is attached to.
29
+ attr_reader :object_id
30
+
31
+ def object_id=(value)
32
+ @object_id = value.to_i
33
+ end
34
+
35
+ ##################################
36
+ # Timestamp
37
+ ##################################
38
+
39
+ # The timestamp of when the event occurred.
40
+ attr_reader :timestamp
41
+
42
+ def timestamp=(value)
43
+ @timestamp = value.to_time if value.class.method_defined?(:to_time)
44
+ end
45
+
46
+ ##################################
47
+ # Action
48
+ ##################################
49
+
50
+ # The hash containing action related information (including the name of
51
+ # the action being performed).
52
+ attr_reader :action
53
+
54
+ def action=(value)
55
+ clone = {}
56
+
57
+ # Copy over keys
58
+ if !value.nil?
59
+ value.each_pair do |k, v|
60
+ # Only copy keys with valid value types.
61
+ if v.is_a?(String) || v.is_a?(Fixnum) || v.is_a?(Float) || v == true || v == false
62
+ clone[k] = value[k]
63
+ end
64
+ end
65
+ end
66
+
67
+ # Only set the data if we have keys.
68
+ @action = clone.keys.length > 0 ? clone : nil
69
+ end
70
+
71
+ ##################################
72
+ # Data
73
+ ##################################
74
+
75
+ # A hash of data properties to set on the event. Properties can only be
76
+ # a String, Fixnum, Float or Boolean.
77
+ attr_reader :data
78
+
79
+ def data=(value)
80
+ clone = {}
81
+
82
+ # Copy over keys
83
+ if !value.nil?
84
+ value.each_pair do |k, v|
85
+ # Only copy keys with valid value types.
86
+ if v.is_a?(String) || v.is_a?(Fixnum) || v.is_a?(Float) || v == true || v == false
87
+ clone[k] = value[k]
88
+ end
89
+ end
90
+ end
91
+
92
+ # Only set the data if we have keys.
93
+ @data = clone.keys.length > 0 ? clone : nil
94
+ end
95
+
96
+
97
+ ##########################################################################
98
+ #
99
+ # Methods
100
+ #
101
+ ##########################################################################
102
+
103
+ # Encodes the event into MsgPack format.
104
+ def to_msgpack
105
+ obj = {
106
+ :objectId => object_id,
107
+ :timestamp => SkyDB::Timestamp.to_timestamp(timestamp)
108
+ }
109
+ obj[:action] = action unless action.nil? || action.empty?
110
+ obj[:data] = data unless data.nil? || data.empty?
111
+ return obj.to_msgpack
112
+ end
113
+ end
114
+ end