restruct 0.0.3 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 348743cb28cb1159c9d59162d3f39233424afc66
4
- data.tar.gz: ec7f662e9b67b0762f0cd5dd7f5314e37cafecca
3
+ metadata.gz: 220d7c61ca9fe2daff93071982a18b7934967c71
4
+ data.tar.gz: bc038fb7d2126b6d30d770ab1d28687b031111a0
5
5
  SHA512:
6
- metadata.gz: be6d4f14461f91b75265eef5a327709c192f966d26a56dec4ff83ec0c8563a7ad5070653a240c8e96f2c8fb5222d3473d83aae10eee72617c795cc9fb7577cd7
7
- data.tar.gz: 3543ef892445534d84dee4aca23430a7a7c0804dbeb45c7a681018152715ae14b29e3cefce3fdb71a7416628b95b7f25f628e75ee19cb9a2f55ae95a17b338a8
6
+ metadata.gz: 3357b7800bb6da26bdb54675d097300cd9f33b652f63ad7c5e9d1a0a71f4038459f20e8bade86f12d2b6562f57e08af5ed74400139307cbaea20d35fa5b2273f
7
+ data.tar.gz: 5740e1bc9699cef63a9c5632c2c6198a48164b99aaf78e3b60bef61983352b5fcc6d48ce3a8b8524af50b673a905caa65c1655e97c3bc4500ee9e0e21fc5b4f5
@@ -1 +1 @@
1
- ruby 2.1
1
+ ruby 2.0
@@ -4,23 +4,28 @@ require 'forwardable'
4
4
  require 'securerandom'
5
5
 
6
6
  require_relative 'restruct/version'
7
+ require_relative 'restruct/errors'
7
8
  require_relative 'restruct/structure'
8
9
  require_relative 'restruct/id'
9
10
  require_relative 'restruct/array'
10
11
  require_relative 'restruct/set'
11
12
  require_relative 'restruct/hash'
13
+ require_relative 'restruct/queue'
12
14
  require_relative 'restruct/nested_hash'
13
15
  require_relative 'restruct/marshalizable'
14
16
  require_relative 'restruct/marshal_array'
15
17
  require_relative 'restruct/marshal_set'
16
18
  require_relative 'restruct/marshal_hash'
17
- require_relative 'restruct/batch'
19
+ require_relative 'restruct/marshal_queue'
20
+ require_relative 'restruct/locker'
21
+ require_relative 'restruct/connection'
22
+
18
23
 
19
24
  module Restruct
20
25
 
21
26
  extend ClassConfig
22
27
 
23
- attr_config :redis, Redic.new
28
+ attr_config :connection, Connection.new
24
29
  attr_config :id_separator, ':'
25
30
  attr_config :id_generator, ->() { Id.new(:restruct)[SecureRandom.uuid] }
26
31
 
@@ -7,7 +7,7 @@ module Restruct
7
7
  def_delegators :to_a, :uniq, :join, :reverse, :+, :-, :&, :|
8
8
 
9
9
  def at(index)
10
- deserialize redis.call('LINDEX', id, index)
10
+ deserialize connection.call('LINDEX', id, index)
11
11
  end
12
12
 
13
13
  def values_at(*args)
@@ -48,11 +48,11 @@ module Restruct
48
48
  validate_index_type! index
49
49
  validate_index_bounds! index
50
50
 
51
- redis.call 'LSET', id, index, serialize(element)
51
+ connection.lazy 'LSET', id, index, serialize(element)
52
52
  end
53
53
 
54
54
  def push(*elements)
55
- redis.call 'RPUSH', id, *elements.map { |e| serialize e }
55
+ connection.lazy 'RPUSH', id, *elements.map { |e| serialize e }
56
56
  self
57
57
  end
58
58
  alias_method :<<, :push
@@ -70,7 +70,7 @@ module Restruct
70
70
 
71
71
  def pop(count=1)
72
72
  if count == 1
73
- deserialize redis.call('RPOP', id)
73
+ deserialize connection.lazy('RPOP', id)
74
74
  else
75
75
  [count, size].min.times.map { pop }.reverse
76
76
  end
@@ -78,15 +78,15 @@ module Restruct
78
78
 
79
79
  def shift(count=1)
80
80
  if count == 1
81
- deserialize redis.call('LPOP', id)
81
+ deserialize connection.lazy('LPOP', id)
82
82
  else
83
83
  [count, size].min.times.map { shift }
84
84
  end
85
85
  end
86
86
 
87
87
  def delete(element)
88
- removed_count = redis.call 'LREM', id, 0, serialize(element)
89
- removed_count > 0 ? element : nil
88
+ removed_count = connection.lazy 'LREM', id, 0, serialize(element)
89
+ removed_count && removed_count > 0 ? element : nil
90
90
  end
91
91
 
92
92
  def delete_at(index)
@@ -117,7 +117,7 @@ module Restruct
117
117
  end
118
118
 
119
119
  def size
120
- redis.call 'LLEN', id
120
+ connection.call 'LLEN', id
121
121
  end
122
122
  alias_method :count, :size
123
123
  alias_method :length, :size
@@ -161,7 +161,7 @@ module Restruct
161
161
 
162
162
  def range(start, stop)
163
163
  return nil if start > size
164
- redis.call('LRANGE', id, start, stop).map { |e| deserialize e }
164
+ connection.call('LRANGE', id, start, stop).map { |e| deserialize e }
165
165
  end
166
166
 
167
167
  def validate_index_type!(index)
@@ -0,0 +1,71 @@
1
+ module Restruct
2
+ class Connection
3
+
4
+ def initialize(*args)
5
+ @redis = Redic.new *args
6
+ @scripts = {}
7
+ @nesting = ::Hash.new { |h,k| h[k] = 0 }
8
+ end
9
+
10
+ def call(*args)
11
+ raise ArgumentError if args.empty?
12
+ redis.call! *args
13
+ rescue RuntimeError => ex
14
+ raise ConnectionErrorFactory.create(ex)
15
+ end
16
+
17
+ def lazy(*args)
18
+ if nested?
19
+ redis.queue *args
20
+ nil
21
+ else
22
+ call *args
23
+ end
24
+ end
25
+
26
+ def script(lua_src, *args)
27
+ scripts[lua_src] ||= call 'SCRIPT', 'LOAD', lua_src
28
+ call 'EVALSHA', scripts[lua_src], *args
29
+ rescue NoScriptError
30
+ scripts.delete lua_src
31
+ retry
32
+ end
33
+
34
+ def batch
35
+ incr_nesting
36
+ begin
37
+ result = yield
38
+ ensure
39
+ decr_nesting
40
+ end
41
+ commit unless nested?
42
+ rescue => ex
43
+ redis.clear unless nested?
44
+ raise ex
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :redis, :scripts
50
+
51
+ def nested?
52
+ @nesting[Thread.current.object_id] > 0
53
+ end
54
+
55
+ def incr_nesting
56
+ @nesting[Thread.current.object_id] += 1
57
+ end
58
+
59
+ def decr_nesting
60
+ @nesting[Thread.current.object_id] -= 1
61
+ end
62
+
63
+ def commit
64
+ results = redis.commit
65
+ error = results.detect { |r| r === RuntimeError }
66
+ raise ConnectionErrorFactory.create(error) if error
67
+ results
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,54 @@
1
+ module Restruct
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ConnectionErrorFactory
6
+ def self.create(exception)
7
+ error_type = NoScriptError.match?(exception) ? NoScriptError : ConnectionError
8
+ error_type.new exception
9
+ end
10
+ end
11
+
12
+ class ConnectionError < Error
13
+
14
+ def initialize(inner_exception)
15
+ @inner_exception = inner_exception
16
+ end
17
+
18
+ def message
19
+ @inner_exception.message
20
+ end
21
+
22
+ def backtrace
23
+ @inner_exception.backtrace
24
+ end
25
+
26
+ end
27
+
28
+ class NoScriptError < ConnectionError
29
+ def self.match?(exception)
30
+ exception.message.start_with? 'NOSCRIPT No matching script'
31
+ end
32
+ end
33
+
34
+ class LockerError < Error
35
+
36
+ def initialize(inner_exception)
37
+ @inner_exception = inner_exception
38
+ end
39
+
40
+ def message
41
+ @parsed_message ||= parse_message @inner_exception.message
42
+ end
43
+
44
+ def backtrace
45
+ @inner_exception.backtrace
46
+ end
47
+
48
+ def parse_message(message)
49
+ match_data = /^(ERR Error running script.*@user_script.*user_script.*: )/.match message
50
+ match_data ? message.gsub(match_data.captures.first,'').strip : message
51
+ end
52
+ end
53
+
54
+ end
@@ -7,7 +7,7 @@ module Restruct
7
7
  def_delegators :to_h, :merge, :flatten, :invert
