combinaut_director 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 558b04f0ba15457cf86c1b03adfd5a33250f0b8683870b53414936d900a07539
4
+ data.tar.gz: 505c7e703bc500c2abe5ef51f88c38131a3fac34d4972084036b3fc0910a8fcb
5
+ SHA512:
6
+ metadata.gz: ccb12a09877d0f43f8ef8644efa8e30dca67344f41624905b4f6f3812ac65a8aa8490ee0f823881743a540e7b3f3e85c1b688d6bb4296fdade4313231b8afd56
7
+ data.tar.gz: c4b9a652ce4a4bdba5963361f427e7550171244bfd57566f6815831aa7827d7bf9a6f7b8b231d039f444105c1ff3c8bdf7a7b40f39d7275c6c8d75ddf0170cbd
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,139 @@
1
+ # Director
2
+
3
+ Director is a Rack Middleware gem that directs incoming requests to their aliased target paths based on a customizable
4
+ alias list stored in the database. It has two basic handlers, but can be extended with your own.
5
+
6
+ ## Usage
7
+
8
+ ### Proxy
9
+ If you want to create a vanity path, where the contents of one page appears at a new path, use a `proxy` handler. The user agent will show the vanity path in the url bar, but the contents of the target path will appear on the page.
10
+ ```ruby
11
+ Director::Alias.create(source_path: '/vanity/path', target_path: 'real/path', handler: :proxy)
12
+ ```
13
+
14
+ ### Redirect
15
+ If you want to redirect a deprecated or non-canonical path to the canonical path, use a `redirect` handler. The user agent will show the target path in the url bar and the contents of the target path will appear on the page.
16
+ ```ruby
17
+ Director::Alias.create(source_path: '/deprecated/path', target_path: 'new/path', handler: :redirect)
18
+ ```
19
+
20
+ ### Custom Alias Handlers
21
+ You can create handlers for your own custom aliases. Handlers must be namespaced under `Director::Handler` and need only
22
+ implement a single `response` method. For those who have played with Rack, you will find the `response` method is very
23
+ similar to Rack's `MiddleWare#call` method in that it must return a Rack-compatible response. See https://rack.github.io/
24
+ for more information on valid return values, and the `lib/direct/handlers` folder basic for handler examples.
25
+
26
+ ```ruby
27
+ class Director::Handler::MyCustomHandler < Base
28
+ def response(app, env)
29
+ # do some amazing stuff, then...
30
+ # return a valid Rack response
31
+ end
32
+ end
33
+ ```
34
+
35
+ ### Source/Target Record
36
+ An alias can also be linked to a record at both the source and target end of the alias. Linked records allow for automatic updating of the corresponding path. When the record is saved, it runs a method that updates all incoming and outgoing aliases.
37
+
38
+ ```ruby
39
+ class MyModel < ActiveRecord::Base
40
+ has_aliased_paths canonical_path: :path # Accepts a symbol, proc, or object that responds to `#canonical_path`
41
+
42
+ private
43
+
44
+ def path
45
+ # Return the path that routes to this record
46
+ end
47
+ end
48
+ ```
49
+
50
+ ### Url params
51
+ Url "params" or "query" from the request will be passed on and merged with target path. Any url params in the target path
52
+ will be preserved.
53
+
54
+ ### Chaining
55
+ Alias lookups will chain if an alias `target_path` points to `source_path` of another. A `Director::AliasChainLoop`
56
+ exception is raised if a cycle is detected in the alias chain in order to avoid infinite lookups.
57
+ A chain ends when there is no alias with a `source_path` matching the `target_path` of the last
58
+ alias found, or if the last alias found is has a `redirect` handler. A redirect alias ends the chain in order to force the browser to update its url, thus continuing the alias chain lookup in a second request.
59
+
60
+ ## Constraints
61
+ There are several constraints that can be applied to limit which requests are handled. Each constraint consists of a
62
+ whitelist and a blacklist that can be independently configured.
63
+
64
+ ### Format
65
+ The format constraint looks at the request extension. This can be used to ignore asset requests or only apply aliasing
66
+ to HTML requests.
67
+ ```ruby
68
+ Director::Configuration.constraints.format.only = [:html, :xml]
69
+ # or
70
+ Director::Configuration.constraints.format.except = :jpg
71
+ ```
72
+
73
+ ### Source Path
74
+ The source constraint limits what can be entered as a source path in an `Alias`. This can be useful if you want to
75
+ prevent aliasing of certain routes, like an admin namespace for example. `only` and `except` are passed to `validates_format_of`
76
+ validations on the Alias model, and accept any patterns the `:with` and `:without` options of that validator.
77
+ ```ruby
78
+ Director::Configuration.constraints.source_path.only = %r{\A/pages/}
79
+ # or
80
+ Director::Configuration.constraints.source_path.except = %r{\A/admin/}
81
+ ```
82
+ NOTE: This constraint will also limit what requests perform an alias lookup. If a constraint is added, it will effectively
83
+ disable existing aliases that do not match the new constraint.
84
+
85
+ ### Target Path
86
+ The target constraint limits what can be entered as a target path in an `Alias`. This can be useful if you want to
87
+ prevent aliasing of certain routes, like an admin namespace for example. `only` and `except` are passed to `validates_format_of`
88
+ validations on the Alias model, and accept any patterns the `:with` and `:without` options of that validator.
89
+ ```ruby
90
+ Director::Configuration.constraints.target_path.only = %r{\A/pages/}
91
+ # or
92
+ Director::Configuration.constraints.target_path.except = %r{\A/admin/}
93
+ ```
94
+
95
+ ### Request
96
+ The request constraint yields the request itself to a given proc. This can be used to ignore asset requests or only
97
+ apply aliasing based on any aspect of the request, for example, params, host name, etc.<sup id="footnote_ref_1">[1](#footnote_1)</sup>
98
+
99
+ ```ruby
100
+ Director::Configuration.constraints.request.only = ->(request) { request.params['my_param'] == 'false' }
101
+ # or
102
+ Director::Configuration.constraints.request.except = ->(request) { request.env['HTTP_HOST'] == 'testing.test' }
103
+ ```
104
+
105
+ ### Lookup Scope
106
+ The lookup scope constraint is applied using the `ActiveRecord::Base.merge` method to inject the scope into alias the
107
+ lookup query. The constraint should be a callable object, and is passed a `Rack::Request` object for the current request.
108
+ This can be used to scope alias lookups based on the request subdomain, or other request criteria.
109
+
110
+
111
+ ```ruby
112
+ # Assuming you have added a `domain` column to the aliases table...
113
+ Director::Configuration.constraints.lookup_scope = lambda do |request|
114
+ Director::Alias.where(domain: Domain.id_from_host(request.host))
115
+ end
116
+
117
+ # or returning a callable object for merging
118
+ Director::Configuration.constraints.lookup_scope = proc do
119
+ -> { where(client: Client.current) }
120
+ end
121
+ ```
122
+
123
+ ## Request Information
124
+ Sometimes it may be useful to know what the request url was before Director modified it, or know if Director handled the
125
+ request or ignored it due to constraints.
126
+
127
+ ```ruby
128
+ Director::Middleware.original_request_url(request) # => Original request url before Director handled it
129
+ Director::Middleware.handled_request?(request) # => Boolean indicating whether or not Director handled the request
130
+ ```
131
+
132
+ ## Upgrading from an older version
133
+
134
+ ### 1.1.x to 1.2.0
135
+
136
+ Alias resolution is now case insensitive. All incoming paths are downcased before saving or resolving. If you have existing aliases, you should call `#save` on each one to trigger the path sanitizer which will downcase the saved paths so the the downcased paths coming in to the `#resolve` method.
137
+
138
+ ## Footnotes
139
+ <span id="footnote_1">1.</span> By default, Director only handles GET and HEAD requests because the `redirect` handler cannot tell the browser to redirect a POST. The `proxy` handler could internally redirect a POST to an aliased target path, but this dichotomy adds too much complexity for it to be enabled out of the box. The constraints configuration gives you full control over which requests are handled by Director in your application. [↩](#footnote_ref_1)
@@ -0,0 +1,137 @@
1
+ module Director
2
+ class Alias < ActiveRecord::Base
3
+ belongs_to :source, polymorphic: true
4
+ belongs_to :target, polymorphic: true
5
+
6
+ before_validation :sanitize_path
7
+ validates_presence_of :source_path, unless: :source
8
+ validates_presence_of :target_path, unless: :target
9
+ validates_format_of :source_path, with: Configuration.constraints.source_path.only
10
+ validates_format_of :target_path, with: Configuration.constraints.target_path.only
11
+ validates_format_of :source_path, without: Configuration.constraints.source_path.except
12
+ validates_format_of :target_path, without: Configuration.constraints.target_path.except
13
+ validates_format_of :source_path, with: %r{\A/}, if: :source_path_relative?, message: 'is a relative path so it must start with a slash'
14
+ validates_format_of :target_path, with: %r{\A/}, if: :target_path_relative?, message: 'is a relative path so it must start with a slash'
15
+ validate :valid_paths
16
+ validate :valid_handler
17
+
18
+ scope :with_source_path, -> { where.not(source_path: nil) }
19
+ scope :with_target_path, -> { where.not(target_path: nil) }
20
+
21
+ before_save :set_source_path, if: :source_changed?
22
+ before_save :set_target_path, if: :target_changed?
23
+
24
+ def self.resolve_with_constraint(source_path, request)
25
+ merge(Configuration.constraints.lookup_scope.call(request)).resolve(source_path)
26
+ end
27
+
28
+ # Returns the alias matching the source_path, traversing any chained aliases and returning the last one
29
+ def self.resolve(source_path)
30
+ source_path = sanitize_path(source_path)
31
+ found = {}
32
+
33
+ # Traverse a chain of aliases
34
+ while alias_entry = find_by_source_path(source_path) do
35
+ raise AliasChainLoop, [*found.keys, source_path].join(' -> ') if found.key?(alias_entry.target_path)
36
+ break if alias_entry.passthrough? # Stop if we reach a passthrough since the app will handle this
37
+ found[source_path] = alias_entry
38
+ break if alias_entry.redirect? # Stop if we reach a redirect since the browser will need to change url at that point
39
+ source_path = alias_entry.target_path
40
+ end
41
+
42
+ return found.values.last
43
+ end
44
+
45
+ def self.valid_uri?(url)
46
+ !!URI(url)
47
+ rescue URI::InvalidURIError
48
+ false
49
+ end
50
+
51
+ def self.relative?(url)
52
+ URI(url).relative?
53
+ rescue URI::InvalidURIError
54
+ false
55
+ end
56
+
57
+ def self.sanitize_path(path = nil, &block)
58
+ path = block.call if block_given?
59
+ path = path.to_s
60
+ path = path.strip
61
+ path = path.downcase
62
+ path = path.remove(%r{/$}) if path.length > 1
63
+ return path
64
+ end
65
+
66
+ def redirect?
67
+ handler_class <= Handler::Redirect
68
+ end
69
+
70
+ def passthrough?
71
+ handler_class <= Handler::Passthrough
72
+ end
73
+
74
+ def handler_class
75
+ handler_name = "Director::Handler::#{handler.classify}"
76
+ handler_name.constantize
77
+ rescue NameError
78
+ raise MissingAliasHandler, "Handler not found '#{handler_name}'"
79
+ end
80
+
81
+ def blank?
82
+ !(source_path? || target_path? || source || target)
83
+ end
84
+
85
+ private
86
+
87
+ def valid_paths
88
+ source_path = source_changed? && source ? source.generate_canonical_path : self.source_path
89
+ target_path = target_changed? && target ? target.generate_canonical_path : self.target_path
90
+
91
+ errors.add(:source_path, 'is not a valid') unless self.class.valid_uri?(source_path)
92
+ errors.add(:target_path, 'is not a valid') unless self.class.valid_uri?(target_path)
93
+ errors.add(:target_path, 'cannot be the same as the source path') if source_path == target_path
94
+ end
95
+
96
+ def sanitize_path
97
+ self.source_path = self.class.sanitize_path(source_path)
98
+ self.target_path = self.class.sanitize_path(target_path)
99
+ end
100
+
101
+ def set_source_path
102
+ self.source_path = source.generate_canonical_path if source
103
+ end
104
+
105
+ def set_target_path
106
+ self.target_path = target.generate_canonical_path if target
107
+ end
108
+
109
+ def source_changed?
110
+ source_path_changed? || source_id_changed? || source_type_changed?
111
+ end
112
+
113
+ def target_changed?
114
+ target_path_changed? || target_id_changed? || target_type_changed?
115
+ end
116
+
117
+ def source_path_relative?
118
+ self.class.relative?(source_path) if source_path?
119
+ end
120
+
121
+ def target_path_relative?
122
+ self.class.relative?(target_path) if target_path?
123
+ end
124
+
125
+ def valid_handler
126
+ handler_class
127
+ rescue MissingAliasHandler
128
+ errors.add(:handler, 'not defined')
129
+ end
130
+ end
131
+
132
+ # EXCEPTIONS
133
+
134
+ class DirectorException < StandardError; end
135
+ class MissingAliasHandler < DirectorException; end
136
+ class AliasChainLoop < DirectorException; end
137
+ end
@@ -0,0 +1 @@
1
+ require "director"
@@ -0,0 +1,6 @@
1
+ require 'rails'
2
+ require 'director/helpers'
3
+ require 'director/handler'
4
+ require 'director/configuration'
5
+ require 'director/railtie'
6
+ require 'director/engine'
@@ -0,0 +1,18 @@
1
+ require 'ostruct'
2
+
3
+ module Director
4
+ module Configuration
5
+ mattr_reader :constraints
6
+
7
+ Constraint = Struct.new(:only, :except)
8
+
9
+ # Defaults
10
+ @@constraints = OpenStruct.new({
11
+ source_path: Constraint.new,
12
+ target_path: Constraint.new,
13
+ request: Constraint.new(->(request) { request.get? || request.head? }),
14
+ format: Constraint.new,
15
+ lookup_scope: ->(request) { {} }
16
+ })
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'director/handler/base'
2
+ require 'director/handler/passthrough'
3
+ require 'director/handler/proxy'
4
+ require 'director/handler/redirect'
5
+ require 'director/model_extensions'
6
+
7
+ module Director
8
+ class Engine < Rails::Engine
9
+ initializer 'director.load_model_extensions' do |app|
10
+ ActiveSupport.on_load(:active_record) do
11
+ ActiveRecord::Base.extend Director::ModelExtensions::ActMethod
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module Director
2
+ module Handler
3
+ def self.for(alias_entry)
4
+ return Passthrough unless alias_entry
5
+ alias_entry.handler_class
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,34 @@
1
+ # TEST: individual handlers work when a target_path is present
2
+ # TEST: individual handlers work when only a target is present
3
+ # TEST: individual handlers can redirect to "" without looping indefinitely
4
+ require 'uri'
5
+
6
+ module Director
7
+ module Handler
8
+ class Base
9
+ attr_reader :alias_entry
10
+
11
+ def initialize(alias_entry)
12
+ @alias_entry = alias_entry
13
+ end
14
+
15
+ def response(app, env)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ private
20
+
21
+ def request_uri(env)
22
+ URI(Rack::Request.new(env).url).freeze
23
+ end
24
+
25
+ def target_uri
26
+ URI(alias_entry.target_path).freeze
27
+ end
28
+
29
+ def merge_query(query1, query2)
30
+ [query1, query2].select(&:present?).join('&').presence
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ module Director
2
+ module Handler
3
+ class Passthrough < Base
4
+ def response(app, env)
5
+ app.call(env)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Director
2
+ module Handler
3
+ class Proxy < Base
4
+ def response(app, env)
5
+ env['QUERY_STRING'] = merge_query(target_uri.query, request_uri(env).query)
6
+ env['PATH_INFO'] = target_uri.path
7
+
8
+ app.call(env)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Director
2
+ module Handler
3
+ class Redirect < Base
4
+ def response(app, env)
5
+ res = Rack::Response.new
6
+ redirect_uri = target_uri.dup
7
+ redirect_uri.query = merge_query(target_uri.query, request_uri(env).query)
8
+
9
+ res.redirect(redirect_uri.to_s || '/')
10
+ res.finish
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ module Director
2
+ module Helpers
3
+ extend self
4
+
5
+ def matches_constraint?(constraint, value, coerce: :itself)
6
+ value = value.send(coerce)
7
+ only = Array(constraint.only).map(&coerce)
8
+ except = Array(constraint.except).map(&coerce)
9
+
10
+ return false if only.present? && only.none? {|matcher| matches?(matcher, value) }
11
+ return false if except.present? && except.any? {|matcher| matches?(matcher, value) }
12
+ return true
13
+ end
14
+
15
+ def matches?(matcher, value)
16
+ case matcher
17
+ when Regexp
18
+ matches_regexp?(matcher, value)
19
+ when Proc
20
+ matches_proc?(matcher, value)
21
+ else
22
+ matcher == value
23
+ end
24
+ end
25
+
26
+ def matches_regexp?(matcher, value)
27
+ case value
28
+ when matcher
29
+ true
30
+ else
31
+ false
32
+ end
33
+ end
34
+
35
+ def matches_proc?(matcher, value)
36
+ matcher.call(value)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ module Director
2
+ class Middleware
3
+ def self.handled_request?(request)
4
+ request.env['director.original_url'].present?
5
+ end
6
+
7
+ def self.original_request_url(request)
8
+ request.env['director.original_url'] || request.url
9
+ end
10
+
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ @request = Rack::Request.new(env)
17
+
18
+ unless ignored?
19
+ env['director.original_url'] = @request.url
20
+ alias_entry = Director::Alias.resolve_with_constraint(request_path, @request)
21
+ end
22
+
23
+ return handle_alias(alias_entry, env)
24
+ end
25
+
26
+ private
27
+
28
+ def handle_alias(alias_entry, env)
29
+ return Handler.for(alias_entry).new(alias_entry).response(@app, env)
30
+ end
31
+
32
+ def ignored?
33
+ ignored_format?(format) || ignored_path?(request_path) || ignored_request?(@request)
34
+ end
35
+
36
+ def ignored_format?(format)
37
+ !Helpers.matches_constraint?(Configuration.constraints.format, format, coerce: :to_s)
38
+ end
39
+
40
+ def ignored_path?(path)
41
+ !Helpers.matches_constraint?(Configuration.constraints.source_path, path)
42
+ end
43
+
44
+ def ignored_request?(request)
45
+ !Helpers.matches_constraint?(Configuration.constraints.request, request)
46
+ end
47
+
48
+ def format
49
+ request_path[/\.([^.]+)$/, 1] || 'html'
50
+ end
51
+
52
+ def request_path
53
+ @request.path_info
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,71 @@
1
+ module Director
2
+ module ModelExtensions
3
+ module ActMethod
4
+ def has_aliased_paths(canonical_path: )
5
+ extend ClassMethods
6
+ include Associations
7
+ include Callbacks
8
+ include InstanceMethods
9
+
10
+ self.aliased_paths_options = { canonical_path: canonical_path }
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ end
16
+
17
+ module InstanceMethods
18
+ end
19
+
20
+ module Associations
21
+ BLANK_ALIAS = ->(attributes) { Director::Alias.new(attributes).blank? }
22
+
23
+ def self.included(base)
24
+ base.has_many :incoming_aliases, class_name: 'Director::Alias', as: :target, dependent: :delete_all, inverse_of: :target
25
+ base.has_one :outgoing_alias, class_name: 'Director::Alias', as: :source, dependent: :delete, inverse_of: :source
26
+
27
+ base.class_attribute :aliased_paths_options
28
+ base.accepts_nested_attributes_for :incoming_aliases, reject_if: BLANK_ALIAS, allow_destroy: true
29
+ base.accepts_nested_attributes_for :outgoing_alias, reject_if: BLANK_ALIAS, allow_destroy: true
30
+ end
31
+ end
32
+
33
+ module Callbacks
34
+ def self.included(base)
35
+ base.after_save :update_aliased_paths
36
+ end
37
+
38
+ def generate_canonical_path
39
+ generator = aliased_paths_options[:canonical_path]
40
+ Alias.sanitize_path do
41
+ case generator
42
+ when Symbol
43
+ send(generator)
44
+ when Proc
45
+ generator.call(self)
46
+ when String
47
+ generator
48
+ else # Assume it's an object that responds to canonical_path
49
+ generator.send(:canonical_path)
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def update_aliased_paths
57
+ path = generate_canonical_path
58
+ update_incoming_alias_paths(path)
59
+ update_outgoing_alias_paths(path)
60
+ end
61
+
62
+ def update_incoming_alias_paths(path)
63
+ Alias.where(target: self).update_all(target_path: path)
64
+ end
65
+
66
+ def update_outgoing_alias_paths(path)
67
+ Alias.where(source: self).update_all(source_path: path)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,9 @@
1
+ require 'director/middleware'
2
+
3
+ module Director
4
+ class Railtie < Rails::Railtie
5
+ initializer "director.init" do |app|
6
+ app.config.middleware.use Director::Middleware
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Director
2
+ VERSION = "1.2.2"
3
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: combinaut_director
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Wallace
8
+ - Nicholas Jakobsen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-10-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '4.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '4.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: combustion
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.7.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.7.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec-rails
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.6'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.6'
56
+ - !ruby/object:Gem::Dependency
57
+ name: sqlite3
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ description: Rack Middleware that handles directs incoming requests to their aliased
71
+ path targets
72
+ email:
73
+ - hello@combinaut.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - MIT-LICENSE
79
+ - README.md
80
+ - app/models/director/alias.rb
81
+ - lib/combinaut_director.rb
82
+ - lib/director.rb
83
+ - lib/director/configuration.rb
84
+ - lib/director/engine.rb
85
+ - lib/director/handler.rb
86
+ - lib/director/handler/base.rb
87
+ - lib/director/handler/passthrough.rb
88
+ - lib/director/handler/proxy.rb
89
+ - lib/director/handler/redirect.rb
90
+ - lib/director/helpers.rb
91
+ - lib/director/middleware.rb
92
+ - lib/director/model_extensions.rb
93
+ - lib/director/railtie.rb
94
+ - lib/director/version.rb
95
+ homepage: http://github.com/combinaut/director
96
+ licenses: []
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.7.9
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Rack Middleware that handles directs incoming requests to their aliased path
118
+ targets
119
+ test_files: []