familia 0.10.2 → 1.0.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.pre-commit-config.yaml +1 -1
  4. data/.rubocop.yml +75 -0
  5. data/.rubocop_todo.yml +63 -0
  6. data/Gemfile +6 -1
  7. data/Gemfile.lock +47 -15
  8. data/README.md +65 -13
  9. data/VERSION.yml +4 -3
  10. data/familia.gemspec +18 -13
  11. data/lib/familia/base.rb +33 -0
  12. data/lib/familia/connection.rb +87 -0
  13. data/lib/familia/core_ext.rb +119 -124
  14. data/lib/familia/errors.rb +33 -0
  15. data/lib/familia/features/api_version.rb +19 -0
  16. data/lib/familia/features/atomic_saves.rb +8 -0
  17. data/lib/familia/features/quantizer.rb +35 -0
  18. data/lib/familia/features/safe_dump.rb +194 -0
  19. data/lib/familia/features.rb +51 -0
  20. data/lib/familia/horreum/class_methods.rb +292 -0
  21. data/lib/familia/horreum/commands.rb +106 -0
  22. data/lib/familia/horreum/relations_management.rb +141 -0
  23. data/lib/familia/horreum/serialization.rb +193 -0
  24. data/lib/familia/horreum/settings.rb +63 -0
  25. data/lib/familia/horreum/utils.rb +44 -0
  26. data/lib/familia/horreum.rb +248 -0
  27. data/lib/familia/logging.rb +232 -0
  28. data/lib/familia/redistype/commands.rb +56 -0
  29. data/lib/familia/redistype/serialization.rb +110 -0
  30. data/lib/familia/redistype.rb +185 -0
  31. data/lib/familia/refinements.rb +88 -0
  32. data/lib/familia/settings.rb +38 -0
  33. data/lib/familia/types/hashkey.rb +107 -0
  34. data/lib/familia/types/list.rb +155 -0
  35. data/lib/familia/types/sorted_set.rb +234 -0
  36. data/lib/familia/types/string.rb +115 -0
  37. data/lib/familia/types/unsorted_set.rb +123 -0
  38. data/lib/familia/utils.rb +125 -0
  39. data/lib/familia/version.rb +25 -0
  40. data/lib/familia.rb +57 -161
  41. data/lib/redis_middleware.rb +109 -0
  42. data/try/00_familia_try.rb +5 -4
  43. data/try/10_familia_try.rb +21 -17
  44. data/try/20_redis_type_try.rb +67 -0
  45. data/try/{21_redis_object_zset_try.rb → 21_redis_type_zset_try.rb} +2 -2
  46. data/try/{22_redis_object_set_try.rb → 22_redis_type_set_try.rb} +2 -2
  47. data/try/{23_redis_object_list_try.rb → 23_redis_type_list_try.rb} +2 -2
  48. data/try/{24_redis_object_string_try.rb → 24_redis_type_string_try.rb} +6 -6
  49. data/try/{25_redis_object_hash_try.rb → 25_redis_type_hash_try.rb} +3 -3
  50. data/try/26_redis_bool_try.rb +10 -6
  51. data/try/27_redis_horreum_try.rb +93 -0
  52. data/try/30_familia_object_try.rb +21 -20
  53. data/try/35_feature_safedump_try.rb +83 -0
  54. data/try/40_customer_try.rb +140 -0
  55. data/try/41_customer_safedump_try.rb +86 -0
  56. data/try/test_helpers.rb +194 -0
  57. metadata +51 -47
  58. data/lib/familia/helpers.rb +0 -70
  59. data/lib/familia/object.rb +0 -533
  60. data/lib/familia/redisobject.rb +0 -1017
  61. data/lib/familia/test_helpers.rb +0 -40
  62. data/lib/familia/tools.rb +0 -67
  63. data/try/20_redis_object_try.rb +0 -44
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Familia
4
+ class Set < RedisType
5
+ def size
6
+ redis.scard rediskey
7
+ end
8
+ alias length size
9
+
10
+ def empty?
11
+ size.zero?
12
+ end
13
+
14
+ def add *values
15
+ values.flatten.compact.each { |v| redis.sadd? rediskey, to_redis(v) }
16
+ update_expiration
17
+ self
18
+ end
19
+
20
+ def <<(v)
21
+ add v
22
+ end
23
+
24
+ def members
25
+ echo :members, caller(1..1).first if Familia.debug
26
+ el = membersraw
27
+ multi_from_redis(*el)
28
+ end
29
+ alias all members
30
+ alias to_a members
31
+
32
+ def membersraw
33
+ redis.smembers(rediskey)
34
+ end
35
+
36
+ def each(&blk)
37
+ members.each(&blk)
38
+ end
39
+
40
+ def each_with_index(&blk)
41
+ members.each_with_index(&blk)
42
+ end
43
+
44
+ def collect(&blk)
45
+ members.collect(&blk)
46
+ end
47
+
48
+ def select(&blk)
49
+ members.select(&blk)
50
+ end
51
+
52
+ def eachraw(&blk)
53
+ membersraw.each(&blk)
54
+ end
55
+
56
+ def eachraw_with_index(&blk)
57
+ membersraw.each_with_index(&blk)
58
+ end
59
+
60
+ def collectraw(&blk)
61
+ membersraw.collect(&blk)
62
+ end
63
+
64
+ def selectraw(&blk)
65
+ membersraw.select(&blk)
66
+ end
67
+
68
+ def member?(val)
69
+ redis.sismember rediskey, to_redis(val)
70
+ end
71
+ alias include? member?
72
+
73
+ def delete(val)
74
+ redis.srem rediskey, to_redis(val)
75
+ end
76
+ alias remove delete
77
+ alias rem delete
78
+ alias del delete
79
+
80
+ def intersection *setkeys
81
+ # TODO
82
+ end
83
+
84
+ def pop
85
+ redis.spop rediskey
86
+ end
87
+
88
+ def move(dstkey, val)
89
+ redis.smove rediskey, dstkey, val
90
+ end
91
+
92
+ def random
93
+ from_redis randomraw
94
+ end
95
+
96
+ def randomraw
97
+ redis.srandmember(rediskey)
98
+ end
99
+
100
+ ## Make the value stored at KEY identical to the given list
101
+ # define_method :"#{name}_sync" do |*latest|
102
+ # latest = latest.flatten.compact
103
+ # # Do nothing if we're given an empty Array.
104
+ # # Otherwise this would clear all current values
105
+ # if latest.empty?
106
+ # false
107
+ # else
108
+ # # Convert to a list of index values if we got the actual objects
109
+ # latest = latest.collect { |obj| obj.index } if klass === latest.first
110
+ # current = send("#{name_plural}raw")
111
+ # added = latest-current
112
+ # removed = current-latest
113
+ # #Familia.info "#{self.index}: adding: #{added}"
114
+ # added.each { |v| self.send("add_#{name_singular}", v) }
115
+ # #Familia.info "#{self.index}: removing: #{removed}"
116
+ # removed.each { |v| self.send("remove_#{name_singular}", v) }
117
+ # true
118
+ # end
119
+ # end
120
+
121
+ Familia::RedisType.register self, :set
122
+ end
123
+ end
@@ -0,0 +1,125 @@
1
+ # rubocop:disable all
2
+
3
+ require 'securerandom'
4
+
5
+ module Familia
6
+ DIGEST_CLASS = Digest::SHA256
7
+
8
+ module Utils
9
+
10
+ def debug?
11
+ @debug == true
12
+ end
13
+
14
+ def generate_id
15
+ input = SecureRandom.hex(32) # 16=128 bits, 32=256 bits
16
+ Digest::SHA256.hexdigest(input).to_i(16).to_s(36) # base-36 encoding
17
+ end
18
+
19
+ def join(*val)
20
+ val.compact.join(Familia.delim)
21
+ end
22
+
23
+ def split(val)
24
+ val.split(Familia.delim)
25
+ end
26
+
27
+ def rediskey(*val)
28
+ join(*val)
29
+ end
30
+
31
+ def redisuri(uri)
32
+ generic_uri = URI.parse(uri.to_s)
33
+
34
+ # Create a new URI::Redis object
35
+ redis_uri = URI::Redis.build(
36
+ scheme: generic_uri.scheme,
37
+ userinfo: generic_uri.userinfo,
38
+ host: generic_uri.host,
39
+ port: generic_uri.port,
40
+ path: generic_uri.path,
41
+ query: generic_uri.query,
42
+ fragment: generic_uri.fragment
43
+ )
44
+
45
+ redis_uri
46
+ end
47
+
48
+ def now(name = Time.now)
49
+ name.utc.to_f
50
+ end
51
+
52
+ # A quantized timestamp
53
+ # e.g. 12:32 -> 12:30
54
+ #
55
+ def qnow(quantum = 10.minutes, now = Familia.now)
56
+ rounded = now - (now % quantum)
57
+ Time.at(rounded).utc.to_i
58
+ end
59
+
60
+ def qstamp(quantum = nil, pattern = nil, now = Familia.now)
61
+ quantum ||= ttl || 10.minutes
62
+ pattern ||= '%H%M'
63
+ rounded = now - (now % quantum)
64
+ Time.at(rounded).utc.strftime(pattern)
65
+ end
66
+
67
+ def generate_sha_hash(*elements)
68
+ concatenated_string = Familia.join(*elements)
69
+ DIGEST_CLASS.hexdigest(concatenated_string)
70
+ end
71
+
72
+ # This method determines the appropriate value to return based on the class of the input argument.
73
+ # It uses a case statement to handle different classes:
74
+ # - For Symbol, String, Integer, and Float classes, it traces the operation and converts the value to a string.
75
+ # - For Familia::Horreum class, it traces the operation and returns the identifier of the value.
76
+ # - For TrueClass, FalseClass, and NilClass, it traces the operation and converts the value to a string ("true", "false", or "").
77
+ # - For any other class, it traces the operation and returns nil.
78
+ #
79
+ # Alternative names for `value_to_distinguish` could be `input_value`, `value`, or `object`.
80
+ def distinguisher(value_to_distinguish, strict_values = true)
81
+ case value_to_distinguish
82
+ when ::Symbol, ::String, ::Integer, ::Float
83
+ Familia.trace :TOREDIS_DISTINGUISHER, redis, "string", caller(1..1) if Familia.debug?
84
+ # Symbols and numerics are naturally serializable to strings
85
+ # so it's a relatively low risk operation.
86
+ value_to_distinguish.to_s
87
+
88
+ when ::TrueClass, ::FalseClass, ::NilClass
89
+ Familia.trace :TOREDIS_DISTINGUISHER, redis, "true/false/nil", caller(1..1) if Familia.debug?
90
+ # TrueClass, FalseClass, and NilClass are high risk because we can't
91
+ # reliably determine the original type of the value from the serialized
92
+ # string. This can lead to unexpected behavior when deserializing. For
93
+ # example, if a TrueClass value is serialized as "true" and then later
94
+ # deserialized as a String, it can cause errors in the application. Worse
95
+ # still, if a NilClass value is serialized as an empty string we lose the
96
+ # ability to distinguish between a nil value and an empty string when
97
+ #
98
+ raise Familia::HighRiskFactor, value_to_distinguish if strict_values
99
+ value_to_distinguish.to_s #=> "true", "false", ""
100
+
101
+ when Familia::Base, Class
102
+ Familia.trace :TOREDIS_DISTINGUISHER, redis, "base", caller(1..1) if Familia.debug?
103
+ if value_to_distinguish.is_a?(Class)
104
+ value_to_distinguish.name
105
+ else
106
+ value_to_distinguish.identifier
107
+ end
108
+
109
+ else
110
+ Familia.trace :TOREDIS_DISTINGUISHER, redis, "else1 #{strict_values}", caller(1..1) if Familia.debug?
111
+
112
+ if value_to_distinguish.class.ancestors.member?(Familia::Base)
113
+ Familia.trace :TOREDIS_DISTINGUISHER, redis, "isabase", caller(1..1) if Familia.debug?
114
+ value_to_distinguish.identifier
115
+
116
+ else
117
+ Familia.trace :TOREDIS_DISTINGUISHER, redis, "else2 #{strict_values}", caller(1..1) if Familia.debug?
118
+ raise Familia::HighRiskFactor, value_to_distinguish if strict_values
119
+ nil
120
+ end
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,25 @@
1
+ # rubocop:disable all
2
+
3
+ require 'yaml'
4
+
5
+ module Familia
6
+ module VERSION
7
+ def self.to_s
8
+ load_config
9
+ version = [@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
10
+ version += "-#{@version[:PRE]}" if @version[:PRE]
11
+ version
12
+ end
13
+ alias inspect to_s
14
+
15
+ def self.version
16
+ @version ||= load_config
17
+ @version
18
+ end
19
+
20
+ def self.load_config
21
+ version_file_path = File.join(__dir__, '..', '..', 'VERSION.yml')
22
+ @version = YAML.load_file(version_file_path)
23
+ end
24
+ end
25
+ end
data/lib/familia.rb CHANGED
@@ -1,177 +1,73 @@
1
- # encoding: utf-8
2
- FAMILIA_LIB_HOME = File.expand_path File.dirname(__FILE__) unless defined?(FAMILIA_LIB_HOME)
1
+ # rubocop:disable all
2
+ # frozen_string_literal: true
3
+
4
+ require 'redis'
3
5
  require 'uri/redis'
4
- require 'gibbler'
5
- require 'familia/core_ext'
6
- require 'multi_json'
7
6
 
8
- module Familia
9
- module VERSION
10
- def self.to_s
11
- load_config
12
- [@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
13
- end
14
- alias_method :inspect, :to_s
15
- def self.load_config
16
- require 'yaml'
17
- @version ||= YAML.load_file(File.join(FAMILIA_LIB_HOME, '..', 'VERSION.yml'))
18
- end
19
- end
20
- end
7
+ require_relative 'familia/core_ext'
8
+ require_relative 'familia/refinements'
9
+ require_relative 'familia/errors'
10
+ require_relative 'familia/version'
21
11
 
12
+ # Familia - A family warehouse for Redis
13
+ #
14
+ # Familia provides a way to organize and store Ruby objects in Redis.
15
+ # It includes various modules and classes to facilitate object-Redis interactions.
16
+ #
17
+ # @example Basic usage
18
+ # class Flower < Familia::Horreum
19
+ #
20
+ # identifier :my_identifier_method
21
+ # field :token
22
+ # field :name
23
+ # list :owners
24
+ # set :tags
25
+ # zset :metrics
26
+ # hash :props
27
+ # string :value, :default => "GREAT!"
28
+ # end
29
+ #
30
+ # @see https://github.com/delano/familia
31
+ #
22
32
  module Familia
23
- include Gibbler::Complex
24
- @secret = '1-800-AWESOME' # Should be modified via Familia.secret = ''
25
- @apiversion = nil
26
- @uri = URI.parse 'redis://127.0.0.1'
27
- @delim = ':'
28
- @clients = {}
29
- @classes = []
30
- @suffix = :object.freeze
31
- @index = :id.freeze
33
+
32
34
  @debug = false
33
- @dump_method = :to_json
34
- @load_method = :from_json
35
+ @members = []
35
36
 
36
37
  class << self
37
- attr_reader :clients, :uri, :logger
38
- attr_accessor :debug, :secret, :delim, :dump_method, :load_method
39
- attr_writer :apiversion
38
+ attr_accessor :debug
39
+ attr_reader :members
40
40
 
41
- alias_method :url, :uri
42
- def debug?() @debug == true end
43
- def info *msg
44
- STDERR.puts *msg
45
- end
46
- def classes with_redis_objects=false
47
- with_redis_objects ? [@classes, RedisObject.classes].flatten : @classes
48
- end
49
- def ld *msg
50
- info *msg if debug?
51
- end
52
- def trace label, redis_instance, ident, context=nil
53
- return unless Familia.debug?
54
- codeline = if context
55
- context = [context].flatten
56
- context.reject! { |line| line =~ /lib\/familia/ }
57
- context.first
58
- end
59
- info "[%s] -> %s <- %s %s" % [label, codeline, redis_instance.id, ident]
60
- end
61
- def uri= v
62
- v = URI.parse v unless URI === v
63
- @uri = v
64
- end
65
- # A convenience method for returning the appropriate Redis
66
- # connection. If +uri+ is an Integer, we'll treat it as a
67
- # database number. If it's a String, we'll treat it as a
68
- # full URI (e.g. redis://1.2.3.4/15).
69
- # Otherwise we'll return the default connection.
70
- def redis(uri=nil)
71
- if Integer === uri
72
- tmp = Familia.uri
73
- tmp.db = uri
74
- uri = tmp
75
- elsif String === uri
76
- uri &&= URI.parse uri
77
- end
78
- uri ||= Familia.uri
79
- connect(uri) unless @clients[uri.serverid]
80
- @clients[uri.serverid]
81
- end
82
- def log(level, path)
83
- logger = Log4r::Logger.new('familia')
84
- logger.outputters = Log4r::FileOutputter.new 'familia', :filename => path
85
- logger.level = Log4r.const_get(level)
86
- logger
87
- end
88
- def connect(uri=nil)
89
- uri &&= URI.parse uri if String === uri
90
- uri ||= Familia.uri
91
- conf = uri.conf
92
- redis = Redis.new conf
93
- Familia.trace(:CONNECT, redis, conf.inspect, caller[0..3])
94
- @clients[uri.serverid] = redis
95
- end
96
- def reconnect_all!
97
- Familia.classes.each do |klass|
98
- klass.redis.client.reconnect
99
- Familia.info "#{klass} ping: #{klass.redis.ping}" if debug?
100
- end
101
- end
102
- def connected?(uri=nil)
103
- uri &&= URI.parse uri if String === uri
104
- @clients.has_key?(uri.serverid)
105
- end
106
- def default_suffix(a=nil) @suffix = a if a; @suffix end
107
- def default_suffix=(a) @suffix = a end
108
- def index(r=nil) @index = r if r; @index end
109
- def index=(r) @index = r; r end
110
- def join(*r) r.join(Familia.delim) end
111
- def split(r) r.split(Familia.delim) end
112
- def rediskey *args
113
- el = args.flatten.compact
114
- el.unshift @apiversion unless @apiversion.nil?
115
- el.join(Familia.delim)
41
+ def included(member)
42
+ raise Problem, "#{member} should subclass Familia::Horreum"
116
43
  end
117
- def apiversion(r=nil, &blk)
118
- if blk.nil?
119
- @apiversion = r if r;
120
- else
121
- tmp = @apiversion
122
- @apiversion = r
123
- blk.call
124
- @apiversion = tmp
125
- end
126
- @apiversion
127
- end
128
- def now n=Time.now
129
- n.utc.to_i
130
- end
131
- # A quantized timestamp
132
- # e.g. 12:32 -> 12:30
44
+
45
+ # A convenience pattern for configuring Familia.
46
+ #
47
+ # @example
48
+ # Familia.configure do |config|
49
+ # config.debug = true
50
+ # config.enable_redis_logging = true
51
+ # end
52
+ #
133
53
  #
134
- def qnow quantum=10.minutes, now=Familia.now
135
- rounded = now - (now % quantum)
136
- Time.at(rounded).utc.to_i
54
+ def configure
55
+ yield self
137
56
  end
138
57
  end
139
58
 
140
- class Problem < RuntimeError; end
141
- class NoIndex < Problem; end
142
- class NonUniqueKey < Problem; end
143
- class NotConnected < Problem
144
- attr_reader :uri
145
- def initialize uri
146
- @uri = uri
147
- end
148
- def message
149
- "No client for #{uri.serverid}"
150
- end
151
- end
152
-
153
- def self.included(obj)
154
- obj.send :include, Familia::InstanceMethods
155
- obj.send :include, Gibbler::Complex
156
- obj.extend Familia::ClassMethods
157
- obj.class_zset :instances, :class => obj, :reference => true
158
- Familia.classes << obj
159
- end
160
-
161
- require 'familia/object'
162
- require 'familia/helpers'
59
+ require_relative 'familia/logging'
60
+ require_relative 'familia/connection'
61
+ require_relative 'familia/settings'
62
+ require_relative 'familia/utils'
163
63
 
64
+ extend Logging
65
+ extend Connection
66
+ extend Settings
67
+ extend Utils
164
68
  end
165
69
 
166
-
167
- module Familia
168
- module Collector
169
- def klasses
170
- @klasses ||= []
171
- @klasses
172
- end
173
- def included(obj)
174
- self.klasses << obj
175
- end
176
- end
177
- end
70
+ require_relative 'familia/base'
71
+ require_relative 'familia/features'
72
+ require_relative 'familia/redistype'
73
+ require_relative 'familia/horreum'
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RedisLogger is RedisClient middleware.
4
+ #
5
+ # This middleware addresses the need for detailed Redis command logging, which
6
+ # was removed from the redis-rb gem due to performance concerns. However, in
7
+ # many development and debugging scenarios, the ability to log Redis commands
8
+ # can be invaluable.
9
+ #
10
+ # @example Enable Redis command logging
11
+ # RedisLogger.logger = Logger.new(STDOUT)
12
+ # RedisClient.register(RedisLogger)
13
+ #
14
+ # @see https://github.com/redis-rb/redis-client?tab=readme-ov-file#instrumentation-and-middlewares
15
+ #
16
+ # @note While there were concerns about the performance impact of logging in
17
+ # the redis-rb gem, this middleware is designed to be optional and can be
18
+ # easily enabled or disabled as needed. The performance impact is minimal
19
+ # when logging is disabled, and the benefits during development and debugging
20
+ # often outweigh the slight performance cost when enabled.
21
+ module RedisLogger
22
+ @logger = nil
23
+
24
+ class << self
25
+ # Gets/sets the logger instance used by RedisLogger.
26
+ # @return [Logger, nil] The current logger instance or nil if not set.
27
+ attr_accessor :logger
28
+ end
29
+
30
+ # Logs the Redis command and its execution time.
31
+ #
32
+ # This method is called for each Redis command when the middleware is active.
33
+ # It logs the command and its execution time only if a logger is set.
34
+ #
35
+ # @param command [Array] The Redis command and its arguments.
36
+ # @param redis_config [Hash] The configuration options for the Redis
37
+ # connection.
38
+ # @return [Object] The result of the Redis command execution.
39
+ #
40
+ # @note The performance impact of this logging is negligible when no logger
41
+ # is set, as it quickly returns control to the Redis client. When a logger
42
+ # is set, the minimal overhead is often offset by the valuable insights
43
+ # gained during development and debugging.
44
+ def call(command, redis_config)
45
+ return yield unless RedisLogger.logger
46
+
47
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
48
+ result = yield
49
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond) - start
50
+ RedisLogger.logger.debug("Redis: #{command.inspect} (#{duration}µs)")
51
+ result
52
+ end
53
+ end
54
+
55
+ # RedisCommandCounter is RedisClient middleware.
56
+ #
57
+ # This middleware counts the number of Redis commands executed. It can be
58
+ # useful for performance monitoring and debugging, allowing you to track
59
+ # the volume of Redis operations in your application.
60
+ #
61
+ # @example Enable Redis command counting
62
+ # RedisCommandCounter.reset
63
+ # RedisClient.register(RedisCommandCounter)
64
+ #
65
+ # @see https://github.com/redis-rb/redis-client?tab=readme-ov-file#instrumentation-and-middlewares
66
+ module RedisCommandCounter
67
+ @count = 0
68
+ @mutex = Mutex.new
69
+
70
+ class << self
71
+ # Gets the current count of Redis commands executed.
72
+ # @return [Integer] The number of Redis commands executed.
73
+ attr_reader :count
74
+
75
+ # Resets the command count to zero.
76
+ # This method is thread-safe.
77
+ # @return [Integer] The reset count (always 0).
78
+ def reset
79
+ @mutex.synchronize { @count = 0 }
80
+ end
81
+
82
+ # Increments the command count.
83
+ # This method is thread-safe.
84
+ # @return [Integer] The new count after incrementing.
85
+ def increment
86
+ @mutex.synchronize { @count += 1 }
87
+ end
88
+
89
+ def count_commands
90
+ start_count = count
91
+ yield
92
+ end_count = count
93
+ end_count - start_count
94
+ end
95
+ end
96
+
97
+ # Counts the Redis command and delegates its execution.
98
+ #
99
+ # This method is called for each Redis command when the middleware is active.
100
+ # It increments the command count and then yields to execute the actual command.
101
+ #
102
+ # @param command [Array] The Redis command and its arguments.
103
+ # @param redis_config [Hash] The configuration options for the Redis connection.
104
+ # @return [Object] The result of the Redis command execution.
105
+ def call(command, redis_config)
106
+ RedisCommandCounter.increment
107
+ yield
108
+ end
109
+ end
@@ -1,11 +1,12 @@
1
- require 'familia'
2
- require 'familia/test_helpers'
3
1
 
4
- Familia.apiversion = 'v1'
2
+ require_relative '../lib/familia'
3
+ require_relative './test_helpers'
4
+
5
+ #Familia.apiversion = 'v1'
5
6
 
6
7
 
7
8
  ## Check for help class
8
- Bone.redis_objects.keys # consistent b/c hashes are ordered
9
+ Bone.redis_types.keys # consistent b/c hashes are ordered
9
10
  #=> [:owners, :tags, :metrics, :props, :value]
10
11
 
11
12
  ## Familia has a uri
@@ -1,25 +1,28 @@
1
- require 'familia'
2
- require 'familia/test_helpers'
3
1
 
4
- ## Has all redis objects
5
- redis_objects = Familia::RedisObject.registration.keys
6
- redis_objects.collect(&:to_s).sort
7
- #=> ["hash", "list", "set", "string", "zset"]
2
+ require 'time'
8
3
 
9
- ## Familia created class methods for redis object class
10
- Familia::ClassMethods.public_method_defined? :list?
4
+ require_relative '../lib/familia'
5
+ require_relative './test_helpers'
6
+
7
+ ## Has all redistype relativess
8
+ registered_types = Familia::RedisType.registered_types.keys
9
+ registered_types.collect(&:to_s).sort
10
+ #=> ["counter", "hash", "hashkey", "list", "lock", "set", "sorted_set", "string", "zset"]
11
+
12
+ ## Familia created class methods for redistype list class
13
+ Familia::Horreum::ClassMethods.public_method_defined? :list?
11
14
  #=> true
12
15
 
13
- ## Familia created class methods for redis object class
14
- Familia::ClassMethods.public_method_defined? :list
16
+ ## Familia created class methods for redistype list class
17
+ Familia::Horreum::ClassMethods.public_method_defined? :list
15
18
  #=> true
16
19
 
17
- ## Familia created class methods for redis object class
18
- Familia::ClassMethods.public_method_defined? :lists
20
+ ## Familia created class methods for redistype list class
21
+ Familia::Horreum::ClassMethods.public_method_defined? :lists
19
22
  #=> true
20
23
 
21
- ## A Familia object knows its redis objects
22
- Bone.redis_objects.is_a?(Hash) && Bone.redis_objects.has_key?(:owners)
24
+ ## A Familia object knows its redistype relativess
25
+ Bone.redis_types.is_a?(Hash) && Bone.redis_types.has_key?(:owners)
23
26
  #=> true
24
27
 
25
28
  ## A Familia object knows its lists
@@ -30,14 +33,15 @@ Bone.lists.size
30
33
  Bone.list? :owners
31
34
  #=> true
32
35
 
33
- ## A Familia object can get a specific redis object def
36
+ ## A Familia object can get a specific redistype relatives def
34
37
  definition = Bone.list :owners
35
38
  definition.klass
36
39
  #=> Familia::List
37
40
 
38
41
  ## Familia.now
39
- Familia.now Time.parse('2011-04-10 20:56:20 UTC').utc
40
- #=> 1302468980
42
+ parsed_time = Familia.now(Time.parse('2011-04-10 20:56:20 UTC').utc)
43
+ [parsed_time, parsed_time.is_a?(Numeric), parsed_time.is_a?(Float)]
44
+ #=> [1302468980.0, true, true]
41
45
 
42
46
  ## Familia.qnow
43
47
  Familia.qnow 10.minutes, 1302468980