8
8
 
9
9
  def [](key)
10
- deserialize redis.call('HGET', id, key)
10
+ deserialize connection.call('HGET', id, key)
11
11
  end
12
12
 
13
13
  def fetch(key, default=nil, &block)
@@ -25,7 +25,7 @@ module Restruct
25
25
  end
26
26
 
27
27
  def store(key, value)
28
- redis.call 'HSET', id, key, serialize(value)
28
+ connection.lazy 'HSET', id, key, serialize(value)
29
29
  value
30
30
  end
31
31
  alias_method :[]=, :store
@@ -38,7 +38,7 @@ module Restruct
38
38
 
39
39
  def delete(key)
40
40
  value = self[key]
41
- redis.call 'HDEL', id, key
41
+ connection.lazy 'HDEL', id, key
42
42
  value
43
43
  end
44
44
 
@@ -59,11 +59,11 @@ module Restruct
59
59
  end
60
60
 
61
61
  def keys
62
- redis.call 'HKEYS', id
62
+ connection.call 'HKEYS', id
63
63
  end
64
64
 
65
65
  def values
66
- redis.call('HVALS', id).map { |v| deserialize v }
66
+ connection.call('HVALS', id).map { |v| deserialize v }
67
67
  end
68
68
 
69
69
  def values_at(*keys)
@@ -71,7 +71,7 @@ module Restruct
71
71
  end
72
72
 
73
73
  def key?(key)
74
- redis.call('HEXISTS', id, key) == 1
74
+ connection.call('HEXISTS', id, key) == 1
75
75
  end
76
76
  alias_method :has_key?, :key?
77
77
 
@@ -81,7 +81,7 @@ module Restruct
81
81
  alias_method :has_value?, :value?
82
82
 
83
83
  def size
84
- redis.call 'HLEN', id
84
+ connection.call 'HLEN', id
85
85
  end
86
86
  alias_method :count, :size
87
87
  alias_method :length, :size
@@ -104,7 +104,7 @@ module Restruct
104
104
  end
105
105
 
106
106
  def to_h
107
- redis.call('HGETALL', id).each_slice(2).each_with_object({}) do |(k,v), hash|
107
+ connection.call('HGETALL', id).each_slice(2).each_with_object({}) do |(k,v), hash|
108
108
  hash[k] = deserialize v
109
109
  end
110
110
  end
@@ -0,0 +1,43 @@
1
+ module Restruct
2
+ class Locker < Structure
3
+
4
+ REGISTER_LUA = File.read "#{File.dirname(__FILE__)}/../../lua/register.lua"
5
+ UNREGISTER_LUA = File.read "#{File.dirname(__FILE__)}/../../lua/unregister.lua"
6
+
7
+ def lock(key, &block)
8
+ _lock key, false, &block
9
+ end
10
+
11
+ def lock!(key, &block)
12
+ _lock key, true, &block
13
+ end
14
+
15
+ alias_method :unlock!, :destroy
16
+
17
+ alias_method :locked?, :exists?
18
+
19
+ def key
20
+ connection.call('HGET', id, 'key')
21
+ end
22
+ alias_method :locked_by, :key
23
+
24
+ def to_h
25
+ ::Hash[connection.call('HGETALL', id).each_slice(2).to_a]
26
+ end
27
+ alias_method :to_primitive, :to_h
28
+
29
+ private
30
+
31
+ def _lock(key, exclusive)
32
+ connection.script REGISTER_LUA, 0, id, key, exclusive
33
+ begin
34
+ yield
35
+ ensure
36
+ connection.script UNREGISTER_LUA, 0, id
37
+ end
38
+ rescue Restruct::ConnectionError => ex
39
+ raise LockerError.new ex
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ module Restruct
2
+ class MarshalQueue < Queue
3
+ include Marshalizable
4
+ end
5
+ end
@@ -9,7 +9,7 @@ module Restruct
9
9
  const_set :TYPE, type
10
10
 
11
11
  def [](key)
12
- self.class::TYPE.new id: id[key], redis: redis, parent: self
12
+ self.class::TYPE.new id: id[key], connection: connection, parent: self
13
13
  end
14
14
 
15
15
  def fetch(key)
@@ -39,7 +39,7 @@ module Restruct
39
39
 
40
40
  def keys
41
41
  sections = id.sections.count + 1
42
- redis.call('KEYS', id['*']).map do |k|
42
+ connection.call('KEYS', id['*']).map do |k|
43
43
  Id.new(k).sections.take(sections).last
44
44
  end.uniq.sort
45
45
  end
@@ -0,0 +1,38 @@
1
+ module Restruct
2
+ class Queue < Structure
3
+
4
+ def push(object)
5
+ connection.lazy 'RPUSH', id, serialize(object)
6
+ end
7
+
8
+ def pop
9
+ deserialize connection.lazy('LPOP', id)
10
+ end
11
+
12
+ def size
13
+ connection.call 'LLEN', id
14
+ end
15
+ alias_method :count, :size
16
+ alias_method :length, :size
17
+
18
+ def empty?
19
+ size == 0
20
+ end
21
+
22
+ def to_a
23
+ connection.call('LRANGE', id, 0, -1).map { |o| deserialize o }
24
+ end
25
+ alias_method :to_primitive, :to_a
26
+
27
+ private
28
+
29
+ def serialize(string)
30
+ string
31
+ end
32
+
33
+ def deserialize(string)
34
+ string
35
+ end
36
+
37
+ end
38
+ end
@@ -57,7 +57,7 @@ module Restruct
57
57
  end
58
58
 
59
59
  def size
60
- redis.call 'SCARD', id
60
+ connection.call 'SCARD', id
61
61
  end
62
62
  alias_method :count, :size
63
63
  alias_method :length, :size
@@ -67,7 +67,7 @@ module Restruct
67
67
  end
68
68
 
69
69
  def include?(member)
70
- redis.call('SISMEMBER', id, serialize(member)) == 1
70
+ connection.call('SISMEMBER', id, serialize(member)) == 1
71
71
  end
72
72
 
73
73
  def each(&block)
@@ -75,7 +75,7 @@ module Restruct
75
75
  end
76
76
 
77
77
  def to_a
78
- redis.call('SMEMBERS', id).map { |e| deserialize e }
78
+ connection.call('SMEMBERS', id).map { |e| deserialize e }
79
79
  end
80
80
 
81
81
  def to_set
@@ -99,11 +99,11 @@ module Restruct
99
99
  private
100
100
 
101
101
  def _add(*members)
102
- redis.call 'SADD', id, *members.map { |m| serialize m }
102
+ connection.lazy 'SADD', id, *members.map { |m| serialize m }
103
103
  end
104
104
 
105
105
  def _delete(*members)
106
- redis.call 'SREM', id, *members.map { |m| serialize m }
106
+ connection.lazy 'SREM', id, *members.map { |m| serialize m }
107
107
  end
108
108
 
109
109