├── .github └── dependabot.yml ├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── local-subdomain.rb └── local-subdomain │ ├── filters │ └── local_subdomain.rb │ ├── rack │ └── handler.rb │ └── version.rb └── local-subdomain.gemspec /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 99 9 | assignees: 10 | - manuelvanrijn 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in dev-subdomain.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Manuel van Rijn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # local-subdomain: Subdomain support for localhost 2 | 3 | ## Description 4 | 5 | This gem helps out when your application depends on subdomain support and you don't want to modify you `/etc/hosts` file all the time for your `development` environment. 6 | 7 | ## Installation 8 | 9 | 1. Add the gem to your `Gemfile` 10 | 11 | ``` 12 | gem 'local-subdomain' 13 | ``` 14 | 15 | 2. Run `bundle install` 16 | 3. Include the `LocalSubdomain` module into your `application_controller.rb` (or the controllers that requires subdomain support) 17 | 18 | ```ruby 19 | class ApplicationController < ActionController::Base 20 | include LocalSubdomain 21 | .... 22 | end 23 | ``` 24 | 25 | **NOTE:** Do not force the gem only to be included in the `development` group. Because of the inclusion of the module `LocalSubdomain`, you'll need to have the gem available in every environment. 26 | The gem itself contains guards to only perform changes when the environment is `development`, so no worries or check it out yourself: 27 | 28 | - [rack/handler.rb](/lib/local-subdomain/rack/handler.rb#L18) 29 | - [filters/local_subdomain.rb](/lib/local-subdomain/filters/local_subdomain.rb#L9) 30 | 31 | ## Configuration (optional) 32 | 33 | By default the gem uses the domain `lvh.me` with the port used, when running the rails server, but it is also possible to provide a custom redirect domain and port using the following `ENV` variables: 34 | 35 | | ENV | Notes |EXAMPLE | 36 | | :------------------------| :--------------------------------|-------------- | 37 | | `SERVER_REDIRECT_PORT` | The port number to redirect to | 5000 | 38 | | `SERVER_REDIRECT_DOMAIN` | The domain to redirect to | my.domain.tld | 39 | 40 | ## What it does 41 | 42 | Basically it does two things: 43 | 44 | 1. Extends the `Rack::Handler` to make sure we bind to `0.0.0.0` instead of `localhost` 45 | 2. Adds the `LocalSubdomain` module which executes a `before_action` to redirect to `http://lvh.me:` (or the configured redirect domain and port) 46 | 47 | ### Rack::Handler 48 | 49 | By default, this gem uses the domain [http://lvh.me](http://lvh.me) to handle our requests for our subdomain(s). Request to the domain `lvh.me` redirects all requests to `127.0.0.1`. 50 | This give's us the ability to browse to [http://subsub.lvh.me:3000](http://subsublvh.me:3000) and be handle `request.subdomain` from our controllers. 51 | 52 | Because we're going to use the external domain [http://lvh.me](http://lvh.me) which redirects to `127.0.0.1` we have to make our server not to bind to `localhost` only. 53 | 54 | ### LocalSubdomain module 55 | 56 | This module includes a `before_action` which will check if the request is served by [http://lvh.me](http://lvh.me). If not it will redirect to the domain. 57 | 58 | So when we browse to [http://localhost:3000](http://localhost:3000) it will redirect you to [http://lvh.me:3000](http://lvh.me:3000) 59 | 60 | ## Supported ruby servers 61 | 62 | I've tested the gem with: 63 | 64 | * [WEBrick](https://rubygems.org/gems/webrick) 65 | * [Puma](http://puma.io/) 66 | * [Thin](http://code.macournoyer.com/thin/) 67 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /lib/local-subdomain.rb: -------------------------------------------------------------------------------- 1 | require 'local-subdomain/version' 2 | require 'local-subdomain/filters/local_subdomain' 3 | require 'local-subdomain/rack/handler' 4 | 5 | module LocalSubdomain 6 | end 7 | -------------------------------------------------------------------------------- /lib/local-subdomain/filters/local_subdomain.rb: -------------------------------------------------------------------------------- 1 | module LocalSubdomain 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_action :redirect_to_lvh_me 6 | end 7 | 8 | def redirect_to_lvh_me 9 | return unless Rails.env.development? 10 | 11 | redirect_domain = ENV['SERVER_REDIRECT_DOMAIN'] || 'lvh.me' 12 | 13 | served_by_lvh_me = !request.env['SERVER_NAME'][/#{redirect_domain}$/].nil? 14 | return if served_by_lvh_me 15 | 16 | http = request.env['rack.url_scheme'] 17 | port = ENV['SERVER_REDIRECT_PORT'] || request.env['SERVER_PORT'] 18 | path = request.env['ORIGINAL_FULLPATH'] 19 | redirect_to "#{http}://#{redirect_domain}#{port == '80' ? '' : ':' + port}#{path}" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/local-subdomain/rack/handler.rb: -------------------------------------------------------------------------------- 1 | # Override the Rack::Handler to bind to 0.0.0.0 which is required to support 2 | # http://lvh.me redirects 3 | 4 | module Rack 5 | module Handler 6 | class << self 7 | alias_method :orig_default, :default 8 | end 9 | 10 | def self.default(options = {}) 11 | orig_default.instance_eval do 12 | class << self 13 | alias_method :orig_run, :run 14 | end 15 | 16 | def self.run(app, options = {}) 17 | env = (options[:environment] || Rails.env) 18 | if options[:Host] == 'localhost' && env == 'development' 19 | message(options[:Port]) 20 | options.merge!(Host: '0.0.0.0') 21 | end 22 | orig_run(app, **options) 23 | end 24 | 25 | def self.message(port) 26 | puts '****************************************************************************************' 27 | puts "* Override binding 'localhost' to '0.0.0.0' for http://lvh.me:#{port}/ support" 28 | puts '****************************************************************************************' 29 | end 30 | end 31 | orig_default 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/local-subdomain/version.rb: -------------------------------------------------------------------------------- 1 | module LocalSubdomain 2 | VERSION = '1.0.3'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /local-subdomain.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'local-subdomain/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'local-subdomain' 8 | spec.version = LocalSubdomain::VERSION 9 | spec.authors = ['Manuel van Rijn'] 10 | spec.email = ['manuel@manuelvanrijn.nl'] 11 | 12 | spec.summary = 'subdomain support in your development environment' 13 | spec.description = "This gem helps out when your application depends on subdomain support and you don't want to modify you /etc/hosts file all the time for your development environment." 14 | spec.homepage = 'https://github.com/manuelvanrijn/local-subdomain' 15 | spec.license = 'MIT' 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 18 | spec.bindir = 'exe' 19 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 20 | spec.require_paths = ['lib'] 21 | 22 | spec.add_development_dependency 'bundler', '~> 2.0' 23 | spec.add_development_dependency 'rake', '~> 13.0' 24 | end 25 | --------------------------------------------------------------------------------