much-rails-redis-record 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 50c638b21e0db5c3a18f560c9f1bc4f26dc7e16ad6a29131b34f3cc4bb98b9ed
4
+ data.tar.gz: 157631dbd216beca52ca5181947e26cc45250d44450c2707e59f6942158101cd
5
+ SHA512:
6
+ metadata.gz: 6dbdca11c92091aab89d74e4e5d1c251b8ebe926ca827de6208df201c029cfad6ab3cff27db0905d61c4a5faa19c8fec139f42de259aa6abbc37146e342bfdd3
7
+ data.tar.gz: c6662b24f14d00893b0f706ed755d8fa9c3726a7217ee544ce85682bb9204ddcd6037535fe495f273329348c52bc4830f35092dc3efa6e8f7427cf95fd017b0e
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ ruby "~> 2.5"
6
+
7
+ gemspec
8
+
9
+ gem "pry"
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2021-Present Kelly Redding and Collin Redding
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # MuchRailsRedisRecord
2
+
3
+ Store records in Redis with MuchRails.
4
+
5
+ Note: I find redis to be a good choice for one-off "token" type records or any temporary record you want removed after a certain amount of time. MuchRailsRedisRecord is ideal for these types of records.
6
+
7
+ For typical records in Rails, ActiveRecord should be preferred.
8
+
9
+ ## Setup
10
+
11
+ ### Mixin on your record object you want persisted in Redis, e.g.:
12
+
13
+ ```ruby
14
+ class Thing
15
+ include MuchRailsRedisRecord
16
+
17
+ def self.TTL_SECS
18
+ @ttl_secs ||= 5 * 60 # 5 minutes
19
+ end
20
+
21
+ def self.REDIS_KEY_NAMESPACE
22
+ "things"
23
+ end
24
+
25
+ attr_accessor :name
26
+
27
+ def initialize(name:, **kargs)
28
+ super(**kargs)
29
+
30
+ @name = name
31
+ end
32
+
33
+ def to_h
34
+ {
35
+ "name" => name,
36
+ }
37
+ end
38
+
39
+ private
40
+
41
+ def on_validate
42
+ validate_presence
43
+ end
44
+
45
+ def validate_presence
46
+ errors[:name] << "can't be blank" if name.blank?
47
+ end
48
+ end
49
+ ```
50
+
51
+ ### Configure the redis connection
52
+
53
+ In e.g. `config/initializers/much_rails.rb`:
54
+
55
+ ```ruby
56
+ MuchRailsRedisRecord.config.redis =
57
+ HellaRedis.new({
58
+ url: ENV.fetch("REDIS_URL"){ "redis://localhost:6379/0" },
59
+ driver: "ruby",
60
+ redis_ns: "my-app",
61
+ size: ENV.fetch("APP_MAX_THREADS"){ 5 },
62
+ timeout: 1,
63
+ })
64
+ ```
65
+
66
+ ## Usage
67
+
68
+ ```
69
+ $ rails c
70
+ Loading development environment (Rails 6.1.3.1)
71
+ [1] pry(main)> chair = Thing.new(name: "Chair")
72
+ => #<Thing:0x00007fd404182358 @created_at=nil, @identifier=nil, @name="Chair", @updated_at=nil, @valid=nil>
73
+ [2] pry(main)> chair.save!
74
+ => true
75
+ [3] pry(main)> chair
76
+ => #<Thing:0x00007fd404182358 @created_at=2021-06-17 13:16:52.059503 UTC, @errors={}, @identifier="6600bdd3-4dc8-447b-910a-3cf079eaae98", @name="Chair", @updated_at=2021-06-17 13:16:52.059506 UTC, @valid=nil>
77
+ [4] pry(main)> chair.name = "Comfy chair"
78
+ => "Comfy chair"
79
+ [5] pry(main)> chair.save!
80
+ => true
81
+ [6] pry(main)> chair
82
+ => #<Thing:0x00007fd404182358 @created_at=2021-06-17 13:16:52.059503 UTC, @errors={}, @identifier="6600bdd3-4dc8-447b-910a-3cf079eaae98", @name="Comfy chair", @updated_at=2021-06-17 13:17:12.409879 UTC, @valid=nil>
83
+ [7] pry(main)> chair.valid?
84
+ => true
85
+ [8] pry(main)> chair.name = nil
86
+ => nil
87
+ [9] pry(main)> chair.valid?
88
+ => false
89
+ [10] pry(main)> chair.save!
90
+ MuchRails::InvalidError: {"name"=>["can't be blank"]}
91
+ from /Users/kelly/projects/redding/gems/much-rails-redis-record/lib/much-rails-redis-record.rb:156:in `validate!'
92
+ [11] pry(main)> chair.name = "Comfy chair"
93
+ => "Comfy chair"
94
+ [12] pry(main)> chair.valid?
95
+ => true
96
+ [13] pry(main)> chair.ttl
97
+ => 242
98
+ [14] pry(main)> chair.ttl
99
+ => 239
100
+ [15] pry(main)> chair.ttl
101
+ => 235
102
+ [16] pry(main)> chair.redis_key
103
+ => "things:6600bdd3-4dc8-447b-910a-3cf079eaae98"
104
+ [17] pry(main)> Thing.find_by_identifier("6600bdd3-4dc8-447b-910a-3cf079eaae98")
105
+ => #<Thing:0x00007fd4043d97c8 @created_at=2021-06-17 13:16:52 UTC, @identifier="6600bdd3-4dc8-447b-910a-3cf079eaae98", @name="Comfy chair", @updated_at=2021-06-17 13:17:12 UTC, @valid=nil>
106
+ [18] pry(main)> chair == Thing.find_by_identifier("6600bdd3-4dc8-447b-910a-3cf079eaae98")
107
+ => true
108
+ [19] pry(main)> Thing.find_all
109
+ => [#<Thing:0x00007fd40440af30 @created_at=2021-06-17 13:16:52 UTC, @identifier="6600bdd3-4dc8-447b-910a-3cf079eaae98", @name="Comfy chair", @updated_at=2021-06-17 13:17:12 UTC, @valid=nil>]
110
+ [20] pry(main)> chair.destroy!
111
+ => true
112
+ [21] pry(main)> Thing.find_all
113
+ => []
114
+ [22] pry(main)>
115
+ ```
116
+
117
+ ## Testing
118
+
119
+ ### Fake redis records
120
+
121
+ Use `MuchRailsRedisRecord::FakeBehaviors` to create test doubles for your redis records. The test doubles have the same API but won't call out to redis to persist.
122
+
123
+ E.g.
124
+
125
+ ```ruby
126
+ require "much-rails-redis-record/fake_behaviors"
127
+
128
+ class Factory::FakeThing < Thing
129
+ include MuchRailsRedisRecord::FakeBehaviors
130
+
131
+ def initialize(name: nil, **kargs)
132
+ super(name || Factory.string, **kargs)
133
+ end
134
+ end
135
+ ```
136
+
137
+ ```
138
+ [3] pry(main)> fake_thing = Factory::FakeThing.new
139
+ => #<Factory::FakeThing:0x00007f98b0865740 @created_at=2000-12-21 05:11:53 UTC, @identifier="6c8f6609-b459-4a8f-b5e2-df1bb02efbaa", @name="ygzzysbhip", @updated_at=2000-04-01 02:17:30 UTC, @valid=nil>
140
+ [4] pry(main)> Thing.find_all
141
+ => []
142
+ [5] pry(main)>
143
+ ```
144
+
145
+ ## Installation
146
+
147
+ Add this line to your application's Gemfile:
148
+
149
+ gem "much-rails-redis-record"
150
+
151
+ And then execute:
152
+
153
+ $ bundle
154
+
155
+ Or install it yourself as:
156
+
157
+ $ gem install much-rails-redis-record
158
+
159
+ ## Contributing
160
+
161
+ 1. Fork it
162
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
163
+ 3. Commit your changes (`git commit -am "Added some feature"`)
164
+ 4. Push to the branch (`git push origin my-new-feature`)
165
+ 5. Create new Pull Request
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "much-rails"
4
+ require "hella-redis"
5
+ require "much-rails-redis-record/version"
6
+
7
+ module MuchRailsRedisRecord
8
+ include MuchRails::Config
9
+ include MuchRails::Mixin
10
+
11
+ add_config
12
+
13
+ mixin_included do
14
+ attr_accessor :identifier, :created_at, :updated_at
15
+ end
16
+
17
+ mixin_class_methods do
18
+ def TTL_SECS
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def REDIS_KEY_NAMESPACE
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def redis
27
+ MuchRailsRedisRecord.config.redis
28
+ end
29
+
30
+ def redis_key(identifier)
31
+ "#{self.REDIS_KEY_NAMESPACE}:#{identifier}"
32
+ end
33
+
34
+ def find_by_identifier(identifier)
35
+ find_by_redis_key(redis_key(identifier))
36
+ end
37
+
38
+ def find_by_redis_key(redis_key)
39
+ args =
40
+ MuchRails::JSON.decode(
41
+ redis.connection do |c|
42
+ unless c.exists?(redis_key)
43
+ raise MuchRailsRedisRecord::NotFoundError
44
+ end
45
+
46
+ c.get(redis_key)
47
+ end,
48
+ )
49
+ new(**args.symbolize_keys)
50
+ end
51
+
52
+ # Note: this should not be used in production code for performance / memory
53
+ # consumption reasons. This should only be used for debugging in development
54
+ # and staging environments.
55
+ def find_all_redis_keys
56
+ redis.connection do |c|
57
+ c.keys("#{self.REDIS_KEY_NAMESPACE}:*")
58
+ end
59
+ end
60
+
61
+ # Note: this should not be used in production code for performance / memory
62
+ # consumption reasons. This should only be used for debugging in development
63
+ # and staging environments.
64
+ def find_all
65
+ find_all_redis_keys.map do |redis_key|
66
+ find_by_redis_key(redis_key)
67
+ end
68
+ end
69
+ end
70
+
71
+ mixin_instance_methods do
72
+ def initialize(
73
+ identifier: Value.not_given,
74
+ created_at: Value.not_given,
75
+ updated_at: Value.not_given,
76
+ **)
77
+ @identifier = Value.given?(identifier) ? identifier : nil
78
+ @created_at =
79
+ Value.given?(created_at) ? MuchRails::Time.for(created_at) : nil
80
+ @updated_at =
81
+ Value.given?(updated_at) ? MuchRails::Time.for(updated_at) : nil
82
+
83
+ @valid = nil
84
+ end
85
+
86
+ def valid?
87
+ errors.clear
88
+ on_validate
89
+ errors.none?
90
+ end
91
+
92
+ def redis_key
93
+ self.class.redis_key(identifier)
94
+ end
95
+
96
+ def ttl
97
+ redis.connection{ |c| c.ttl(redis_key) }
98
+ end
99
+
100
+ def errors
101
+ @errors ||= HashWithIndifferentAccess.new{ |hash, key| hash[key] = [] }
102
+ end
103
+
104
+ def save!
105
+ validate!
106
+ set_save_transaction_data
107
+
108
+ redis.connection do |c|
109
+ c.multi do
110
+ c.set(
111
+ redis_key,
112
+ MuchRails::JSON.encode(
113
+ to_h.merge({
114
+ "identifier" => identifier,
115
+ "created_at" => created_at.iso8601,
116
+ "updated_at" => updated_at.iso8601,
117
+ }),
118
+ ),
119
+ )
120
+ c.expire(redis_key, self.class.TTL_SECS) if self.class.TTL_SECS
121
+ end
122
+ end
123
+
124
+ true
125
+ end
126
+
127
+ def destroy!
128
+ redis.connection{ |c| c.del(redis_key) } if identifier
129
+
130
+ true
131
+ end
132
+
133
+ def to_h
134
+ raise NotImplementedError
135
+ end
136
+
137
+ def ==(other)
138
+ return super unless other.is_a?(self.class)
139
+
140
+ to_h == other.to_h
141
+ end
142
+
143
+ private
144
+
145
+ def redis
146
+ self.class.redis
147
+ end
148
+
149
+ def set_save_transaction_data
150
+ @identifier ||= SecureRandom.uuid
151
+ @created_at ||= Time.now.utc
152
+ @updated_at = Time.now.utc
153
+ end
154
+
155
+ def validate!
156
+ raise(MuchRails::InvalidError.new(**errors)) unless valid?
157
+ end
158
+
159
+ def on_validate
160
+ end
161
+ end
162
+
163
+ NotFoundError = Class.new(RuntimeError)
164
+
165
+ module Value
166
+ include MuchRails::NotGiven
167
+ end
168
+
169
+ class Config
170
+ attr_accessor :redis
171
+ end
172
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MuchRailsRedisRecord; end
4
+
5
+ module MuchRailsRedisRecord::FakeBehaviors
6
+ include MuchRails::Mixin
7
+
8
+ mixin_instance_methods do
9
+ def initialize(
10
+ identifier: Value.not_given,
11
+ created_at: Value.not_given,
12
+ updated_at: Value.not_given,
13
+ **kargs)
14
+ super(
15
+ identifier: Value.given?(identifier) ? identifier : Factory.uuid,
16
+ created_at: Value.given?(created_at) ? created_at : Factory.time.utc,
17
+ updated_at: Value.given?(updated_at) ? updated_at : Factory.time.utc,
18
+ **kargs,
19
+ )
20
+ end
21
+
22
+ def save_bang_called?
23
+ !!@save_bang_called
24
+ end
25
+
26
+ def destroy_bang_called?
27
+ !!@destroy_bang_called
28
+ end
29
+
30
+ def save!
31
+ validate!
32
+ set_save_transaction_data
33
+
34
+ @save_bang_called = true
35
+
36
+ true
37
+ end
38
+
39
+ def destroy!
40
+ @destroy_bang_called = true if identifier
41
+ end
42
+ end
43
+
44
+ Value = MuchRailsRedisRecord::Value
45
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MuchRailsRedisRecord
4
+ VERSION = "0.1.0"
5
+ end
data/log/.keep ADDED
File without changes
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path("../lib", __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require "much-rails-redis-record/version"
7
+
8
+ Gem::Specification.new do |gem|
9
+ gem.name = "much-rails-redis-record"
10
+ gem.version = MuchRailsRedisRecord::VERSION
11
+ gem.authors = ["Kelly Redding", "Collin Redding"]
12
+ gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
13
+ gem.summary = "Store records in Redis with MuchRails."
14
+ gem.description = "Store records in Redis with MuchRails."
15
+ gem.homepage = "https://github.com/redding/much-rails-redis-record"
16
+ gem.license = "MIT"
17
+
18
+ gem.files = `git ls-files | grep "^[^.]"`.split($INPUT_RECORD_SEPARATOR)
19
+
20
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+ gem.require_paths = ["lib"]
23
+
24
+ gem.required_ruby_version = "~> 2.5"
25
+
26
+ gem.add_development_dependency("much-style-guide", ["~> 0.6.4"])
27
+ gem.add_development_dependency("assert", ["~> 2.19.6"])
28
+
29
+ gem.add_dependency("much-rails", ["~> 0.4.2"])
30
+ gem.add_dependency("hella-redis", ["~> 0.5.0"])
31
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["HELLA_REDIS_TEST_MODE"] = "yes"
4
+
5
+ # Add the root dir to the load path.
6
+ $LOAD_PATH.unshift(File.expand_path("../..", __FILE__))
7
+
8
+ # Require pry for debugging (`binding.pry`).
9
+ require "pry"
10
+
11
+ require "test/support/factory"
12
+ require "much-rails-redis-record"
13
+
14
+ MuchRailsRedisRecord.configure do |config|
15
+ config.redis =
16
+ HellaRedis.new({
17
+ url: ENV.fetch("REDIS_URL"){ "redis://localhost:6379/0" },
18
+ driver: "ruby",
19
+ redis_ns: "much-rails-redis-record:tests",
20
+ size: ENV.fetch("APP_MAX_THREADS"){ 5 },
21
+ timeout: 1,
22
+ })
23
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert/factory"
4
+
5
+ module Factory
6
+ extend Assert::Factory
7
+
8
+ def self.uuid
9
+ SecureRandom.uuid
10
+ end
11
+ end
data/test/system/.keep ADDED
File without changes
File without changes
@@ -0,0 +1,397 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails-redis-record"
5
+
6
+ module MuchRailsRedisRecord
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRailsRedisRecord"
9
+ subject{ unit_module }
10
+
11
+ let(:unit_module){ MuchRailsRedisRecord }
12
+ end
13
+
14
+ class ReceiverTests < UnitTests
15
+ desc "receiver"
16
+ subject{ receiver_class }
17
+
18
+ setup do
19
+ redis.reset!
20
+
21
+ secure_random_uuid
22
+ Assert.stub(SecureRandom, :uuid){ secure_random_uuid }
23
+ end
24
+
25
+ teardown do
26
+ redis.reset!
27
+ end
28
+
29
+ let(:receiver_class) do
30
+ Class
31
+ .new{
32
+ def self.TTL_SECS
33
+ 1
34
+ end
35
+
36
+ def self.REDIS_KEY_NAMESPACE
37
+ "test:redis-records"
38
+ end
39
+
40
+ attr_reader :field1
41
+
42
+ def initialize(
43
+ field1:,
44
+ **kargs)
45
+ super(**kargs)
46
+
47
+ @field1 = field1.to_s.strip
48
+ end
49
+
50
+ def to_h
51
+ {
52
+ "field1" => field1,
53
+ }
54
+ end
55
+
56
+ private
57
+
58
+ def on_validate
59
+ validate_presence
60
+ end
61
+
62
+ def validate_presence
63
+ errors[:field1] << "can’t be blank" if field1.blank?
64
+ end
65
+ }
66
+ .tap do |c|
67
+ c.include(unit_module)
68
+ end
69
+ end
70
+
71
+ let(:redis){ unit_module.config.redis }
72
+ let(:secure_random_uuid){ Factory.uuid }
73
+
74
+ let(:field1_value){ Factory.string }
75
+ let(:identifier){ secure_random_uuid }
76
+ let(:created_at){ Time.now.utc }
77
+ let(:updated_at){ Time.now.utc }
78
+
79
+ let(:record_data) do
80
+ {
81
+ "field1" => field1_value,
82
+ "identifier" => identifier,
83
+ "created_at" => created_at.iso8601,
84
+ "updated_at" => updated_at.iso8601,
85
+ }
86
+ end
87
+
88
+ should have_imeths :TTL_SECS, :REDIS_KEY_NAMESPACE
89
+ should have_imeths :redis_key, :find_by_identifier, :find_by_redis_key
90
+ should have_imeths :find_all_redis_keys, :find_all
91
+
92
+ should "know its attributes" do
93
+ assert_that(subject.TTL_SECS).equals(1)
94
+ assert_that(subject.REDIS_KEY_NAMESPACE).equals("test:redis-records")
95
+ assert_that(subject.redis_key(secure_random_uuid))
96
+ .equals("#{subject.REDIS_KEY_NAMESPACE}:#{identifier}")
97
+ end
98
+ end
99
+
100
+ class FindByIdentifierSetupTests < ReceiverTests
101
+ desc ".find_by_identifier"
102
+
103
+ setup do
104
+ Assert
105
+ .stub(redis.connection_spy, :get)
106
+ .with(subject.redis_key(identifier)){ encoded_record_data }
107
+ end
108
+
109
+ let(:encoded_record_data){ MuchRails::JSON.encode(record_data) }
110
+ end
111
+
112
+ class FindByExistingIdentifierTests < FindByIdentifierSetupTests
113
+ desc "with an existing identifier"
114
+
115
+ setup do
116
+ Assert
117
+ .stub(redis.connection_spy, :exists?)
118
+ .with(subject.redis_key(identifier)){ true }
119
+ end
120
+
121
+ should "lookup the existing record data and build an instance with it" do
122
+ record = subject.find_by_identifier(identifier)
123
+ assert_that(record).equals(subject.new(**record_data.symbolize_keys))
124
+ end
125
+ end
126
+
127
+ class FindByNonExistingIdentifierTests < FindByIdentifierSetupTests
128
+ desc "with an non-existing identifier"
129
+
130
+ setup do
131
+ Assert
132
+ .stub(redis.connection_spy, :exists?)
133
+ .with(subject.redis_key(identifier)){ false }
134
+ end
135
+
136
+ should "raise an exception" do
137
+ assert_that{ subject.find_by_identifier(identifier) }
138
+ .raises(subject::NotFoundError)
139
+ end
140
+ end
141
+
142
+ class FindByRedisKeySetupTests < ReceiverTests
143
+ desc ".find_by_redis_key"
144
+
145
+ setup do
146
+ Assert
147
+ .stub(redis.connection_spy, :get)
148
+ .with(subject.redis_key(identifier)){ encoded_record_data }
149
+ end
150
+
151
+ let(:encoded_record_data){ MuchRails::JSON.encode(record_data) }
152
+ end
153
+
154
+ class FindByExistingRedisKeyTests < FindByRedisKeySetupTests
155
+ desc "with an existing redis key"
156
+
157
+ setup do
158
+ Assert
159
+ .stub(redis.connection_spy, :exists?)
160
+ .with(subject.redis_key(identifier)){ true }
161
+ end
162
+
163
+ should "lookup the existing record data and build an instance with it" do
164
+ record = subject.find_by_redis_key(subject.redis_key(identifier))
165
+ assert_that(record).equals(subject.new(**record_data.symbolize_keys))
166
+ end
167
+ end
168
+
169
+ class FindByNonExistingRedisKeyTests < FindByRedisKeySetupTests
170
+ desc "with an non-existing redis key"
171
+
172
+ setup do
173
+ Assert
174
+ .stub(redis.connection_spy, :exists?)
175
+ .with(subject.redis_key(identifier)){ false }
176
+ end
177
+
178
+ should "raise an exception" do
179
+ assert_that{ subject.find_by_redis_key(subject.redis_key(identifier)) }
180
+ .raises(subject::NotFoundError)
181
+ end
182
+ end
183
+
184
+ class FindAllRedisKeysTests < ReceiverTests
185
+ desc ".find_all_redis_keys"
186
+
187
+ setup do
188
+ Assert
189
+ .stub(redis.connection_spy, :keys)
190
+ .with("#{receiver_class.REDIS_KEY_NAMESPACE}:*"){ all_redis_keys }
191
+ end
192
+
193
+ let(:all_redis_keys) do
194
+ Array.new(Factory.integer(3)) do
195
+ "#{receiver_class.REDIS_KEY_NAMESPACE}:#{Factory.uuid}"
196
+ end
197
+ end
198
+
199
+ should "lookup all redis keys matching the key namespace" do
200
+ assert_that(subject.find_all_redis_keys).equals(all_redis_keys)
201
+ end
202
+ end
203
+
204
+ class FindAllTests < ReceiverTests
205
+ desc ".find_all_tests"
206
+
207
+ setup do
208
+ Assert.stub(receiver_class, :find_all_redis_keys) do
209
+ redis_record_redis_keys
210
+ end
211
+ Assert
212
+ .stub(receiver_class, :find_by_redis_key)
213
+ .with(redis_record_redis_keys.first) do
214
+ redis_records.first
215
+ end
216
+ end
217
+
218
+ let(:redis_records) do
219
+ [receiver_class.new(**record_data.symbolize_keys)]
220
+ end
221
+ let(:redis_record_redis_keys){ redis_records.map(&:redis_key) }
222
+
223
+ should "lookup all redis keys matching the key namespace" do
224
+ assert_that(subject.find_all).equals(redis_records)
225
+ end
226
+ end
227
+
228
+ class InitTests < ReceiverTests
229
+ desc "when init"
230
+ subject{ receiver_class.new(field1: field1_value) }
231
+
232
+ should have_imeths :valid?, :errors, :save!, :destroy!, :to_h
233
+
234
+ should "know its attributes" do
235
+ assert_that(subject.valid?).equals(true)
236
+ assert_that(subject.errors).equals({})
237
+ assert_that(subject.to_h)
238
+ .equals({
239
+ "field1" => field1_value,
240
+ })
241
+ end
242
+ end
243
+
244
+ class SaveSetupTests < InitTests
245
+ desc ".save!"
246
+
247
+ setup do
248
+ create_time_now
249
+ Assert.stub(Time, :now){ create_time_now }
250
+ end
251
+
252
+ let(:create_time_now){ Time.now }
253
+ end
254
+
255
+ class SaveNewRecordTests < SaveSetupTests
256
+ desc "on a new record"
257
+
258
+ should "set save data and save the record" do
259
+ assert_that(subject.identifier).is_nil
260
+ assert_that(subject.created_at).is_nil
261
+ assert_that(subject.updated_at).is_nil
262
+
263
+ result = subject.save!
264
+ assert_that(result).is_true
265
+
266
+ assert_that(subject.identifier).equals(identifier)
267
+ assert_that(subject.created_at).equals(create_time_now)
268
+ assert_that(subject.updated_at).equals(create_time_now)
269
+
270
+ assert_that(redis.calls.size).equals(3)
271
+ multi_call, set_call, expire_call = redis.calls
272
+
273
+ assert_that(multi_call.command).equals(:multi)
274
+
275
+ assert_that(set_call.command).equals(:set)
276
+ assert_that(set_call.args)
277
+ .equals([
278
+ subject.class.redis_key(identifier),
279
+ MuchRails::JSON.encode(
280
+ subject.to_h.merge({
281
+ "identifier" => subject.identifier,
282
+ "created_at" => subject.created_at.iso8601,
283
+ "updated_at" => subject.updated_at.iso8601,
284
+ }),
285
+ ),
286
+ ])
287
+
288
+ assert_that(expire_call.command).equals(:expire)
289
+ assert_that(expire_call.args)
290
+ .equals([subject.class.redis_key(identifier), subject.class.TTL_SECS])
291
+ end
292
+ end
293
+
294
+ class SaveExistingRecordTests < SaveSetupTests
295
+ desc "on an existing record"
296
+
297
+ setup do
298
+ subject.save!
299
+
300
+ Assert.unstub(Time, :now)
301
+ update_time_now
302
+ Assert.stub(Time, :now){ update_time_now }
303
+
304
+ redis.reset!
305
+ end
306
+
307
+ let(:update_time_now){ Time.now }
308
+
309
+ should "set save data and save the record" do
310
+ assert_that(subject.identifier).equals(identifier)
311
+ assert_that(subject.created_at).equals(create_time_now)
312
+ assert_that(subject.updated_at).equals(create_time_now)
313
+
314
+ result = subject.save!
315
+ assert_that(result).is_true
316
+
317
+ assert_that(subject.identifier).equals(identifier)
318
+ assert_that(subject.created_at).equals(create_time_now)
319
+ assert_that(subject.updated_at).equals(update_time_now)
320
+ end
321
+ end
322
+
323
+ class SaveWithNoTTLTests < SaveSetupTests
324
+ desc "with no TTL_SECS"
325
+
326
+ setup do
327
+ Assert.stub(receiver_class, :TTL_SECS){ nil }
328
+ end
329
+
330
+ should "set save data and save the record" do
331
+ subject.save!
332
+
333
+ assert_that(redis.calls.size).equals(2)
334
+ multi_call, set_call = redis.calls
335
+
336
+ assert_that(multi_call.command).equals(:multi)
337
+ assert_that(set_call.command).equals(:set)
338
+ end
339
+ end
340
+
341
+ class SaveWithValidationErrorsTests < SaveSetupTests
342
+ desc "with validation errors"
343
+
344
+ setup do
345
+ Assert.stub(subject, :field1){ [nil, ""].sample }
346
+ end
347
+
348
+ should "not set save data and not save the record" do
349
+ assert_that(subject.valid?).equals(false)
350
+ exception =
351
+ assert_that{ subject.save! }.raises(MuchRails::InvalidError)
352
+
353
+ assert_that(exception.errors).equals(subject.errors)
354
+ assert_that(redis.calls.size).equals(0)
355
+ end
356
+ end
357
+
358
+ class DestroySetupTests < InitTests
359
+ desc ".destroy!"
360
+ end
361
+
362
+ class DestroyNewRecordTests < DestroySetupTests
363
+ desc "on a new record"
364
+
365
+ should "do nothing in Redis" do
366
+ assert_that(subject.identifier).is_nil
367
+
368
+ result = subject.destroy!
369
+ assert_that(result).is_true
370
+
371
+ assert_that(redis.calls.size).equals(0)
372
+ end
373
+ end
374
+
375
+ class DestroyExistingRecordTests < DestroySetupTests
376
+ desc "on an existing record"
377
+
378
+ setup do
379
+ subject.save!
380
+ redis.reset!
381
+ end
382
+
383
+ should "delete the record in Redis" do
384
+ assert_that(subject.identifier).is_not_nil
385
+
386
+ result = subject.destroy!
387
+ assert_that(result).is_true
388
+
389
+ assert_that(redis.calls.size).equals(1)
390
+ delete_call = redis.calls.last
391
+
392
+ assert_that(delete_call.command).equals(:del)
393
+ assert_that(delete_call.args)
394
+ .equals([subject.class.redis_key(identifier)])
395
+ end
396
+ end
397
+ end
data/tmp/.keep ADDED
File without changes
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: much-rails-redis-record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kelly Redding
8
+ - Collin Redding
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-06-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: much-style-guide
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.6.4
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.6.4
28
+ - !ruby/object:Gem::Dependency
29
+ name: assert
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 2.19.6
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 2.19.6
42
+ - !ruby/object:Gem::Dependency
43
+ name: much-rails
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.4.2
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 0.4.2
56
+ - !ruby/object:Gem::Dependency
57
+ name: hella-redis
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.5.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 0.5.0
70
+ description: Store records in Redis with MuchRails.
71
+ email:
72
+ - kelly@kellyredding.com
73
+ - collin.redding@me.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - lib/much-rails-redis-record.rb
82
+ - lib/much-rails-redis-record/fake_behaviors.rb
83
+ - lib/much-rails-redis-record/version.rb
84
+ - log/.keep
85
+ - much-rails-redis-record.gemspec
86
+ - test/helper.rb
87
+ - test/support/factory.rb
88
+ - test/system/.keep
89
+ - test/unit/fake_behaviors_tests.rb
90
+ - test/unit/much-rails-redis-record_tests.rb
91
+ - tmp/.keep
92
+ homepage: https://github.com/redding/much-rails-redis-record
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '2.5'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.1.6
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Store records in Redis with MuchRails.
115
+ test_files:
116
+ - test/helper.rb
117
+ - test/support/factory.rb
118
+ - test/system/.keep
119
+ - test/unit/fake_behaviors_tests.rb
120
+ - test/unit/much-rails-redis-record_tests.rb