rack-freeze 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aee544065c394af39bb0593a17193538594a7c34
4
- data.tar.gz: d82a8b27bc3277c9e030adddb4ac32ef5ff4f210
3
+ metadata.gz: 3bc56dafbfc276a4b560e73d0043a29a42197022
4
+ data.tar.gz: 7c17bb6e88b2b6cf4183b278a9d5e46bef0fb429
5
5
  SHA512:
6
- metadata.gz: d6998afca2847208bb4280501c9ac17dd8130704b9a4b3403595350ab03d1946387eca7ca8e62afe82d8a041b0fb566c45181b82556cc0ec629b1ec4727f6cab
7
- data.tar.gz: 0d4f2e3f8223bf34cc90f9bf8c28ec428fda2d28a5ce259f27bdfaa00ac33603d32733e048be1371ae396a8843a73afa08c337774a2ef6d70f286df87fb369ba
6
+ metadata.gz: c42c134837c30103be30583c162d96e9ee70d6815aa09dfd8452a8d968aab7039b86c158854a6b607c4e461d9a2c54e8004cbd9aaa7707f4cadf4f82c95ae585
7
+ data.tar.gz: ef9492576931fabef0b648e6cb441c537de7fdcca8eefdda294b1f31cd861054ca634319a01f309885ea16d4ff15d6847df290793b7ed3bd57cef081ca03a48b
data/README.md CHANGED
@@ -32,7 +32,7 @@ Now all your middleware will be frozen by default.
32
32
 
33
33
  ### What bugs does this fix?
34
34
 
35
- It guarantees, within the limits of the freeze API, that middleware won't mutate during a request.
35
+ It guarantees as much as is possible, that middleware won't mutate during a request.
36
36
 
37
37
  ```ruby
38
38
  # This modifies `Rack::Builder#use` and `Rack::Builder#to_app` to generate a frozen stack of middleware.
@@ -54,7 +54,39 @@ end
54
54
  use NonThreadSafeMiddleware
55
55
  ```
56
56
 
57
- As `NonThreadSafeMiddleware` mutates it's state `@state += 1`, it will raise a `RuntimeError`. In a multi-threaded web-server, unprotected mutation of internal state will lead to undefined behavior.
57
+ As `NonThreadSafeMiddleware` mutates it's state `@state += 1`, it will raise a `RuntimeError`. In a multi-threaded web-server, unprotected mutation of internal state will lead to undefined behavior. [5 out of 4 dentists agree that multi-threaded programming is hard to get right](http://www.rubyinside.com/does-the-gil-make-your-ruby-code-thread-safe-6051.html).
58
+
59
+ ### How to write thread-safe middleware?
60
+
61
+ There are two options: Don't mutate state, or if you need to for the purposes of performance, implement `#freeze` and use data-structures from [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby).
62
+
63
+ ```ruby
64
+ require 'concurrent/map'
65
+
66
+ # Cache every request based on the path. Don't do this in production :)
67
+ class CacheEverythingForever
68
+ def initialize(app)
69
+ @app = app
70
+ @cache_all_the_things = Concurrent::Map.new
71
+ end
72
+
73
+ # Because you supply your own implementation of #freeze, Rack::Freeze won't touch this middleware.
74
+ def freeze
75
+ return self if frozen?
76
+
77
+ @app.freeze
78
+
79
+ super
80
+ end
81
+
82
+ def call(env)
83
+ # Use the thread-safe `Concurrent::Map` to fetch the value or store it if it doesn't exist already.
84
+ @cache_all_the_things.fetch_or_store(env[Rack::PATH_INFO]) do
85
+ @app.call(env)
86
+ end
87
+ end
88
+ end
89
+ ```
58
90
 
59
91
  ## Contributing
60
92
 
data/Rakefile CHANGED
@@ -1,12 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new(:spec) do |task|
5
- begin
6
- require('simplecov/version')
7
- task.rspec_opts = %w{--require simplecov} if ENV['COVERAGE']
8
- rescue LoadError
9
- end
10
- end
4
+ RSpec::Core::RakeTask.new(:spec)
11
5
 
12
6
  task :default => :spec
@@ -19,6 +19,7 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative 'freeze/version'
22
-
23
- require_relative 'freeze/freezer'
24
22
  require_relative 'freeze/builder'
23
+
24
+ # Enforce the policy globally.
25
+ Rack::Builder.prepend(Rack::Freeze::Builder)
@@ -25,7 +25,11 @@ module Rack
25
25
  module Freeze
26
26
  module Builder
27
27
  def use(klass, *args, &block)
28
- super(Freeze[klass], *args, &block)
28
+ super Freezer.new(klass), *args, &block
29
+ end
30
+
31
+ def run(app)
32
+ super app.freeze
29
33
  end
30
34
 
31
35
  def to_app
@@ -33,6 +37,4 @@ module Rack
33
37
  end
34
38
  end
35
39
  end
36
-
37
- Builder.prepend(Freeze::Builder)
38
40
  end
@@ -20,28 +20,18 @@
20
20
 
21
21
  module Rack
22
22
  module Freeze
23
- # Check if the given klass overrides `Kernel#freeze`.
24
- def self.implements_freeze?(klass)
25
- klass.instance_method(:freeze).owner != Kernel
26
- end
27
-
28
- # Generate a subclass with a generic #freeze method to freeze all instance variables.
29
- def self.[] klass
30
- # Check if the class already has a custom implementation of #freeze.. which we assume works correctly.
31
- return klass if implements_freeze?(klass)
23
+ class Freezer
24
+ def initialize(klass)
25
+ @klass = klass
26
+ end
32
27
 
33
- subclass = Class.new(klass) do
34
- def freeze
35
- # This ensures that all class variables are frozen.
36
- self.instance_variables.each do |name|
37
- self.instance_variable_get(name).freeze
38
- end
39
-
40
- super
41
- end
28
+ def to_s
29
+ "#{self.class}<#{@klass}>"
42
30
  end
43
31
 
44
- return subclass
32
+ def new(*args, &block)
33
+ @klass.new(*args, &block).freeze
34
+ end
45
35
  end
46
36
  end
47
37
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Rack
22
22
  module Freeze
23
- VERSION = "1.1.0"
23
+ VERSION = "1.2.0"
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-freeze
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-09 00:00:00.000000000 Z
11
+ date: 2017-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -75,7 +75,6 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - ".gitignore"
77
77
  - ".rspec"
78
- - ".simplecov"
79
78
  - ".travis.yml"
80
79
  - Gemfile
81
80
  - README.md
data/.simplecov DELETED
@@ -1,9 +0,0 @@
1
-
2
- SimpleCov.start do
3
- add_filter "/spec/"
4
- end
5
-
6
- if ENV['TRAVIS']
7
- require 'coveralls'
8
- Coveralls.wear!
9
- end