Class: SGS::RedisBase

Inherits:
Object
  • Object
show all
Defined in:
lib/sgs/redis_base.rb

Direct Known Subclasses

Alarm, Config, GPS, MissionStatus, Otto, Timing

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.loadObject

Load the instance variables for the class.



83
84
85
86
87
# File 'lib/sgs/redis_base.rb', line 83

def self.load()
  cls = new
  cls.load
  cls
end

.redisObject



44
45
46
# File 'lib/sgs/redis_base.rb', line 44

def redis
  @@redis ||= Redis.new
end

.redis_handleObject

Convert the class name into something suitable for Redis



256
257
258
# File 'lib/sgs/redis_base.rb', line 256

def self.redis_handle
  self.name.downcase.gsub(/^sgs::/, 'sgs_')
end

.setupObject

Initialize the (sub-)class variables in Redis.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sgs/redis_base.rb', line 56

def self.setup
  cls = new
  cls.instance_variables.each do |var|
    val = cls.instance_variable_get var
    if val.kind_of? Array
      #
      # Arrays are handled separately. We instead
      # use the index to create a series of 'fooN'
      # variables.
      val.size.times do |idx|
        var_init var, val, idx
      end
    else
      var_init var, val
    end
  end
end

.subscribeObject

Subscribe to messages from this particular channel. Each count is sent to the code block. It’s up to the called code block to decide if the count has changed and if so, to read the data from Redis.



167
168
169
170
171
172
173
174
# File 'lib/sgs/redis_base.rb', line 167

def self.subscribe
  redis = Redis.new
  redis.subscribe(redis_handle) do |on|
    on.message do |channel, count|
      yield count.to_i
    end
  end
end

.to_redis(var, local_val, idx = nil) ⇒ Object

Set a variable - convert from Ruby format to Redis format. As of now, we only convert times. Floats and integers are dealt with by Redis (converted to strings, unfortunately).



226
227
228
229
230
231
232
233
234
# File 'lib/sgs/redis_base.rb', line 226

def self.to_redis(var, local_val, idx = nil)
  if local_val
    local_val = local_val[idx] if idx
    if local_val.class == Time
      local_val = local_val.to_f
    end
  end
  local_val
end

.var_init(var, val, idx = nil) ⇒ Object

Initialize a Redis variable.



76
77
78
79
# File 'lib/sgs/redis_base.rb', line 76

def self.var_init(var, val, idx = nil)
  cls = new
  RedisBase.redis.setnx cls.make_redis_name(var, :idx => idx), self.to_redis(var, val, idx)
end

Instance Method Details

#countObject

Retrieve the count



184
185
186
# File 'lib/sgs/redis_base.rb', line 184

def count
  RedisBase.redis.get count_name
end

#count_nameObject

What is the official name of the count instance variable



190
191
192
# File 'lib/sgs/redis_base.rb', line 190

def count_name
  make_redis_name "@count"
end

#loadObject

Load the instance variables for the class.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/sgs/redis_base.rb', line 91

def load
  instance_variables.each do |var|
    lval = instance_variable_get var
    if lval.kind_of? Array
      #
      # It's an array - iterate and read the values.
      lval.size.times do |idx|
        idx_val = lval[idx]
        lval[idx] = redis_read_var var, idx_val.class, :idx => idx
     end
    elsif lval.kind_of? Location
      #
      # ::FIXME:: Yes. this is a hack.
      # This belongs in the Location class itself. It's arguable that a lot
      # of the stuff belongs in the parent class. Perhaps the thing to do
      # is ask the class to return a hash of names and values, and then
      # set them accordingly.
      lval.latitude = redis_read_var var, Float, :name => 'latitude'
      lval.longitude = redis_read_var var, Float, :name => 'longitude'
    else
      lval = redis_read_var var, lval.class
    end
    instance_variable_set var, lval
  end
  true
end

#make_redis_name(var, opts = {}) ⇒ Object

Translate an instance variable into a Redis key name. This is simply the class name, a dot and the instance variable. A bit of jiggery-pokery to convert the instance variable into a proper name. Probably an easier way to do this, but…

Instance method for above



244
245
246
247
248
249
250
251
252
# File 'lib/sgs/redis_base.rb', line 244

def make_redis_name(var, opts = {})
  var_name = opts[:name] || var.to_s.gsub(/^@/, '')
  prefix = opts[:prefix] || self.class.redis_handle
  if opts[:idx]
    "#{prefix}.#{var_name}#{opts[:idx] + 1}"
  else
    "#{prefix}.#{var_name}"
  end
end

#publishObject

Publish the count onto a Redis pub/sub channel. The trick to subscribing to a channel is that whenever there’s a publish, the new count is published as a string. If you subscribe to the channel (usually the class name), you can remember the last received count and decide if there is fresh data. Or, you can just act anyway.



159
160
161
# File 'lib/sgs/redis_base.rb', line 159

def publish
  RedisBase.redis.publish self.class.redis_handle, count.to_s
end

#redis_read_var(var, klass, opts = {}) ⇒ Object

Get an instance variable value from a Redis value.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/sgs/redis_base.rb', line 196

def redis_read_var(var, klass, opts = {})
  redis_name = make_redis_name var, opts
  redis_val = RedisBase.redis.get redis_name
  redis_val = nil if redis_val == ""
  if redis_val
    if not klass or klass == NilClass
      redis_val = true if redis_val == "true"
      redis_val = false if redis_val == "false"
      klass = Float if redis_val =~ /[0-9+-\.]+/
    end
    case
    when klass == Time
      redis_val = Time.at(redis_val.to_f).gmtime
    when klass == Integer
      redis_val = redis_val.to_i
    when klass == Float
      redis_val = redis_val.to_f
    when klass == FalseClass
      redis_val = (redis_val == "true" or redis_val == "TRUE")
    when klass == TrueClass
      redis_val = (redis_val == "true" or redis_val == "TRUE")
    end
  end
  redis_val
end

#saveObject

Write the instance to Redis. IWe produce a Hash of keys and values. From this and inside a Redis “multi” block, we set all the values and finally increment the count. @count is actually an instance variable of redis_base



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/sgs/redis_base.rb', line 122

def save
  #
  # Get the Hash of settable values (including count).
  var_list = {}
  self.instance_variables.each do |var|
    lval = self.instance_variable_get var
    if lval.kind_of? Array
      lval.size.times do |idx|
        var_list[make_redis_name(var, :idx => idx)] = self.class.to_redis var, lval, idx
      end
    elsif lval.kind_of? Location
      #
      # ::FIXME:: Yes. this is a hack. see 'load' above.
      var_list[make_redis_name(var, :name => 'latitude')] = lval.latitude
      var_list[make_redis_name(var, :name => 'longitude')] = lval.longitude
    else
      var_list[make_redis_name(var)] = self.class.to_redis var, lval
    end
  end
  #
  # Inside a multi-block, set all the variables and increment
  # the count.
  RedisBase.redis.multi do |pipeline|
    var_list.each do |key, value|
      pipeline.set key, value
    end
    pipeline.incr count_name
  end
  true
end

#save_and_publishObject

Combined save and publish



178
179
180
# File 'lib/sgs/redis_base.rb', line 178

def save_and_publish
  save && publish
end