turnout 2.3.1 → 2.4.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: 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: