redcord 0.0.1.alpha → 0.0.2.alpha

Sign up to get free protection for your applications and to get access to all the features.
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