simplevoc-open 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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