simplevoc-open 1.8.0

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.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.8.0
@@ -0,0 +1,4 @@
1
+ protocol: http
2
+ host: localhost
3
+ port: 12345
4
+ strict_mode: true
@@ -0,0 +1,16 @@
1
+ # Copyright:: 2011 triAGENS GmbH
2
+ # Author:: Oliver Kiessler (mailto:kiessler@inceedo.com)
3
+ require 'rubygems'
4
+ gem 'httparty'
5
+ require 'httparty'
6
+ gem 'json'
7
+ require 'json'
8
+ require 'uri'
9
+ require 'logger'
10
+ require 'yaml'
11
+
12
+ require File.dirname(__FILE__) + '/simplevoc/open/configuration.rb'
13
+ require File.dirname(__FILE__) + '/simplevoc/open/base.rb'
14
+ require File.dirname(__FILE__) + '/simplevoc/open/client.rb'
15
+ require File.dirname(__FILE__) + '/simplevoc/open/exceptions.rb'
16
+ require File.dirname(__FILE__) + '/simplevoc/open/hash.rb'
@@ -0,0 +1,68 @@
1
+ # Copyright:: 2011 triAGENS GmbH
2
+ # Author:: Oliver Kiessler (mailto:kiessler@inceedo.com)
3
+ module Simplevoc
4
+ module Open
5
+ # This ruby library is a REST client for the SimpleVOC key/value store.
6
+ #
7
+ # SimpleVOC is a pure in-memory key/value store with a REST Interface. It was developed with easy installation, administration
8
+ # and operation in mind. Checkout [World of Voc](http://www.worldofvoc.com) for more information.
9
+ class Base
10
+ VERSION = '1.8.0'
11
+ attr_accessor :configuration, :logger
12
+
13
+ # SimpleVOC messages used in the response JSON.
14
+ MESSAGES = {
15
+ :stored => "stored",
16
+ :insert_failed => "use 'PUT /value/<key>' to overwrite value",
17
+ :deleted => "deleted",
18
+ :delete_failed => 'key not found',
19
+ :get_failed => 'key not found',
20
+ :update_failed => 'not stored',
21
+ :flush => 'flush started'
22
+ }
23
+
24
+ # === Parameters
25
+ # * host = Hostname (required)
26
+ # * port = Port (required)
27
+ # * protocol = "http"/"https", defaults to "http"
28
+ # * strict_mode = true/false, defaults to true
29
+ # * log_level = defaults to LOGGER::INFO
30
+ def initialize(options = {})
31
+ default_options = {'host' => 'localhost', 'port' => '12345', 'protocol' => 'http', 'log_level' => Logger::INFO, 'strict_mode' => true}
32
+ options = default_options.merge(options)
33
+ options.empty? ? read_configuration : self.configuration = Simplevoc::Open::Configuration.new(options)
34
+ self.logger = Logger.new(STDOUT)
35
+ self.logger.level = options['log_level']
36
+ self.logger.info("SimpleVOC Ruby Client Version: #{VERSION}")
37
+ end
38
+
39
+ protected
40
+ # Reads the configuration from the current directory and when it doesn't find it falls back to the
41
+ # global configuration in the home directory (~/.simplevoc.yml) under which this ruby interpreter is run.
42
+ def read_configuration
43
+ if File.exists?("config.yml")
44
+ puts "Using config in this directory"
45
+ self.configuration = Simplevoc::Open::Configuration.new(YAML.load(File.read("config.yml")))
46
+ else
47
+ begin
48
+ puts "Using config in your home directory"
49
+ self.configuration = Simplevoc::Open::Configuration.new(YAML.load(File.read("#{ENV['HOME']}/.simplevoc.yml")))
50
+ rescue Errno::ENOENT
51
+ raise Simplevoc::Open::ConfigurationMissingException, "config.yml expected in current directory or ~/.simplevoc.yml"
52
+ end
53
+ end
54
+ end
55
+
56
+ # Returns the HTTP base URL for the SimpleVOC Rest Service.
57
+ def base_url
58
+ "#{self.configuration.options['protocol']}://#{self.configuration.options['host']}:#{self.configuration.options['port']}"
59
+ end
60
+
61
+ # Returns true/false whether to use the client in strict-mode or not.
62
+ # In strict-mode exceptions are raised for the most common client operations.
63
+ def use_strict_mode?
64
+ self.configuration.options['strict_mode']
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,225 @@
1
+ # Copyright:: 2011 triAGENS GmbH
2
+ # Author:: Oliver Kiessler (mailto:kiessler@inceedo.com)
3
+ module Simplevoc
4
+ module Open
5
+ # You can setup the client by passing options into the constructor or by using a configuration file in YAML.
6
+ # client = Simplevoc::Open::Client.new('host' => 'localhost', 'port' => 12345)
7
+ #
8
+ # To avoid exceptions, create a client in non-strict mode
9
+ #
10
+ # client = Simplevoc::Open::Client.new('host' => 'localhost', 'port' => 12345, 'log_level' => LOGGER::DEBUG, 'strict_mode' => false)
11
+ class Client < Base
12
+ # The insert method creates a new key-value pair in SimpleVOC.
13
+ # === Parameters
14
+ # * key = Key string
15
+ # * value = Value string
16
+ # * options = Optional header options hash with meta values for the values (x-voc-expires, x-voc-flags, x-voc-delete-time, x-voc-flush-delay, x-voc-extended)
17
+ def insert(key, value, options = {})
18
+ if key and value
19
+ response = get_response(:post, "/value/#{key}", options.merge(:body => value.to_s))
20
+ if response and response.parsed_response
21
+ return true if response.response.is_a?(Net::HTTPOK) and not response.parsed_response["error"] and response.parsed_response["message"] == MESSAGES[:stored]
22
+ if response.parsed_response["message"] == MESSAGES[:insert_failed]
23
+ raise Simplevoc::Open::ValueExistsException, MESSAGES[:insert_failed] if use_strict_mode?
24
+ else
25
+ raise Simplevoc::Open::OperationFailedException, response.parsed_response["message"] if use_strict_mode?
26
+ end
27
+ end
28
+ else
29
+ raise Simplevoc::Open::EmptyKeyException if use_strict_mode? and not key
30
+ raise Simplevoc::Open::EmptyValueException if use_strict_mode? and not value
31
+ end
32
+ false
33
+ end
34
+
35
+ # Convenience method to insert a key-value pair with extended values. The extended value is expected to be a JSON formatted string with
36
+ # key-values know to the SimpleVOC server. The server needs to be started with the "--server.extended-key-values=your_key:your_type" argument.
37
+ # === Parameters
38
+ # * key = Key string
39
+ # * value = Value string
40
+ # * extended_values_json = JSON string
41
+ # * options = Options headers hash
42
+ def insert_with_extended_values(key, value, extended_values_json, options = {})
43
+ json = extended_values_json.is_a?(String) ? extended_values_json : extended_values_json.to_json
44
+ insert(key, value, options.merge(:headers => {'x-voc-extended' => json}))
45
+ end
46
+
47
+ # Updates an existing key-value. The key must exist in SimpleVOC, otherwise an exception is raised.
48
+ # === Parameters
49
+ # * key = Key string
50
+ # * value = Value string
51
+ # * options = Options headers hash
52
+ def update(key, value, options = {})
53
+ if key and value
54
+ response = get_response(:put, "/value/#{key}", options.merge(:body => value.to_s))
55
+ if response and response.parsed_response
56
+ return true if response.response.is_a?(Net::HTTPOK) and not response.parsed_response["error"] and response.parsed_response["message"] == MESSAGES[:stored]
57
+ if response.parsed_response["message"] == MESSAGES[:update_failed]
58
+ raise Simplevoc::Open::KeyNotFoundException, MESSAGES[:update_failed] if use_strict_mode?
59
+ else
60
+ raise Simplevoc::Open::OperationFailedException, response.parsed_response["message"] if use_strict_mode?
61
+ end
62
+ end
63
+ else
64
+ raise Simplevoc::Open::EmptyKeyException if use_strict_mode? and not key
65
+ raise Simplevoc::Open::EmptyValueException if use_strict_mode? and not value
66
+ end
67
+ false
68
+ end
69
+
70
+ # Convenience method to insert or update a key-value pair in one go. Raises an exception if the key doesn't exist in SimpleVOC.
71
+ # === Parameters
72
+ # * key = Key string
73
+ # * value = Value string
74
+ # * options = Options headers hash
75
+ def insert_or_update(key, value, options = {})
76
+ if key and value
77
+ response = get_response(:put, "/value/#{key}?create=1", options.merge(:body => value.to_s))
78
+ if response and response.parsed_response
79
+ return true if response.response.is_a?(Net::HTTPOK) and not response.parsed_response["error"] and response.parsed_response["message"] == MESSAGES[:stored]
80
+ raise Simplevoc::Open::OperationFailedException, response.parsed_response["message"] if use_strict_mode?
81
+ end
82
+ else
83
+ raise Simplevoc::Open::EmptyKeyException if use_strict_mode? and not key
84
+ raise Simplevoc::Open::EmptyValueException if use_strict_mode? and not value
85
+ end
86
+ false
87
+ end
88
+
89
+ # Deletes a key-value pair in SimpleVOC identified by its key. Raises an exception if the key doesn't exist in SimpleVOC.
90
+ # === Parameters
91
+ # * key = Key string
92
+ def delete(key)
93
+ if key
94
+ response = get_response(:delete, "/value/#{key}")
95
+ if response and response.parsed_response
96
+ return true if response.response.is_a?(Net::HTTPOK) and not response.parsed_response["error"] and response.parsed_response["message"] == MESSAGES[:deleted]
97
+ if response.parsed_response["message"] == MESSAGES[:delete_failed]
98
+ raise Simplevoc::Open::KeyNotFoundException, MESSAGES[:delete_failed] if use_strict_mode?
99
+ else
100
+ raise Simplevoc::Open::OperationFailedException, response.parsed_response["message"] if use_strict_mode?
101
+ end
102
+ end
103
+ else
104
+ raise Simplevoc::Open::EmptyKeyException if use_strict_mode?
105
+ end
106
+ false
107
+ end
108
+
109
+ # Returns a hash with the value and known meta information about a given key. Raises an exception if the key doesn't exist in SimpleVOC.
110
+ # === Parameters
111
+ # * key = Key string
112
+ # * with_response_headers = true/false, set to true if you only want the value
113
+ def get(key, with_response_headers = true)
114
+ if key
115
+ response = get_response(:get, "/value/#{key}")
116
+ if response and response.parsed_response
117
+ if response.response.is_a?(Net::HTTPOK)
118
+ if with_response_headers
119
+ sv_response = {'value' => response.parsed_response}
120
+ sv_response['created'] = DateTime.parse(response.headers['x-voc-created']) if response.headers['x-voc-created']
121
+ sv_response['expires'] = DateTime.parse(response.headers['x-voc-expires']) if response.headers['x-voc-expires']
122
+ sv_response['flags'] = response.headers['x-voc-flags'].to_i if response.headers['x-voc-flags']
123
+ sv_response['extended-values'] = JSON response.headers['x-voc-extended'] if response.headers['x-voc-extended']
124
+ return sv_response
125
+ else
126
+ return response.parsed_response
127
+ end
128
+ elsif response.parsed_response.is_a?(::Hash) and response.parsed_response["error"] and response.parsed_response["message"] == MESSAGES[:get_failed]
129
+ raise Simplevoc::Open::KeyNotFoundException, MESSAGES[:get_failed] if use_strict_mode?
130
+ end
131
+ end
132
+ else
133
+ raise Simplevoc::Open::EmptyKeyException if use_strict_mode?
134
+ end
135
+ nil
136
+ end
137
+
138
+ # Returns an array of known keys. Use a prefix to query for keys and you can also use a filter expression. Raises an exception if the prefix is
139
+ # unknown or doesn't match.
140
+ # === Parameters
141
+ # * prefix = Prefix string
142
+ # * filter = Optional filter expression
143
+ def keys(prefix, filter = nil)
144
+ if prefix
145
+ response = get_response(:get, filter ? "/keys/#{prefix}?filter=#{filter}" : "/keys/#{prefix}")
146
+ if response.response.is_a?(Net::HTTPOK)
147
+ return response.parsed_response
148
+ elsif response.response.is_a?(Net::HTTPNoContent)
149
+ raise Simplevoc::Open::NoContentAtURLPathException if use_strict_mode?
150
+ else
151
+ raise Simplevoc::Open::PrefixNotFoundException if use_strict_mode?
152
+ end
153
+ else
154
+ raise Simplevoc::Open::EmptyPrefixException if use_strict_mode?
155
+ end
156
+ nil
157
+ end
158
+
159
+ # Deletes all key-value pairs in SimpleVOC.
160
+ def flush
161
+ response = get_response(:put, "/flush")
162
+ if response and response.parsed_response
163
+ return true if response.response.is_a?(Net::HTTPOK) and response.parsed_response["message"] == MESSAGES[:flush]
164
+ raise Simplevoc::Open::OperationFailedException, response.parsed_response["message"] if use_strict_mode?
165
+ end
166
+ false
167
+ end
168
+
169
+ # Returns information about the SimpleVOC server.
170
+ def server_info
171
+ info = {}
172
+ response = get_response(:get, '/version')
173
+ if response and response.parsed_response
174
+ info['version'] = response.parsed_response['version']
175
+ info['server'] = response.header['server']
176
+ end
177
+ info
178
+ end
179
+
180
+ # Returns the server version of the SimpleVOC server.
181
+ def server_version
182
+ server_info['version']
183
+ end
184
+
185
+ # Convenience method to create a option headers hash with common attributes.
186
+ # === Parameters
187
+ # * expires
188
+ # * flags
189
+ # * delete_time
190
+ # * flush_delay
191
+ # === Examples
192
+ # expires = your_time_object.utc.iso8601
193
+ # flags = 1
194
+ def extra_options(expires = nil, flags = nil, delete_time = nil, flush_delay = nil)
195
+ options = {:headers => {}}
196
+ options[:headers]['x-voc-expires'] = expires.to_s if expires
197
+ options[:headers]['x-voc-flags'] = flags.to_s if flags
198
+ options[:headers]['x-voc-delete-time'] = delete_time.to_s if delete_time
199
+ options[:headers]['x-voc-flush-delay'] = flush_delay.to_s if flush_delay
200
+ logger.debug "Using header options: #{options[:headers].inspect}"
201
+ options
202
+ end
203
+
204
+ private
205
+ # Internal method to create the httparty request and return the http response that is already parsed by httparty.
206
+ # === Parameters
207
+ # * http_verb = HTTP verb symbol (:get, :post, :put, :delete)
208
+ # * endpoint = HTTP URL endpoint
209
+ # * options = Optional options with body and headers
210
+ def get_response(http_verb, endpoint, options = {})
211
+ begin
212
+ endpoint_value = "#{base_url}#{URI.escape(endpoint)}"
213
+ logger.debug "Using endpoint: #{endpoint_value}"
214
+ body = {}; body.merge!(options) if options and options.any?
215
+ response = (http_verb == :post or http_verb == :put) ? HTTParty.send(http_verb, endpoint_value, body) : HTTParty.send(http_verb, endpoint_value)
216
+ logger.debug "Response: #{response.inspect}" if response
217
+ response
218
+ rescue => e
219
+ logger.error("Could not connect to SimpleVOC: #{e.message}")
220
+ raise Simplevoc::Open::ConnectionRefusedException
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright:: 2011 triAGENS GmbH
2
+ # Author:: Oliver Kiessler (mailto:kiessler@inceedo.com)
3
+ module Simplevoc
4
+ module Open
5
+ # You can also provide a configuration file in the current directory of your client to avoid setting up the client within your code.
6
+ # Checkout the config.yml.sample file as an example.
7
+ #
8
+ # If this configuration file isn't found in the current directory, the client will look for a "global" configuration file in your
9
+ # home directory (~/.simplevoc.yml).
10
+ class Configuration
11
+ attr_accessor :options
12
+
13
+ # === Parameters
14
+ # * host = Hostname (required)
15
+ # * port = Port (required)
16
+ # * protocol = "http"/"https", defaults to "http"
17
+ # * strict_mode = true/false, defaults to true
18
+ # * log_level = defaults to LOGGER::INFO
19
+ def initialize(options)
20
+ self.options = options
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright:: 2011 triAGENS GmbH
2
+ # Author:: Oliver Kiessler (mailto:kiessler@inceedo.com)
3
+ module Simplevoc
4
+ module Open
5
+ # The ConfigurationMissingException is raised when no configuration options are provided
6
+ # to the client constructor, a config.yml is missing in the current directory or a global
7
+ # configuration can't be found in the home directory.
8
+ class ConfigurationMissingException < Exception; end
9
+
10
+ # The EmptyKeyException is raised when an empty key was passed to the Client#insert, Client#insert_or_update,
11
+ # Client#update or Client#delete methods.
12
+ class EmptyKeyException < Exception; end
13
+
14
+ # The EmptyValueException is raised when nil is passed into a Client#insert, Client#insert_or_update or
15
+ # Client#update method.
16
+ class EmptyValueException < Exception; end
17
+
18
+ # The ValueExistsException is raised when a value for an existing key is passed to the Client#insert method.
19
+ class ValueExistsException < Exception; end
20
+
21
+ # The ConnectionRefusedException is raised when the HTTP connection to the SimpleVOC server fails.
22
+ class ConnectionRefusedException < Exception; end
23
+
24
+ # The KeyNotFoundException is raised when a key doesn't exist during a Client#get method call.
25
+ class KeyNotFoundException < Exception; end
26
+
27
+ # The OperationFailedException is a general exception that is raised during a REST method call. Returns the error
28
+ # message from the SimpleVOC server.
29
+ class OperationFailedException < Exception; end
30
+
31
+ # The EmptyPrefixException is raised when no prefix is passed to the Client#keys method.
32
+ class EmptyPrefixException < Exception; end
33
+
34
+ # The PrefixNotFoundException is raised when the Client#keys method doesn't return a result for a given prefix.
35
+ class PrefixNotFoundException < Exception; end
36
+
37
+ # The NoContentAtURLPathException can be raised by the Client#keys method when the given prefix can't be interpreted.
38
+ class NoContentAtURLPathException < Exception; end
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ # Copyright:: 2011 triAGENS GmbH
2
+ # Author:: Oliver Kiessler (mailto:kiessler@inceedo.com)
3
+ module Simplevoc
4
+ module Open
5
+ # The Simplevoc Hash serves as a simple non-strict mode alternative to the regular client class.
6
+ #
7
+ # hash = Simplevoc::Open::Hash.new('host' => 'localhost', 'port' => 12345)
8
+ #
9
+ # === Examples:
10
+ # hash["key"] = "value" (create or update)
11
+ #
12
+ # hash["key"] (get)
13
+ #
14
+ # hash.delete("key")
15
+ #
16
+ # hash.include?("key") (check for existence of a key in simplevoc)
17
+ #
18
+ # hash.values_at(["key", "key2"]) (get multiple values at once)
19
+ #
20
+ # returns: {"key" => "value", "key2" => "value2"}
21
+ class Hash < Client
22
+ # === Parameters
23
+ # * host = Hostname (required)
24
+ # * port = Port (required)
25
+ # * protocol = "http"/"https", defaults to "http"
26
+ # * strict_mode = true/false, defaults to false
27
+ # * log_level = defaults to LOGGER::INFO
28
+ def initialize(options = {})
29
+ super(options.merge('strict_mode' => false))
30
+ end
31
+
32
+ # Insert key-value pair.
33
+ def []=(key, value)
34
+ insert_or_update(key, value)
35
+ end
36
+ # Alias for Hash#[]=.
37
+ def store(key, value); self[key] = value; end
38
+
39
+ # Getter for key-value pair.
40
+ def [](key)
41
+ get(key, false)
42
+ end
43
+ # Alias for Hash#[].
44
+ def fetch(key); self[key]; end
45
+
46
+ # Checks for the existance of a key-value pair in SimpleVOC.
47
+ def has_key?(key)
48
+ not get(key, false).nil?
49
+ end
50
+ # Alias for Hash#has_key?.
51
+ def include?(key); self.has_key?(key); end
52
+ # Alias for Hash#has_key?.
53
+ def key?(key); self.has_key?(key); end
54
+ # Alias for Hash#has_key?.
55
+ def member?(key); self.has_key?(key); end
56
+
57
+ # Convenience method to fetch values for a given list of keys.
58
+ def values_at(keys = [])
59
+ return_values = {}
60
+ keys.each {|key| return_values[key] = get(key, false)}
61
+ return_values
62
+ end
63
+
64
+ # Alias for Client#flush.
65
+ def clear; flush; end
66
+ end
67
+ end
68
+ end