Class: Redstruct::List
- Includes:
- Utils::Iterable, Utils::Scriptable
- Defined in:
- lib/redstruct/list.rb
Overview
Class to manipulate redis lists, modeled after Ruby's Array class. TODO: Add maximum instance variable and modify all methods (where applicable) to take it into account.
Instance Attribute Summary
Attributes inherited from Struct
Instance Method Summary collapse
-
#<<(item) ⇒ Integer
Pushes the given element onto the list.
-
#[](index) ⇒ String?
Returns the item located at index.
-
#[]=(index, value) ⇒ Boolean
Sets or updates the value for item at index.
-
#append(*items, max: 0, exists: false) ⇒ Integer
(also: #push)
Appends the given items (from the right) to the list.
-
#clear ⇒ Object
Clears the set by simply removing the key from the DB.
-
#empty? ⇒ Boolean
Checks if the set is empty by checking if the key actually exists on the underlying redis db.
-
#insert(value, index) ⇒ Object
Inserts the given value at the given zero-based index.
-
#pop(size = 1, timeout: nil) ⇒ nil, String
Pops an item from the list, optionally blocking to wait until the list is non-empty.
-
#popshift(list, timeout: nil) ⇒ String
Pops an element from this list and shifts it onto the given list.
-
#prepend(*items, max: nil, exists: false) ⇒ Integer
(also: #unshift)
Prepends the given items (from the right) to the list.
-
#remove(value, count: 1) ⇒ Integer
Removes the given item from the list.
-
#shift(size = 1, timeout: nil) ⇒ nil, String
Shifts an item from the list, optionally blocking to wait until the list is non-empty.
-
#size ⇒ Integer
Checks how many items are in the list.
-
#slice(start: 0, length: -1)) ⇒ Array<String>
Returns a slice of the list starting at start (inclusively), up to length (inclusively).
-
#to_a ⇒ Array<String>
Loads all items into memory and returns an array.
-
#to_enum(match: '*', count: 10) ⇒ Enumerator
Since the list can be modified in between loops, this does not guarantee completion of the operation, nor that every single element of the list will be visited once; rather, it guarantees that it loops until no more elements are returned, using an incrementing offset.
Methods inherited from Struct
#delete, #dump, #exists?, #expire, #expire_at, #initialize, #inspectable_attributes, #persist, #restore, #ttl, #type
Constructor Details
This class inherits a constructor from Redstruct::Struct
Instance Method Details
#<<(item) ⇒ Integer
Pushes the given element onto the list. As << is a binary operator, it can only take one argument in. It's more of a convenience method.
86 87 88 |
# File 'lib/redstruct/list.rb', line 86 def <<(item) return append(item) end |
#[](index) ⇒ String?
Returns the item located at index
31 32 33 |
# File 'lib/redstruct/list.rb', line 31 def [](index) return self.connection.lindex(@key, index.to_i) end |
#[]=(index, value) ⇒ Boolean
Sets or updates the value for item at index
40 41 42 |
# File 'lib/redstruct/list.rb', line 40 def []=(index, value) return coerce_bool(set_script(keys: @key, argv: [index.to_i, value])) end |
#append(*items, max: 0, exists: false) ⇒ Integer Also known as: push
Appends the given items (from the right) to the list
70 71 72 73 74 75 76 77 78 79 |
# File 'lib/redstruct/list.rb', line 70 def append(*items, max: 0, exists: false) max = max.to_i results = if max.positive? || exists push_and_trim_script(keys: @key, argv: [max - 1, false, exists] + items) else self.connection.rpush(@key, items) end return results end |
#clear ⇒ Object
Clears the set by simply removing the key from the DB
17 18 19 |
# File 'lib/redstruct/list.rb', line 17 def clear delete end |
#empty? ⇒ Boolean
Checks if the set is empty by checking if the key actually exists on the underlying redis db
24 25 26 |
# File 'lib/redstruct/list.rb', line 24 def empty? return !exists? end |
#insert(value, index) ⇒ Object
Inserts the given value at the given zero-based index. TODO: Support multiple insertion like Array#insert? The biggest issue here is that concatenating lists in Lua is O(n), so on very large lists, this operation would become slow. There are Redis Modules which implement splice operations (so a O(1) list split/merge), but there's no way to guarantee if the module will be present. Perhaps provide optional support if the module is detected?
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/redstruct/list.rb', line 53 def insert(value, index) result = case index when 0 then prepend(value) when -1 then append(value) else index += 1 if index.negative? insert_script(keys: @key, argv: [value, index]) end return coerce_bool(result) end |
#pop(size = 1, timeout: nil) ⇒ nil, String
Pops an item from the list, optionally blocking to wait until the list is non-empty
115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/redstruct/list.rb', line 115 def pop(size = 1, timeout: nil) raise ArgumentError, 'size must be positive' unless size.positive? if timeout.nil? return self.connection.rpop(@key) if size == 1 return shift_pop_script(keys: @key, argv: [-size, -1, 1]) else raise ArgumentError, 'timeout is only supported if size == 1' unless size == 1 return self.connection.brpop(@key, timeout: timeout)&.last end end |
#popshift(list, timeout: nil) ⇒ String
Pops an element from this list and shifts it onto the given list.
146 147 148 149 150 151 152 153 154 |
# File 'lib/redstruct/list.rb', line 146 def popshift(list, timeout: nil) raise ArgumentError, 'list must respond to #key' unless list.respond_to?(:key) if timeout.nil? return self.connection.rpoplpush(@key, list.key) else return self.connection.brpoplpush(@key, list.key, timeout: timeout) end end |
#prepend(*items, max: nil, exists: false) ⇒ Integer Also known as: unshift
Prepends the given items (from the right) to the list
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/redstruct/list.rb', line 95 def prepend(*items, max: nil, exists: false) max = max.to_i # redis literally prepends each element one at a time, so 1 2 will end up 2 1 # to keep behaviour in sync with Array#unshift we preemptively reverse the list items = items.reverse results = if max.positive? || exists push_and_trim_script(keys: @key, argv: [max - 1, true, exists] + items) else self.connection.lpush(@key, items) end return results end |
#remove(value, count: 1) ⇒ Integer
Removes the given item from the list.
161 162 163 |
# File 'lib/redstruct/list.rb', line 161 def remove(value, count: 1) self.connection.lrem(@key, count.to_i, value) end |
#shift(size = 1, timeout: nil) ⇒ nil, String
Shifts an item from the list, optionally blocking to wait until the list is non-empty
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/redstruct/list.rb', line 130 def shift(size = 1, timeout: nil) raise ArgumentError, 'size must be positive' unless size.positive? if timeout.nil? return self.connection.lpop(@key) if size == 1 return shift_pop_script(keys: @key, argv: [0, size - 1, 0]) else raise ArgumentError, 'timeout is only supported if size == 1' unless size == 1 return self.connection.blpop(@key, timeout: timeout)&.last end end |
#size ⇒ Integer
Checks how many items are in the list.
167 168 169 |
# File 'lib/redstruct/list.rb', line 167 def size return self.connection.llen(@key) end |
#slice(start: 0, length: -1)) ⇒ Array<String>
Returns a slice of the list starting at start (inclusively), up to length (inclusively)
177 178 179 180 181 182 |
# File 'lib/redstruct/list.rb', line 177 def slice(start: 0, length: -1) length = length.to_i end_index = length.positive? ? start + length - 1 : length return self.connection.lrange(@key, start.to_i, end_index) end |
#to_a ⇒ Array<String>
Loads all items into memory and returns an array. NOTE: if the list is expected to be large, use to_enum
187 188 189 |
# File 'lib/redstruct/list.rb', line 187 def to_a return slice(start: 0, length: -1) end |
#to_enum(match: '*', count: 10) ⇒ Enumerator
Since the list can be modified in between loops, this does not guarantee completion of the operation, nor that every single element of the list will be visited once; rather, it guarantees that it loops until no more elements are returned, using an incrementing offset. This means that should elements be removed in the meantime, they will not be seen, and others might be skipped as a result of this. If elements are added, it is however not an issue (although keep in mind that if elements are added faster than consumed, this can loop forever)
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/redstruct/list.rb', line 200 def to_enum(match: '*', count: 10) pattern = Regexp.compile("^#{Regexp.escape(match).gsub('\*', '.*')}$") return Enumerator.new do |yielder| offset = 0 loop do items = slice(start: offset, length: offset + count) offset += items.size matched = items.select { |item| item =~ pattern } yielder << matched unless matched.empty? raise StopIteration if items.size < count end end end |