rack-redic 1.3.0 → 1.4.0

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
- SHA1:
3
- metadata.gz: bf92c89449d8d8709de10c6764860f6bf170e5d0
4
- data.tar.gz: bf999b9c9675ad376c52ae1eb6df3042f6c4d7a1
2
+ SHA256:
3
+ metadata.gz: b5d2082c913c7e03778bf029522eaf82c69321d279d2faba665d6fb72f6d60ad
4
+ data.tar.gz: e3ee0c189726c401312cb2fccf54dd7c491d1788ef0baf4c74e2649715bc0f3d
5
5
  SHA512:
6
- metadata.gz: 245c12a951c0c0c395064a64ab594f2d5e8774979bdcb3feb23fc855666612e50db63eead26aa5df911287a7c1f184918ea1a2cc69ae01f07f52e8fa17c0807b
7
- data.tar.gz: 5a0d6bff6ad7faae40884f75174d43728e87ea4526f73eb0b826ac414a2f05851b4c10f643ee0c53fb1bae5faa6ae6fe58f52f6222c1df731ff33d331b9f8e3a
6
+ metadata.gz: bee20ab72c2041aeb81c944c9635d5c736f5074854ec016d0c695a85979a9ddb5ed78c1940d5993e684c2cc6b6107dd105bdf4c8b794cdb72e199e11d9eb3ce0
7
+ data.tar.gz: fa4455eb28f23b911dd1ce00441d6098f2386373f0fc74162e4b9f4b7e2cf614ca7f08b9391fab8f2cc923159191a9079614d1f225dea85300ec3bc682965c9a
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
3
  /Gemfile.lock
4
+ /gems.locked
4
5
  /_yardoc/
5
6
  /coverage/
6
7
  /doc/
@@ -0,0 +1,55 @@
1
+ # http://rubocop.readthedocs.io
2
+ # https://github.com/bbatsov/rubocop/blob/master/config/enabled.yml
3
+ AllCops:
4
+ DisplayCopNames: true
5
+ DisplayStyleGuide: true
6
+ ExtraDetails: true
7
+ TargetRubyVersion: 2.4
8
+
9
+ Layout/AlignParameters:
10
+ EnforcedStyle: with_fixed_indentation
11
+
12
+ Layout/EmptyLineAfterMagicComment:
13
+ Enabled: false
14
+
15
+ Layout/IndentHash:
16
+ EnforcedStyle: consistent
17
+
18
+ Layout/MultilineOperationIndentation:
19
+ EnforcedStyle: indented
20
+
21
+ Layout/SpaceInsideStringInterpolation:
22
+ EnforcedStyle: space
23
+
24
+ Metrics/AbcSize:
25
+ Enabled: false
26
+
27
+ Metrics/BlockLength:
28
+ Enabled: false
29
+
30
+ Metrics/ClassLength:
31
+ Enabled: false
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Enabled: false
35
+
36
+ Metrics/LineLength:
37
+ Enabled: false
38
+
39
+ Metrics/MethodLength:
40
+ Enabled: false
41
+
42
+ Metrics/ModuleLength:
43
+ Enabled: false
44
+
45
+ Metrics/PerceivedComplexity:
46
+ Enabled: false
47
+
48
+ # Style/Documentation:
49
+ # Enabled: false
50
+
51
+ Style/IfUnlessModifier:
52
+ Enabled: false
53
+
54
+ Style/Next:
55
+ MinBodyLength: 8
data/README.md CHANGED
@@ -13,7 +13,7 @@ Any other options will get passed to `Rack::Session::Abstract::Persisted`.
13
13
 
14
14
  ## Installation
15
15
 
16
- Add this line to your application's Gemfile:
16
+ Add this line to your application's Gemfile or gems.rb file:
17
17
 
18
18
  ```ruby
19
19
  gem 'rack-redic', require: 'rack/session/redic'
data/Rakefile CHANGED
@@ -1,3 +1,2 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
  require 'bundler/gem_tasks'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  source 'https://rubygems.org'
2
3
 
3
4
  # Load gem dependencies.
@@ -1,11 +1,10 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
  require 'rack/session/abstract/id'
4
3
  require 'redic'
4
+ require 'securerandom'
5
5
 
6
6
  module Rack
7
7
  module Session
8
- #
9
8
  # Rack::Session::Redic provides simple cookie based session management.
10
9
  # Session data is stored in Redis via the Redic gem. The corresponding
11
10
  # session key is maintained in the cookie.
@@ -25,46 +24,64 @@ module Rack
25
24
  # refresh the expiration set in Redis with each request.
26
25
  #
27
26
  # Any other options will get passed to Rack::Session::Abstract::Persisted.
28
- #
29
27
  class Redic < Abstract::Persisted
30
- REDIS_URL = 'REDIS_URL'.freeze
31
-
28
+ # Redis commands.
29
+ DELETE = 'DEL'
30
+ EX = 'EX'
31
+ EXISTS = 'EXISTS'
32
+ GET = 'GET'
33
+ SET = 'SET'
34
+
35
+ # Assorted.
36
+ REDIS_URL = 'REDIS_URL'
37
+ ZERO = 0
38
+
39
+ # Access the storage interface directly. Needed for testing.
40
+ #
41
+ # @return [Redic]
32
42
  attr_reader :storage
33
43
 
34
44
  def initialize(app, options = {})
35
45
  super
36
46
 
47
+ @expires = options[:expire_after]
48
+ @marshaller = options.fetch(:marshaller) { Marshal }
37
49
  @mutex = Mutex.new
38
- @storage = Storage.new(
39
- options[:expire_after],
40
- options.fetch(:marshaller) { Marshal },
41
- options.fetch(:url) { ENV.fetch(REDIS_URL) }
42
- )
50
+ @storage = ::Redic.new(options.fetch(:url) { ENV.fetch(REDIS_URL) })
43
51
  end
44
52
 
45
- # Only accept a generated session ID if it doesn't exist.
53
+ # Generate a session ID that doesn't already exist.
54
+ #
55
+ # Based on Rack::Session::Abstract::Persisted#generate_sid and
56
+ # Rack::Session::Memcache#generate_sid but without the conditional check.
57
+ # We always generate the session ID from SecureRandom#hex.
58
+ #
59
+ # @return [String]
46
60
  def generate_sid
47
61
  loop do
48
- sid = super
49
- break sid unless @storage.exists?(sid)
62
+ session_id = SecureRandom.hex(@sid_length)
63
+ break session_id unless @storage.call(EXISTS, session_id) != ZERO
50
64
  end
51
65
  end
52
66
 
53
67
  # Find the session (or generate a blank one).
54
- def find_session(_req, sid)
68
+ def find_session(_req, session_id)
55
69
  @mutex.synchronize do
56
- unless sid && session = @storage.get(sid)
57
- sid, session = generate_sid, {}
70
+ unless session_id && (session = deserialize(@storage.call(GET, session_id)))
71
+ session_id, session = generate_sid, {} # rubocop:disable Style/ParallelAssignment
58
72
  end
59
73
 
60
- [sid, session]
74
+ [session_id, session]
61
75
  end
62
76
  end
63
77
 
64
78
  # Write the session.
65
79
  def write_session(_req, session_id, new_session, _options)
80
+ arguments = [SET, session_id, serialize(new_session)]
81
+ arguments += [EX, @expires] if @expires
82
+
66
83
  @mutex.synchronize do
67
- @storage.set(session_id, new_session)
84
+ @storage.call(*arguments)
68
85
 
69
86
  session_id
70
87
  end
@@ -73,105 +90,34 @@ module Rack
73
90
  # Kill the session.
74
91
  def delete_session(_req, session_id, options)
75
92
  @mutex.synchronize do
76
- @storage.delete(session_id)
93
+ @storage.call(DELETE, session_id)
77
94
  generate_sid unless options[:drop]
78
95
  end
79
96
  end
80
97
 
81
- # A wrapper around Redic to simplify calls.
82
- class Storage
83
- # Redis commands.
84
- DELETE = 'DEL'.freeze
85
- EX = 'EX'.freeze
86
- EXISTS = 'EXISTS'.freeze
87
- GET = 'GET'.freeze
88
- SET = 'SET'.freeze
89
-
90
- # Assorted.
91
- ZERO = 0
92
-
93
- # @param expires [Integer]
94
- # The number of seconds for Redis to retain keys.
95
- # @param marshaller [#dump, #load]
96
- # The module or class used to marshal objects. It must respond to
97
- # #dump and #load.
98
- # @param url [String]
99
- # The URL to access Redis at.
100
- def initialize(expires, marshaller, url)
101
- @expires = expires
102
- @marshaller = marshaller
103
- @storage = ::Redic.new(url)
104
- end
105
-
106
- # Check for an identifier's existence.
107
- #
108
- # @param id [String]
109
- # The key to check for.
110
- # @return [Boolean]
111
- def exists?(id)
112
- @storage.call(EXISTS, id) != ZERO
113
- end
114
-
115
- # Retrieve an object.
116
- #
117
- # @param id [String]
118
- # The key in Redis to retrieve from.
119
- # @return [Object, nil]
120
- # The object stored at the identifier provided, or nil.
121
- def get(id)
122
- deserialize(@storage.call(GET, id))
123
- end
124
-
125
- # Store an object.
126
- #
127
- # @param id [String]
128
- # The key to use to store the object.
129
- # @param object [Object]
130
- # Any object that can be serialized.
131
- # @return [String]
132
- # See {https://redis.io/commands/set#return-value Redis' docs for more}.
133
- def set(id, object)
134
- arguments = [SET, id, serialize(object)]
135
- arguments += [EX, @expires] if @expires
136
-
137
- @storage.call(*arguments)
138
- end
139
-
140
- # Remove an object.
141
- #
142
- # @param id [String]
143
- # The key to delete.
144
- # @return [Integer]
145
- # The number of keys that were deleted. See
146
- # {https://redis.io/commands/del#return-value Redis' docs for more}.
147
- def delete(id)
148
- @storage.call(DELETE, id)
149
- end
150
-
151
- private
152
-
153
- # Serialize an object using our marshaller.
154
- #
155
- # @param object [Object]
156
- # @return [String]
157
- # The object serialized by the marshaller.
158
- def serialize(object)
159
- @marshaller.dump(object)
160
- end
161
-
162
- # Deserialize a string back into an object.
163
- #
164
- # @param string [String]
165
- # @return [Object, nil]
166
- # Returns the object as loaded by the marshaller, or nil.
167
- def deserialize(string)
168
- @marshaller.load(string) if string
169
-
170
- # In the case that loading fails, return a nil.
171
- rescue
172
- nil
173
- end
98
+ # Serialize an object using our marshaller.
99
+ #
100
+ # @param object [Object]
101
+ # @return [String]
102
+ # The object as serialized by the marshaller.
103
+ def serialize(object)
104
+ @marshaller.dump(object)
105
+ end
106
+ private :serialize
107
+
108
+ # Deserialize a string back into an object.
109
+ #
110
+ # @param string [String]
111
+ # @return [Object, nil]
112
+ # Returns the object as loaded by the marshaller, or nil.
113
+ def deserialize(string)
114
+ @marshaller.load(string) if string
115
+
116
+ # In the case that loading fails, return a nil.
117
+ rescue # rubocop:disable Lint/RescueWithoutErrorClass
118
+ nil
174
119
  end
120
+ private :deserialize
175
121
  end
176
122
  end
177
123
  end
@@ -1,8 +1,7 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
  Gem::Specification.new do |spec|
4
3
  spec.name = 'rack-redic'
5
- spec.version = '1.3.0'
4
+ spec.version = '1.4.0'
6
5
  spec.authors = ['Evan Lecklider']
7
6
  spec.email = ['evan@lecklider.com']
8
7
 
@@ -17,6 +16,5 @@ Gem::Specification.new do |spec|
17
16
  spec.add_dependency 'redic'
18
17
 
19
18
  spec.add_development_dependency 'minitest'
20
- spec.add_development_dependency 'msgpack'
21
- spec.add_development_dependency 'oj'
19
+ spec.add_development_dependency 'rubocop'
22
20
  end
@@ -1,5 +1,5 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
2
+ require_relative 'helper'
3
3
 
4
4
  # Require all test files.
5
5
  Dir.glob('test/*.rb').each(&method(:require))
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  # Add our project folder to the root of our load path.
@@ -8,7 +7,7 @@ $LOAD_PATH.unshift File.expand_path('../..', __FILE__)
8
7
  ENV['REDIS_URL'] ||= 'redis://localhost:6379'
9
8
 
10
9
  # Require our core library.
11
- require 'rack/session/redic'
10
+ require 'lib/rack/session/redic'
12
11
 
13
12
  # Kick off the tests.
14
13
  require 'minitest/autorun'
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
  require 'rack/lint'
4
3
  require 'rack/mock'
@@ -9,7 +8,7 @@ describe Rack::Session::Redic do
9
8
  ROOT = '/'
10
9
 
11
10
  session_key = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS[:key]
12
- session_match = /#{session_key}=([0-9a-fA-F]+);/
11
+ session_match = /#{ session_key }=([0-9a-fA-F]+);/
13
12
 
14
13
  incrementor = lambda do |env|
15
14
  env['rack.session']['counter'] ||= 0
@@ -65,8 +64,8 @@ describe Rack::Session::Redic do
65
64
  response = request.get(ROOT)
66
65
  sid = response[Rack::SET_COOKIE][session_match, 1]
67
66
 
68
- assert_equal request.get("/?rack.session=#{sid}").body, '{"counter"=>1}'
69
- assert_equal request.get("/?rack.session=#{sid}").body, '{"counter"=>1}'
67
+ assert_equal request.get("/?rack.session=#{ sid }").body, '{"counter"=>1}'
68
+ assert_equal request.get("/?rack.session=#{ sid }").body, '{"counter"=>1}'
70
69
  end
71
70
 
72
71
  it 'determines session from params' do
@@ -75,8 +74,8 @@ describe Rack::Session::Redic do
75
74
  response = request.get(ROOT)
76
75
  sid = response[Rack::SET_COOKIE][session_match, 1]
77
76
 
78
- assert_equal request.get("/?rack.session=#{sid}").body, '{"counter"=>2}'
79
- assert_equal request.get("/?rack.session=#{sid}").body, '{"counter"=>3}'
77
+ assert_equal request.get("/?rack.session=#{ sid }").body, '{"counter"=>2}'
78
+ assert_equal request.get("/?rack.session=#{ sid }").body, '{"counter"=>3}'
80
79
  end
81
80
 
82
81
  it 'survives nonexistant cookies' do
@@ -88,7 +87,7 @@ describe Rack::Session::Redic do
88
87
  assert_equal response.body, '{"counter"=>1}'
89
88
 
90
89
  cookie = response[Rack::SET_COOKIE][session_match]
91
- refute_match /#{ bad_cookie }/, cookie
90
+ refute_match(/#{ bad_cookie }/, cookie)
92
91
  end
93
92
 
94
93
  it 'maintains freshness' do
@@ -224,10 +223,10 @@ describe Rack::Session::Redic do
224
223
 
225
224
  res0 = request.get(ROOT)
226
225
  session_id = (cookie = res0[Rack::SET_COOKIE])[session_match, 1]
227
- ses0 = redic.storage.get(session_id)
226
+ ses0 = redic.storage.call('GET', session_id)
228
227
 
229
228
  request.get(ROOT, Rack::HTTP_COOKIE => cookie)
230
- ses1 = redic.storage.get(session_id)
229
+ ses1 = redic.storage.call('GET', session_id)
231
230
 
232
231
  refute_equal ses1, ses0
233
232
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-redic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Lecklider
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-12 00:00:00.000000000 Z
11
+ date: 2017-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -53,21 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: msgpack
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: oj
56
+ name: rubocop
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
59
  - - ">="
@@ -89,21 +75,18 @@ extra_rdoc_files: []
89
75
  files:
90
76
  - ".editorconfig"
91
77
  - ".gitignore"
78
+ - ".rubocop.yml"
92
79
  - CODE_OF_CONDUCT.md
93
- - Gemfile
94
80
  - LICENSE.txt
95
81
  - Makefile
96
82
  - README.md
97
83
  - Rakefile
84
+ - gems.rb
98
85
  - lib/rack/session/redic.rb
99
86
  - rack-redic.gemspec
100
87
  - test/all.rb
101
88
  - test/helper.rb
102
89
  - test/session_redic_test.rb
103
- - test/storage_msgpack_test.rb
104
- - test/storage_oj_test.rb
105
- - test/storage_test.rb
106
- - test/support/storage_marshaller_interface.rb
107
90
  homepage: https://github.com/evanleck/rack-redic
108
91
  licenses:
109
92
  - MIT
@@ -124,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
107
  version: '0'
125
108
  requirements: []
126
109
  rubyforge_project:
127
- rubygems_version: 2.6.13
110
+ rubygems_version: 2.7.3
128
111
  signing_key:
129
112
  specification_version: 4
130
113
  summary: Rack::Session in Redis via Redic
@@ -132,7 +115,3 @@ test_files:
132
115
  - test/all.rb
133
116
  - test/helper.rb
134
117
  - test/session_redic_test.rb
135
- - test/storage_msgpack_test.rb
136
- - test/storage_oj_test.rb
137
- - test/storage_test.rb
138
- - test/support/storage_marshaller_interface.rb
@@ -1,28 +0,0 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
- require 'msgpack'
4
- require_relative 'support/storage_marshaller_interface'
5
-
6
- MessagePack::DefaultFactory.register_type(0x00, Symbol)
7
-
8
- module MessagePackMarshaller
9
- def dump(object)
10
- MessagePack.pack(object)
11
- end
12
- module_function :dump
13
-
14
- def load(string)
15
- MessagePack.unpack(string)
16
- end
17
- module_function :load
18
- end
19
-
20
- describe Rack::Session::Redic::Storage do
21
- describe 'using the MessagePack as the marshaller' do
22
- include StorageMarshallerInterface
23
-
24
- before do
25
- @store = Rack::Session::Redic::Storage.new(nil, MessagePackMarshaller, ENV['REDIS_URL'])
26
- end
27
- end
28
- end
@@ -1,14 +0,0 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
- require 'oj'
4
- require_relative 'support/storage_marshaller_interface'
5
-
6
- describe Rack::Session::Redic::Storage do
7
- describe 'using the Oj as the marshaller' do
8
- include StorageMarshallerInterface
9
-
10
- before do
11
- @store = Rack::Session::Redic::Storage.new(nil, Oj, ENV['REDIS_URL'])
12
- end
13
- end
14
- end
@@ -1,13 +0,0 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
- require_relative 'support/storage_marshaller_interface'
4
-
5
- describe Rack::Session::Redic::Storage do
6
- describe 'using the default marshaller' do
7
- include StorageMarshallerInterface
8
-
9
- before do
10
- @store = Rack::Session::Redic::Storage.new(nil, Marshal, ENV['REDIS_URL'])
11
- end
12
- end
13
- end
@@ -1,30 +0,0 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
- module StorageMarshallerInterface
4
- def test_returns_nil_for_empty_keys
5
- assert_nil @store.get('not-here')
6
- end
7
-
8
- def test_saves_objects
9
- object = { saved: true }
10
- @store.set('saving', object)
11
-
12
- assert_equal @store.get('saving'), object
13
- end
14
-
15
- def test_existence_of_keys
16
- @store.set('exists', false)
17
-
18
- assert_equal @store.exists?('exists'), true
19
- end
20
-
21
- def test_deletes_objects
22
- object = { deleted: true }
23
- @store.set('deleted', object)
24
-
25
- assert_equal @store.get('deleted'), object
26
- @store.delete('deleted')
27
-
28
- assert_nil @store.get('deleted')
29
- end
30
- end