redis_hash 0.9.0 → 0.9.1

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
  SHA256:
3
- metadata.gz: 14c0920d865fbe1f15499c0c50876cef5f72d179c49ad352ad61cae430722c06
4
- data.tar.gz: cc78b095bd8043d381832900daad5ae60fcdf7b3ae0bdac2c354d604cca151e9
3
+ metadata.gz: d3131d41ea67e65aa544a9ebdb7da08c0b0611a0f1439715af115e8c33ff9c96
4
+ data.tar.gz: 3dc68b7eaf87c5730073d4f4eb71c64d7369220f9c334e7d89fe495a1b4538af
5
5
  SHA512:
6
- metadata.gz: b092d1ca79de529e0e8ab93aa738c173d6719c1f01ffa2a5a35cd16376befa11fcbeaf2f4ea3b136c24d545fca2446b7464ef836cf4e0dacd679dcd4a3e047aa
7
- data.tar.gz: 1e40e8fa91ee20f8c5160d0ac33b4a99e85c12e8ff758448240364e2339526afe3aee03964c943b80666cdf210cee3de23ff9436cad0fc5c65c1f1a572203251
6
+ metadata.gz: a636758a36b3167865640201ac84227360d441be44b441ea17586681ed7167145b31e3e0d497783ee770840de87c3ed9b6aea58c483da140d39581a6beb0e315
7
+ data.tar.gz: f8c94d4d75e2c9bbc17a658d66c898dfb863955360779e5480973f9f5dd810124a513e5fc9af56dc0194d819c4da3d45118e7d9d5809662992d4e3ba92b61473
data/lib/redis_hash.rb CHANGED
@@ -2,8 +2,10 @@ require "active_support"
2
2
 
3
3
  require "redis"
4
4
 
5
- require "technologic"
6
-
7
5
  require "redis_hash/version"
8
6
 
