turnout 2.3.1 → 2.4.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: 2e23a0f9e95613f840f72654c2250f0a2f32bcb4
4
- data.tar.gz: 5a9906f6096a154d0e46ce9c1a2bc466a75ad403
3
+ metadata.gz: c4ae8144b28714433ab814cb9604ed7e4eb82d5c
4
+ data.tar.gz: c2b3041f66b67b9e446067657818a15bbd396d3d
5
5
  SHA512:
6
- metadata.gz: c3be20dedbee8b73ee09e2822697869b0ba22b3f1c2ee5c13f8ab66e6b39cf72fa3525b1546e805ca9967f591a3ec8ec633934fb767c74b694680ee78c78a1b3
7
- data.tar.gz: 91df6435f2030872f014e45b8ea54653e213d35d70a58f577147faa9aebf36a95fc4c58234dde15144d0f5e3072bac822d15d7423afa50490eadadc22066fcde
6
+ metadata.gz: b5c5a40abcc33d99d789d3c4dfe115da5a571c9ac07bf8ae627d18f030934044e83d1931281b48bc60b5dbfe9afd50ac9642549fdd69506090ade13fbbc25171
7
+ data.tar.gz: ecda16629ec28698fb40318aa156e9a4511c96f0482c8d60ff15f6f4d9c81c1dd8c57ddc3233aabe96b1fe5f116096bd05a3b31d863a5802b3c62f65fb70b381
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Turnout [![Build Status](https://travis-ci.org/biola/turnout.png?branch=master)](https://travis-ci.org/biola/turnout) [![Code Climate](https://codeclimate.com/github/biola/turnout.png)](https://codeclimate.com/github/biola/turnout)
1
+ Turnout [![Build Status](https://travis-ci.org/biola/turnout.svg?branch=master)](https://travis-ci.org/biola/turnout) [![Code Climate](https://codeclimate.com/github/biola/turnout.svg)](https://codeclimate.com/github/biola/turnout) [![Gem Version](https://badge.fury.io/rb/turnout.svg)](https://badge.fury.io/rb/turnout)
2
2
  =======
3
3
  Turnout is [Rack](http://rack.rubyforge.org/) middleware with a [Ruby on Rails](http://rubyonrails.org) engine that allows you to easily put your app in maintenance mode.
4
4
 
@@ -151,14 +151,23 @@ The source code of any custom maintenance files you created in the `/public` dir
151
151
  Tips
152
152
  ====
153
153
 
154
+ Denied Paths
155
+ --------------
154
156
  There is no `denied_paths` feature because turnout denies everything by default.
155
157
  However you can achieve the same sort of functionality by using
156
158
  [negative lookaheads](http://www.regular-expressions.info/lookaround.html) with the `allowed_paths` setting, like so:
157
159
 
158
160
  rake maintenance:start allowed_paths="^(?!/your/under/maintenance/path)"
159
161
 
162
+ Multi-App Maintenance
163
+ ------------------------
160
164
  A central `named_maintenance_file_path` can be configured in all your apps such as `/tmp/turnout.yml` so that all apps on a server can be put into mainteance mode at once. You could even configure service based paths such as `/tmp/mongodb_maintenance.yml` so that all apps using MongoDB could be put into maintenance mode.
161
165
 
166
+ Detecting Maintenance Mode
167
+ -------------------------------
168
+
169
+ If you'd like to detect if maintenance mode is on in your app (for those users or pages that aren't blocked) just call `!Turnout::MaintenanceFile.find.nil?`.
170
+
162
171
  Behind the Scenes
163
172
  =================
164
173
  On every request the Rack app will check to see if `tmp/maintenance.yml` exists. If the file exists the maintenance page will be shown (unless allowed IPs are given and the requester is in the allowed range).
@@ -1,8 +1,9 @@
1
+ require_relative './ordered_options'
1
2
  module Turnout
2
3
  class Configuration
3
4
  SETTINGS = [:app_root, :named_maintenance_file_paths,
4
5
  :maintenance_pages_path, :default_maintenance_page, :default_reason,
5
- :default_allowed_paths, :default_response_code, :default_retry_after]
6
+ :default_allowed_paths, :default_response_code, :default_retry_after, :i18n]
6
7
 
7
8
  SETTINGS.each do |setting|
8
9
  attr_accessor setting
@@ -17,6 +18,12 @@ module Turnout
17
18
  @default_allowed_paths = []
18
19
  @default_response_code = 503
19
20
  @default_retry_after = 7200 # 2 hours by default
21
+ @i18n = Turnout::OrderedOptions.new
22
+ @i18n.railties_load_path = []
23
+ @i18n.load_path = []
24
+ @i18n.fallbacks = Turnout::OrderedOptions.new
25
+ @i18n.enabled = false
26
+ @i18n.use_language_header = false
20
27
  end
21
28
 
22
29
  def app_root
@@ -0,0 +1,109 @@
1
+ module Turnout
2
+ class AcceptLanguageParser
3
+ attr_accessor :header
4
+
5
+ def initialize(header)
6
+ @header = header
7
+ end
8
+
9
+ # Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE.
10
+ # Browsers send this HTTP header, so don't think this is holy.
11
+ #
12
+ # Example:
13
+ #
14
+ # request.user_preferred_languages
15
+ # # => [ 'nl-NL', 'nl-BE', 'nl', 'en-US', 'en' ]
16
+ #
17
+ def user_preferred_languages
18
+ return [] if header.to_s.strip.empty?
19
+ @user_preferred_languages ||= begin
20
+ header.to_s.gsub(/\s+/, '').split(',').map do |language|
21
+ locale, quality = language.split(';q=')
22
+ raise ArgumentError, 'Not correctly formatted' unless locale =~ /^[a-z\-0-9]+|\*$/i
23
+
24
+ locale = locale.downcase.gsub(/-[a-z0-9]+$/i, &:upcase) # Uppercase territory
25
+ locale = nil if locale == '*' # Ignore wildcards
26
+
27
+ quality = quality ? quality.to_f : 1.0
28
+
29
+ [locale, quality]
30
+ end.sort do |(_, left), (_, right)|
31
+ right <=> left
32
+ end.map(&:first).compact
33
+ rescue ArgumentError # Just rescue anything if the browser messed up badly.
34
+ []
35
+ end
36
+ end
37
+
38
+ # Sets the user languages preference, overriding the browser
39
+ #
40
+ def user_preferred_languages=(languages)
41
+ @user_preferred_languages = languages
42
+ end
43
+
44
+ # Finds the locale specifically requested by the browser.
45
+ #
46
+ # Example:
47
+ #
48
+ # request.preferred_language_from I18n.available_locales
49
+ # # => 'nl'
50
+ #
51
+ def preferred_language_from(array)
52
+ (user_preferred_languages & array.map(&:to_s)).first
53
+ end
54
+
55
+ # Returns the first of the user_preferred_languages that is compatible
56
+ # with the available locales. Ignores region.
57
+ #
58
+ # Example:
59
+ #
60
+ # request.compatible_language_from I18n.available_locales
61
+ #
62
+ def compatible_language_from(available_languages)
63
+ user_preferred_languages.map do |preferred| #en-US
64
+ preferred = preferred.downcase
65
+ preferred_language = preferred.split('-', 2).first
66
+
67
+ available_languages.find do |available| # en
68
+ available = available.to_s.downcase
69
+ preferred == available || preferred_language == available.split('-', 2).first
70
+ end
71
+ end.compact.first
72
+ end
73
+
74
+ # Returns a supplied list of available locals without any extra application info
75
+ # that may be attached to the locale for storage in the application.
76
+ #
77
+ # Example:
78
+ # [ja_JP-x1, en-US-x4, en_UK-x5, fr-FR-x3] => [ja-JP, en-US, en-UK, fr-FR]
79
+ #
80
+ def sanitize_available_locales(available_languages)
81
+ available_languages.map do |available|
82
+ available.to_s.split(/[_-]/).reject { |part| part.start_with?("x") }.join("-")
83
+ end
84
+ end
85
+
86
+ # Returns the first of the user preferred languages that is
87
+ # also found in available languages. Finds best fit by matching on
88
+ # primary language first and secondarily on region. If no matching region is
89
+ # found, return the first language in the group matching that primary language.
90
+ #
91
+ # Example:
92
+ #
93
+ # request.language_region_compatible(available_languages)
94
+ #
95
+ def language_region_compatible_from(available_languages)
96
+ available_languages = sanitize_available_locales(available_languages)
97
+ user_preferred_languages.map do |preferred| #en-US
98
+ preferred = preferred.downcase
99
+ preferred_language = preferred.split('-', 2).first
100
+
101
+ lang_group = available_languages.select do |available| # en
102
+ preferred_language == available.downcase.split('-', 2).first
103
+ end
104
+
105
+ lang_group.find { |lang| lang.downcase == preferred } || lang_group.first #en-US, en-UK
106
+ end.compact.first
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,141 @@
1
+ require 'i18n'
2
+ require 'i18n/backend/fallbacks'
3
+ require_relative './accept_language_parser'
4
+ require_relative '../ordered_options'
5
+
6
+ module Turnout
7
+ class Internationalization
8
+ class << self
9
+ attr_reader :env
10
+ attr_writer :env
11
+
12
+ def initialize_i18n(env)
13
+ @env = env
14
+ setup_i18n_config
15
+ end
16
+
17
+ def i18n_config
18
+ @i18n_config = Turnout.config.i18n
19
+ @i18n_config = @i18n_config.is_a?(Turnout::OrderedOptions) ? @i18n_config : Turnout::InheritableOptions.new(@i18n_config)
20
+ end
21
+
22
+ def turnout_page
23
+ @turnout_page ||= Turnout.config.default_maintenance_page
24
+ end
25
+
26
+ def http_accept_language
27
+ language = (env.nil? || env.empty?) ? nil : env["HTTP_ACCEPT_LANGUAGE"]
28
+ @http_accept_language ||= Turnout::AcceptLanguageParser.new(language)
29
+ end
30
+
31
+ def setup_additional_helpers
32
+ i18n_additional_helpers = i18n_config.delete(:additional_helpers)
33
+ i18n_additional_helpers = i18n_additional_helpers.is_a?(Array) ? i18n_additional_helpers : []
34
+
35
+ i18n_additional_helpers.each do |helper|
36
+ turnout_page.send(:include, helper) if helper.is_a?(Module)
37
+ end
38
+ end
39
+
40
+ def expanded(path)
41
+ result = []
42
+ if File.directory?(path)
43
+ result.concat(Dir.glob(File.join(path, '**', '**')).map { |file| file }.sort)
44
+ else
45
+ result << path
46
+ end
47
+ result.uniq!
48
+ result
49
+ end
50
+
51
+ # Returns all expanded paths but only if they exist in the filesystem.
52
+ def existent(path)
53
+ expanded(path).select { |f| File.exist?(f) }
54
+ end
55
+
56
+ # Setup i18n configuration.
57
+ def setup_i18n_config
58
+ return unless i18n_config.enabled
59
+ setup_additional_helpers
60
+ fallbacks = i18n_config.delete(:fallbacks)
61
+
62
+
63
+ # Avoid issues with setting the default_locale by disabling available locales
64
+ # check while configuring.
65
+ enforce_available_locales = i18n_config.delete(:enforce_available_locales)
66
+ enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
67
+ I18n.enforce_available_locales = false
68
+
69
+ i18n_config.except(:enabled, :use_language_header).each do |setting, value|
70
+ case setting
71
+ when :railties_load_path
72
+ I18n.load_path.unshift(*value.map { |file| existent(file) }.flatten)
73
+ when :load_path
74
+ I18n.load_path += value
75
+ else
76
+ I18n.send("#{setting}=", value)
77
+ end
78
+ end
79
+
80
+
81
+ init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
82
+ I18n.backend.load_translations
83
+
84
+ # Restore available locales check so it will take place from now on.
85
+ I18n.enforce_available_locales = enforce_available_locales
86
+
87
+ begin
88
+ if i18n_config.use_language_header
89
+ I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
90
+ else
91
+ I18n.locale = I18n.default_locale
92
+ end
93
+ rescue
94
+ #nothing
95
+ end
96
+
97
+ end
98
+
99
+ def array_wrap(object)
100
+ if object.nil?
101
+ []
102
+ elsif object.respond_to?(:to_ary)
103
+ object.to_ary || [object]
104
+ else
105
+ [object]
106
+ end
107
+ end
108
+
109
+ def include_fallbacks_module
110
+ I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
111
+ end
112
+
113
+ def init_fallbacks(fallbacks)
114
+ include_fallbacks_module
115
+
116
+ args = case fallbacks
117
+ when Turnout::OrderedOptions
118
+ [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
119
+ when Hash, Array
120
+ array_wrap(fallbacks)
121
+ else # TrueClass
122
+ []
123
+ end
124
+
125
+ I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
126
+ end
127
+
128
+ def validate_fallbacks(fallbacks)
129
+ case fallbacks
130
+ when Turnout::OrderedOptions
131
+ !fallbacks.empty?
132
+ when TrueClass, Array, Hash
133
+ true
134
+ else
135
+ raise "Unexpected fallback type #{fallbacks.inspect}"
136
+ end
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -2,17 +2,21 @@ require 'erb'
2
2
  require 'tilt'
3
3
  require 'tilt/erb'
4
4
  require_relative './html'
5
+ require_relative '../i18n/internationalization'
6
+
5
7
  module Turnout
6
8
  module MaintenancePage
7
9
  class Erb < Turnout::MaintenancePage::HTML
8
10
 
9
11
  def content
12
+ Turnout::Internationalization.initialize_i18n(@options[:env])
10
13
  Tilt.new(File.expand_path(path)).render(self, {reason: reason}.merge(@options))
11
14
  end
12
-
15
+
13
16
  def self.extension
14
17
  'html.erb'
15
18
  end
19
+
16
20
  end
17
21
  end
18
22
  end
@@ -0,0 +1,98 @@
1
+ module Turnout
2
+ class OrderedOptions < Hash
3
+ alias_method :_get, :[] # preserve the original #[] method
4
+ protected :_get # make it protected
5
+
6
+ def initialize(constructor = {}, &block)
7
+ if constructor.respond_to?(:to_hash)
8
+ super()
9
+ update(constructor, &block)
10
+ hash = constructor.to_hash
11
+
12
+ self.default = hash.default if hash.default
13
+ self.default_proc = hash.default_proc if hash.default_proc
14
+ else
15
+ super()
16
+ end
17
+ end
18
+
19
+ def update(other_hash)
20
+ if other_hash.is_a? Hash
21
+ super(other_hash)
22
+ else
23
+ other_hash.to_hash.each_pair do |key, value|
24
+ if block_given?
25
+ value = yield(key, value)
26
+ end
27
+ self[key] = value
28
+ end
29
+ self
30
+ end
31
+ end
32
+
33
+ def []=(key, value)
34
+ super(key.to_sym, value)
35
+ end
36
+
37
+ def [](key)
38
+ super(key.to_sym)
39
+ end
40
+
41
+ def method_missing(name, *args)
42
+ name_string = name.to_s
43
+ if name_string.chomp!('=')
44
+ self[name_string] = args.first
45
+ else
46
+ bangs = name_string.chomp!('!')
47
+
48
+ if bangs
49
+ value = fetch(name_string.to_sym)
50
+ raise(RuntimeError.new("#{name_string} is blank.")) if value.nil? || value.empty?
51
+ value
52
+ else
53
+ self[name_string]
54
+ end
55
+ end
56
+ end
57
+
58
+ def except(*keys)
59
+ dup.except!(*keys)
60
+ end
61
+
62
+ def except!(*keys)
63
+ keys.each { |key| delete(key) }
64
+ self
65
+ end
66
+
67
+
68
+ def respond_to_missing?(name, include_private)
69
+ true
70
+ end
71
+ end
72
+
73
+
74
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
75
+ # hash inherited from another hash.
76
+ #
77
+ # Use this if you already have some hash and you want to create a new one based on it.
78
+ #
79
+ # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
80
+ # h.girl # => 'Mary'
81
+ # h.boy # => 'John'
82
+ class InheritableOptions < Turnout::OrderedOptions
83
+ def initialize(parent = nil)
84
+ if parent.kind_of?(Turnout::OrderedOptions)
85
+ # use the faster _get when dealing with OrderedOptions
86
+ super(parent) {|key,value| parent._get(key) }
87
+ elsif parent
88
+ super(parent) { |key, value| parent[key] }
89
+ else
90
+ super(parent)
91
+ end
92
+ end
93
+
94
+ def inheritable_copy
95
+ self.class.new(self)
96
+ end
97
+ end
98
+ end
@@ -1,3 +1,3 @@
1
1
  module Turnout
2
- VERSION = '2.3.1'
2
+ VERSION = '2.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turnout
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Crownoble
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-21 00:00:00.000000000 Z
11
+ date: 2016-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tilt
@@ -34,16 +34,22 @@ dependencies:
34
34
  name: rack
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.3'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '3'
40
43
  type: :runtime
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
44
- - - "~>"
47
+ - - ">="
45
48
  - !ruby/object:Gem::Version
46
49
  version: '1.3'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '3'
47
53
  - !ruby/object:Gem::Dependency
48
54
  name: rack-accept
49
55
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +64,20 @@ dependencies:
58
64
  - - "~>"
59
65
  - !ruby/object:Gem::Version
60
66
  version: '0.4'
67
+ - !ruby/object:Gem::Dependency
68
+ name: i18n
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '0.7'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.7'
61
81
  - !ruby/object:Gem::Dependency
62
82
  name: rack-test
63
83
  requirement: !ruby/object:Gem::Requirement
@@ -100,6 +120,46 @@ dependencies:
100
120
  - - "~>"
101
121
  - !ruby/object:Gem::Version
102
122
  version: '1.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: simplecov
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '0.10'
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0.10'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0.10'
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0.10'
143
+ - !ruby/object:Gem::Dependency
144
+ name: simplecov-summary
145
+ requirement: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: 0.0.4
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 0.0.4
153
+ type: :development
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.0.4
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 0.0.4
103
163
  description: Turnout makes it easy to put your Rails application into maintenance
104
164
  mode
105
165
  email: adam@codenoble.com
@@ -114,12 +174,15 @@ files:
114
174
  - lib/turnout.rb
115
175
  - lib/turnout/configuration.rb
116
176
  - lib/turnout/engine.rb
177
+ - lib/turnout/i18n/accept_language_parser.rb
178
+ - lib/turnout/i18n/internationalization.rb
117
179
  - lib/turnout/maintenance_file.rb
118
180
  - lib/turnout/maintenance_page.rb
119
181
  - lib/turnout/maintenance_page/base.rb
120
182
  - lib/turnout/maintenance_page/erb.rb
121
183
  - lib/turnout/maintenance_page/html.rb
122
184
  - lib/turnout/maintenance_page/json.rb
185
+ - lib/turnout/ordered_options.rb
123
186
  - lib/turnout/rake_tasks.rb
124
187
  - lib/turnout/request.rb
125
188
  - lib/turnout/version.rb
@@ -151,3 +214,4 @@ signing_key:
151
214
  specification_version: 4
152
215
  summary: A Rack based maintenance mode plugin for Rails
153
216
  test_files: []
217
+ has_rdoc: