wovnrb 0.1.36 → 0.1.37
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/wovnrb.rb +4 -1
- data/lib/wovnrb/headers.rb +20 -17
- data/lib/wovnrb/store.rb +70 -72
- data/lib/wovnrb/version.rb +1 -1
- data/test/lib/{test_headers.rb → headers_test.rb} +9 -35
- data/test/lib/{test_lang.rb → lang_test.rb} +2 -4
- data/test/test_helper.rb +1 -0
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48267e3d6147c0fa23c27a54e53895a0830e61a6
|
|
4
|
+
data.tar.gz: 47c15e957323c220e9348c0736247f3a2f67215d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 211018b6b73db62671b34a2d31b2455479f9af9652d56351291674def10ef63be24299df32049800708c26b03ef1fa3be3df79d345e0d3c029ad1eb14bb868ab
|
|
7
|
+
data.tar.gz: 71947f8e26a230990bb6cd2b1035d89ce6b029deb811dba1431498877fb8cad18f20967fae34f9a5fc770aadd7ac8fbd8e010f9ce767ce2e3dde60fb20bb88a7
|
data/lib/wovnrb.rb
CHANGED
|
@@ -22,12 +22,15 @@ module Wovnrb
|
|
|
22
22
|
end
|
|
23
23
|
@env = env
|
|
24
24
|
headers = Headers.new(env, STORE.settings)
|
|
25
|
+
if STORE.settings.test_mode && STORE.settings.test_url != headers.url
|
|
26
|
+
return @app.call(env)
|
|
27
|
+
end
|
|
25
28
|
# redirect if the path is set to the default language (for SEO purposes)
|
|
26
29
|
if (headers.path_lang == STORE.settings['default_lang'])
|
|
27
30
|
redirect_headers = headers.redirect(STORE.settings['default_lang'])
|
|
28
31
|
return [307, redirect_headers, ['']]
|
|
29
32
|
end
|
|
30
|
-
lang = headers.
|
|
33
|
+
lang = headers.lang_code
|
|
31
34
|
|
|
32
35
|
# pass to application
|
|
33
36
|
status, res_headers, body = @app.call(headers.request_out)
|
data/lib/wovnrb/headers.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require 'pry'
|
|
2
1
|
module Wovnrb
|
|
3
2
|
|
|
4
3
|
class Headers
|
|
@@ -13,6 +12,7 @@ module Wovnrb
|
|
|
13
12
|
|
|
14
13
|
def initialize(env, settings)
|
|
15
14
|
@env = env
|
|
15
|
+
@settings = settings
|
|
16
16
|
@protocol = @env['rack.url_scheme']
|
|
17
17
|
@unmasked_host = @env['HTTP_HOST']
|
|
18
18
|
unless @env.has_key?('REQUEST_URI')
|
|
@@ -27,11 +27,11 @@ module Wovnrb
|
|
|
27
27
|
@unmasked_pathname = @env['REQUEST_URI'].split('?')[0]
|
|
28
28
|
@unmasked_pathname += '/' unless @unmasked_pathname =~ /\/$/ || @unmasked_pathname =~ /\/[^\/.]+\.[^\/.]+$/
|
|
29
29
|
@unmasked_url = "#{@protocol}://#{@unmasked_host}#{@unmasked_pathname}"
|
|
30
|
-
@host = remove_lang(@env['HTTP_HOST'], self.
|
|
30
|
+
@host = settings['url_pattern'] == 'subdomain' ? remove_lang(@env['HTTP_HOST'], self.lang_code) : @env['HTTP_HOST']
|
|
31
31
|
@pathname, @query = @env['REQUEST_URI'].split('?')
|
|
32
|
-
@pathname = remove_lang(@pathname, self.
|
|
32
|
+
@pathname = settings['url_pattern'] == 'path' ? remove_lang(@pathname, self.lang_code) : @pathname
|
|
33
33
|
@query = @query || ''
|
|
34
|
-
@url = "#{@host}#{@pathname}#{(@query.length > 0 ? '?' : '') + remove_lang(@query, self.
|
|
34
|
+
@url = "#{@host}#{@pathname}#{(@query.length > 0 ? '?' : '') + remove_lang(@query, self.lang_code)}"
|
|
35
35
|
if settings['query'].length > 0
|
|
36
36
|
query_vals = []
|
|
37
37
|
settings['query'].each do |qv|
|
|
@@ -47,18 +47,21 @@ module Wovnrb
|
|
|
47
47
|
else
|
|
48
48
|
@query = ''
|
|
49
49
|
end
|
|
50
|
-
@query = remove_lang(@query, self.
|
|
50
|
+
@query = remove_lang(@query, self.lang_code)
|
|
51
51
|
@pathname = @pathname.gsub(/\/$/, '')
|
|
52
52
|
@redis_url = "#{@host}#{@pathname}#{@query}"
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
# Get the language code of the current request
|
|
56
|
+
#
|
|
57
|
+
# @return [String] The lang code of the current page
|
|
58
|
+
def lang_code
|
|
59
|
+
(self.path_lang && self.path_lang.length > 0) ? self.path_lang : @settings['default_lang']
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
def path_lang
|
|
60
63
|
if @path_lang.nil?
|
|
61
|
-
rp = Regexp.new(
|
|
64
|
+
rp = Regexp.new(@settings['url_pattern_reg'])
|
|
62
65
|
match = "#{@env['SERVER_NAME']}#{@env['REQUEST_URI']}".match(rp)
|
|
63
66
|
if match && match[:lang] && Lang::LANG[match[:lang]]
|
|
64
67
|
@path_lang = match[:lang]
|
|
@@ -97,13 +100,13 @@ module Wovnrb
|
|
|
97
100
|
end
|
|
98
101
|
|
|
99
102
|
def redirect_location(lang)
|
|
100
|
-
if lang ==
|
|
103
|
+
if lang == @settings['default_lang']
|
|
101
104
|
# IS THIS RIGHT??
|
|
102
105
|
return "#{self.protocol}://#{self.url}"
|
|
103
106
|
#return remove_lang("#{@env['HTTP_HOST']}#{@env['REQUEST_URI']}", lang)
|
|
104
107
|
else
|
|
105
108
|
location = self.url
|
|
106
|
-
case
|
|
109
|
+
case @settings['url_pattern']
|
|
107
110
|
when 'query'
|
|
108
111
|
if location !~ /\?/
|
|
109
112
|
location = "#{location}?wovn=#{lang}"
|
|
@@ -120,8 +123,8 @@ module Wovnrb
|
|
|
120
123
|
end
|
|
121
124
|
end
|
|
122
125
|
|
|
123
|
-
def request_out(def_lang
|
|
124
|
-
case
|
|
126
|
+
def request_out(def_lang=@settings['default_lang'])
|
|
127
|
+
case @settings['url_pattern']
|
|
125
128
|
when 'query'
|
|
126
129
|
@env['REQUEST_URI'] = remove_lang(@env['REQUEST_URI']) if @env.has_key?('REQUEST_URI')
|
|
127
130
|
@env['QUERY_STRING'] = remove_lang(@env['QUERY_STRING']) if @env.has_key?('QUERY_STRING')
|
|
@@ -147,7 +150,7 @@ module Wovnrb
|
|
|
147
150
|
end
|
|
148
151
|
|
|
149
152
|
def remove_lang(uri, lang=self.path_lang)
|
|
150
|
-
case
|
|
153
|
+
case @settings['url_pattern']
|
|
151
154
|
when 'query'
|
|
152
155
|
return uri.sub(/(^|\?|&)wovn=#{lang}(&|$)/, '\1').gsub(/(\?|&)$/, '')
|
|
153
156
|
when 'subdomain'
|
|
@@ -161,19 +164,19 @@ module Wovnrb
|
|
|
161
164
|
|
|
162
165
|
def out(headers)
|
|
163
166
|
if headers.has_key?("Location")
|
|
164
|
-
case
|
|
167
|
+
case @settings['url_pattern']
|
|
165
168
|
when 'query'
|
|
166
169
|
if headers["Location"] =~ /\?/
|
|
167
170
|
headers["Location"] += "&"
|
|
168
171
|
else
|
|
169
172
|
headers["Location"] += "?"
|
|
170
173
|
end
|
|
171
|
-
headers['Location'] += "wovn=#{self.
|
|
174
|
+
headers['Location'] += "wovn=#{self.lang_code}"
|
|
172
175
|
when 'subdomain'
|
|
173
|
-
headers["Location"] = headers["Location"].sub(/\/\/([^.]+)/, '//' + self.
|
|
176
|
+
headers["Location"] = headers["Location"].sub(/\/\/([^.]+)/, '//' + self.lang_code + '.\1')
|
|
174
177
|
#when 'path'
|
|
175
178
|
else
|
|
176
|
-
headers["Location"] = headers['Location'].sub(/(\/\/[^\/]+)/, '\1/' + self.
|
|
179
|
+
headers["Location"] = headers['Location'].sub(/(\/\/[^\/]+)/, '\1/' + self.lang_code)
|
|
177
180
|
end
|
|
178
181
|
end
|
|
179
182
|
headers
|
data/lib/wovnrb/store.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
require 'redis'
|
|
2
2
|
require 'net/http'
|
|
3
|
+
require 'Logger'
|
|
3
4
|
|
|
4
5
|
module Wovnrb
|
|
5
6
|
|
|
6
7
|
class Store
|
|
7
8
|
attr_reader :settings
|
|
8
9
|
|
|
9
|
-
|
|
10
10
|
def initialize
|
|
11
11
|
@settings =
|
|
12
12
|
{
|
|
@@ -23,33 +23,64 @@ module Wovnrb
|
|
|
23
23
|
'backend_port' => '6379',
|
|
24
24
|
'default_lang' => 'en',
|
|
25
25
|
'supported_langs' => ['en'],
|
|
26
|
+
'test_mode' => false,
|
|
27
|
+
'test_url' => '',
|
|
26
28
|
}
|
|
27
29
|
# When Store is initialized, the Rails.configuration object is not yet initialized
|
|
28
30
|
@config_loaded = false
|
|
29
31
|
end
|
|
30
32
|
|
|
33
|
+
# Returns true or false based on whether the settings are valid or not, logs any invalid settings to ../error.log
|
|
34
|
+
#
|
|
35
|
+
# @return [Boolean] Returns true if the settings are valid, and false if they are not
|
|
31
36
|
def valid_settings?
|
|
37
|
+
valid = true
|
|
38
|
+
errors = [];
|
|
32
39
|
if !settings.has_key?('user_token') || settings['user_token'].length < 5 || settings['user_token'].length > 6
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
valid = false
|
|
41
|
+
errors.push("User token #{settings['user_token']} is not valid.");
|
|
42
|
+
end
|
|
43
|
+
if !settings.has_key?('secret_key') || settings['secret_key'].length == 0 #|| settings['secret_key'].length < 5 || settings['secret_key'].length > 6
|
|
44
|
+
valid = false
|
|
45
|
+
errors.push("Secret key #{settings['secret_key']} is not valid.");
|
|
46
|
+
end
|
|
47
|
+
if !settings.has_key?('url_pattern') || settings['url_pattern'].length == 0
|
|
48
|
+
valid = false
|
|
49
|
+
errors.push("Url pattern #{settings['url_pattern']} is not valid.");
|
|
50
|
+
end
|
|
51
|
+
if !settings.has_key?('query') || !settings['query'].kind_of?(Array)
|
|
52
|
+
valid = false
|
|
53
|
+
errors.push("query config #{settings['query']} is not valid.");
|
|
54
|
+
end
|
|
55
|
+
if !settings.has_key?('backend_host') || settings['backend_host'].length == 0
|
|
56
|
+
valid = false
|
|
57
|
+
errors.push("Backend host is not configured.");
|
|
58
|
+
end
|
|
59
|
+
if !settings.has_key?('backend_port') || settings['backend_port'].length == 0
|
|
60
|
+
valid = false
|
|
61
|
+
errors.push("Backend port is not configured.");
|
|
62
|
+
end
|
|
63
|
+
if !settings.has_key?('default_lang') || settings['default_lang'].length == 0
|
|
64
|
+
valid = false
|
|
65
|
+
errors.push("Default lang #{settings['default_lang']} is not valid.");
|
|
50
66
|
end
|
|
67
|
+
if !settings.has_key?('supported_langs') || !settings['supported_langs'].kind_of?(Array) || settings['supported_langs'].size < 1
|
|
68
|
+
valid = false
|
|
69
|
+
errors.push("Supported langs configuration is not valid.");
|
|
70
|
+
end
|
|
71
|
+
# log errors
|
|
72
|
+
if errors.length > 0
|
|
73
|
+
logger = Logger.new('../error.log')
|
|
74
|
+
errors.each do |e|
|
|
75
|
+
logger.error(e)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
return valid
|
|
51
79
|
end
|
|
52
80
|
|
|
81
|
+
# Returns the settings object, pulling from Rails config the first time this is called
|
|
82
|
+
#
|
|
83
|
+
# @return [Hash] The settings which are pulled from the config file given by the user and filled in by defaults
|
|
53
84
|
def settings
|
|
54
85
|
if !@config_loaded
|
|
55
86
|
# get Rails config.wovnrb
|
|
@@ -77,70 +108,37 @@ module Wovnrb
|
|
|
77
108
|
elsif @settings['url_pattern'] == 'subdomain'
|
|
78
109
|
@settings['url_pattern_reg'] = "^(?<lang>[^.]+)\."
|
|
79
110
|
end
|
|
80
|
-
@
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
if @settings['test_mode'] != true || @settings['test_mode'] != 'on'
|
|
112
|
+
@settings['test_mode'] = false
|
|
113
|
+
else
|
|
114
|
+
@settings['test_mode'] = true
|
|
115
|
+
end
|
|
84
116
|
|
|
85
|
-
|
|
86
|
-
# add timer so this only accesses redis once every 5 minutes etc
|
|
87
|
-
user_token = @settings['user_token']
|
|
88
|
-
#user_token = 'lYWQ9'
|
|
89
|
-
redis_key = 'WOVN:BACKEND:SETTING::' + user_token
|
|
90
|
-
cli = Redis.new(host: @settings['backend_host'], port: @settings['backend_port'])
|
|
91
|
-
begin
|
|
92
|
-
vals = cli.hgetall(redis_key) || {}
|
|
93
|
-
rescue
|
|
94
|
-
vals = {}
|
|
95
|
-
end
|
|
96
|
-
if vals.has_key?('query')
|
|
97
|
-
vals['query'] = JSON.parse(vals['query'])
|
|
98
|
-
end
|
|
99
|
-
if vals.has_key?('supported_langs')
|
|
100
|
-
vals['supported_langs'] = JSON.parse(vals['supported_langs'])
|
|
101
|
-
end
|
|
102
|
-
@settings.merge!(vals)
|
|
103
|
-
@settings['backend_port'] = @settings['backend_port'].to_s
|
|
104
|
-
@settings['default_lang'] = Lang.get_code(@settings['default_lang'])
|
|
105
|
-
if !vals.has_key?('supported_langs')
|
|
106
|
-
@settings['supported_langs'] = [@settings['default_lang']]
|
|
107
|
-
end
|
|
108
|
-
if @settings['url_pattern'] == 'path'
|
|
109
|
-
@settings['url_pattern_reg'] = "/(?<lang>[^/.?]+)"
|
|
110
|
-
elsif @settings['url_pattern'] == 'query'
|
|
111
|
-
@settings['url_pattern_reg'] = "((\\?.*&)|\\?)wovn=(?<lang>[^&]+)(&|$)"
|
|
117
|
+
@config_loaded = true
|
|
112
118
|
end
|
|
113
119
|
@settings
|
|
114
120
|
end
|
|
115
121
|
|
|
122
|
+
# Get the values for the passed in url
|
|
123
|
+
#
|
|
124
|
+
# @param url [String] The url to get the values for
|
|
125
|
+
# @return [Hash] The values Hash for the passed in url
|
|
116
126
|
def get_values(url)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
#user_token = 'lYWQ9'
|
|
120
|
-
redis_key = 'WOVN:BACKEND:STORAGE:' + url.gsub(/\/$/, '') + ':' + user_token
|
|
121
|
-
vals = request_values(redis_key)
|
|
122
|
-
if vals.empty?
|
|
123
|
-
uri = URI.parse('http://api.wovn.io/v0/page/add')
|
|
124
|
-
Net::HTTP.post_form(uri, :user_token => user_token, :secret_key => @settings['secret_key'], :url => url)
|
|
125
|
-
end
|
|
126
|
-
vals
|
|
127
|
-
#f = File.open('./values/values', 'r')
|
|
128
|
-
#return JSON.parse(f.read)
|
|
129
|
-
#f.close
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def request_values(key)
|
|
133
|
-
Rails.logger.info("*******************************************************")
|
|
134
|
-
Rails.logger.info(key)
|
|
135
|
-
cli = Redis.new(host: @settings['backend_host'], port: @settings['backend_port'])
|
|
136
|
-
Rails.logger.info(@settings['backend_host'])
|
|
137
|
-
Rails.logger.info(@settings['backend_port'])
|
|
127
|
+
redis_key = 'WOVN:BACKEND:STORAGE:' + url.gsub(/\/$/, '') + ':' + settings['user_token']
|
|
128
|
+
cli = Redis.new(host: settings['backend_host'], port: settings['backend_port'])
|
|
138
129
|
begin
|
|
139
|
-
vals = cli.get(
|
|
140
|
-
vals = JSON.parse(vals)
|
|
130
|
+
vals = cli.get(redis_key) || '{}'
|
|
141
131
|
rescue
|
|
132
|
+
logger = Logger.new('../error.log')
|
|
133
|
+
logger.error("Redis GET request failed with the following parameters:\nhost: #{settings['backend_host']}\nport: #{settings['backend_port']}\nkey: #{redis_key}")
|
|
142
134
|
vals = {}
|
|
143
135
|
end
|
|
136
|
+
# handle this on the widget
|
|
137
|
+
#if vals.empty?
|
|
138
|
+
# uri = URI.parse('http://api.wovn.io/v0/page/add')
|
|
139
|
+
# Net::HTTP.post_form(uri, :user_token => user_token, :secret_key => @settings['secret_key'], :url => url)
|
|
140
|
+
#end
|
|
141
|
+
JSON.parse(vals)
|
|
144
142
|
end
|
|
145
143
|
|
|
146
144
|
end
|
data/lib/wovnrb/version.rb
CHANGED
|
@@ -1,50 +1,24 @@
|
|
|
1
|
-
require 'wovnrb'
|
|
1
|
+
require 'wovnrb/headers'
|
|
2
2
|
require 'minitest/autorun'
|
|
3
3
|
require 'pry'
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class HeadersTest < Minitest::Test
|
|
6
6
|
|
|
7
7
|
def test_initialize
|
|
8
8
|
h = Wovnrb::Headers.new(get_env, get_settings)
|
|
9
9
|
refute_nil(h)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
|
|
12
|
+
def test_initialize_env
|
|
13
|
+
env = get_env
|
|
14
|
+
h = Wovnrb::Headers.new(env, {})
|
|
15
|
+
binding.pry
|
|
14
16
|
#assert_equal(''
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def
|
|
18
|
-
Wovnrb::
|
|
19
|
-
|
|
20
|
-
assert(l.has_key?(:code))
|
|
21
|
-
assert(l.has_key?(:en))
|
|
22
|
-
assert_equal(k, l[:code])
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def test_get_code_with_valid_code
|
|
27
|
-
assert_equal('ms', Wovnrb::Lang.get_code('ms'))
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def test_get_code_with_valid_english_name
|
|
31
|
-
assert_equal('pt', Wovnrb::Lang.get_code('Portuguese'))
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def test_get_code_with_valid_native_name
|
|
35
|
-
assert_equal('hi', Wovnrb::Lang.get_code('हिन्दी'))
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def test_get_code_with_invalid_name
|
|
39
|
-
assert_equal(nil, Wovnrb::Lang.get_code('WOVN4LYFE'))
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def test_get_code_with_empty_string
|
|
43
|
-
assert_equal(nil, Wovnrb::Lang.get_code(''))
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def test_get_code_with_nil
|
|
47
|
-
assert_equal(nil, Wovnrb::Lang.get_code(nil))
|
|
19
|
+
def test_initialize_with_simple_url
|
|
20
|
+
h = Wovnrb::Headers.new(get_env('url' => 'https://wovn.io'), get_settings)
|
|
21
|
+
#assert_equal(''
|
|
48
22
|
end
|
|
49
23
|
|
|
50
24
|
def get_settings(options={})
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
puts "HEYO"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wovnrb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.37
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeff Sandford
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2015-
|
|
12
|
+
date: 2015-08-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: nokogumbo
|
|
@@ -243,8 +243,9 @@ files:
|
|
|
243
243
|
- lib/wovnrb/version.rb
|
|
244
244
|
- spec/spec_helper.rb
|
|
245
245
|
- spec/wovnrb_spec.rb
|
|
246
|
-
- test/lib/
|
|
247
|
-
- test/lib/
|
|
246
|
+
- test/lib/headers_test.rb
|
|
247
|
+
- test/lib/lang_test.rb
|
|
248
|
+
- test/test_helper.rb
|
|
248
249
|
- values/values
|
|
249
250
|
- wovnrb.gemspec
|
|
250
251
|
homepage: ''
|
|
@@ -274,5 +275,6 @@ summary: Gem for WOVN.io
|
|
|
274
275
|
test_files:
|
|
275
276
|
- spec/spec_helper.rb
|
|
276
277
|
- spec/wovnrb_spec.rb
|
|
277
|
-
- test/lib/
|
|
278
|
-
- test/lib/
|
|
278
|
+
- test/lib/headers_test.rb
|
|
279
|
+
- test/lib/lang_test.rb
|
|
280
|
+
- test/test_helper.rb
|