reloadable_middleware 1.0.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: e0281e16f5f7150ffe47f3e5925013d3e4abdc9f2b98d3935f1a91a2c338647f
4
+ data.tar.gz: 6a26b27aa61dd2c294295a488d8cd4c862498381cfdfe33adba6663a9ef04bda
5
+ SHA512:
6
+ metadata.gz: f282832e0fc9d22624e5f0db462318d1eb174fd6a5722632f7842b0d2a6cf2a5393b01165c0807b662be3c13e701a1862aeb9dfd33c502bd68d000b1cc845da6
7
+ data.tar.gz: 7961961f2f5d2b0db76180a99663c2bad75af91f3b5831214fc2ee62623f4cd963eaa1c0a06ac3440fadb34bef386959986a5091538abf9ca784049147952bbb
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # Bundler lock
11
+ Gemfile.lock
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.1
7
+ before_install: gem install bundler -v 1.17.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 1.0.0
2
+
3
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in reloadable_middleware.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # reloadable_midleware
2
+
3
+ Makes any Rack middleware reloadable. Or, rather - wraps a given piece of Rack middleware with a
4
+ special module, which will call `.new` on your middleware at `call()` time, not at instantiation
5
+ time. This means that the Rack application stack is not going to contain an object reference and
6
+ will be able to reload during constant teardown/setup.
7
+
8
+ ## Usage
9
+
10
+ Replace your standard middleware initialization block:
11
+
12
+ ```ruby
13
+ use CustomAuthentication, option: 'bar'
14
+ ```
15
+
16
+ with this alteration:
17
+
18
+ ```ruby
19
+ use ReloadableMiddleware.wrap(CustomAuthentication), option: 'bar'
20
+ ```
21
+
22
+ This will make your middleware reloadable. In Rails, use
23
+
24
+ ```ruby
25
+ Rails.application.config.middleware.use ReloadableMiddleware.wrap(MyMiddleware)
26
+ ```
27
+
28
+ ## Performance in production modes
29
+
30
+ If you are running with `RACK_ENV` set to `production` or with `Rails.env.production? == true`
31
+ the reloading will be disabled and you will have one instance of your middleware as usual. This
32
+ reduces object churn, since Rack assumes that your entire app stack is going to be built once
33
+ and then cached between requests - which helps performance.
34
+
35
+ ## Installation
36
+
37
+ Add this line to your application's Gemfile:
38
+
39
+ ```ruby
40
+ gem 'reloadable_middleware'
41
+ ```
42
+
43
+ And then execute:
44
+
45
+ $ bundle
46
+
47
+ Or install it yourself as:
48
+
49
+ $ gem install reloadable_middleware
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
54
+
55
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
56
+
57
+ ## Contributing
58
+
59
+ Bug reports and pull requests are welcome on GitHub at https://github.com/julik/reloadable_middleware.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "reloadable_middleware"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,53 @@
1
+ require_relative "reloadable_middleware/version"
2
+ require_relative 'reloadable_middleware/railtie' if defined?(Rails)
3
+
4
+ module ReloadableMiddleware
5
+ # Base class for the reloadable middleware. We need to save
6
+ # the middleware name somewhere, so this class will be
7
+ # subclassed dynamically and preconfigured with the
8
+ # correct middleware name, to be used by its `.to_s` return value
9
+ class Lazy
10
+ # Saves the app and the arguments for configuring the middleware and applies them
11
+ # when `call()` gets called.
12
+ def initialize(app, *args_for_middleware_new, &block_for_middleware_new)
13
+ @app = app
14
+ @args_for_middleware_new = args_for_middleware_new
15
+ @block_for_middleware_new = block_for_middleware_new.to_proc if block_for_middleware_new
16
+ end
17
+
18
+ # Instantiate the given middleware by name, on the spot, and call() it immediately
19
+ def call(env)
20
+ middleware_module = lookup_middleware_module
21
+ app_wrapped_with_middleware = middleware_module.new(@app, *@args_for_middleware_new, &@block_for_middleware_new)
22
+ app_wrapped_with_middleware.call(env)
23
+ end
24
+
25
+ # This is needed since Rails, for instance, calls "to_s" on the middleware
26
+ # class when showing its middleware list. The output also is valid Ruby code,
27
+ # so we need to print something that can be pasted into code again.
28
+ def self.to_s
29
+ "ReloadableMiddleware.wrap(#{@middleware_name})"
30
+ end
31
+
32
+ private
33
+
34
+ def lookup_middleware_module
35
+ middleware_module_name = self.class.instance_variable_get('@middleware_name')
36
+ # Rails constantize() on the cheap
37
+ middleware_module_name.split('::').reject(&:empty?).inject(Kernel) {|namespace, const_name| namespace.const_get(const_name) }
38
+ end
39
+ end
40
+
41
+ def self.wrap(middleware_class_or_name)
42
+ # Specific case: if the middleware is being _defined_ in production (Rails or bare Rack)
43
+ # instead of making it reloadable return the object itself, since we otherwise churn
44
+ # one instance of the middleware we wrap per request.
45
+ if ENV['RACK_ENV'] == 'production' || defined?(Rails.env) && Rails.env.production?
46
+ return middleware_class_or_name
47
+ else
48
+ middleware_module_name = middleware_class_or_name.to_s
49
+ Class.new(Lazy) { @middleware_name = middleware_module_name }
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,3 @@
1
+ module ReloadableMiddleware
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,40 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "reloadable_middleware/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reloadable_middleware"
8
+ spec.version = ReloadableMiddleware::VERSION
9
+ spec.authors = ["Julik Tarkhanov"]
10
+ spec.email = ["me@julik.nl"]
11
+
12
+ spec.summary = %q{ Wraps any Rack middleware with lazy resolve at call() }
13
+ spec.description = %q{ Wraps any Rack middleware with lazy resolve at call() }
14
+ spec.homepage = "https://github.com/julik/reloadable_middleware"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
20
+ spec.metadata["homepage_uri"] = spec.homepage
21
+ spec.metadata["source_code_uri"] = "https://github.com/julik/reloadable_middleware"
22
+ spec.metadata["changelog_uri"] = "https://github.com/julik/reloadable_middleware/blob/master/CHANGELOG.md"
23
+ else
24
+ raise "RubyGems 2.0 or newer is required to protect against " \
25
+ "public gem pushes."
26
+ end
27
+
28
+ # Specify which files should be added to the gem when it is released.
29
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
+ `git ls-files -z`.split("\x0")
32
+ end
33
+ spec.bindir = "exe"
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ["lib"]
36
+
37
+ spec.add_development_dependency "bundler", "~> 1.17"
38
+ spec.add_development_dependency "rake", "~> 10.0"
39
+ spec.add_development_dependency "rspec", "~> 3.0"
40
+ end
@@ -0,0 +1,69 @@
1
+ RSpec.describe ReloadableMiddleware do
2
+ it "has a version number" do
3
+ expect(ReloadableMiddleware::VERSION).not_to be nil
4
+ end
5
+
6
+ it "wraps a given module with a reloading class, giving it a sensible to_s return value" do
7
+ class MiddlewareWithArguments
8
+ def initialize(app, frobnicate:)
9
+ @app = app
10
+ @frobnicate = frobnicate
11
+ end
12
+
13
+ def call(env)
14
+ raise "Should not call the same middleware object for the second time" if @got_called
15
+ @got_called = :yes
16
+
17
+ env['did_pass_middleware1'] = true
18
+ env['frobnicate'] = @frobnicate
19
+ @app.call(env)
20
+ end
21
+ end
22
+
23
+ class MiddlewareWithoutArguments
24
+ def initialize(app)
25
+ @app = app
26
+ end
27
+
28
+ def call(env)
29
+ raise "Should not call the same middleware object for the second time" if @got_called
30
+ @got_called = :yes
31
+
32
+ env['did_pass_middleware2'] = true
33
+ @app.call(env)
34
+ end
35
+ end
36
+
37
+ reloadable_class = ReloadableMiddleware.wrap(MiddlewareWithArguments)
38
+ expect(reloadable_class.to_s).to include('MiddlewareWithArguments')
39
+
40
+ rack_app = ->(env) {
41
+ expect(env['did_pass_middleware1']).to eq(true)
42
+ expect(env['did_pass_middleware2']).to eq(true)
43
+ expect(env['frobnicate']).to eq('totally')
44
+ }
45
+ expect(rack_app).to receive(:call).twice.and_call_original
46
+
47
+ reloadable_class2 = ReloadableMiddleware.wrap(MiddlewareWithoutArguments)
48
+
49
+ # Build out a tiny Rack app stack
50
+ mounted_middleware = reloadable_class2.new(reloadable_class.new(rack_app, frobnicate: 'totally'))
51
+
52
+ # ...and call into it, twice. If our middleware objects were retained (as they are with standard
53
+ # Rack wrapping process) we will get an exception, since these particular middleware objects
54
+ # will only tolerate being called once.
55
+ mounted_middleware.call({})
56
+ mounted_middleware.call({})
57
+ end
58
+
59
+ it 'turns wrap() into a no-op if RACK_ENV is set to "production"' do
60
+ class SomeMiddleware
61
+ end
62
+
63
+ ENV['RACK_ENV'] = 'production'
64
+ wrapped = ReloadableMiddleware.wrap(SomeMiddleware)
65
+ expect(wrapped).to eq(SomeMiddleware)
66
+
67
+ ENV.delete('RACK_ENV')
68
+ end
69
+ end
@@ -0,0 +1,14 @@
1
+ require "bundler/setup"
2
+ require "reloadable_middleware"
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = ".rspec_status"
7
+
8
+ # Disable RSpec exposing methods globally on `Module` and `main`
9
+ config.disable_monkey_patching!
10
+
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reloadable_middleware
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-04-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: " Wraps any Rack middleware with lazy resolve at call() "
56
+ email:
57
+ - me@julik.nl
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - CHANGELOG.md
66
+ - Gemfile
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - lib/reloadable_middleware.rb
72
+ - lib/reloadable_middleware/version.rb
73
+ - reloadable_middleware.gemspec
74
+ - spec/reloadable_middleware_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: https://github.com/julik/reloadable_middleware
77
+ licenses: []
78
+ metadata:
79
+ homepage_uri: https://github.com/julik/reloadable_middleware
80
+ source_code_uri: https://github.com/julik/reloadable_middleware
81
+ changelog_uri: https://github.com/julik/reloadable_middleware/blob/master/CHANGELOG.md
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.7.6
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Wraps any Rack middleware with lazy resolve at call()
102
+ test_files: []