wurfl_cloud_client 1.0.1
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 +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/Rakefile +19 -0
- data/lib/wurfl_cloud.rb +49 -0
- data/lib/wurfl_cloud/cache/cookie.rb +43 -0
- data/lib/wurfl_cloud/cache/local_memory.rb +43 -0
- data/lib/wurfl_cloud/cache/memcached.rb +64 -0
- data/lib/wurfl_cloud/cache/null.rb +41 -0
- data/lib/wurfl_cloud/cache/rails.rb +56 -0
- data/lib/wurfl_cloud/client.rb +105 -0
- data/lib/wurfl_cloud/configuration.rb +88 -0
- data/lib/wurfl_cloud/device_capabilities.rb +78 -0
- data/lib/wurfl_cloud/environment.rb +48 -0
- data/lib/wurfl_cloud/errors.rb +23 -0
- data/lib/wurfl_cloud/helper.rb +21 -0
- data/lib/wurfl_cloud/rack/cache_manager.rb +54 -0
- data/lib/wurfl_cloud/rails.rb +34 -0
- data/lib/wurfl_cloud/version.rb +14 -0
- data/spec/files/generic.json +1 -0
- data/spec/files/generic_filtered.json +1 -0
- data/spec/files/lumia.json +1 -0
- data/spec/files/lumia_filtered.json +1 -0
- data/spec/files/strange_values.json +1 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/rack_helpers.rb +79 -0
- data/spec/support/string_extensions.rb +33 -0
- data/spec/wurfl_cloud/client_spec.rb +207 -0
- data/spec/wurfl_cloud/configuration_spec.rb +118 -0
- data/spec/wurfl_cloud/cookie_cache_spec.rb +51 -0
- data/spec/wurfl_cloud/device_capabilities_spec.rb +143 -0
- data/spec/wurfl_cloud/environment_spec.rb +93 -0
- data/spec/wurfl_cloud/helper_spec.rb +35 -0
- data/spec/wurfl_cloud/memcached_cache_spec.rb +122 -0
- data/spec/wurfl_cloud/null_cache_spec.rb +37 -0
- data/spec/wurfl_cloud/rack_cache_manager_spec.rb +76 -0
- data/spec/wurfl_cloud/rails_cache_spec.rb +126 -0
- data/spec/wurfl_cloud/server_request_spec.rb +45 -0
- data/wurfl_cloud_client.gemspec +38 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d04e8db9f6b0603971e674521eab47bbf65eac09
|
4
|
+
data.tar.gz: a3ca3e5922319626662c2e11b513c086c0bec125
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 98b2cdd9945df9fc89fb7f3bcaf58f7dade7121c7d2f2fb3e7f32facaf9cf1b1954679b786569fcb5000e62115b32e0f6f5061cebb580bc5995c1d44524f394b
|
7
|
+
data.tar.gz: 6c704bd2cba72ea4f0d0726ac5b1e2f98c3b3183e005acd80d024bae92bd41dd5c76807b54858eff3375507cde7e04405a85db6f51ba20a9e04e7c3bf3a868a1
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
wurfl_cloud_client (1.0.0)
|
5
|
+
json
|
6
|
+
rack
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.2.7)
|
12
|
+
crack (0.3.1)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
json (1.8.2)
|
15
|
+
multi_json (1.1.0)
|
16
|
+
rack (1.6.1)
|
17
|
+
rspec (2.8.0)
|
18
|
+
rspec-core (~> 2.8.0)
|
19
|
+
rspec-expectations (~> 2.8.0)
|
20
|
+
rspec-mocks (~> 2.8.0)
|
21
|
+
rspec-core (2.8.0)
|
22
|
+
rspec-expectations (2.8.0)
|
23
|
+
diff-lcs (~> 1.1.2)
|
24
|
+
rspec-mocks (2.8.0)
|
25
|
+
simplecov (0.6.1)
|
26
|
+
multi_json (~> 1.0)
|
27
|
+
simplecov-html (~> 0.5.3)
|
28
|
+
simplecov-html (0.5.3)
|
29
|
+
webmock (1.8.0)
|
30
|
+
addressable (>= 2.2.7)
|
31
|
+
crack (>= 0.1.7)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
rspec
|
38
|
+
simplecov
|
39
|
+
webmock
|
40
|
+
wurfl_cloud_client!
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
## Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
require 'bundler'
|
13
|
+
|
14
|
+
task 'build' do
|
15
|
+
Bundler::GemHelper.new(Dir.pwd, 'wurfl_cloud_client').build_gem
|
16
|
+
end
|
17
|
+
|
18
|
+
# Bundler::GemHelper.install_tasks
|
19
|
+
|
data/lib/wurfl_cloud.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
require 'wurfl_cloud/configuration'
|
13
|
+
require 'wurfl_cloud/cache/null'
|
14
|
+
require 'wurfl_cloud/cache/cookie'
|
15
|
+
require 'wurfl_cloud/version'
|
16
|
+
require 'wurfl_cloud/client'
|
17
|
+
require 'wurfl_cloud/environment'
|
18
|
+
require 'wurfl_cloud/device_capabilities'
|
19
|
+
require 'wurfl_cloud/errors'
|
20
|
+
|
21
|
+
module WurflCloud
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
# A configuration object. See WurflCloud::Configuration.
|
26
|
+
attr_writer :configuration
|
27
|
+
|
28
|
+
# Call this method to modify defaults in your initializers.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# WurflCloud.configure do |config|
|
32
|
+
# config.api_key = '00000000:xxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
33
|
+
# config.host = 'staging.wurflcloud.com'
|
34
|
+
# end
|
35
|
+
def configure(silent = false)
|
36
|
+
yield(configuration)
|
37
|
+
end
|
38
|
+
|
39
|
+
# The configuration object.
|
40
|
+
# @see WurflCloud.configure
|
41
|
+
def configuration
|
42
|
+
@configuration ||= WurflCloud::Configuration.new
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
require 'wurfl_cloud/rails' if defined?(Rails)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
module WurflCloud
|
13
|
+
module Cache
|
14
|
+
class Cookie
|
15
|
+
|
16
|
+
def initialize(options, environment)
|
17
|
+
@env = environment
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :mtime
|
21
|
+
|
22
|
+
def mtime
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the cache
|
27
|
+
# it's a no-op in the cookie cache
|
28
|
+
def validate(current_mtime)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Should return the value stored for the key, nil if the key is not in cache
|
32
|
+
# The Cookie cache always returns the value of the env (no matter what the key)
|
33
|
+
def [](key)
|
34
|
+
@env['wurfl.cookie.device_cache']
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets the value in the cache, for the Cookie cache it sets the env
|
38
|
+
def []=(key, value)
|
39
|
+
@env['wurfl.cookie.device_cache'] = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
module WurflCloud
|
13
|
+
module Cache
|
14
|
+
class LocalMemory
|
15
|
+
|
16
|
+
def initialize(options=nil, environment=nil)
|
17
|
+
@cache = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :mtime
|
21
|
+
|
22
|
+
def mtime
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the cache
|
27
|
+
# it's a no-op in the local memory cache
|
28
|
+
def validate(current_mtime)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Should return the value stored for the key, nil if the key is not in cache
|
32
|
+
# The LocalMemory cache stores the values in the local hash
|
33
|
+
def [](key)
|
34
|
+
@cache[key]
|
35
|
+
end
|
36
|
+
|
37
|
+
# reads the value from the local hash
|
38
|
+
def []=(key, value)
|
39
|
+
@cache[key] = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
require 'digest/md5'
|
13
|
+
module WurflCloud
|
14
|
+
module Cache
|
15
|
+
class Memcached
|
16
|
+
|
17
|
+
def initialize(options, environment)
|
18
|
+
opts = options.clone
|
19
|
+
server = opts.delete(:server)
|
20
|
+
@prefix = opts.delete(:prefix)
|
21
|
+
opts[:expires_in] ||= 86400
|
22
|
+
@memcached = Dalli::Client.new(server, opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def prefix
|
26
|
+
"#{(@prefix.nil? || @prefix=="") ? "": "#{@prefix}:"}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def key_prefix
|
30
|
+
"#{prefix}:#{mtime}:"
|
31
|
+
end
|
32
|
+
|
33
|
+
def mtime
|
34
|
+
@memcached.get("#{prefix}mtime")
|
35
|
+
end
|
36
|
+
|
37
|
+
def mtime=(new_mtime)
|
38
|
+
@memcached.set("#{prefix}mtime", new_mtime)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Validates the cache
|
42
|
+
# checks if the cache is still valid with respect to a new mtime received from the server
|
43
|
+
# if the new mtime is different from the one cached then it overwrites the current key
|
44
|
+
# prefix thus actually invalidationg the cache
|
45
|
+
def validate(current_mtime)
|
46
|
+
old_mtime = self.mtime
|
47
|
+
if !old_mtime || old_mtime!=current_mtime
|
48
|
+
self.mtime = current_mtime
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Should return the value stored for the key, nil if the key is not in cache
|
53
|
+
# The Memcached cache reads from the mamcached server
|
54
|
+
def [](key)
|
55
|
+
@memcached.get("#{key_prefix}#{Digest::MD5.hexdigest(key)}")
|
56
|
+
end
|
57
|
+
|
58
|
+
# Stores the value for the key in the mamcached server
|
59
|
+
def []=(key, value)
|
60
|
+
@memcached.set("#{key_prefix}#{Digest::MD5.hexdigest(key)}", value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
module WurflCloud
|
13
|
+
module Cache
|
14
|
+
class Null
|
15
|
+
|
16
|
+
def initialize(options, environment)
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :mtime
|
20
|
+
|
21
|
+
def mtime
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Validates the cache
|
26
|
+
# it's a no-op in the null cache
|
27
|
+
def validate(current_mtime)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Should return the value stored for the key, nil if the key is not in cache
|
31
|
+
# The Null cache always returns nil (nothing is cacheed)
|
32
|
+
def [](key)
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# It's a no-op for the Null cache
|
37
|
+
def []=(key, value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
require 'digest/md5'
|
13
|
+
module WurflCloud
|
14
|
+
module Cache
|
15
|
+
class Rails
|
16
|
+
|
17
|
+
def initialize(options, environment)
|
18
|
+
@prefix = options[:prefix]||''
|
19
|
+
end
|
20
|
+
|
21
|
+
def prefix
|
22
|
+
"#{(@prefix.nil? || @prefix=="") ? "": "#{@prefix}:"}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def key_prefix
|
26
|
+
"#{prefix}:#{mtime}:"
|
27
|
+
end
|
28
|
+
|
29
|
+
def mtime
|
30
|
+
::Rails.cache.read("#{prefix}mtime")
|
31
|
+
end
|
32
|
+
|
33
|
+
def mtime=(new_mtime)
|
34
|
+
::Rails.cache.write("#{prefix}mtime", new_mtime)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Validates the cache
|
38
|
+
# checks if the cache is still valid with respect to a new mtime received from the server
|
39
|
+
# if the new mtime is different from the one cached then it overwrites the current key
|
40
|
+
# prefix thus actually invalidationg the cache
|
41
|
+
def validate(current_mtime)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Should return the value stored for the key, nil if the key is not in cache
|
45
|
+
# The Rails cache reads from the Rails.cache object
|
46
|
+
def [](key)
|
47
|
+
::Rails.cache.read("#{key_prefix}#{Digest::MD5.hexdigest(key)}")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Stores the value for the key in the Rails.cache
|
51
|
+
def []=(key, value)
|
52
|
+
::Rails.cache.write("#{key_prefix}#{Digest::MD5.hexdigest(key)}", value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 ScientiaMobile Inc.
|
3
|
+
#
|
4
|
+
# The WURFL Cloud Client is intended to be used in both open-source and
|
5
|
+
# commercial environments. To allow its use in as many situations as possible,
|
6
|
+
# the WURFL Cloud Client is dual-licensed. You may choose to use the WURFL
|
7
|
+
# Cloud Client under either the GNU GENERAL PUBLIC LICENSE, Version 2.0, or
|
8
|
+
# the MIT License.
|
9
|
+
#
|
10
|
+
# Refer to the COPYING.txt file distributed with this package.
|
11
|
+
#
|
12
|
+
require 'wurfl_cloud/device_capabilities'
|
13
|
+
require 'net/http'
|
14
|
+
|
15
|
+
module WurflCloud
|
16
|
+
class Client
|
17
|
+
|
18
|
+
attr_reader :device_capabilities, :read_from_cache
|
19
|
+
|
20
|
+
def initialize(e, c)
|
21
|
+
@environment = e
|
22
|
+
@cache = c
|
23
|
+
@device_capabilities = WurflCloud::DeviceCapabilities.new(@environment.user_agent)
|
24
|
+
@read_from_cache = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](capability_key)
|
28
|
+
if !@device_capabilities.has_key?(capability_key) && @read_from_cache
|
29
|
+
load_from_server!
|
30
|
+
store_in_cache
|
31
|
+
end
|
32
|
+
@device_capabilities[capability_key]
|
33
|
+
end
|
34
|
+
|
35
|
+
def detect
|
36
|
+
unless load_from_cache
|
37
|
+
load_from_server!
|
38
|
+
store_in_cache
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_from_server!
|
43
|
+
begin
|
44
|
+
# make the request
|
45
|
+
Net::HTTP.start(WurflCloud.configuration.host,WurflCloud.configuration.port) do |http|
|
46
|
+
req = Net::HTTP::Get.new(WurflCloud.configuration.path, http_headers)
|
47
|
+
req.basic_auth WurflCloud.configuration.api_user, WurflCloud.configuration.api_password
|
48
|
+
res = http.request(req)
|
49
|
+
|
50
|
+
# emit the result
|
51
|
+
if res.code=="200"
|
52
|
+
# read the response
|
53
|
+
@device_capabilities = WurflCloud::DeviceCapabilities.parse(res.body)
|
54
|
+
@cache.validate(@device_capabilities.mtime.to_i)
|
55
|
+
@read_from_cache = false
|
56
|
+
else
|
57
|
+
raise WurflCloud::Errors::ConnectionError.new("#{res.code} #{res.message}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue WurflCloud::Errors::GenericError => exc
|
61
|
+
raise exc
|
62
|
+
rescue Exception => exc
|
63
|
+
raise WurflCloud::Errors::ConnectionError.new(exc.message)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class << self
|
68
|
+
|
69
|
+
def detect_device(environment, cache)
|
70
|
+
new(environment, cache).tap do |client|
|
71
|
+
client.detect
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def http_headers
|
80
|
+
{
|
81
|
+
'X-Cloud-Client' => "WurflCloudClient/Ruby_#{WurflCloud::VERSION}",
|
82
|
+
"Content-Type"=>"application/json; charset=UTF-8"
|
83
|
+
}.tap do |h|
|
84
|
+
h['User-Agent'] = @environment.user_agent unless @environment.user_agent.nil?
|
85
|
+
h['X-Forwarded-For'] = @environment.x_forwarded_for unless @environment.x_forwarded_for.nil?
|
86
|
+
h['X-Accept'] = @environment.x_accept unless @environment.x_accept.nil?
|
87
|
+
h['X-Wap-Profile'] = @environment.x_wap_profile unless @environment.x_wap_profile.nil?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def store_in_cache
|
92
|
+
@cache[@environment.user_agent] = @device_capabilities.to_hash
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_from_cache
|
96
|
+
if cached_capabilities = @cache[@environment.user_agent]
|
97
|
+
@device_capabilities = WurflCloud::DeviceCapabilities.new(@environment.user_agent)
|
98
|
+
@device_capabilities.merge(cached_capabilities)
|
99
|
+
@read_from_cache = true
|
100
|
+
return true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|