Class: LiveResource::RedisClient

Inherits:
Object
  • Object
show all
Includes:
LogHelper
Defined in:
lib/live_resource/redis_client.rb,
lib/live_resource/redis_client/methods.rb,
lib/live_resource/redis_client/attributes.rb,
lib/live_resource/redis_client/registration.rb

Constant Summary collapse

@@logger =
Logger.new(STDERR)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from LogHelper

#logger, #logger=

Constructor Details

#initialize(resource_class, resource_name) ⇒ RedisClient

Returns a new instance of RedisClient.



31
32
33
34
35
36
# File 'lib/live_resource/redis_client.rb', line 31

def initialize(resource_class, resource_name)
  @redis_class = RedisClient.redisized_key(resource_class)
  @redis_name = RedisClient.redisized_key(resource_name)

  self.logger = self.class.logger
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *params, &block) ⇒ Object



38
39
40
41
42
43
44
# File 'lib/live_resource/redis_client.rb', line 38

def method_missing(method, *params, &block)
  if self.class.redis.respond_to? method
    redis_command(method, params, &block)
  else
    super
  end
end

Instance Attribute Details

#redis=(value) ⇒ Object (writeonly)

Sets the attribute redis

Parameters:

  • value

    the value to set the attribute redis to.



25
26
27
# File 'lib/live_resource/redis_client.rb', line 25

def redis=(value)
  @redis = value
end

#redis_classObject (readonly)

Returns the value of attribute redis_class.



26
27
28
# File 'lib/live_resource/redis_client.rb', line 26

def redis_class
  @redis_class
end

#redis_nameObject (readonly)

Returns the value of attribute redis_name.



26
27
28
# File 'lib/live_resource/redis_client.rb', line 26

def redis_name
  @redis_name
end

Class Method Details

.loggerObject



73
74
75
# File 'lib/live_resource/redis_client.rb', line 73

def self.logger
  @@logger
end

.logger=(logger) ⇒ Object



77
78
79
# File 'lib/live_resource/redis_client.rb', line 77

def self.logger=(logger)
  @@logger = logger
end

.redisObject



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/live_resource/redis_client.rb', line 56

def self.redis
  # Hash of Thread -> Redis instances
  @@redis ||= {}
  @@proto_redis ||= Redis.new

  if @@redis[Thread.current].nil?
    @@redis[Thread.current] = @@proto_redis.clone
  end

  @@redis[Thread.current]
end

.redis=(redis) ⇒ Object



68
69
70
71
# File 'lib/live_resource/redis_client.rb', line 68

def self.redis=(redis)
  @@proto_redis = redis
  @@redis = {}
end

.redisized_key(word) ⇒ Object



81
82
83
84
85
86
87
88
89
# File 'lib/live_resource/redis_client.rb', line 81

def self.redisized_key(word)
  word = word.to_s.dup
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
  word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
  word.tr!("-", "_")
  word.gsub!('::', '-')
  word.downcase!
  word
end

Instance Method Details

#allObject



15
16
17
18
19
20
21
22
23
# File 'lib/live_resource/redis_client/registration.rb', line 15

def all
  names = []

  hgetall(instances_key).each_pair do |i, count|
    names << i if (count.to_i > 0)
  end

  names
end

#attribute_read(key, options = {}) ⇒ Object



23
24
25
# File 'lib/live_resource/redis_client/attributes.rb', line 23

def attribute_read(key, options={})
  deserialize(get("#{@redis_class}.#{@redis_name}.attributes.#{key}"))
end

#attribute_watch(key) ⇒ Object



36
37
38
# File 'lib/live_resource/redis_client/attributes.rb', line 36

def attribute_watch(key)
  watch("#{@redis_class}.#{redis_name}.attributes.#{key}")
end

#attribute_write(key, value, options = {}) ⇒ Object



27
28
29
30
31
32
33
34
# File 'lib/live_resource/redis_client/attributes.rb', line 27

def attribute_write(key, value, options={})
  redis_key = "#{@redis_class}.#{@redis_name}.attributes.#{key}"
  if options[:no_overwrite]
    setnx(redis_key, serialize(value))
  else
    set(redis_key, serialize(value))
  end
end

#execObject

Override default (Ruby) exec with Redis exec.



52
53
54
# File 'lib/live_resource/redis_client.rb', line 52

def exec
  redis_command(:exec, nil)
end

#instances_keyObject



3
4
5
# File 'lib/live_resource/redis_client/registration.rb', line 3

def instances_key
  "#{@redis_class}.instances"
end

#method_details(token) ⇒ Object



21
22
23
# File 'lib/live_resource/redis_client/methods.rb', line 21

def method_details(token)
  "#{token.redis_class}.#{token.redis_name}.method.#{token.seq}"
end

#method_discard_result(token) ⇒ Object



129
130
131
132
# File 'lib/live_resource/redis_client/methods.rb', line 129

def method_discard_result(token)
  del result_details(token)
  del method_details(token)
end

#method_done(token) ⇒ Object



50
51
52
# File 'lib/live_resource/redis_client/methods.rb', line 50

def method_done(token)
  lrem methods_in_progress_list, 0, serialize(token)
end

#method_done_with?(method) ⇒ Boolean

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/live_resource/redis_client/methods.rb', line 134