9
- module RedisHash; end
7
+ require "redis_hash/base"
8
+
9
+ module RedisHash
10
+ class AlreadyDefinedError < StandardError; end
11
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "concerns/adapter"
4
+ require_relative "concerns/default"
5
+ require_relative "concerns/callbacks"
6
+ require_relative "concerns/core"
7
+ require_relative "concerns/identity"
8
+ require_relative "concerns/accessors"
9
+ require_relative "concerns/comparisons"
10
+ require_relative "concerns/predicates"
11
+ require_relative "concerns/insertions"
12
+ require_relative "concerns/deletions"
13
+ require_relative "concerns/enumerators"
14
+ require_relative "concerns/mutations"
15
+ require_relative "concerns/converters"
16
+ require_relative "concerns/counters"
17
+ require_relative "concerns/expiration"
18
+
19
+ module RedisHash
20
+ class Base
21
+ include RedisHash::Adapter
22
+ include RedisHash::Default
23
+ include RedisHash::Callbacks
24
+ include RedisHash::Core
25
+ include RedisHash::Identity
26
+ include RedisHash::Accessors
27
+ include RedisHash::Comparisons
28
+ include RedisHash::Predicates
29
+ include RedisHash::Insertions
30
+ include RedisHash::Deletions
31
+ include RedisHash::Enumerators
32
+ include RedisHash::Mutations
33
+ include RedisHash::Converters
34
+ include RedisHash::Counters
35
+ include RedisHash::Expiration
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Accessors allow for the retrieval of data from the Hash.
4
+ module RedisHash
5
+ module Accessors
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :hget, :hkeys, :hlen, :hmget, :hvals, to: :redis
10
+ delegate :assoc, :compact, :dig, :fetch_values, :flatten, :key, :rassoc, :rehash, to: :to_h
11
+ end
12
+
13
+ def [](field)
14
+ hget(redis_key, field) || default(field)
15
+ end
16
+
17
+ def fetch(field, default = nil)
18
+ value = self[field]
19
+ return value if value.present?
20
+ return yield(field) if block_given?
21
+ return default unless default.nil?
22
+
23
+ raise KeyError, "key not found: \"#{field}\""
24
+ end
25
+
26
+ def keys
27
+ hkeys(redis_key)
28
+ end
29
+
30
+ def length
31
+ hlen(redis_key)
32
+ end
33
+ alias_method :size, :length
34
+
35
+ def values
36
+ hvals(redis_key)
37
+ end
38
+
39
+ def values_at(*fields)
40
+ hmget(*fields.flatten.unshift(redis_key))
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adapter which handles the storage and retrieval of hash data in Redis.
4
+ module RedisHash
5
+ module Adapter
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :redis, :redis_key, :redis_ttl
10
+
11
+ delegate :default_redis, :default_redis_key, :default_redis_ttl, to: :class
12
+
13
+ private
14
+
15
+ def initialize_redis(redis, redis_key, redis_ttl)
16
+ @redis = redis || default_redis
17
+ @redis_key = redis_key || default_redis_key
18
+ @redis_ttl = redis_ttl || default_redis_ttl
19
+ end
20
+ end
21
+
22
+ class_methods do
23
+ def default_redis
24
+ Redis.new
25
+ end
26
+
27
+ def default_redis_key
28
+ SecureRandom.hex
29
+ end
30
+
31
+ def default_redis_ttl
32
+ nil
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Callbacks provide an extensible mechanism for hooking into a RedisHash.
4
+ module RedisHash
5
+ module Callbacks
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveSupport::Callbacks
10
+ define_callbacks :initialize, :insertion, :deletion
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Comparisons encompasses equality and set inclusion.
4
+ module RedisHash
5
+ module Comparisons
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :<, :<=, :>, :>=, :compare_by_identity, :compare_by_identity?, to: :to_h
10
+ end
11
+
12
+ def eql?(other)
13
+ other.hash == hash
14
+ end
15
+ alias_method :==, :eql?
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Converters allow for other objects to be coerced into a RedisHash.
4
+ module RedisHash
5
+ module Converters
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def [](*arguments)
10
+ options = block_given? ? yield({}) : {}
11
+ new(**options).merge!(Hash[*arguments])
12
+ end
13
+
14
+ def try_convert(object, &block)
15
+ return object if object.is_a?(RedisHash)
16
+
17
+ self[object, &block] if object.respond_to?(:to_hash)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A RedisHash is a wrapper around a h<key> stored in Redis.
4
+ module RedisHash
5
+ module Core
6
+ extend ActiveSupport::Concern
7
+
8
+ def initialize(default = nil, redis: nil, redis_key: nil, redis_ttl: nil, &block)
9
+ run_callbacks(:initialize) do
10
+ initialize_default(default, &block)
11
+ initialize_redis(redis, redis_key, redis_ttl)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Counters allow for the incrementing and decrementing of numeric values into the RedisHash.
4
+ module RedisHash
5
+ module Counters
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :hincrby, :hincrbyfloat, to: :redis
10
+
11
+ private
12
+
13
+ def increment_field_by(field, by, modifier: 1)
14
+ raise ArgumentError, "by must be greater than or equal to 0" if by < 0
15
+
16
+ by *= modifier
17
+
18
+ return hincrbyfloat(redis_key, field, by) if by.is_a?(Float)
19
+
20
+ hincrby(redis_key, field, by)
21
+ end
22
+ end
23
+
24
+ def increment(field, by: 1)
25
+ increment_field_by(field, by)
26
+ end
27
+
28
+ def decrement(field, by: 1)
29
+ increment_field_by(field, by, modifier: -1)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A Default allows a static or procedurally generated value to be returned on failed lookups.
4
+ module RedisHash
5
+ module Default
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :default_proc
10
+
11
+ private
12
+
13
+ def initialize_default(default, &block)
14
+ raise ArgumentError, "cannot specify both block and static default" if block_given? && default.present?
15
+
16
+ set_default(default, &block)
17
+ end
18
+
19
+ def set_default(default, &block)
20
+ self.default = default if default.present?
21
+ self.default_proc = block if block_given?
22
+ end
23
+
24
+ def to_default(field = nil, allow_nil_field: true)
25
+ @default.presence || (default_proc&.call(self, field) if !field.nil? || allow_nil_field)
26
+ end
27
+
28
+ def validate_proc(proc)
29
+ raise TypeError, "wrong default_proc type #{proc.class.name} (expected Proc)" unless proc.is_a? Proc
30
+
31
+ validate_lambda_arity(proc.arity) if proc.lambda?
32
+ end
33
+
34
+ def validate_lambda_arity(arity)
35
+ raise TypeError, "default_proc takes two arguments (2 for #{arity})" if arity >= 0 && arity != 2
36
+ end
37
+ end
38
+
39
+ def default=(value)
40
+ @default = value
41
+ @default_proc = nil
42
+ end
43
+
44
+ def default_proc=(value)
45
+ validate_proc(value) unless value.nil?
46
+
47
+ @default = nil
48
+ @default_proc = value
49
+ end
50
+
51
+ def default(field = nil)
52
+ to_default(field, allow_nil_field: false)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Deletions allow for the removal of data from the Hash.
4
+ module RedisHash
5
+ module Deletions
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :del, :hdel, to: :redis
10
+ end
11
+
12
+ def clear
13
+ del(redis_key) and {}
14
+ end
15
+
16
+ def delete(field)
17
+ run_callbacks(:deletion) do
18
+ value = self[field]
19
+ result = hdel(redis_key, field)
20
+ (result == 0 && block_given?) ? yield(field) : value
21
+ end
22
+ end
23
+
24
+ def shift
25
+ return to_default if empty?
26
+
27
+ field = keys.first
28
+ [ field, delete(field) ]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Enumerators allow for the traversal and manipulation of data in the Hash.
4
+ module RedisHash
5
+ module Enumerators
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :each, :each_pair, :each_key, :each_value, :reject, :select, :transform_values, to: :to_h
10
+ end
11
+
12
+ def delete_if
13
+ return enum_for(__method__) unless block_given?
14
+
15
+ each { |field, value| delete(field) if yield(field, value) }
16
+
17
+ to_h
18
+ end
19
+
20
+ def keep_if
21
+ return enum_for(__method__) unless block_given?
22
+
23
+ delete_if { |field, value| !yield(field, value) }
24
+ end
25
+
26
+ def reject!(&block)
27
+ return enum_for(__method__) unless block_given?
28
+
29
+ original = to_h
30
+ delete_if(&block)
31
+ current = to_h
32
+
33
+ (original == current) ? nil : current
34
+ end
35
+
36
+ def select!
37
+ return enum_for(__method__) unless block_given?
38
+
39
+ reject! { |*arguments| !yield(*arguments) }
40
+ end
41
+
42
+ def transform_values!
43
+ return enum_for(__method__) unless block_given?
44
+
45
+ each { |field, value| store(field, yield(value)) }
46
+
47
+ to_h
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Expiration ensures that volatile hashes are properly configured to expire in Redis.
4
+ module RedisHash
5
+ module Expiration
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ set_callback :insertion, :before, :before_insertion
10
+ set_callback :insertion, :after, :after_insertion
11
+
12
+ private
13
+
14
+ attr_writer :was_empty_before_insertion
15
+
16
+ def before_insertion
17
+ self.was_empty_before_insertion = empty?
18
+ end
19
+
20
+ def after_insertion
21
+ expire(redis_ttl) if redis_ttl.present? && empty_before_insertion?
22
+ end
23
+ end
24
+
25
+ def empty_before_insertion?
26
+ @was_empty_before_insertion.present?
27
+ end
28
+
29
+ def expire(seconds)
30
+ redis.expire(redis_key, seconds)
31
+ end
32
+
33
+ def ttl
34
+ redis.ttl(redis_key)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Identity relates to the specific object instance.
4
+ module RedisHash
5
+ module Identity
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :hgetall, to: :redis
10
+ delegate :inspect, :to_proc, :to_s, to: :to_h
11
+ end
12
+
13
+ def hash
14
+ { redis_id: redis.id, redis_key: redis_key }.hash
15
+ end
16
+
17
+ def to_hash
18
+ to_h
19
+ end
20
+
21
+ def to_h
22
+ hgetall(redis_key)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Insertions allow for the addition of data into the RedisHash.
4
+ module RedisHash
5
+ module Insertions
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :hmset, :hset, :hsetnx, to: :redis
10
+ delegate :merge, to: :to_h
11
+ end
12
+
13
+ def merge!(other_hash)
14
+ run_callbacks(:insertion) { hmset(*other_hash.to_a.unshift(redis_key)) }
15
+
16
+ self
17
+ end
18
+ alias_method :update, :merge!
19
+
20
+ def store(field, value)
21
+ run_callbacks(:insertion) { hset(redis_key, field, value) }
22
+ end
23
+ alias_method :[]=, :store
24
+
25
+ def setnx!(field, value)
26
+ run_callbacks(:insertion) do
27
+ success = hsetnx(redis_key, field, value)
28
+
29
+ raise RedisHash::AlreadyDefinedError, "#{field} already defined" unless success
30
+ end
31
+
32
+ true
33
+ end
34
+
35
+ def setnx(field, value)
36
+ setnx!(field, value)
37
+ rescue RedisHash::AlreadyDefinedError
38
+ false
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Mutations allow for the manipulation of data in the Hash.
4
+ module RedisHash
5
+ module Mutations
6
+ extend ActiveSupport::Concern
7
+
8
+ def compact!
9
+ delete_if { |_, value| value.blank? }
10
+ end
11
+
12
+ def invert
13
+ inversion = to_h.invert
14
+ clear
15
+ inversion.each { |key, value| self[key] = value }
16
+ end
17
+
18
+ def replace(other_hash)
19
+ clear and merge!(other_hash)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Predicates enable querying the Hash for data.
4
+ module RedisHash
5
+ module Predicates
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :hexists, to: :redis
10
+ end
11
+
12
+ def any?(&block)
13
+ return length > 0 unless block_given?
14
+
15
+ to_h.any?(&block)
16
+ end
17
+
18
+ def empty?
19
+ length == 0
20
+ end
21
+
22
+ def include?(field)
23
+ hexists(redis_key, field)
24
+ end
25
+ alias_method :has_key?, :include?
26
+ alias_method :key?, :include?
27
+ alias_method :member?, :include?
28
+
29
+ def value?(value)
30
+ values.include? value
31
+ end
32
+ alias_method :has_value?, :value?
33
+ end
34
+ end
@@ -1,4 +1,4 @@
1
1
  module RedisHash
