wovnrb 0.2.01 → 0.2.02
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/.gitignore +3 -6
- data/README.md +116 -12
- data/lib/wovnrb/api_data.rb +59 -0
- data/lib/wovnrb/headers.rb +30 -5
- data/lib/wovnrb/html_replacers/image_replacer.rb +44 -0
- data/lib/wovnrb/html_replacers/link_replacer.rb +18 -0
- data/lib/wovnrb/html_replacers/meta_replacer.rb +20 -0
- data/lib/wovnrb/html_replacers/replacer_base.rb +17 -0
- data/lib/wovnrb/html_replacers/script_replacer.rb +39 -0
- data/lib/wovnrb/html_replacers/text_replacer.rb +19 -0
- data/lib/wovnrb/lang.rb +133 -23
- data/lib/wovnrb/services/url.rb +2 -68
- data/lib/wovnrb/services/wovn_logger.rb +45 -0
- data/lib/wovnrb/store.rb +19 -38
- data/lib/wovnrb/text_caches/cache_base.rb +52 -0
- data/lib/wovnrb/text_caches/memory_cache.rb +52 -0
- data/lib/wovnrb/version.rb +1 -1
- data/lib/wovnrb.rb +19 -224
- data/test/lib/api_data_test.rb +62 -0
- data/test/lib/headers_test.rb +1221 -1212
- data/test/lib/html_replacers/image_replacer_test.rb +86 -0
- data/test/lib/html_replacers/link_replacer_test.rb +43 -0
- data/test/lib/html_replacers/meta_replacer_test.rb +105 -0
- data/test/lib/html_replacers/replacer_base_test.rb +31 -0
- data/test/lib/html_replacers/script_replacer_test.rb +90 -0
- data/test/lib/html_replacers/text_replacer_test.rb +57 -0
- data/test/lib/lang_test.rb +446 -33
- data/test/lib/services/wovn_logger_test.rb +40 -0
- data/test/lib/store_test.rb +39 -29
- data/test/lib/text_caches/cache_base_test.rb +31 -0
- data/test/lib/text_caches/memory_cache_test.rb +91 -0
- data/test/lib/wovnrb_test.rb +39 -886
- data/test/test_helper.rb +96 -1
- data/wovnrb.gemspec +4 -0
- metadata +89 -2
data/lib/wovnrb/services/url.rb
CHANGED
@@ -1,78 +1,12 @@
|
|
1
1
|
module Wovnrb
|
2
2
|
class URL
|
3
|
-
|
4
3
|
def self.resolve_absolute_url(curr_location, rel_location)
|
5
4
|
end
|
6
5
|
|
7
|
-
# Set the path lang to
|
6
|
+
# Set the path lang to
|
8
7
|
def self.prepend_path(url, dir)
|
9
|
-
|
10
|
-
result = url.sub(/(.+\.[^\/]+)(\/|$)/, '\1/' + dir + '\2')
|
8
|
+
result = url.sub(/(.+\.[^\/]+)(\/|$)/, '\1/' + dir + '\2')
|
11
9
|
return result
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
=begin
|
16
|
-
|
17
|
-
url_slash = url.count("/")
|
18
|
-
|
19
|
-
if url.include?("//") && url_slash >= 3 then
|
20
|
-
|
21
|
-
url_base = url.split("/", 4)
|
22
|
-
|
23
|
-
url_begin = url_base[0]
|
24
|
-
url_middle = url_base[2]
|
25
|
-
url_end = url_base[3]
|
26
|
-
|
27
|
-
result = url_begin + "//" + url_middle + "/" + dir + "/" + url_end
|
28
|
-
|
29
|
-
return result
|
30
|
-
|
31
|
-
elsif url.include?("//") then
|
32
|
-
|
33
|
-
result = url + "/" + dir
|
34
|
-
|
35
|
-
return result
|
36
|
-
|
37
|
-
elsif url.include?("/") #&& url_slash >= 2 then
|
38
|
-
|
39
|
-
url_base = url.split("/", 2)
|
40
|
-
|
41
|
-
url_begin = url_base[0]
|
42
|
-
url_end = url_base[1]
|
43
|
-
|
44
|
-
result = url_begin + "/" + dir + "/" + url_end
|
45
|
-
|
46
|
-
return result
|
47
|
-
|
48
|
-
else
|
49
|
-
|
50
|
-
result = url + "/" + dir
|
51
|
-
|
52
|
-
return result
|
53
|
-
|
54
10
|
end
|
55
|
-
end
|
56
|
-
|
57
|
-
=end
|
58
|
-
|
59
|
-
#def self.set_query_lang(url, lang, param_name='wovn')
|
60
|
-
# url =
|
61
|
-
# lang =
|
62
|
-
# param_name = 'wovn'
|
63
|
-
# return
|
64
|
-
#end
|
65
|
-
|
66
|
-
#def self.set_subdomain_lang(url, lang)
|
67
|
-
#end
|
68
|
-
|
69
|
-
#def self.remove_subdomain(url)
|
70
|
-
#end
|
71
|
-
|
72
|
-
#def self.add_subdomain(url, subdomain)
|
73
|
-
#end
|
74
|
-
#def iself.set_query_param(url, param, value)
|
75
|
-
#end
|
76
|
-
|
77
11
|
end
|
78
12
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'logger' unless defined?(Logger)
|
3
|
+
|
4
|
+
module Wovnrb
|
5
|
+
class WovnLogger
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
path = Store.instance.settings['log_path']
|
10
|
+
if path
|
11
|
+
begin
|
12
|
+
@logger = Logger.new(path)
|
13
|
+
rescue
|
14
|
+
begin
|
15
|
+
@logger = Logger.new('wovn_error.log')
|
16
|
+
@logger.error("Wovn Error: log_path(#{path}) is invalid, please change log_path at config")
|
17
|
+
rescue
|
18
|
+
@logger = $stderr
|
19
|
+
$stderr.puts("Wovn Error: log_path(#{path}) is invalid, please change log_path at config")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
@logger = $stderr
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_logger(logger)
|
28
|
+
[:error].each do |method|
|
29
|
+
unless logger.respond_to? method
|
30
|
+
raise 'not suite for logger'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@logger = logger
|
35
|
+
end
|
36
|
+
|
37
|
+
def error(message)
|
38
|
+
if @logger == $stderr
|
39
|
+
@logger.puts "Wovnrb Error: #{message}"
|
40
|
+
else
|
41
|
+
@logger.error message
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/wovnrb/store.rb
CHANGED
@@ -1,29 +1,40 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'uri'
|
3
3
|
require 'cgi'
|
4
|
-
require '
|
4
|
+
require 'singleton'
|
5
|
+
require 'wovnrb/services/wovn_logger'
|
5
6
|
|
6
7
|
module Wovnrb
|
7
|
-
|
8
8
|
class Store
|
9
|
+
include Singleton
|
9
10
|
|
10
11
|
def initialize
|
11
|
-
@settings =
|
12
|
+
@settings = {}
|
13
|
+
@config_loaded = false
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
# Reset @settings and @config_loaded variables to default.
|
18
|
+
#
|
19
|
+
# @return [nil]
|
20
|
+
def reset
|
21
|
+
@settings =
|
12
22
|
{
|
13
23
|
'user_token' => '',
|
14
24
|
'secret_key' => '',
|
15
|
-
|
16
|
-
# 'url_pattern_reg' => "?.*wovn=(?<lang>[^&]+)(&|$)",
|
25
|
+
'log_path' => 'log/wovn_error.log',
|
17
26
|
'url_pattern' => 'path',
|
18
27
|
'url_pattern_reg' => "/(?<lang>[^/.?]+)",
|
19
|
-
#'url_pattern' => 'subdomain',
|
20
|
-
#'url_pattern_reg' => "^(?<lang>[^.]+)\.",
|
21
28
|
'query' => [],
|
22
29
|
'api_url' => 'https://api.wovn.io/v0/values',
|
30
|
+
'api_timeout_seconds' => 0.5,
|
23
31
|
'default_lang' => 'en',
|
24
32
|
'supported_langs' => ['en'],
|
25
33
|
'test_mode' => false,
|
26
34
|
'test_url' => '',
|
35
|
+
'cache_megabytes' => nil,
|
36
|
+
'ttl_seconds' => nil,
|
37
|
+
'use_proxy' => false, # use env['HTTP_X_FORWARDED_HOST'] instead of env['HTTP_HOST'] and env['SERVER_NAME'] when this setting is true.
|
27
38
|
}
|
28
39
|
# When Store is initialized, the Rails.configuration object is not yet initialized
|
29
40
|
@config_loaded = false
|
@@ -65,9 +76,8 @@ module Wovnrb
|
|
65
76
|
end
|
66
77
|
# log errors
|
67
78
|
if errors.length > 0
|
68
|
-
logger = Logger.new('log/error.log')
|
69
79
|
errors.each do |e|
|
70
|
-
|
80
|
+
WovnLogger.instance.error(e)
|
71
81
|
end
|
72
82
|
end
|
73
83
|
return valid
|
@@ -117,35 +127,6 @@ module Wovnrb
|
|
117
127
|
end
|
118
128
|
@settings
|
119
129
|
end
|
120
|
-
|
121
|
-
# Get the values for the passed in url
|
122
|
-
#
|
123
|
-
# @param url [String] The url to get the values for
|
124
|
-
# @return [Hash] The values Hash for the passed in url
|
125
|
-
def get_values(url)
|
126
|
-
url = url.gsub(/\/$/, '')
|
127
|
-
|
128
|
-
begin
|
129
|
-
uri = URI.parse("#{settings['api_url']}?token=#{settings['user_token']}&url=#{url}")
|
130
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
131
|
-
http.use_ssl = true if uri.scheme == 'https'
|
132
|
-
res = http.start {
|
133
|
-
http.get(uri.request_uri)
|
134
|
-
}
|
135
|
-
if res.code == "200"
|
136
|
-
vals = JSON.parse(res.body || '{}')
|
137
|
-
else
|
138
|
-
vals = {}
|
139
|
-
end
|
140
|
-
rescue
|
141
|
-
vals = {}
|
142
|
-
logger = Logger.new('../error.log')
|
143
|
-
logger.error("API server GET request failed with the following parameters:\napi_url: #{settings['api_url']}\ntoken: #{settings['user_token']}\nurl: #{url}")
|
144
|
-
end
|
145
|
-
|
146
|
-
vals
|
147
|
-
end
|
148
|
-
|
149
130
|
end
|
150
131
|
|
151
132
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module Wovnrb
|
4
|
+
class CacheBase
|
5
|
+
@@strategy_map = {
|
6
|
+
memory: :memory_cache
|
7
|
+
}
|
8
|
+
|
9
|
+
@@default_base_config = {
|
10
|
+
strategy: :memory
|
11
|
+
}
|
12
|
+
|
13
|
+
@@singleton_cache = nil
|
14
|
+
def self.get_single
|
15
|
+
raise 'cache is not initialized' unless @@singleton_cache
|
16
|
+
@@singleton_cache
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.set_single(config)
|
20
|
+
@@singleton_cache = self.build(config)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.reset_cache
|
24
|
+
@@singleton_cache = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.build(config)
|
28
|
+
@config = @@default_base_config.merge config
|
29
|
+
|
30
|
+
strategy = @@strategy_map[@config[:strategy]]
|
31
|
+
raise "Invalid strategy: #{strategy}" unless strategy
|
32
|
+
|
33
|
+
strategy_sym = strategy.to_sym
|
34
|
+
begin
|
35
|
+
require "wovnrb/text_caches/#{strategy_sym}"
|
36
|
+
rescue LoadError => e
|
37
|
+
raise "Could not find #{strategy_sym} (#{e})"
|
38
|
+
end
|
39
|
+
|
40
|
+
strategy_class = Wovnrb.const_get(ActiveSupport::Inflector.camelize(strategy_sym))
|
41
|
+
strategy_class.new(config)
|
42
|
+
end
|
43
|
+
|
44
|
+
def put(key, value)
|
45
|
+
raise NotImplementedError.new('put is not defined')
|
46
|
+
end
|
47
|
+
|
48
|
+
def get(key)
|
49
|
+
raise NotImplementedError.new('put is not defined')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'active_support/cache'
|
2
|
+
require 'active_support/cache/memory_store'
|
3
|
+
require 'lz4-ruby'
|
4
|
+
|
5
|
+
module Wovnrb
|
6
|
+
class MemoryCache < CacheBase
|
7
|
+
@@default_memory_cache_config = {
|
8
|
+
cache_megabytes: 200,
|
9
|
+
ttl_seconds: 300
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@config = merge_setting(@@default_memory_cache_config, config)
|
14
|
+
cache_size = @config[:cache_megabytes].to_f
|
15
|
+
ttl = @config[:ttl_seconds].to_i
|
16
|
+
@cache_store = ActiveSupport::Cache::MemoryStore.new(expires_in: ttl.seconds, size: cache_size.megabytes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def put(key, value)
|
20
|
+
@cache_store.write(key, compress(value))
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(key)
|
24
|
+
stored_value =@cache_store.fetch(key)
|
25
|
+
decompress(stored_value) if stored_value
|
26
|
+
end
|
27
|
+
|
28
|
+
def options
|
29
|
+
@cache_store.options.clone
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def merge_setting(original_config, merging_config)
|
34
|
+
config = original_config.clone
|
35
|
+
config.keys.each do |key|
|
36
|
+
key_string = key.to_s
|
37
|
+
if merging_config.has_key?(key_string) && merging_config[key_string].present?
|
38
|
+
config[key] = merging_config[key_string]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
config
|
42
|
+
end
|
43
|
+
|
44
|
+
def compress(value)
|
45
|
+
LZ4::compress(value)
|
46
|
+
end
|
47
|
+
|
48
|
+
def decompress(value)
|
49
|
+
LZ4::decompress(value, value.bytesize, 'UTF-8')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/wovnrb/version.rb
CHANGED
data/lib/wovnrb.rb
CHANGED
@@ -4,21 +4,29 @@ require 'wovnrb/lang'
|
|
4
4
|
require 'nokogumbo'
|
5
5
|
#require 'dom'
|
6
6
|
require 'json'
|
7
|
-
|
7
|
+
require 'wovnrb/api_data'
|
8
|
+
require 'wovnrb/text_caches/cache_base'
|
9
|
+
require 'wovnrb/html_replacers/replacer_base'
|
10
|
+
require 'wovnrb/html_replacers/link_replacer'
|
11
|
+
require 'wovnrb/html_replacers/text_replacer'
|
12
|
+
require 'wovnrb/html_replacers/meta_replacer'
|
13
|
+
require 'wovnrb/html_replacers/image_replacer'
|
14
|
+
require 'wovnrb/html_replacers/script_replacer'
|
8
15
|
require 'wovnrb/railtie' if defined?(Rails)
|
16
|
+
require 'wovnrb/version'
|
9
17
|
|
10
18
|
module Wovnrb
|
11
|
-
|
12
19
|
class Interceptor
|
13
20
|
def initialize(app, opts={})
|
14
21
|
@app = app
|
15
|
-
@store = Store.
|
22
|
+
@store = Store.instance
|
16
23
|
opts = opts.each_with_object({}){|(k,v),memo| memo[k.to_s]=v}
|
17
24
|
@store.settings(opts)
|
25
|
+
CacheBase.set_single(@store.settings)
|
18
26
|
end
|
19
27
|
|
20
28
|
def call(env)
|
21
|
-
unless
|
29
|
+
unless Store.instance.valid_settings?
|
22
30
|
return @app.call(env)
|
23
31
|
end
|
24
32
|
@env = env
|
@@ -26,7 +34,7 @@ module Wovnrb
|
|
26
34
|
if @store.settings['test_mode'] && @store.settings['test_url'] != headers.url
|
27
35
|
return @app.call(env)
|
28
36
|
end
|
29
|
-
|
37
|
+
#redirect if the path is set to the default language (for SEO purposes)
|
30
38
|
if (headers.path_lang == @store.settings['default_lang'])
|
31
39
|
redirect_headers = headers.redirect(@store.settings['default_lang'])
|
32
40
|
return [307, redirect_headers, ['']]
|
@@ -37,7 +45,10 @@ module Wovnrb
|
|
37
45
|
status, res_headers, body = @app.call(headers.request_out)
|
38
46
|
|
39
47
|
if res_headers["Content-Type"] =~ /html/ # && !body[0].nil?
|
40
|
-
|
48
|
+
# ApiData creates request for external server, but cannot use async.
|
49
|
+
# Because some server not allow multi thread. (env['async.callback'] is not supported at all Server).
|
50
|
+
api_data = ApiData.new(headers.redis_url, @store)
|
51
|
+
values = api_data.get_data
|
41
52
|
url = {
|
42
53
|
:protocol => headers.protocol,
|
43
54
|
:host => headers.host,
|
@@ -55,86 +66,9 @@ module Wovnrb
|
|
55
66
|
#[status, res_headers, d.transform()]
|
56
67
|
end
|
57
68
|
|
58
|
-
def add_lang_code(href, pattern, lang, headers)
|
59
|
-
return href if href =~ /^(#.*)?$/
|
60
|
-
# absolute links
|
61
|
-
new_href = href
|
62
|
-
if href && href =~ /^(https?:)?\/\//i
|
63
|
-
# in the future, perhaps validate url rather than using begin rescue
|
64
|
-
# "#{url =~ /\// ? 'http:' : ''}#{url}" =~ URI::regexp
|
65
|
-
begin
|
66
|
-
uri = URI(href)
|
67
|
-
rescue
|
68
|
-
return new_href
|
69
|
-
end
|
70
|
-
# only add lang if it's an internal link
|
71
|
-
# DNS names are case insensitive
|
72
|
-
if uri.host.downcase === headers.host.downcase
|
73
|
-
case pattern
|
74
|
-
when 'subdomain'
|
75
|
-
sub_d = href.match(/\/\/([^\.]*)\./)[1]
|
76
|
-
sub_code = Lang.get_code(sub_d)
|
77
|
-
if sub_code && sub_code.downcase == lang.downcase
|
78
|
-
new_href = href.sub(Regexp.new(lang, 'i'), lang.downcase)
|
79
|
-
else
|
80
|
-
new_href = href.sub(/(\/\/)([^\.]*)/, '\1' + lang.downcase + '.' + '\2')
|
81
|
-
end
|
82
|
-
when 'query'
|
83
|
-
new_href = href =~ /\?/ ? href + '&wovn=' + lang : href + '?wovn=' + lang
|
84
|
-
else # path
|
85
|
-
new_href = href.sub(/([^\.]*\.[^\/]*)(\/|$)/, '\1/' + lang + '/')
|
86
|
-
end
|
87
|
-
end
|
88
|
-
elsif href
|
89
|
-
case pattern
|
90
|
-
when 'subdomain'
|
91
|
-
lang_url = headers.protocol + '://' + lang.downcase + '.' + headers.host
|
92
|
-
current_dir = headers.pathname.sub(/[^\/]*\.[^\.]{2,6}$/, '')
|
93
|
-
if href =~ /^\.\..*$/
|
94
|
-
# ../path
|
95
|
-
new_href = lang_url + '/' + href.gsub(/^\.\.\//, '')
|
96
|
-
elsif href =~ /^\..*$/
|
97
|
-
# ./path
|
98
|
-
new_href = lang_url + current_dir + '/' + href.gsub(/^\.\//, '')
|
99
|
-
elsif href =~ /^\/.*$/
|
100
|
-
# /path
|
101
|
-
new_href = lang_url + href
|
102
|
-
else
|
103
|
-
# path
|
104
|
-
new_href = lang_url + current_dir + '/' + href
|
105
|
-
end
|
106
|
-
when 'query'
|
107
|
-
new_href = href =~ /\?/ ? href + '&wovn=' + lang : href + '?wovn=' + lang
|
108
|
-
else # path
|
109
|
-
if href =~ /^\//
|
110
|
-
new_href = '/' + lang + href
|
111
|
-
else
|
112
|
-
current_dir = headers.pathname.sub(/[^\/]*\.[^\.]{2,6}$/, '')
|
113
|
-
new_href = '/' + lang + current_dir + href
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
new_href
|
118
|
-
end
|
119
|
-
|
120
|
-
# returns true if a wovn_ignore is found in the tree from the node to the body tag
|
121
|
-
def check_wovn_ignore(node)
|
122
|
-
if !node.get_attribute('wovn-ignore').nil?
|
123
|
-
return true
|
124
|
-
elsif node.name === 'html'
|
125
|
-
return false
|
126
|
-
end
|
127
|
-
check_wovn_ignore(node.parent)
|
128
|
-
end
|
129
|
-
|
130
69
|
def switch_lang(body, values, url, lang=@store.settings['default_lang'], headers)
|
131
|
-
|
132
|
-
lang = Lang.get_code(lang)
|
133
|
-
text_index = values['text_vals'] || {}
|
134
|
-
src_index = values['img_vals'] || {}
|
135
|
-
img_src_prefix = values['img_src_prefix'] || ''
|
70
|
+
lang = Lang.new(lang)
|
136
71
|
ignore_all = false
|
137
|
-
string_index = {}
|
138
72
|
new_body = []
|
139
73
|
body.each do |b|
|
140
74
|
d = Nokogiri::HTML5(b)
|
@@ -148,151 +82,12 @@ module Wovnrb
|
|
148
82
|
next
|
149
83
|
end
|
150
84
|
|
151
|
-
|
152
|
-
if lang != @store.settings['default_lang']
|
153
|
-
pattern = @store.settings['url_pattern']
|
154
|
-
|
155
|
-
d.xpath('//a').each do |a|
|
156
|
-
next if check_wovn_ignore(a)
|
157
|
-
href = a.get_attribute('href')
|
158
|
-
new_href = add_lang_code(href, pattern, lang, headers)
|
159
|
-
a.set_attribute('href', new_href)
|
160
|
-
end
|
161
|
-
|
162
|
-
d.xpath('//form').each do |form|
|
163
|
-
next if check_wovn_ignore(form)
|
164
|
-
method = form.get_attribute('method')
|
165
|
-
if pattern == 'query' && (method.nil? || method.upcase == 'GET')
|
166
|
-
insert_node = Nokogiri::XML::Node.new('input', d)
|
167
|
-
insert_node['type'] = 'hidden'
|
168
|
-
insert_node['name'] = 'wovn'
|
169
|
-
insert_node['value'] = lang
|
170
|
-
if form.children.size > 0
|
171
|
-
form.children.first.add_previous_sibling(insert_node)
|
172
|
-
else
|
173
|
-
form.add_child(insert_node)
|
174
|
-
end
|
175
|
-
else
|
176
|
-
action = form.get_attribute('action')
|
177
|
-
new_action = add_lang_code(action, pattern, lang, headers)
|
178
|
-
form['action'] = new_action
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# swap text
|
184
|
-
d.xpath('//text()').each do |node|
|
185
|
-
next if check_wovn_ignore(node)
|
186
|
-
node_text = node.content.strip
|
187
|
-
# shouldn't need size check, but for now...
|
188
|
-
if text_index[node_text] && text_index[node_text][lang] && text_index[node_text][lang].size > 0
|
189
|
-
node.content = node.content.gsub(/^(\s*)[\S\s]*(\s*)$/, '\1' + text_index[node_text][lang][0]['data'] + '\2')
|
190
|
-
end
|
191
|
-
end
|
192
|
-
# swap meta tag values
|
193
|
-
d.xpath('//meta').select { |t|
|
194
|
-
next if check_wovn_ignore(t)
|
195
|
-
(t.get_attribute('name') || t.get_attribute('property') || '') =~ /^(description|title|og:title|og:description|twitter:title|twitter:description)$/
|
196
|
-
}.each do |node|
|
197
|
-
node_content = node.get_attribute('content').strip
|
198
|
-
# shouldn't need size check, but for now...
|
199
|
-
if text_index[node_content] && text_index[node_content][lang] && text_index[node_content][lang].size > 0
|
200
|
-
node.set_attribute('content', node_content.gsub(/^(\s*)[\S\s]*(\s*)$/, '\1' + text_index[node_content][lang][0]['data'] + '\2'))
|
201
|
-
end
|
202
|
-
end
|
203
|
-
# swap img srcs
|
204
|
-
d.xpath('//img').each do |node|
|
205
|
-
next if check_wovn_ignore(node)
|
206
|
-
# use regular expressions to support case insensitivity (right?)
|
207
|
-
if node.to_html =~ /src=['"][^'"]*['"]/i
|
208
|
-
src = node.to_html.match(/src=['"]([^'"]*)['"]/i)[1]
|
209
|
-
# THIS SRC CORRECTION DOES NOT HANDLE ONE IMPORTANT CASE
|
210
|
-
# 1) "../path/with/ellipse"
|
211
|
-
# if this is not an absolute src
|
212
|
-
if src !~ /:\/\//
|
213
|
-
# if this is a path with a leading slash
|
214
|
-
if src =~ /^\//
|
215
|
-
src = "#{url[:protocol]}://#{url[:host]}#{src}"
|
216
|
-
else
|
217
|
-
src = "#{url[:protocol]}://#{url[:host]}#{url[:path]}#{src}"
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# shouldn't need size check, but for now...
|
222
|
-
if src_index[src] && src_index[src][lang] && src_index[src][lang].size > 0
|
223
|
-
node.attribute('src').value = "#{img_src_prefix}#{src_index[src][lang][0]['data']}"
|
224
|
-
end
|
225
|
-
end
|
226
|
-
if node.get_attribute('alt')
|
227
|
-
alt = node.get_attribute('alt').strip
|
228
|
-
if text_index[alt] && text_index[alt][lang] && text_index[alt][lang].size > 0
|
229
|
-
node.attribute('alt').value = alt.gsub(/^(\s*)[\S\s]*(\s*)$/, '\1' + text_index[alt][lang][0]['data'] + '\2')
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# REMOVE WIDGET
|
235
|
-
d.xpath('//script').each do |script_node|
|
236
|
-
if script_node['src'] && script_node['src'].include?('//j.(dev-)?wovn.io(:3000)?/')
|
237
|
-
#binding.pry
|
238
|
-
script_node.remove
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
# PARENT NODE FOR INSERTS
|
243
|
-
parent_node = d.at_css('head') || d.at_css('body') || d.at_css('html')
|
244
|
-
|
245
|
-
# INSERT BACKEND WIDGET
|
246
|
-
insert_node = Nokogiri::XML::Node.new('script', d)
|
247
|
-
# TODO: CHANGE THIS BACK; Should be '//j.wovn.io/0' in production
|
248
|
-
insert_node['src'] = '//j.wovn.io/1'
|
249
|
-
insert_node['async'] = true
|
250
|
-
#insert_node['src'] = '//j.dev-wovn.io:3000/0'
|
251
|
-
version = defined?(VERSION) ? VERSION : ''
|
252
|
-
insert_node['data-wovnio'] = "key=#{@store.settings['user_token']}&backend=true¤tLang=#{lang}&defaultLang=#{@store.settings['default_lang']}&urlPattern=#{@store.settings['url_pattern']}&version=#{version}"
|
253
|
-
# do this so that there will be a closing tag (better compatibility with browsers)
|
254
|
-
insert_node.content = ' '
|
255
|
-
if parent_node.children.size > 0
|
256
|
-
parent_node.children.first.add_previous_sibling(insert_node)
|
257
|
-
else
|
258
|
-
parent_node.add_child(insert_node)
|
259
|
-
end
|
260
|
-
|
261
|
-
|
262
|
-
# INSERT LANGUAGE METALINKS
|
263
|
-
published_langs = get_langs(values)
|
264
|
-
published_langs.each do |l|
|
265
|
-
insert_node = Nokogiri::XML::Node.new('link', d)
|
266
|
-
insert_node['rel'] = 'alternate'
|
267
|
-
insert_node['hreflang'] = l
|
268
|
-
insert_node['href'] = headers.redirect_location(l)
|
269
|
-
parent_node.add_child(insert_node)
|
270
|
-
end
|
271
|
-
|
272
|
-
# set lang property on HTML tag
|
273
|
-
if d.at_css('html') || d.at_css('HTML')
|
274
|
-
(d.at_css('html') || d.at_css('HTML')).set_attribute('lang', lang)
|
275
|
-
end
|
276
|
-
|
277
|
-
output = d.to_html.gsub(/href="([^"]*)"/) { |m| "href=\"#{URI.decode($1)}\"" }
|
85
|
+
output = lang.switch_dom_lang(d, @store, values, url, headers)
|
278
86
|
new_body.push(output)
|
279
87
|
end
|
280
88
|
body.close if body.respond_to?(:close)
|
281
89
|
new_body
|
282
|
-
#body
|
283
90
|
end
|
284
|
-
|
285
|
-
# this clearly needs to be refactored. I'm thinking maybe a Value service? (@store.values.get_langs)
|
286
|
-
def get_langs(values)
|
287
|
-
langs = Set.new
|
288
|
-
(values['text_vals'] || {}).merge(values['img_vals'] || {}).each do |key, index|
|
289
|
-
index.each do |l, val|
|
290
|
-
langs.add(l)
|
291
|
-
end
|
292
|
-
end
|
293
|
-
langs
|
294
|
-
end
|
295
|
-
|
296
91
|
end
|
297
92
|
|
298
93
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'wovnrb/text_caches/cache_base'
|
2
|
+
require 'wovnrb/text_caches/memory_cache'
|
3
|
+
require 'wovnrb/api_data'
|
4
|
+
require 'test_helper'
|
5
|
+
require 'webmock/minitest'
|
6
|
+
|
7
|
+
module Wovnrb
|
8
|
+
class MemoryCacheTest < WovnMiniTest
|
9
|
+
def setup
|
10
|
+
Wovnrb::CacheBase.set_single({})
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
Wovnrb::CacheBase.reset_cache
|
15
|
+
WebMock.reset!
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_initialize
|
19
|
+
Wovnrb::ApiData.new('http://wwww.example.com', Wovnrb::Store.instance)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_get_data
|
23
|
+
token = 'a'
|
24
|
+
url = 'url'
|
25
|
+
stub_request(:get, "https://api.wovn.io/v0/values?token=#{token}&url=#{url}").
|
26
|
+
to_return(:body => '{"test_body": "a"}')
|
27
|
+
store = Wovnrb::Store.instance
|
28
|
+
store.settings['user_token'] = token
|
29
|
+
api_data = Wovnrb::ApiData.new(url, store)
|
30
|
+
|
31
|
+
assert_equal({'test_body' => 'a'}, api_data.get_data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_data_when_cache_exists
|
35
|
+
token = 'a'
|
36
|
+
url = 'url'
|
37
|
+
stub = stub_request(:get, "https://api.wovn.io/v0/values?token=#{token}&url=#{url}").
|
38
|
+
to_return(:body => '{"test_body": "a"}')
|
39
|
+
store = Wovnrb::Store.instance
|
40
|
+
store.settings['user_token'] = token
|
41
|
+
api_data = Wovnrb::ApiData.new(url, store)
|
42
|
+
|
43
|
+
assert_equal({'test_body' => 'a'}, api_data.get_data)
|
44
|
+
assert_equal({'test_body' => 'a'}, api_data.get_data)
|
45
|
+
assert_requested(stub, :times => 1)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_get_data_fail
|
49
|
+
token = 'a'
|
50
|
+
url = 'url'
|
51
|
+
stub_request(:get, "https://api.wovn.io/v0/values?token=#{token}&url=#{url}").
|
52
|
+
to_return(:status => [500, "Internal Server Error"])
|
53
|
+
store = Wovnrb::Store.instance
|
54
|
+
store.settings['user_token'] = token
|
55
|
+
api_data = Wovnrb::ApiData.new(url, store)
|
56
|
+
log_mock = Wovnrb::LogMock.mock_log
|
57
|
+
|
58
|
+
assert_equal({}, api_data.get_data)
|
59
|
+
assert(log_mock.errors[0].start_with?('API server GET request failed'))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|