def method_done_with?(method)
  st = serialize(method.token)

  # Need to do a multi/exec so we can atomically look in 3 lists
  # for the token
  multi
  lrange methods_list, 0, -1
  lrange methods_in_progress_list, 0, -1
  lrange result_details(method.token), 0, -1
  result = exec

  if (result[2] != [])
    # Result already pending
    true
  elsif result[0].include?(st) or result[1].include?(st)
    # Still in methods or methods-in-progress
    false
  else
    raise ArgumentError.new("No method #{token} pending")
  end
end

#method_get(token) ⇒ Object



54
55
56
57
58
# File 'lib/live_resource/redis_client/methods.rb', line 54

def method_get(token)
  method = get method_details(token)

  deserialize(method)
end

#method_push(token) ⇒ Object



46
47
48
# File 'lib/live_resource/redis_client/methods.rb', line 46

def method_push(token)
  lpush methods_list, serialize(token)
end

#method_result(method, result) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/live_resource/redis_client/methods.rb', line 81

def method_result(method, result)
  token = method.token

  # Need to watch the method while setting the result; if the caller
  # has given up waiting before we set the result, we don't want to
  # leave extra crud in Redis.
  watch method_details(token)

  unless exists(method_details(token))
    # Caller must have deleted method
    warn "setting result for method #{token}, but caller deleted it"
    unwatch
    return
  end

  begin
    multi
    lpush result_details(token), serialize(result)
    exec
  rescue RuntimeError => e
    # Must have been deleted while we were working on it, bail out.
    warn e
    discard
  end
end

#method_send(method) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/live_resource/redis_client/methods.rb', line 60

def method_send(method)
  unless method.token
    # Choose unique token for this action; retry if token is already in
    # use by another action.
    loop do
      method.token = RemoteMethodToken.new(
                                       @redis_class,
                                       @redis_name,
                                       sprintf("%05d", Kernel.rand(100000)))

      break if setnx(method_details(method.token), serialize(method))
    end
  else
    # Re-serialize current state of method to existing location.
    set method_details(method.token), serialize(method)
  end

  method_push method.token
  method
end

#method_waitObject



41
42
43
44
# File 'lib/live_resource/redis_client/methods.rb', line 41

def method_wait
  token = brpoplpush methods_list, methods_in_progress_list, 0
  deserialize(token)
end

#method_wait_for_result(method, timeout) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/live_resource/redis_client/methods.rb', line 107

def method_wait_for_result(method, timeout)
  token = method.token
  result = nil

  begin
    list, result = brpop result_details(token), timeout

    if result.nil?
      raise RuntimeError.new("timed out waiting for method #{token}")
    end

    result = deserialize(result)
  rescue
    # Clean token from any lists before passing up exception
    method_cleanup(token)
    raise
  ensure
    # Clear out original method call details
    del method_details(token)
  end
end

#methods_in_progress_listObject



17
18
19
# File 'lib/live_resource/redis_client/methods.rb', line 17

def methods_in_progress_list
  "#{@redis_class}.#{@redis_name}.methods_in_progress"
end

#methods_listObject



13
14
15
# File 'lib/live_resource/redis_client/methods.rb', line 13

def methods_list
  "#{@redis_class}.#{@redis_name}.methods_pending"
end

#registerObject



7
8
9
# File 'lib/live_resource/redis_client/registration.rb', line 7

def register
  hincrby instances_key, @redis_name, 1
end

#register_attributes(attributes) ⇒ Object



11
12
13
14
15
# File 'lib/live_resource/redis_client/attributes.rb', line 11

def register_attributes(attributes)
  unless attributes.empty?
    sadd remote_attributes_key, attributes
  end
end

#register_methods(methods) ⇒ Object



29
30
31
32
33
# File 'lib/live_resource/redis_client/methods.rb', line 29

def register_methods(methods)
  unless methods.empty?
    sadd remote_methods_key, methods
  end
end

#registered_attributesObject



17
18
19
20
21
# File 'lib/live_resource/redis_client/attributes.rb', line 17

def registered_attributes
  attributes = smembers remote_attributes_key

  attributes.map { |a| a.to_sym }
end

#registered_methodsObject



35
36
37
38
39
# File 'lib/live_resource/redis_client/methods.rb', line 35

def registered_methods
  methods = smembers remote_methods_key

  methods.map { |m| m.to_sym }
end

#remote_attributes_keyObject



3
4
5
6
7
8
9
# File 'lib/live_resource/redis_client/attributes.rb', line 3

def remote_attributes_key
  if @redis_class == "class"
    "#{@redis_name}.class_attributes"
  else
    "#{@redis_class}.attributes"
  end
end

#remote_methods_keyObject



5
6
7
8
9
10
11
# File 'lib/live_resource/redis_client/methods.rb', line 5

def remote_methods_key
  if @redis_class == "class"
    "#{@redis_name}.class_methods"
  else
    "#{@redis_class}.methods"
  end
end

#respond_to?(method) ⇒ Boolean

Returns:

  • (Boolean)


46
47
48
49
# File 'lib/live_resource/redis_client.rb', line 46

def respond_to?(method)
  return true if self.class.redis.respond_to?(method)
  super
end

#result_details(token) ⇒ Object



25
26
27
# File 'lib/live_resource/redis_client/methods.rb', line 25

def result_details(token)
  "#{token.redis_class}.#{token.redis_name}.result.#{token.seq}"
end

#unregisterObject



11
12
13
# File 'lib/live_resource/redis_client/registration.rb', line 11

def unregister
  hincrby instances_key, @redis_name, -1
end