redcord 0.0.1.alpha → 0.0.2.alpha

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: abc776db5aa9171e1f8a478013a55179158d7c9dc97e65953c27c162ad4069c7
4
- data.tar.gz: 55d37148a8069725c09637f29392a6461993d93f6999bc76247e646f96a94528
3
+ metadata.gz: 679b0154429c95be05676a47608f0778c3d34e714d60eb5ec065863691fa9461
4
+ data.tar.gz: 43847db0f9fd339c8a91db402b8b2da70ea4b3f874b6faf8a05d20f234cf0aea
5
5
  SHA512:
6
- metadata.gz: 1cfe27b4241554f39ce0f8f0dbce4492f308e3a1db5607f4dbb7153832f976b692987926a6cc9c7c051121f55819f9e9b9b10c02b82250986d3aa6fdf4964e8e
7
- data.tar.gz: fa4865ee7e9ad1b70b4c7037d196bb60ed2aed8ab774356ec07b06a9cefd1f964d05f4da1cdd7cd2e09a5366f7bfb8da06ab84e50e282cfc74cccbb16a185621
6
+ metadata.gz: b01c381b8e3436aaf0f4d2ebc8604bfd582c64663601a17044b0a29ca45c46e4e8ced22969ed5c53123af65808e1c4749cf262bec17e26468c1624c0023d2b9d
7
+ data.tar.gz: cb4403b960c7f2e017143738edcbbfbe356e7e8c8e5b761d1f98482296541dcc2255b480d1285ce233d5add5c7e7e78fda29ab8f5fafd561f106a64e9e144460
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # typed: strict
4
+
2
5
  require 'sorbet-coerce'
3
6
 
4
7
  require 'redcord/relation'
@@ -6,9 +9,6 @@ require 'redcord/relation'
6
9
  module Redcord
7
10
  # Raised by Model.find
8
11
  class RecordNotFound < StandardError; end
9
- # Raised by Model.where
10
- class AttributeNotIndexed < StandardError; end
11
- class WrongAttributeType < TypeError; end
12
12
  end
13
13
 
14
14
  module Redcord::Actions
@@ -38,13 +38,17 @@ module Redcord::Actions
38
38
  instance_key = "#{model_key}:id:#{id}"
39
39
  args = redis.hgetall(instance_key)
40
40
  if args.empty?
41
- raise Redcord::RecordNotFound.new(
42
- "Couldn't find #{name} with 'id'=#{id}"
43
- )
41
+ raise Redcord::RecordNotFound, "Couldn't find #{name} with 'id'=#{id}"
44
42
  end
43
+
45
44
  coerce_and_set_id(args, id)
46
45
  end
47
46
 
47
+ sig { params(args: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
48
+ def find_by(args)
49
+ where(args).to_a.first
50
+ end
51
+
48
52
  sig { params(args: T::Hash[Symbol, T.untyped]).returns(Redcord::Relation) }
49
53
  def where(args)
50
54
  Redcord::Relation.new(T.let(self, T.untyped)).where(args)
@@ -52,7 +56,7 @@ module Redcord::Actions
52
56
 
53
57
  sig { params(id: T.untyped).returns(T::Boolean) }
54
58
  def destroy(id)
55
- return redis.delete_hash(model_key, id) == 1
59
+ redis.delete_hash(model_key, id) == 1
56
60
  end
57
61
  end
58
62
 
@@ -65,13 +69,21 @@ module Redcord::Actions
65
69
  sig { abstract.returns(T.nilable(ActiveSupport::TimeWithZone)) }
66
70
  def created_at; end
67
71
 
68
- sig { abstract.params(time: ActiveSupport::TimeWithZone).returns(T.nilable(ActiveSupport::TimeWithZone)) }
72
+ sig {
73
+ abstract.params(
74
+ time: ActiveSupport::TimeWithZone,
75
+ ).returns(T.nilable(ActiveSupport::TimeWithZone))
76
+ }
69
77
  def created_at=(time); end
70
78
 
71
79
  sig { abstract.returns(T.nilable(ActiveSupport::TimeWithZone)) }
72
80
  def updated_at; end
73
81
 
74
- sig { abstract.params(time: ActiveSupport::TimeWithZone).returns(T.nilable(ActiveSupport::TimeWithZone)) }
82
+ sig {
83
+ abstract.params(
84
+ time: ActiveSupport::TimeWithZone,
85
+ ).returns(T.nilable(ActiveSupport::TimeWithZone))
86
+ }
75
87
  def updated_at=(time); end
76
88
 
77
89
  sig { void }
@@ -80,15 +92,22 @@ module Redcord::Actions
80
92
  _id = id
81
93
  if _id.nil?
82
94
  self.created_at = T.must(self.updated_at)
83
- _id = redis.create_hash_returning_id(self.class.model_key, self.class.to_redis_hash(serialize))
95
+ _id = redis.create_hash_returning_id(
96
+ self.class.model_key,
97
+ self.class.to_redis_hash(serialize),
98
+ )
84
99
  send(:id=, _id)
85
100
  else
86
- redis.update_hash(self.class.model_key, _id, self.class.to_redis_hash(serialize))
101
+ redis.update_hash(
102
+ self.class.model_key,
103
+ _id,
104
+ self.class.to_redis_hash(serialize),
105
+ )
87
106
  end
88
107
  end
89
108
 
90
109
  sig { params(args: T::Hash[Symbol, T.untyped]).void }
91
- def update!(args={})
110
+ def update!(args = {})
92
111
  _id = id
93
112
  if _id.nil?
94
113
  _set_args!(args)
@@ -96,13 +115,18 @@ module Redcord::Actions
96
115
  else
97
116
  args[:updated_at] = Time.zone.now
98
117
  _set_args!(args)
99
- redis.update_hash(self.class.model_key, _id, self.class.to_redis_hash(args))
118
+ redis.update_hash(
119
+ self.class.model_key,
120
+ _id,
121
+ self.class.to_redis_hash(args),
122
+ )
100
123
  end
101
124
  end
102
125
 
103
126
  sig { returns(T::Boolean) }
104
127
  def destroy
105
128
  return false if id.nil?
129
+
106
130
  self.class.destroy(T.must(id))
107
131
  end
108
132
 
@@ -123,7 +147,7 @@ module Redcord::Actions
123
147
  instance_variable_get(:@_id)
124
148
  end
125
149
 
126
- private
150
+ private
127
151
 
128
152
  sig { params(id: Integer).returns(Integer) }
129
153
  def id=(id)
@@ -1,11 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # typed: strict
4
+
2
5
  module Redcord::Attribute
3
6
  extend T::Sig
4
7
  extend T::Helpers
5
8
 
6
- # We implicitly determine what should be a range index on Redis based on Ruby type.
9
+ # We implicitly determine what should be a range index on Redis based on Ruby
10
+ # type.
7
11
  RangeIndexType = T.type_alias {
8
- T.any(T.nilable(Time), T.nilable(Float), T.nilable(Integer))
12
+ T.any(
13
+ T.nilable(Float),
14
+ T.nilable(Integer),
15
+ T.nilable(Time),
16
+ )
9
17
  }
10
18
 
11
19
  sig { params(klass: T.class_of(T::Struct)).void }
@@ -26,23 +34,22 @@ module Redcord::Attribute
26
34
  options: T::Hash[Symbol, T.untyped],
27
35
  ).void
28
36
  end
29
- def attribute(name, type, options={})
37
+ def attribute(name, type, options = {})
30
38
  # TODO: support uniq options
39
+ # TODO: validate types
31
40
  prop(name, type)
32
- if options[:index]
33
- index_attribute(name, type)
34
- end
41
+
42
+ index_attribute(name, type) if options[:index]
35
43
  end
36
-
37
44
 
38
- sig { params(attr: Symbol, type: T.any(Class,T::Types::Base)).void }
45
+ sig { params(attr: Symbol, type: T.any(Class, T::Types::Base)).void }
39
46
  def index_attribute(attr, type)
40
47
  if should_range_index?(type)
41
48
  class_variable_get(:@@range_index_attributes) << attr
42
- sadd_proc_on_redis_connection("range_index_attrs", attr.to_s)
49
+ sadd_proc_on_redis_connection('range_index_attrs', attr.to_s)
43
50
  else
44
51
  class_variable_get(:@@index_attributes) << attr
45
- sadd_proc_on_redis_connection("index_attrs", attr.to_s)
52
+ sadd_proc_on_redis_connection('index_attrs', attr.to_s)
46
53
  end
47
54
  end
48
55
 
@@ -52,22 +59,23 @@ module Redcord::Attribute
52
59
  end
53
60
 
54
61
  private
62
+
55
63
  sig { params(redis_key: String, item_to_add: String).void }
56
64
  def sadd_proc_on_redis_connection(redis_key, item_to_add)
57
- # TODO: Currently we're setting indexed attributes through procs that are run
58
- # when a RedisConnection is established. This should be replaced with migrations
59
- Redcord::RedisConnection.procs_to_prepare << Proc.new do |redis|
65
+ # TODO: Currently we're setting indexed attributes through procs that are
66
+ # run when a RedisConnection is established. This should be replaced with
67
+ # migrations
68
+ Redcord::RedisConnection.procs_to_prepare << proc do |redis|
60
69
  redis.sadd("#{model_key}:#{redis_key}", item_to_add)
61
70
  end
62
71
  end
63
72
 
64
- sig { params(type: T.any(Class,T::Types::Base)).returns(T::Boolean) }
73
+ sig { params(type: T.any(Class, T::Types::Base)).returns(T::Boolean) }
65
74
  def should_range_index?(type)
66
75
  # Change Ruby raw type to Sorbet type in order to call subtype_of?
67
- if type.is_a?(Class)
68
- type = T::Types::Simple.new(type)
69
- end
70
- return type.subtype_of?(RangeIndexType)
76
+ type = T::Types::Simple.new(type) if type.is_a?(Class)
77
+
78
+ type.subtype_of?(RangeIndexType)
71
79
  end
72
80
  end
73
81
 
@@ -52,8 +52,9 @@ module Redcord::Base
52
52
  # coerced to the specified attribute types. Like ActiveRecord,
53
53
  # Redcord manages the created_at and updated_at fields behind the
54
54
  # scene.
55
- prop :created_at, T.nilable(Time)
56
- prop :updated_at, T.nilable(Time)
55
+ attribute :id, T.nilable(Integer), index: true
56
+ attribute :created_at, T.nilable(Time), index: true
57
+ attribute :updated_at, T.nilable(Time), index: true
57
58
  end
58
59
  end
59
60
  end
@@ -1,16 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # typed: strict
4
+
5
+ require 'erb'
6
+
2
7
  module Redcord::LuaScriptReader
3
8
  extend T::Sig
4
9
 
5
- sig {params(script_name: String).returns(String) }
10
+ sig { params(script_name: String).returns(String) }
6
11
  def self.read_lua_script(script_name)
7
- path = File.join(File.dirname(__FILE__), "server_scripts/#{script_name}.erb.lua")
12
+ path = File.join(
13
+ File.dirname(__FILE__),
14
+ "server_scripts/#{script_name}.erb.lua",
15
+ )
8
16
  ERB.new(File.read(path)).result(binding)
9
17
  end
10
18
 
11
- sig {params(relative_path: String).returns(String) }
19
+ sig { params(relative_path: String).returns(String) }
12
20
  def self.include_lua(relative_path)
13
- path = File.join(File.dirname(__FILE__), "server_scripts/#{relative_path}.erb.lua")
21
+ path = File.join(
22
+ File.dirname(__FILE__),
23
+ "server_scripts/#{relative_path}.erb.lua",
24
+ )
14
25
  File.read(path)
15
26
  end
16
- end
27
+ end
@@ -50,7 +50,7 @@ class Redcord::Migration::Migrator
50
50
  MIGRATION_FILENAME_REGEX = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/
51
51
 
52
52
  @@migrations_paths = T.let(
53
- ['db/redisrecord/migrate'],
53
+ ['db/redcord/migrate'],
54
54
  T::Array[String],
55
55
  )
56
56
 
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # typed: strict
4
+
2
5
  class Redcord::Migration::Version
3
6
  extend T::Sig
4
7
 
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # typed: strict
4
+
2
5
  require 'rails'
3
- require 'redcord/prepared_redis'
6
+
4
7
  require 'redcord/lua_script_reader'
8
+ require 'redcord/prepared_redis'
5
9
 
6
10
  module Redcord::RedisConnection
7
11
  extend T::Sig
@@ -37,7 +41,8 @@ module Redcord::RedisConnection
37
41
 
38
42
  sig { params(redis: Redis).returns(Redcord::PreparedRedis) }
39
43
  def redis=(redis)
40
- Redcord::RedisConnection.connections[name.underscore] = prepare_redis!(redis)
44
+ Redcord::RedisConnection.connections[name.underscore] =
45
+ prepare_redis!(redis)
41
46
  end
42
47
 
43
48
  # We prepare the model definition such as TTL, index, and uniq when we
@@ -46,11 +51,17 @@ module Redcord::RedisConnection
46
51
  #
47
52
  # TODO: Replace this with Redcord migrations
48
53
  sig { params(client: T.nilable(Redis)).returns(Redcord::PreparedRedis) }
49
- def prepare_redis!(client=nil)
54
+ def prepare_redis!(client = nil)
50
55
  return client if client.is_a?(Redcord::PreparedRedis)
51
56
 
52
57
  client = Redcord::PreparedRedis.new(
53
- **(client.nil? ? connection_config : client.instance_variable_get(:@options)),
58
+ **(
59
+ if client.nil?
60
+ connection_config
61
+ else
62
+ client.instance_variable_get(:@options)
63
+ end
64
+ ),
54
65
  logger: Redcord::Logger.proxy,
55
66
  )
56
67
 
@@ -63,7 +74,10 @@ module Redcord::RedisConnection
63
74
  script_names = Redcord::ServerScripts.instance_methods
64
75
  res = client.pipelined do
65
76
  script_names.each do |script_name|
66
- client.script(:load, Redcord::LuaScriptReader.read_lua_script(script_name.to_s))
77
+ client.script(
78
+ :load,
79
+ Redcord::LuaScriptReader.read_lua_script(script_name.to_s),
80
+ )
67
81
  end
68
82
  end
69
83
 
@@ -81,11 +95,15 @@ module Redcord::RedisConnection
81
95
  end
82
96
  end
83
97
 
84
- sig { params(config: T::Hash[String, T.untyped]).returns(T::Hash[String, T.untyped]) }
98
+ sig {
99
+ params(
100
+ config: T::Hash[String, T.untyped],
101
+ ).returns(T::Hash[String, T.untyped])
102
+ }
85
103
  def self.merge_and_resolve_default(config)
86
104
  env = Rails.env
87
- config[env] = {} if !config.include?(env)
88
- config[env]['default'] = {} if !config[env].include?('default')
105
+ config[env] = {} unless config.include?(env)
106
+ config[env]['default'] = {} unless config[env].include?('default')
89
107
  config
90
108
  end
91
109
 
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # typed: strict
4
+
5
+ require 'active_support/core_ext/array'
2
6
  require 'active_support/core_ext/module'
3
7
 
4
8
  class Redcord::Relation
@@ -6,33 +10,25 @@ class Redcord::Relation
6
10
 
7
11
  sig { returns(T.class_of(Redcord::Base)) }
8
12
  attr_reader :model
9
-
13
+
10
14
  sig { returns(T::Hash[Symbol, T.untyped]) }
11
15
  attr_reader :query_conditions
12
16
 
13
17
  sig { returns(T::Set[Symbol]) }
14
18
  attr_reader :select_attrs
15
19
 
16
- # TODO: Add sig for []
17
- delegate :[], to: :to_a
18
-
19
- sig do
20
- type_parameters(:U).params(
21
- blk: T.proc.params(arg0: Redcord::Base).returns(T.type_parameter(:U)),
22
- ).returns(T::Array[T.type_parameter(:U)])
23
- end
24
- def map(&blk)
25
- to_a.map(&blk)
26
- end
27
-
28
20
  sig do
29
21
  params(
30
22
  model: T.class_of(Redcord::Base),
31
23
  query_conditions: T::Hash[Symbol, T.untyped],
32
- select_attrs: T::Set[Symbol]
24
+ select_attrs: T::Set[Symbol],
33
25
  ).void
34
26
  end
35
- def initialize(model, query_conditions={}, select_attrs=Set.new)
27
+ def initialize(
28
+ model,
29
+ query_conditions = {},
30
+ select_attrs = Set.new
31
+ )
36
32
  @model = model
37
33
  @query_conditions = query_conditions
38
34
  @select_attrs = select_attrs
@@ -50,14 +46,17 @@ class Redcord::Relation
50
46
 
51
47
  sig do
52
48
  params(
53
- args: Symbol,
49
+ args: T.untyped,
54
50
  blk: T.nilable(T.proc.params(arg0: T.untyped).void),
55
51
  ).returns(T.any(Redcord::Relation, T::Array[T.untyped]))
56
52
  end
57
53
  def select(*args, &blk)
58
54
  if block_given?
59
- return execute_query.select { |*item| blk.call(*item) }
55
+ return execute_query.select do |*item|
56
+ blk.call(*item)
57
+ end
60
58
  end
59
+
61
60
  select_attrs.merge(args)
62
61
  self
63
62
  end
@@ -67,24 +66,93 @@ class Redcord::Relation
67
66
  redis.find_by_attr_count(model.model_key, query_conditions)
68
67
  end
69
68
 
70
- sig { returns(T::Array[T.untyped]) }
71
- def to_a
72
- execute_query
73
- end
69
+ delegate(
70
+ :&,
71
+ :[],
72
+ :all?,
73
+ :any?,
74
+ :any?,
75
+ :at,
76
+ :collect!,
77
+ :collect,
78
+ :compact!,
79
+ :compact,
80
+ :each,
81
+ :each_index,
82
+ :empty?,
83
+ :eql?,
84
+ :exists?,
85
+ :fetch,
86
+ :fifth!,
87
+ :fifth,
88
+ :filter!,
89
+ :filter,
90
+ :first!,
91
+ :first,
92
+ :forty_two!,
93
+ :forty_two,
94
+ :fourth!,
95
+ :fourth,
96
+ :include?,
97
+ :inspect,
98
+ :last!,
99
+ :last,
100
+ :many?,
101
+ :map!,
102
+ :map,
103
+ :none?,
104
+ :one?,
105
+ :reject!,
106
+ :reject,
107
+ :reverse!,
108
+ :reverse,
109
+ :reverse_each,
110
+ :second!,
111
+ :second,
112
+ :second_to_last!,
113
+ :second_to_last,
114
+ :size,
115
+ :sort!,
116
+ :sort,
117
+ :sort_by!,
118
+ :take!,
119
+ :take,
120
+ :third!,
121
+ :third,
122
+ :third_to_last!,
123
+ :third_to_last,
124
+ :to_a,
125
+ :to_ary,
126
+ :to_h,
127
+ :to_s,
128
+ :zip,
129
+ :|,
130
+ to: :execute_query,
131
+ )
74
132
 
75
133
  private
134
+
76
135
  sig { returns(T::Array[T.untyped]) }
77
136
  def execute_query
78
137
  if !select_attrs.empty?
79
- res_hash = redis.find_by_attr(model.model_key, query_conditions, select_attrs)
80
- return res_hash.map do |id, args|
81
- args = model.from_redis_hash(args)
82
- args = args.map { |k, v| [k.to_sym, TypeCoerce[model.get_attr_type(k.to_sym)].new.from(v)] }.to_h
83
- args.merge!(:id => id)
138
+ res_hash = redis.find_by_attr(
139
+ model.model_key,
140
+ query_conditions,
141
+ select_attrs,
142
+ )
143
+
144
+ res_hash.map do |id, args|
145
+ model.from_redis_hash(args).map do |k, v|
146
+ [k.to_sym, TypeCoerce[model.get_attr_type(k.to_sym)].new.from(v)]
147
+ end.to_h.merge(id: id)
84
148
  end
85
149
  else
86
- res_hash = redis.find_by_attr(model.model_key, query_conditions)
87
- return res_hash.map { |id, args| model.coerce_and_set_id(args, id) }
150
+ res_hash = redis.find_by_attr(
151
+ model.model_key,
152
+ query_conditions,
153
+ )
154
+
155
+ res_hash.map { |id, args| model.coerce_and_set_id(args, id) }
88
156
  end
89
157
  end
90
158
 
@@ -1,14 +1,17 @@
1
- require 'redcord/range_interval'
1
+ # frozen_string_literal: true
2
+
2
3
  # typed: strict
3
- #
4
- # This module defines various helper methods on Redcord for serialization between the
5
- # Ruby client and Redis server.
4
+
5
+ require 'redcord/range_interval'
6
+
6
7
  module Redcord
7
8
  # Raised by Model.where
8
9
  class AttributeNotIndexed < StandardError; end
9
10
  class WrongAttributeType < TypeError; end
10
11
  end
11
12
 
13
+ # This module defines various helper methods on Redcord for serialization
14
+ # between the Ruby client and Redis server.
12
15
  module Redcord::Serializer
13
16
  extend T::Sig
14
17
 
@@ -16,27 +19,30 @@ module Redcord::Serializer
16
19
  def self.included(klass)
17
20
  klass.extend(ClassMethods)
18
21
  end
19
-
22
+
20
23
  module ClassMethods
21
24
  extend T::Sig
22
25
 
23
- # Redis only allows range queries on floats. To allow range queries on the Ruby Time
24
- # type, encode_attr_value and decode_attr_value will implicitly encode and decode
25
- # Time attributes to a float.
26
+ # Redis only allows range queries on floats. To allow range queries on the
27
+ # Ruby Time type, encode_attr_value and decode_attr_value will implicitly
28
+ # encode and decode Time attributes to a float.
26
29
  TIME_TYPES = T.let(Set[Time, T.nilable(Time)], T::Set[T.untyped])
30
+
27
31
  sig { params(attribute: Symbol, val: T.untyped).returns(T.untyped) }
28
32
  def encode_attr_value(attribute, val)
29
- if val && TIME_TYPES.include?(props[attribute][:type])
33
+ if !val.blank? && TIME_TYPES.include?(props[attribute][:type])
30
34
  val = val.to_f
31
35
  end
36
+
32
37
  val
33
38
  end
34
39
 
35
40
  sig { params(attribute: Symbol, val: T.untyped).returns(T.untyped) }
36
41
  def decode_attr_value(attribute, val)
37
- if val && TIME_TYPES.include?(props[attribute][:type])
42
+ if !val.blank? && TIME_TYPES.include?(props[attribute][:type])
38
43
  val = Time.zone.at(val.to_f)
39
44
  end
45
+
40
46
  val
41
47
  end
42
48
 
@@ -44,11 +50,13 @@ module Redcord::Serializer
44
50
  def validate_and_encode_query(attr_key, attr_val)
45
51
  # Validate that attributes queried for are index attributes
46
52
  if !class_variable_get(:@@index_attributes).include?(attr_key) &&
47
- !class_variable_get(:@@range_index_attributes).include?(attr_key)
48
- raise Redcord::AttributeNotIndexed.new(
49
- "#{attr_key} is not an indexed attribute."
53
+ !class_variable_get(:@@range_index_attributes).include?(attr_key)
54
+ raise(
55
+ Redcord::AttributeNotIndexed,
56
+ "#{attr_key} is not an indexed attribute.",
50
57
  )
51
58
  end
59
+
52
60
  # Validate attribute types for normal index attributes
53
61
  attr_type = get_attr_type(attr_key)
54
62
  if class_variable_get(:@@index_attributes).include?(attr_key)
@@ -56,44 +64,66 @@ module Redcord::Serializer
56
64
  else
57
65
  # Validate attribute types for range index attributes
58
66
  if attr_val.is_a?(Redcord::RangeInterval)
59
- validate_attr_type(attr_val.min, T.cast(T.nilable(attr_type), T::Types::Base))
60
- validate_attr_type(attr_val.max, T.cast(T.nilable(attr_type), T::Types::Base))
67
+ validate_attr_type(
68
+ attr_val.min,
69
+ T.cast(T.nilable(attr_type), T::Types::Base),
70
+ )
71
+ validate_attr_type(
72
+ attr_val.max,
73
+ T.cast(T.nilable(attr_type), T::Types::Base),
74
+ )
61
75
  else
62
76
  validate_attr_type(attr_val, attr_type)
63
77
  end
64
- # Range index attributes need to be further encoded into a format understood by the Lua script.
65
- if attr_val != nil
78
+
79
+ # Range index attributes need to be further encoded into a format
80
+ # understood by the Lua script.
81
+ unless attr_val.nil?
66
82
  attr_val = encode_range_index_attr_val(attr_key, attr_val)
67
83
  end
68
84
  end
85
+
69
86
  attr_val
70
87
  end
71
88
 
72
- sig { params(attr_val: T.untyped, attr_type: T.any(Class, T::Types::Base)).void }
89
+ sig {
90
+ params(
91
+ attr_val: T.untyped,
92
+ attr_type: T.any(Class, T::Types::Base),
93
+ ).void
94
+ }
73
95
  def validate_attr_type(attr_val, attr_type)
74
96
  if (attr_type.is_a?(Class) && !attr_val.is_a?(attr_type)) ||
75
- (attr_type.is_a?(T::Types::Base) && !attr_type.valid?(attr_val))
76
- raise Redcord::WrongAttributeType.new(
77
- "Expected type #{attr_type}, got #{attr_val.class}"
97
+ (attr_type.is_a?(T::Types::Base) && !attr_type.valid?(attr_val))
98
+ raise(
99
+ Redcord::WrongAttributeType,
100
+ "Expected type #{attr_type}, got #{attr_val.class.name}",
78
101
  )
79
102
  end
80
103
  end
81
104
 
82
- sig { params(attribute: Symbol, val: T.untyped).returns([T.untyped, T.untyped]) }
105
+ sig {
106
+ params(
107
+ attribute: Symbol,
108
+ val: T.untyped,
109
+ ).returns([T.untyped, T.untyped])
110
+ }
83
111
  def encode_range_index_attr_val(attribute, val)
84
112
  if val.is_a?(Redcord::RangeInterval)
85
- # nil is treated as -inf and +inf. This is supported in Redis sorted sets
86
- # so clients aren't required to know the highest and lowest scores in a range
113
+ # nil is treated as -inf and +inf. This is supported in Redis sorted
114
+ # sets so clients aren't required to know the highest and lowest scores
115
+ # in a range
87
116
  min_val = !val.min ? '-inf' : encode_attr_value(attribute, val.min)
88
117
  max_val = !val.max ? '+inf' : encode_attr_value(attribute, val.max)
89
118
 
90
- # In Redis, by default min and max is closed. You can prefix the score with '(' to
91
- # specify an open interval.
119
+ # In Redis, by default min and max is closed. You can prefix the score
120
+ # with '(' to specify an open interval.
92
121
  min_val = val.min_exclusive ? '(' + min_val.to_s : min_val.to_s
93
122
  max_val = val.max_exclusive ? '(' + max_val.to_s : max_val.to_s
94
- return [min_val, max_val]
123
+ [min_val, max_val]
95
124
  else
96
- # Equality queries for range indices are be passed to redis as a range [val, val].
125
+ # Equality queries for range indices are be passed to redis as a range
126
+ # [val, val].
97
127
  encoded_val = encode_attr_value(attribute, val)
98
128
  [encoded_val, encoded_val]
99
129
  end
@@ -104,24 +134,41 @@ module Redcord::Serializer
104
134
  props[attr_key][:type_object]
105
135
  end
106
136
 
107
- sig { params(redis_hash: T::Hash[T.untyped, T.untyped], id: Integer).returns(T.untyped) }
137
+ sig {
138
+ params(
139
+ redis_hash: T::Hash[T.untyped, T.untyped],
140
+ id: Integer,
141
+ ).returns(T.untyped)
142
+ }
108
143
  def coerce_and_set_id(redis_hash, id)
109
- # Coerce each serialized result returned from Redis back into Model instance
144
+ # Coerce each serialized result returned from Redis back into Model
145
+ # instance
110
146
  instance = TypeCoerce.send(:[], self).new.from(from_redis_hash(redis_hash))
111
147
  instance.send(:id=, id)
112
148
  instance
113
149
  end
150
+
114
151
  sig { returns(String) }
115
152
  def model_key
116
153
  "Redcord:#{name}"
117
154
  end
118
155
 
119
- sig { params(args: T::Hash[T.any(String, Symbol), T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
156
+ sig {
157
+ params(
158
+ args: T::Hash[T.any(String, Symbol), T.untyped],
159
+ ).returns(T::Hash[Symbol, T.untyped])
160
+ }
120
161
  def to_redis_hash(args)
121
- args.map { |key, val| [key.to_sym, encode_attr_value(key.to_sym, val)] }.to_h
162
+ args.map do |key, val|
163
+ [key.to_sym, encode_attr_value(key.to_sym, val)]
164
+ end.to_h
122
165
  end
123
166
 
124
- sig { params(args: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped]) }
167
+ sig {
168
+ params(
169
+ args: T::Hash[T.untyped, T.untyped],
170
+ ).returns(T::Hash[T.untyped, T.untyped])
171
+ }
125
172
  def from_redis_hash(args)
126
173
  args.map { |key, val| [key, decode_attr_value(key.to_sym, val)] }.to_h
127
174
  end
@@ -60,6 +60,7 @@ if #index_attr_keys > 0 then
60
60
  end
61
61
  end
62
62
  local range_index_attr_keys = redis.call('smembers', model .. ':range_index_attrs')
63
+ attrs_hash['id'] = id
63
64
  if #range_index_attr_keys > 0 then
64
65
  for _, attr_key in ipairs(range_index_attr_keys) do
65
66
  add_id_to_range_index_attr(model, attr_key, attrs_hash[attr_key], id)
@@ -52,6 +52,11 @@ local function batch_exists(model, ids_set)
52
52
  for id, _ in pairs(ids_set) do
53
53
  table.insert(id_keys, model .. ':id:' .. id)
54
54
  end
55
+
56
+ if #id_keys == 0 then
57
+ return 0
58
+ end
59
+
55
60
  return redis.call('exists', unpack(id_keys))
56
61
  end
57
62
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redcord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.alpha
4
+ version: 0.0.2.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chan Zuckerberg Initiative
@@ -14,28 +14,28 @@ dependencies:
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: railties
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '5'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '5'
41
41
  - !ruby/object:Gem::Dependency
@@ -203,7 +203,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
203
  - !ruby/object:Gem::Version
204
204
  version: 1.3.1
205
205
  requirements: []
206
- rubygems_version: 3.1.3
206
+ rubyforge_project:
207
+ rubygems_version: 2.7.6.2
207
208
  signing_key:
208
209
  specification_version: 4
209
210
  summary: A Ruby ORM like Active Record, but for Redis