utopia 1.1.4 → 1.2.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 +4 -4
- data/lib/utopia/content/link.rb +5 -4
- data/lib/utopia/content/links.rb +20 -19
- data/lib/utopia/content/transaction.rb +4 -1
- data/lib/utopia/controller.rb +1 -2
- data/lib/utopia/controller/base.rb +42 -52
- data/lib/utopia/exception_handler.rb +3 -3
- data/lib/utopia/extensions/rack.rb +14 -6
- data/lib/utopia/http.rb +50 -1
- data/lib/utopia/locale.rb +43 -0
- data/lib/utopia/localization.rb +99 -37
- data/lib/utopia/path.rb +5 -5
- data/lib/utopia/redirector.rb +3 -3
- data/lib/utopia/session.rb +1 -1
- data/lib/utopia/static.rb +1 -1
- data/lib/utopia/version.rb +1 -1
- data/setup/site/pages/_page.xnode +1 -1
- data/spec/utopia/content/links_spec.rb +3 -3
- data/spec/utopia/content/node_spec.rb +2 -2
- data/spec/utopia/locale_spec.rb +48 -0
- data/spec/utopia/localization_spec.rb +39 -7
- data/spec/utopia/localization_spec.ru +8 -5
- data/spec/utopia/localization_spec/controller.rb +18 -0
- data/spec/utopia/{pages → localization_spec}/localized.de.txt +0 -0
- data/spec/utopia/{pages → localization_spec}/localized.en.txt +0 -0
- data/spec/utopia/localization_spec/localized.ja.txt +1 -0
- data/spec/utopia/localization_spec/test.txt +1 -0
- data/spec/utopia/path_spec.rb +4 -4
- data/spec/utopia/session_spec.ru +3 -3
- data/utopia.gemspec +17 -15
- metadata +19 -12
- data/spec/utopia/pages/localized.jp.txt +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31ace1eb6c636da91d00f8eee84b9dcaf839f817
|
4
|
+
data.tar.gz: 27d8124f0df0d66189d9230cce119db1a39901cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ccc85d249b116d20fcba8403244b8d466aecfe527cf0a13aa79b19ee78e162c6acd9e458a40bf5064dae17b2108c0b4bd851ca2e4fc06a7e84990db148abeac
|
7
|
+
data.tar.gz: 5e95784314d4c28253714caac80b9efec859f988bf908784da15d65c2d460623ee2607505a93b931b392558b7bbfe41c328814cd1631a6979a024016931dd062
|
data/lib/utopia/content/link.rb
CHANGED
@@ -23,6 +23,7 @@ require 'trenni/builder'
|
|
23
23
|
|
24
24
|
require_relative '../content'
|
25
25
|
require_relative '../path'
|
26
|
+
require_relative '../locale'
|
26
27
|
|
27
28
|
module Utopia
|
28
29
|
class Content
|
@@ -35,16 +36,16 @@ module Utopia
|
|
35
36
|
|
36
37
|
case @kind
|
37
38
|
when :file
|
38
|
-
@name, @
|
39
|
+
@name, @locale = path.last.split('.', 2)
|
39
40
|
@path = path
|
40
41
|
when :directory
|
41
42
|
# raise ArgumentError unless path.last.start_with? INDEX
|
42
43
|
|
43
44
|
@name = path.dirname.last
|
44
|
-
@
|
45
|
+
@locale = path.last.split('.', 2)[1]
|
45
46
|
@path = path
|
46
47
|
when :virtual
|
47
|
-
@name, @
|
48
|
+
@name, @locale = path.to_s.split('.', 2)
|
48
49
|
@path = @info[:path] ? Path.create(@info[:path]) : nil
|
49
50
|
else
|
50
51
|
raise ArgumentError.new("Unknown link kind #{@kind} with path #{path}")
|
@@ -67,7 +68,7 @@ module Utopia
|
|
67
68
|
attr :name
|
68
69
|
attr :path
|
69
70
|
attr :info
|
70
|
-
attr :
|
71
|
+
attr :locale
|
71
72
|
|
72
73
|
def href?
|
73
74
|
!!href
|
data/lib/utopia/content/links.rb
CHANGED
@@ -26,10 +26,10 @@ module Utopia
|
|
26
26
|
|
27
27
|
# Links are essentially a static list of information relating to the structure of the content. They are formed from the `links.yaml` file and the actual files on disk.
|
28
28
|
class Links
|
29
|
-
def self.for(root, path,
|
29
|
+
def self.for(root, path, locale = nil)
|
30
30
|
links = self.new(root, path.dirname)
|
31
31
|
|
32
|
-
links.lookup(path.last,
|
32
|
+
links.lookup(path.last, locale)
|
33
33
|
end
|
34
34
|
|
35
35
|
DEFAULT_INDEX_OPTIONS = {
|
@@ -56,18 +56,18 @@ module Utopia
|
|
56
56
|
ordered.select!{|link| link.name[options[:name]]}
|
57
57
|
end
|
58
58
|
|
59
|
-
if
|
60
|
-
|
59
|
+
if locale = options[:locale]
|
60
|
+
locales = {}
|
61
61
|
|
62
62
|
ordered.each do |link|
|
63
|
-
if link.
|
64
|
-
|
65
|
-
elsif link.
|
66
|
-
|
63
|
+
if link.locale == locale
|
64
|
+
locales[link.name] = link
|
65
|
+
elsif link.locale == nil
|
66
|
+
locales[link.name] ||= link
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
ordered =
|
70
|
+
ordered = locales.values
|
71
71
|
end
|
72
72
|
|
73
73
|
# Sort:
|
@@ -115,18 +115,18 @@ module Utopia
|
|
115
115
|
attr :ordered
|
116
116
|
attr :named
|
117
117
|
|
118
|
-
def each(
|
119
|
-
return to_enum(:each,
|
118
|
+
def each(locale)
|
119
|
+
return to_enum(:each, locale) unless block_given?
|
120
120
|
|
121
121
|
ordered.each do |links|
|
122
|
-
yield links.find{|link| link.
|
122
|
+
yield links.find{|link| link.locale == locale}
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
-
def lookup(name,
|
127
|
-
# This allows generic links to serve any
|
126
|
+
def lookup(name, locale = nil)
|
127
|
+
# This allows generic links to serve any locale requested.
|
128
128
|
if links = @named[name]
|
129
|
-
links.find{|link| link.
|
129
|
+
links.find{|link| link.locale == locale} || links.find{|link| link.locale == nil}
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
@@ -145,7 +145,7 @@ module Utopia
|
|
145
145
|
links_path = File.join(path, LINKS_YAML)
|
146
146
|
|
147
147
|
hash = if File.exist?(links_path)
|
148
|
-
YAML::
|
148
|
+
YAML::load_file(links_path) || {}
|
149
149
|
else
|
150
150
|
{}
|
151
151
|
end
|
@@ -171,9 +171,10 @@ module Utopia
|
|
171
171
|
directory_link = Link.new(:directory, @top + [name, index_name], index_metadata)
|
172
172
|
|
173
173
|
# Merge metadata from foo.en into foo/index.en
|
174
|
-
if directory_link.
|
175
|
-
|
176
|
-
|
174
|
+
if directory_link.locale
|
175
|
+
localized_key = "#{directory_link.name}.#{directory_link.locale}"
|
176
|
+
if localized_metadata = metadata.delete(localized_key)
|
177
|
+
directory_link.info.update(localized_metadata)
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
@@ -18,7 +18,6 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
require_relative 'processor'
|
22
21
|
require_relative 'links'
|
23
22
|
|
24
23
|
module Utopia
|
@@ -152,6 +151,10 @@ module Utopia
|
|
152
151
|
return current.attributes
|
153
152
|
end
|
154
153
|
|
154
|
+
def localization
|
155
|
+
@localization ||= Utopia::Localization[request]
|
156
|
+
end
|
157
|
+
|
155
158
|
def current
|
156
159
|
@begin_tags[-1]
|
157
160
|
end
|
data/lib/utopia/controller.rb
CHANGED
@@ -45,7 +45,6 @@ module Utopia
|
|
45
45
|
|
46
46
|
class Controller
|
47
47
|
CONTROLLER_RB = 'controller.rb'.freeze
|
48
|
-
PATH_INFO_KEY = 'PATH_INFO'.freeze
|
49
48
|
|
50
49
|
def initialize(app, **options)
|
51
50
|
@app = app
|
@@ -119,7 +118,7 @@ module Utopia
|
|
119
118
|
end
|
120
119
|
|
121
120
|
# The controllers may have rewriten the path so we update the path info:
|
122
|
-
request.env[
|
121
|
+
request.env[Rack::PATH_INFO] = controller_path.to_s
|
123
122
|
|
124
123
|
# No controller gave a useful result:
|
125
124
|
return nil
|
@@ -22,8 +22,6 @@ require_relative '../http'
|
|
22
22
|
|
23
23
|
module Utopia
|
24
24
|
class Controller
|
25
|
-
EMPTY_BODY = [].freeze
|
26
|
-
|
27
25
|
class Base
|
28
26
|
def self.base_path
|
29
27
|
self.const_get(:BASE_PATH)
|
@@ -75,13 +73,9 @@ module Utopia
|
|
75
73
|
end
|
76
74
|
|
77
75
|
def catch_response
|
78
|
-
|
76
|
+
catch(:response) do
|
79
77
|
yield and nil
|
80
78
|
end
|
81
|
-
|
82
|
-
if response
|
83
|
-
return self.respond_with(*response)
|
84
|
-
end
|
85
79
|
end
|
86
80
|
|
87
81
|
# Given a request, call associated actions if at least one exists.
|
@@ -106,69 +100,65 @@ module Utopia
|
|
106
100
|
end
|
107
101
|
end
|
108
102
|
|
103
|
+
# Call into the next app as defined by rack.
|
109
104
|
def call(env)
|
110
105
|
self.class.controller.app.call(env)
|
111
106
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
107
|
+
|
108
|
+
# This will cause the middleware to generate a response.
|
109
|
+
def respond!(response)
|
110
|
+
throw :response, response
|
115
111
|
end
|
116
112
|
|
113
|
+
# This will cause the controller middleware to pass on the request.
|
117
114
|
def ignore!
|
118
115
|
throw :response, nil
|
119
116
|
end
|
120
117
|
|
118
|
+
# Request relative redirect. Respond with a redirect to the given target.
|
121
119
|
def redirect! (target, status = 302)
|
122
|
-
|
120
|
+
status = HTTP::Status.new(status, 300...400)
|
121
|
+
location = target.to_s
|
122
|
+
|
123
|
+
respond! [status.to_i, {HTTP::LOCATION => location}, [status.to_s]]
|
123
124
|
end
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
|
126
|
+
# Controller relative redirect.
|
127
|
+
def goto!(target, status = 302)
|
128
|
+
redirect! self.class.uri_path + target
|
127
129
|
end
|
128
|
-
|
129
|
-
|
130
|
-
|
130
|
+
|
131
|
+
# Respond with an error which indiciates some kind of failure.
|
132
|
+
def fail!(error = 400, message = nil)
|
133
|
+
status = HTTP::Status.new(error, 400...600)
|
134
|
+
|
135
|
+
message ||= status.to_s
|
136
|
+
respond! [status.to_i, {}, [message]]
|
131
137
|
end
|
132
138
|
|
133
|
-
def
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
if Numeric === args[0] || Symbol === args[0]
|
140
|
-
status = args[0]
|
141
|
-
options = args[1] || {}
|
142
|
-
else
|
143
|
-
options = args[0]
|
144
|
-
status = options[:status] || status
|
145
|
-
end
|
146
|
-
|
147
|
-
status = HTTP::STATUS_CODES[status] || status
|
148
|
-
headers = options[:headers] || {}
|
149
|
-
|
150
|
-
if type = options[:type]
|
151
|
-
headers[HTTP::CONTENT_TYPE] ||= type
|
152
|
-
end
|
153
|
-
|
154
|
-
if redirect = options[:redirect]
|
155
|
-
headers[HTTP::LOCATION] = redirect.to_s
|
156
|
-
status = 302 if status < 300 || status >= 400
|
139
|
+
def succeed!(status: 200, headers: {}, **options)
|
140
|
+
status = HTTP::Status.new(status, 200...300)
|
141
|
+
|
142
|
+
if options[:type]
|
143
|
+
headers[Rack::CONTENT_TYPE] = options[:type].to_s
|
157
144
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
body
|
145
|
+
|
146
|
+
body = body_for(status, headers, options)
|
147
|
+
respond! [status.to_i, headers, body || []]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Generate the body for the given status, headers and options.
|
151
|
+
def body_for(status, headers, options)
|
152
|
+
if body = options[:body]
|
153
|
+
return body
|
154
|
+
elsif content = options[:content]
|
155
|
+
return [content]
|
167
156
|
end
|
168
|
-
|
169
|
-
return [status, headers, body]
|
170
157
|
end
|
171
158
|
|
159
|
+
# Legacy method name:
|
160
|
+
alias success! succeed!
|
161
|
+
|
172
162
|
# Return nil if this controller didn't do anything. Request will keep on processing. Return a valid rack response if the controller can do so.
|
173
163
|
def process!(request, path)
|
174
164
|
passthrough(request, path)
|
@@ -35,7 +35,7 @@ module Utopia
|
|
35
35
|
|
36
36
|
body.puts "<!DOCTYPE html><html><head><title>Fatal Error</title></head><body>"
|
37
37
|
body.puts "<h1>Fatal Error</h1>"
|
38
|
-
body.puts "<p>While requesting resource #{Trenni::Strings::to_html env[
|
38
|
+
body.puts "<p>While requesting resource #{Trenni::Strings::to_html env[Rack::PATH_INFO]}, a fatal error occurred.</p>"
|
39
39
|
body.puts "<blockquote><strong>#{Trenni::Strings::to_html exception.class.name}</strong>: #{Trenni::Strings::to_html exception.to_s}</blockquote>"
|
40
40
|
body.puts "<p>There is nothing more we can do to fix the problem at this point.</p>"
|
41
41
|
body.puts "<p>We apologize for the inconvenience.</p>"
|
@@ -46,7 +46,7 @@ module Utopia
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def redirect(env, exception)
|
49
|
-
response = @app.call(env.merge(
|
49
|
+
response = @app.call(env.merge(Rack::PATH_INFO => @location, Rack::REQUEST_METHOD => Rack::GET))
|
50
50
|
|
51
51
|
return [500, response[1], response[2]]
|
52
52
|
end
|
@@ -65,7 +65,7 @@ module Utopia
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# If the error occurred while accessing the error handler, we finish with a fatal error:
|
68
|
-
if env[
|
68
|
+
if env[Rack::PATH_INFO] == @location
|
69
69
|
return fatal_error(env, exception)
|
70
70
|
else
|
71
71
|
# If redirection fails, we also finish with a fatal error:
|
@@ -21,9 +21,9 @@
|
|
21
21
|
require 'rack'
|
22
22
|
|
23
23
|
module Rack
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
# Compatibility with older versions of rack:
|
25
|
+
EXPIRES = 'Expires'.freeze unless defined? EXPIRES
|
26
|
+
HTTP_HOST = 'HTTP_HOST'.freeze unless defined? HTTP_HOST
|
27
27
|
|
28
28
|
class Response
|
29
29
|
# Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
|
@@ -33,16 +33,24 @@ module Rack
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# Specify that the content should be cached.
|
36
|
-
def cache!(duration = 3600)
|
36
|
+
def cache!(duration = 3600, access = "public")
|
37
37
|
unless headers[CACHE_CONTROL] =~ /no-cache/
|
38
|
-
headers[CACHE_CONTROL] = "
|
38
|
+
headers[CACHE_CONTROL] = "#{access}, max-age=#{duration}"
|
39
39
|
headers[EXPIRES] = (Time.now + duration).httpdate
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
# Specify the content type of the response data.
|
44
44
|
def content_type!(value)
|
45
|
-
|
45
|
+
self.content_type = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def content_type= value
|
49
|
+
headers[CONTENT_TYPE] = value
|
50
|
+
end
|
51
|
+
|
52
|
+
def content_type
|
53
|
+
headers[CONTENT_TYPE]
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
data/lib/utopia/http.rb
CHANGED
@@ -35,6 +35,7 @@ module Utopia
|
|
35
35
|
:unauthorized => 401,
|
36
36
|
:forbidden => 403,
|
37
37
|
:not_found => 404,
|
38
|
+
:not_allowed => 405,
|
38
39
|
:unsupported_method => 405,
|
39
40
|
:gone => 410,
|
40
41
|
:teapot => 418,
|
@@ -42,13 +43,34 @@ module Utopia
|
|
42
43
|
:unimplemented => 501,
|
43
44
|
:unavailable => 503
|
44
45
|
}
|
45
|
-
|
46
|
+
|
47
|
+
# For a more detailed description see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
46
48
|
STATUS_DESCRIPTIONS = {
|
49
|
+
200 => 'OK'.freeze,
|
50
|
+
201 => 'Created'.freeze,
|
51
|
+
202 => 'Accepted'.freeze,
|
52
|
+
203 => 'Non-Authoritive Information'.freeze,
|
53
|
+
204 => 'No Content'.freeze,
|
54
|
+
205 => 'Reset Content'.freeze,
|
55
|
+
206 => 'Partial Content'.freeze,
|
56
|
+
300 => 'Multiple Choices'.freeze,
|
57
|
+
301 => 'Moved Permanently'.freeze,
|
58
|
+
302 => 'Found'.freeze,
|
59
|
+
303 => 'See Other'.freeze,
|
60
|
+
304 => 'Not Modified'.freeze,
|
61
|
+
305 => 'Use Proxy'.freeze,
|
62
|
+
307 => 'Temporary Redirect'.freeze,
|
63
|
+
308 => 'Permanent Redirect'.freeze,
|
47
64
|
400 => 'Bad Request'.freeze,
|
48
65
|
401 => 'Permission Denied'.freeze,
|
66
|
+
402 => 'Payment Required'.freeze,
|
49
67
|
403 => 'Access Forbidden'.freeze,
|
50
68
|
404 => 'Resource Not Found'.freeze,
|
51
69
|
405 => 'Unsupported Method'.freeze,
|
70
|
+
406 => 'Not Acceptable'.freeze,
|
71
|
+
408 => 'Request Timeout'.freeze,
|
72
|
+
409 => 'Request Conflict'.freeze,
|
73
|
+
410 => 'Resource Removed'.freeze,
|
52
74
|
416 => 'Byte range unsatisfiable'.freeze,
|
53
75
|
500 => 'Internal Server Error'.freeze,
|
54
76
|
501 => 'Not Implemented'.freeze,
|
@@ -57,5 +79,32 @@ module Utopia
|
|
57
79
|
|
58
80
|
CONTENT_TYPE = 'Content-Type'.freeze
|
59
81
|
LOCATION = 'Location'.freeze
|
82
|
+
|
83
|
+
class Status
|
84
|
+
def initialize(code, valid_range = 100...600)
|
85
|
+
if code.is_a? Symbol
|
86
|
+
code = STATUS_CODES[code]
|
87
|
+
end
|
88
|
+
|
89
|
+
unless 100...600
|
90
|
+
raise ArgumentError.new("Status must be in range #{valid_range}, was given #{code}!")
|
91
|
+
end
|
92
|
+
|
93
|
+
@code = code
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_i
|
97
|
+
@code
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_s
|
101
|
+
STATUS_DESCRIPTIONS[@code] || @code.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
# Allow to be used for rack body:
|
105
|
+
def each
|
106
|
+
yield to_s
|
107
|
+
end
|
108
|
+
end
|
60
109
|
end
|
61
110
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright, 2015, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
module Utopia
|
22
|
+
class Locale < Struct.new(:language, :country, :variant)
|
23
|
+
def to_s
|
24
|
+
to_a.compact.join('-')
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.dump(instance)
|
28
|
+
if instance
|
29
|
+
instance.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.load(instance)
|
34
|
+
if instance.is_a? String
|
35
|
+
self.new(*instance.split('-', 3))
|
36
|
+
elsif instance.is_a? Array
|
37
|
+
return self.new(*instance)
|
38
|
+
elsif instance.is_a? self
|
39
|
+
return instance
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/utopia/localization.rb
CHANGED
@@ -20,64 +20,125 @@
|
|
20
20
|
|
21
21
|
require_relative 'middleware'
|
22
22
|
|
23
|
-
module
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
module Utopia
|
24
|
+
# If you request a URL which has localized content, a localized redirect would be returned based on the content requested.
|
25
|
+
class Localization
|
26
|
+
# A wrapper to provide easy access to locale related data in the request.
|
27
|
+
class RequestWrapper
|
28
|
+
def initialize(request)
|
29
|
+
if request.is_a? Rack::Request
|
30
|
+
@env = request.env
|
31
|
+
else
|
32
|
+
@env = request
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def localization
|
37
|
+
@env[LOCALIZATION_KEY]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the current locale or nil if not localized.
|
41
|
+
def current_locale
|
42
|
+
@env[CURRENT_LOCALE_KEY]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the default locale or nil if not localized.
|
46
|
+
def default_locale
|
47
|
+
localization && localization.default_locale
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns an empty array if not localized.
|
51
|
+
def all_locales
|
52
|
+
localization && localization.all_locales || []
|
53
|
+
end
|
31
54
|
end
|
32
55
|
|
33
|
-
def
|
34
|
-
|
56
|
+
def self.[] request
|
57
|
+
RequestWrapper.new(request)
|
35
58
|
end
|
36
59
|
|
37
|
-
def localization
|
38
|
-
env[Utopia::Localization::LOCALIZATION_KEY]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
module Utopia
|
44
|
-
# If you request a URL which has localized content, a localized redirect would be returned based on the content requested.
|
45
|
-
class Localization
|
46
60
|
RESOURCE_NOT_FOUND = [400, {}, []].freeze
|
47
61
|
|
48
62
|
HTTP_ACCEPT_LANGUAGE = 'HTTP_ACCEPT_LANGUAGE'.freeze
|
49
63
|
LOCALIZATION_KEY = 'utopia.localization'.freeze
|
50
64
|
CURRENT_LOCALE_KEY = 'utopia.localization.current_locale'.freeze
|
51
65
|
|
66
|
+
DEFAULT_LOCALE = 'en'
|
67
|
+
|
52
68
|
def initialize(app, **options)
|
53
69
|
@app = app
|
54
|
-
|
55
|
-
@
|
56
|
-
|
70
|
+
|
71
|
+
@all_locales = options[:locales]
|
72
|
+
|
73
|
+
# Locales here are represented as an array of strings, e.g. ['en', 'ja', 'cn', 'de'].
|
74
|
+
unless @default_locales = options[:default_locales]
|
75
|
+
@default_locales = @all_locales + [nil]
|
76
|
+
end
|
77
|
+
|
78
|
+
if @default_locale = options[:default_locale]
|
79
|
+
@default_locales.unshift(default_locale)
|
80
|
+
else
|
81
|
+
@default_locale = @default_locales.first
|
82
|
+
end
|
83
|
+
|
84
|
+
@hosts = options[:hosts] || {}
|
57
85
|
|
58
86
|
@nonlocalized = options.fetch(:nonlocalized, [])
|
87
|
+
|
88
|
+
# puts "All:#{@all_locales.inspect} defaults:#{@default_locales.inspect} default:#{default_locale}"
|
59
89
|
end
|
60
90
|
|
61
91
|
attr :all_locales
|
62
92
|
attr :default_locale
|
63
93
|
|
64
94
|
def preferred_locales(env)
|
65
|
-
|
95
|
+
return to_enum(:preferred_locales, env) unless block_given?
|
96
|
+
|
97
|
+
# Keep track of what locales have been tried:
|
98
|
+
locales = Set.new
|
99
|
+
|
100
|
+
host_preferred_locales(env).each do |locale|
|
101
|
+
yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
|
102
|
+
end
|
103
|
+
|
104
|
+
request_preferred_locale(env) do |locale, path|
|
105
|
+
# We have extracted a locale from the path, so from this point on we should use the updated path:
|
106
|
+
env = env.merge(Rack::PATH_INFO => path.to_s)
|
107
|
+
|
108
|
+
yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
|
109
|
+
end
|
110
|
+
|
111
|
+
browser_preferred_locales(env).each do |locale|
|
112
|
+
yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
|
113
|
+
end
|
114
|
+
|
115
|
+
@default_locales.each do |locale|
|
116
|
+
yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def host_preferred_locales(env)
|
121
|
+
http_host = env[Rack::HTTP_HOST]
|
122
|
+
|
123
|
+
locales = []
|
124
|
+
|
125
|
+
# Get a list of all hosts which match the incoming htt_host:
|
126
|
+
matching_hosts = @hosts.select{|host_pattern, locale| http_host =~ host_pattern}
|
127
|
+
|
128
|
+
# Extract all the valid locales:
|
129
|
+
matching_hosts.flat_map{|host_pattern, locale| locale}
|
66
130
|
end
|
67
131
|
|
68
|
-
def
|
69
|
-
path = Path[env[
|
132
|
+
def request_preferred_locale(env)
|
133
|
+
path = Path[env[Rack::PATH_INFO]]
|
70
134
|
|
71
|
-
if all_locales.include? path.first
|
135
|
+
if @all_locales.include? path.first
|
72
136
|
request_locale = path.first
|
73
137
|
|
74
|
-
# Remove the localization prefix
|
138
|
+
# Remove the localization prefix:
|
75
139
|
path.delete_at(0)
|
76
|
-
env['PATH_INFO'] = path.to_s
|
77
140
|
|
78
|
-
|
79
|
-
else
|
80
|
-
return []
|
141
|
+
yield request_locale, path
|
81
142
|
end
|
82
143
|
end
|
83
144
|
|
@@ -86,17 +147,17 @@ module Utopia
|
|
86
147
|
|
87
148
|
# No user prefered languages:
|
88
149
|
return [] unless accept_languages
|
89
|
-
|
150
|
+
|
90
151
|
languages = accept_languages.split(',').map { |language|
|
91
152
|
language.split(';q=').tap{|x| x[1] = (x[1] || 1.0).to_f}
|
92
153
|
}.sort{|a, b| b[1] <=> a[1]}.collect(&:first)
|
93
154
|
|
94
|
-
# Returns languages based on the order of the first argument
|
155
|
+
# Returns available languages based on the order of the first argument:
|
95
156
|
return languages & @all_locales
|
96
157
|
end
|
97
158
|
|
98
159
|
def nonlocalized?(env)
|
99
|
-
path_info = env[
|
160
|
+
path_info = env[Rack::PATH_INFO]
|
100
161
|
|
101
162
|
@nonlocalized.any? { |pattern| path_info[pattern] != nil }
|
102
163
|
end
|
@@ -115,13 +176,14 @@ module Utopia
|
|
115
176
|
# Althought this header is generally not supported, we supply it anyway as it is useful for debugging:
|
116
177
|
if locale = env[CURRENT_LOCALE_KEY]
|
117
178
|
# Set the Content-Location to point to the localized URI as requested:
|
118
|
-
headers['Content-Location'] = "/#{locale}" + env[
|
179
|
+
headers['Content-Location'] = "/#{locale}" + env[Rack::PATH_INFO]
|
119
180
|
end
|
120
181
|
|
121
182
|
return response
|
122
183
|
end
|
123
184
|
|
124
185
|
def call(env)
|
186
|
+
# Pass the request through with no localization if it is a nonlocalized path:
|
125
187
|
return @app.call(env) if nonlocalized?(env)
|
126
188
|
|
127
189
|
env[LOCALIZATION_KEY] = self
|
@@ -129,8 +191,8 @@ module Utopia
|
|
129
191
|
response = nil
|
130
192
|
|
131
193
|
# We have a non-localized request, but there might be a localized resource. We return the best localization possible:
|
132
|
-
preferred_locales(env)
|
133
|
-
env[CURRENT_LOCALE_KEY]
|
194
|
+
preferred_locales(env) do |env|
|
195
|
+
# puts "Trying locale: #{env[CURRENT_LOCALE_KEY]}: #{env[Rack::PATH_INFO]}..."
|
134
196
|
|
135
197
|
response = @app.call(env)
|
136
198
|
|
data/lib/utopia/path.rb
CHANGED
@@ -52,7 +52,7 @@ module Utopia
|
|
52
52
|
@parts ||= @name.split('.')
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
55
|
+
def locale
|
56
56
|
parts.last if parts.size > 1
|
57
57
|
end
|
58
58
|
|
@@ -322,10 +322,10 @@ module Utopia
|
|
322
322
|
def == other
|
323
323
|
return false unless other
|
324
324
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
325
|
+
case other
|
326
|
+
when String then self.to_s == other
|
327
|
+
when Array then self.to_a == other
|
328
|
+
else other.is_a?(self.class) && @components == other.components
|
329
329
|
end
|
330
330
|
end
|
331
331
|
|
data/lib/utopia/redirector.rb
CHANGED
@@ -117,7 +117,7 @@ module Utopia
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def call(env)
|
120
|
-
base_path = env[
|
120
|
+
base_path = env[Rack::PATH_INFO]
|
121
121
|
|
122
122
|
if uri = @strings[base_path]
|
123
123
|
return redirect(@strings[base_path], base_path)
|
@@ -134,11 +134,11 @@ module Utopia
|
|
134
134
|
response = @app.call(env)
|
135
135
|
|
136
136
|
if @errors && response[0] >= 400 && uri = @errors[response[0]]
|
137
|
-
error_request = env.merge(
|
137
|
+
error_request = env.merge(Rack::PATH_INFO => uri, Rack::REQUEST_METHOD => Rack::GET)
|
138
138
|
error_response = @app.call(error_request)
|
139
139
|
|
140
140
|
if error_response[0] >= 400
|
141
|
-
raise FailedRequestError.new(env[
|
141
|
+
raise FailedRequestError.new(env[Rack::PATH_INFO], response[0], uri, error_response[0])
|
142
142
|
else
|
143
143
|
# Feed the error code back with the error document
|
144
144
|
error_response[0] = response[0]
|
data/lib/utopia/session.rb
CHANGED
data/lib/utopia/static.rb
CHANGED
data/lib/utopia/version.rb
CHANGED
@@ -60,7 +60,7 @@ module Utopia::Content::LinksSpec
|
|
60
60
|
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"))
|
61
61
|
expect(links.size).to be == 2
|
62
62
|
|
63
|
-
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"),
|
63
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"), locale: 'en')
|
64
64
|
expect(links.size).to be == 1
|
65
65
|
end
|
66
66
|
|
@@ -68,7 +68,7 @@ module Utopia::Content::LinksSpec
|
|
68
68
|
root = File.expand_path("localized", __dir__)
|
69
69
|
|
70
70
|
# Select both test links
|
71
|
-
links = Utopia::Content::Links.index(root, Utopia::Path.create("/"),
|
71
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/"), locale: 'en')
|
72
72
|
|
73
73
|
expect(links.collect(&:title)).to be == ['One', 'Two', 'Three', 'Four', 'Five']
|
74
74
|
end
|
@@ -77,7 +77,7 @@ module Utopia::Content::LinksSpec
|
|
77
77
|
root = File.expand_path("localized", __dir__)
|
78
78
|
|
79
79
|
# Select both test links
|
80
|
-
links = Utopia::Content::Links.index(root, Utopia::Path.create("/"),
|
80
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/"), locale: 'zh')
|
81
81
|
|
82
82
|
expect(links.collect(&:title)).to be == ['One', 'Two', 'Three', '四']
|
83
83
|
end
|
@@ -52,10 +52,10 @@ module Utopia::ContentSpec
|
|
52
52
|
|
53
53
|
expect(links.size).to be == 2
|
54
54
|
expect(links[0].name).to be == 'foo'
|
55
|
-
expect(links[0].
|
55
|
+
expect(links[0].locale).to be == 'en'
|
56
56
|
|
57
57
|
expect(links[1].name).to be == 'foo'
|
58
|
-
expect(links[1].
|
58
|
+
expect(links[1].locale).to be == 'ja'
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'utopia/locale'
|
23
|
+
|
24
|
+
module Utopia::LocaleSpec
|
25
|
+
describe Utopia::Locale do
|
26
|
+
it "should load from string" do
|
27
|
+
locale = Utopia::Locale.load('en-US')
|
28
|
+
|
29
|
+
expect(locale.language).to be == 'en'
|
30
|
+
expect(locale.country).to be == 'US'
|
31
|
+
expect(locale.variant).to be == nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should load from nil and return nil" do
|
35
|
+
expect(Utopia::Locale.load(nil)).to be == nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should dump nil and give nil" do
|
39
|
+
expect(Utopia::Locale.dump(nil)).to be == nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should dump locale and give string" do
|
43
|
+
locale = Utopia::Locale.new('en', 'US')
|
44
|
+
|
45
|
+
expect(Utopia::Locale.dump(locale)).to be == 'en-US'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -24,6 +24,7 @@ require 'rack/test'
|
|
24
24
|
|
25
25
|
require 'utopia/static'
|
26
26
|
require 'utopia/content'
|
27
|
+
require 'utopia/controller'
|
27
28
|
require 'utopia/localization'
|
28
29
|
|
29
30
|
module Utopia::StaticSpec
|
@@ -38,21 +39,52 @@ module Utopia::StaticSpec
|
|
38
39
|
expect(last_response.body).to be == 'localized.en.txt'
|
39
40
|
end
|
40
41
|
|
41
|
-
it "should
|
42
|
+
it "should localize request based on path" do
|
42
43
|
get '/en/localized.txt'
|
43
44
|
expect(last_response.body).to be == 'localized.en.txt'
|
44
|
-
|
45
|
+
|
45
46
|
get '/de/localized.txt'
|
46
47
|
expect(last_response.body).to be == 'localized.de.txt'
|
47
|
-
|
48
|
-
get '/
|
49
|
-
expect(last_response.body).to be == 'localized.
|
48
|
+
|
49
|
+
get '/ja/localized.txt'
|
50
|
+
expect(last_response.body).to be == 'localized.ja.txt'
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should localize request based on domain name" do
|
54
|
+
get '/localized.txt', {}, 'HTTP_HOST' => 'foobar.com'
|
55
|
+
expect(last_response.body).to be == 'localized.en.txt'
|
56
|
+
|
57
|
+
get '/localized.txt', {}, 'HTTP_HOST' => 'foobar.de'
|
58
|
+
expect(last_response.body).to be == 'localized.de.txt'
|
59
|
+
|
60
|
+
get '/localized.txt', {}, 'HTTP_HOST' => 'foobar.co.jp'
|
61
|
+
expect(last_response.body).to be == 'localized.ja.txt'
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should get a non-localized resource" do
|
65
|
+
get "/en/test.txt"
|
66
|
+
expect(last_response.body).to be == 'Hello World!'
|
50
67
|
end
|
51
68
|
|
52
69
|
it "should respond with accepted language localization" do
|
53
|
-
get '/localized.txt', {}, 'HTTP_ACCEPT_LANGUAGE' => '
|
70
|
+
get '/localized.txt', {}, 'HTTP_ACCEPT_LANGUAGE' => 'ja,en'
|
54
71
|
|
55
|
-
expect(last_response.body).to be == 'localized.
|
72
|
+
expect(last_response.body).to be == 'localized.ja.txt'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should get a list of all localizations" do
|
76
|
+
get '/all_locales'
|
77
|
+
expect(last_response.body).to be == 'en,ja,de'
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should get the default locale" do
|
81
|
+
get '/default_locale'
|
82
|
+
expect(last_response.body).to be == 'en'
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should get the current locale (german)" do
|
86
|
+
get '/current_locale', {}, 'HTTP_HOST' => 'foobar.de'
|
87
|
+
expect(last_response.body).to be == 'de'
|
56
88
|
end
|
57
89
|
end
|
58
90
|
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
|
2
|
+
localization_spec_root = File.expand_path('localization_spec', __dir__)
|
3
|
+
|
2
4
|
use Utopia::Localization,
|
3
|
-
locales: ['en', '
|
5
|
+
locales: ['en', 'ja', 'de'],
|
6
|
+
hosts: {/foobar\.com$/ => 'en', /foobar\.co\.jp$/ => 'ja', /foobar\.de$/ => 'de'}
|
4
7
|
|
5
|
-
use Utopia::
|
6
|
-
root:
|
8
|
+
use Utopia::Controller,
|
9
|
+
root: localization_spec_root
|
7
10
|
|
8
|
-
use Utopia::
|
9
|
-
root:
|
11
|
+
use Utopia::Static,
|
12
|
+
root: localization_spec_root
|
10
13
|
|
11
14
|
run lambda { |env| [404, {}, []] }
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
on 'all_locales' do |request, path|
|
3
|
+
wrapper = Utopia::Localization[request]
|
4
|
+
|
5
|
+
succeed! content: wrapper.all_locales.join(',')
|
6
|
+
end
|
7
|
+
|
8
|
+
on 'default_locale' do |request, path|
|
9
|
+
wrapper = Utopia::Localization[request]
|
10
|
+
|
11
|
+
succeed! content: wrapper.default_locale
|
12
|
+
end
|
13
|
+
|
14
|
+
on 'current_locale' do |request, path|
|
15
|
+
wrapper = Utopia::Localization[request]
|
16
|
+
|
17
|
+
succeed! content: wrapper.current_locale
|
18
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
localized.ja.txt
|
@@ -0,0 +1 @@
|
|
1
|
+
Hello World!
|
data/spec/utopia/path_spec.rb
CHANGED
@@ -102,16 +102,16 @@ module Utopia::PathSpec
|
|
102
102
|
expect{path[0] = 'bob'}.to raise_exception(RuntimeError)
|
103
103
|
end
|
104
104
|
|
105
|
-
it "should give the correct
|
105
|
+
it "should give the correct locale" do
|
106
106
|
path = Utopia::Path["foo.en"]
|
107
107
|
|
108
|
-
expect(path.basename.
|
108
|
+
expect(path.basename.locale).to be == 'en'
|
109
109
|
end
|
110
110
|
|
111
|
-
it "should give no
|
111
|
+
it "should give no locale" do
|
112
112
|
path = Utopia::Path["foo"]
|
113
113
|
|
114
|
-
expect(path.basename.
|
114
|
+
expect(path.basename.locale).to be == nil
|
115
115
|
end
|
116
116
|
|
117
117
|
it "should expand relative paths" do
|
data/spec/utopia/session_spec.ru
CHANGED
@@ -4,15 +4,15 @@ use Utopia::Session::EncryptedCookie, secret: "97111cabf4c1a5e85b8029cf7c61aa444
|
|
4
4
|
run lambda { |env|
|
5
5
|
request = Rack::Request.new(env)
|
6
6
|
|
7
|
-
if env[
|
7
|
+
if env[Rack::PATH_INFO] =~ /login/
|
8
8
|
env['rack.session']['login'] = 'true'
|
9
9
|
|
10
10
|
[200, {}, []]
|
11
|
-
elsif env[
|
11
|
+
elsif env[Rack::PATH_INFO] =~ /session-set/
|
12
12
|
env['rack.session'][request[:key]] = request[:value]
|
13
13
|
|
14
14
|
[200, {}, []]
|
15
|
-
elsif env[
|
15
|
+
elsif env[Rack::PATH_INFO] =~ /session-get/
|
16
16
|
[200, {}, [env['rack.session'][request[:key]]]]
|
17
17
|
else
|
18
18
|
[404, {}, []]
|
data/utopia.gemspec
CHANGED
@@ -4,10 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'utopia/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'utopia'
|
8
8
|
spec.version = Utopia::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Samuel Williams']
|
10
|
+
spec.email = ['samuel.williams@oriontransfer.co.nz']
|
11
11
|
spec.description = <<-EOF
|
12
12
|
Utopia is a website generation framework which provides a robust set of tools
|
13
13
|
to build highly complex dynamic websites. It uses the filesystem heavily for
|
@@ -15,25 +15,27 @@ Gem::Specification.new do |spec|
|
|
15
15
|
structure representing the website.
|
16
16
|
EOF
|
17
17
|
spec.summary = %q{Utopia is a framework for building dynamic content-driven websites.}
|
18
|
-
spec.homepage =
|
18
|
+
spec.homepage = 'https://github.com/ioquatix/utopia'
|
19
19
|
|
20
20
|
spec.files = `git ls-files`.split($/)
|
21
21
|
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
-
spec.require_paths = [
|
23
|
+
spec.require_paths = ['lib']
|
24
24
|
|
25
|
-
spec.
|
26
|
-
spec.add_dependency "mime-types", "~> 2.0"
|
25
|
+
spec.required_ruby_version = '~> 2.0'
|
27
26
|
|
28
|
-
spec.add_dependency
|
29
|
-
spec.add_dependency
|
27
|
+
spec.add_dependency 'trenni', '~> 1.4.1'
|
28
|
+
spec.add_dependency 'mime-types', '~> 2.0'
|
30
29
|
|
31
|
-
spec.add_dependency
|
30
|
+
spec.add_dependency 'rack', '~> 1.6'
|
31
|
+
spec.add_dependency 'rack-cache', '~> 1.2.0'
|
32
32
|
|
33
|
-
spec.add_dependency
|
33
|
+
spec.add_dependency 'mail', '~> 2.6.1'
|
34
34
|
|
35
|
-
spec.
|
36
|
-
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
35
|
+
spec.add_dependency 'concurrent-ruby', '~> 1.0.0'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.4.0'
|
39
|
+
spec.add_development_dependency 'puma'
|
40
|
+
spec.add_development_dependency 'rake'
|
39
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: utopia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trenni
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 3.
|
117
|
+
version: 3.4.0
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 3.
|
124
|
+
version: 3.4.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: puma
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- lib/utopia/extensions/date.rb
|
188
188
|
- lib/utopia/extensions/rack.rb
|
189
189
|
- lib/utopia/http.rb
|
190
|
+
- lib/utopia/locale.rb
|
190
191
|
- lib/utopia/localization.rb
|
191
192
|
- lib/utopia/mail_exceptions.rb
|
192
193
|
- lib/utopia/middleware.rb
|
@@ -263,17 +264,20 @@ files:
|
|
263
264
|
- spec/utopia/exception_handler_spec.ru
|
264
265
|
- spec/utopia/exception_handler_spec/controller.rb
|
265
266
|
- spec/utopia/extensions_spec.rb
|
267
|
+
- spec/utopia/locale_spec.rb
|
266
268
|
- spec/utopia/localization_spec.rb
|
267
269
|
- spec/utopia/localization_spec.ru
|
270
|
+
- spec/utopia/localization_spec/controller.rb
|
271
|
+
- spec/utopia/localization_spec/localized.de.txt
|
272
|
+
- spec/utopia/localization_spec/localized.en.txt
|
273
|
+
- spec/utopia/localization_spec/localized.ja.txt
|
274
|
+
- spec/utopia/localization_spec/test.txt
|
268
275
|
- spec/utopia/middleware_spec.rb
|
269
276
|
- spec/utopia/pages/_heading.xnode
|
270
277
|
- spec/utopia/pages/content/_show-value.xnode
|
271
278
|
- spec/utopia/pages/content/links.yaml
|
272
279
|
- spec/utopia/pages/content/test-partial.xnode
|
273
280
|
- spec/utopia/pages/index.xnode
|
274
|
-
- spec/utopia/pages/localized.de.txt
|
275
|
-
- spec/utopia/pages/localized.en.txt
|
276
|
-
- spec/utopia/pages/localized.jp.txt
|
277
281
|
- spec/utopia/pages/node/index.xnode
|
278
282
|
- spec/utopia/pages/test.txt
|
279
283
|
- spec/utopia/path/matcher_spec.rb
|
@@ -293,9 +297,9 @@ require_paths:
|
|
293
297
|
- lib
|
294
298
|
required_ruby_version: !ruby/object:Gem::Requirement
|
295
299
|
requirements:
|
296
|
-
- - "
|
300
|
+
- - "~>"
|
297
301
|
- !ruby/object:Gem::Version
|
298
|
-
version: '0'
|
302
|
+
version: '2.0'
|
299
303
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
300
304
|
requirements:
|
301
305
|
- - ">="
|
@@ -348,17 +352,20 @@ test_files:
|
|
348
352
|
- spec/utopia/exception_handler_spec.ru
|
349
353
|
- spec/utopia/exception_handler_spec/controller.rb
|
350
354
|
- spec/utopia/extensions_spec.rb
|
355
|
+
- spec/utopia/locale_spec.rb
|
351
356
|
- spec/utopia/localization_spec.rb
|
352
357
|
- spec/utopia/localization_spec.ru
|
358
|
+
- spec/utopia/localization_spec/controller.rb
|
359
|
+
- spec/utopia/localization_spec/localized.de.txt
|
360
|
+
- spec/utopia/localization_spec/localized.en.txt
|
361
|
+
- spec/utopia/localization_spec/localized.ja.txt
|
362
|
+
- spec/utopia/localization_spec/test.txt
|
353
363
|
- spec/utopia/middleware_spec.rb
|
354
364
|
- spec/utopia/pages/_heading.xnode
|
355
365
|
- spec/utopia/pages/content/_show-value.xnode
|
356
366
|
- spec/utopia/pages/content/links.yaml
|
357
367
|
- spec/utopia/pages/content/test-partial.xnode
|
358
368
|
- spec/utopia/pages/index.xnode
|
359
|
-
- spec/utopia/pages/localized.de.txt
|
360
|
-
- spec/utopia/pages/localized.en.txt
|
361
|
-
- spec/utopia/pages/localized.jp.txt
|
362
369
|
- spec/utopia/pages/node/index.xnode
|
363
370
|
- spec/utopia/pages/test.txt
|
364
371
|
- spec/utopia/path/matcher_spec.rb
|
@@ -1 +0,0 @@
|
|
1
|
-
localized.jp.txt
|