rack-freeze 1.1.0 → 1.2.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
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