2
2
  # This constant is managed by spicerack
3
- VERSION = "0.9.0"
3
+ VERSION = "0.9.1"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Garside
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.2.1
27
- - !ruby/object:Gem::Dependency
28
- name: technologic
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - '='
32
- - !ruby/object:Gem::Version
33
- version: 0.9.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - '='
39
- - !ruby/object:Gem::Version
40
- version: 0.9.0
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: redis
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -63,6 +49,22 @@ files:
63
49
  - LICENSE.txt
64
50
  - README.md
65
51
  - lib/redis_hash.rb
52
+ - lib/redis_hash/base.rb
53
+ - lib/redis_hash/concerns/accessors.rb
54
+ - lib/redis_hash/concerns/adapter.rb
55
+ - lib/redis_hash/concerns/callbacks.rb
56
+ - lib/redis_hash/concerns/comparisons.rb
57
+ - lib/redis_hash/concerns/converters.rb
58
+ - lib/redis_hash/concerns/core.rb
59
+ - lib/redis_hash/concerns/counters.rb
60
+ - lib/redis_hash/concerns/default.rb
61
+ - lib/redis_hash/concerns/deletions.rb
62
+ - lib/redis_hash/concerns/enumerators.rb
63
+ - lib/redis_hash/concerns/expiration.rb
64
+ - lib/redis_hash/concerns/identity.rb
65
+ - lib/redis_hash/concerns/insertions.rb
66
+ - lib/redis_hash/concerns/mutations.rb
67
+ - lib/redis_hash/concerns/predicates.rb
66
68
  - lib/redis_hash/version.rb
67
69
  homepage: https://github.com/Freshly/spicerack/tree/master/redis_hash
68
70
  licenses: