webdrivers 3.9.1 → 3.9.2

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.
@@ -1,6 +1,6 @@
1
- # frozen_string_literal: true
2
-
3
- require 'webdrivers/chromedriver'
4
- require 'webdrivers/geckodriver'
5
- require 'webdrivers/iedriver'
6
- require 'webdrivers/mswebdriver'
1
+ # frozen_string_literal: true
2
+
3
+ require 'webdrivers/chromedriver'
4
+ require 'webdrivers/geckodriver'
5
+ require 'webdrivers/iedriver'
6
+ require 'webdrivers/mswebdriver'
@@ -1,167 +1,185 @@
1
- # frozen_string_literal: true
2
-
3
- require 'shellwords'
4
- require 'webdrivers/common'
5
-
6
- module Webdrivers
7
- class Chromedriver < Common
8
- class << self
9
- def current_version
10
- Webdrivers.logger.debug 'Checking current version'
11
- return nil unless exists?
12
-
13
- version = binary_version
14
- return nil if version.nil?
15
-
16
- # Matches 2.46, 2.46.628411 and 73.0.3683.75
17
- normalize_version version[/\d+\.\d+(\.\d+)?(\.\d+)?/]
18
- end
19
-
20
- def latest_version
21
- @latest_version ||= begin
22
- # Versions before 70 do not have a LATEST_RELEASE file
23
- return normalize_version('2.41') if release_version < normalize_version('70')
24
-
25
- latest_applicable = with_cache(file_name) { latest_point_release(release_version) }
26
-
27
- Webdrivers.logger.debug "Latest version available: #{latest_applicable}"
28
- normalize_version(latest_applicable)
29
- end
30
- end
31
-
32
- # Returns currently installed Chrome version
33
- def chrome_version
34
- ver = send("chrome_on_#{System.platform}").chomp
35
-
36
- raise VersionError, 'Failed to find Chrome binary or its version.' if ver.nil? || ver.empty?
37
-
38
- Webdrivers.logger.debug "Browser version: #{ver}"
39
- normalize_version ver[/\d+\.\d+\.\d+\.\d+/] # Google Chrome 73.0.3683.75 -> 73.0.3683.75
40
- end
41
-
42
- private
43
-
44
- def latest_point_release(version)
45
- release_file = "LATEST_RELEASE_#{version}"
46
- begin
47
- normalize_version(Network.get(URI.join(base_url, release_file)))
48
- rescue StandardError
49
- latest_release = normalize_version(Network.get(URI.join(base_url, 'LATEST_RELEASE')))
50
- Webdrivers.logger.debug "Unable to find a driver for: #{version}"
51
-
52
- msg = version > latest_release ? 'you appear to be using a non-production version of Chrome; ' : ''
53
- msg = "#{msg}please set `Webdrivers::Chromedriver.required_version = <desired driver version>` to an known "\
54
- 'chromedriver version: https://chromedriver.storage.googleapis.com/index.html'
55
- raise VersionError, msg
56
- end
57
- end
58
-
59
- def file_name
60
- System.platform == 'win' ? 'chromedriver.exe' : 'chromedriver'
61
- end
62
-
63
- def base_url
64
- 'https://chromedriver.storage.googleapis.com'
65
- end
66
-
67
- def download_url
68
- return @download_url if @download_url
69
-
70
- version = if required_version == EMPTY_VERSION
71
- latest_version
72
- else
73
- normalize_version(required_version)
74
- end
75
-
76
- file_name = System.platform == 'win' ? 'win32' : "#{System.platform}64"
77
- url = "#{base_url}/#{version}/chromedriver_#{file_name}.zip"
78
- Webdrivers.logger.debug "chromedriver URL: #{url}"
79
- @download_url = url
80
- end
81
-
82
- # Returns release version from the currently installed Chrome version
83
- #
84
- # @example
85
- # 73.0.3683.75 -> 73.0.3683
86
- def release_version
87
- chrome = normalize_version(chrome_version)
88
- normalize_version(chrome.segments[0..2].join('.'))
89
- end
90
-
91
- def chrome_on_win
92
- if browser_binary
93
- Webdrivers.logger.debug "Browser executable: '#{browser_binary}'"
94
- return System.call("powershell (Get-ItemProperty '#{browser_binary}').VersionInfo.ProductVersion").strip
95
- end
96
-
97
- # Workaround for Google Chrome when using Jruby on Windows.
98
- # @see https://github.com/titusfortner/webdrivers/issues/41
99
- if RUBY_PLATFORM == 'java'
100
- ver = 'powershell (Get-Item -Path ((Get-ItemProperty "HKLM:\\Software\\Microsoft' \
101
- "\\Windows\\CurrentVersion\\App` Paths\\chrome.exe\").\\'(default)\\'))" \
102
- '.VersionInfo.ProductVersion'
103
- return System.call(ver).strip
104
- end
105
-
106
- # Default to Google Chrome
107
- reg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe'
108
- executable = System.call("powershell (Get-ItemProperty '#{reg}' -Name '(default)').'(default)'").strip
109
- Webdrivers.logger.debug "Browser executable: '#{executable}'"
110
- ps = "(Get-Item (Get-ItemProperty '#{reg}').'(default)').VersionInfo.ProductVersion"
111
- System.call("powershell #{ps}").strip
112
- end
113
-
114
- def chrome_on_linux
115
- if browser_binary
116
- Webdrivers.logger.debug "Browser executable: '#{browser_binary}'"
117
- return System.call("#{Shellwords.escape browser_binary} --product-version").strip
118
- end
119
-
120
- # Default to Google Chrome
121
- executable = System.call('which google-chrome').strip
122
- Webdrivers.logger.debug "Browser executable: '#{executable}'"
123
- System.call("#{executable} --product-version").strip
124
- end
125
-
126
- def chrome_on_mac
127
- if browser_binary
128
- Webdrivers.logger.debug "Browser executable: '#{browser_binary}'"
129
- return System.call("#{Shellwords.escape browser_binary} --version").strip
130
- end
131
-
132
- # Default to Google Chrome
133
- executable = Shellwords.escape '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
134
- Webdrivers.logger.debug "Browser executable: #{executable}"
135
- System.call("#{executable} --version").strip
136
- end
137
-
138
- #
139
- # Returns user defined browser executable path from Selenium::WebDrivers::Chrome#path.
140
- #
141
- def browser_binary
142
- # For Chromium, Brave, or whatever else
143
- Selenium::WebDriver::Chrome.path
144
- end
145
-
146
- def sufficient_binary?
147
- super && current_version && (current_version < normalize_version('70.0.3538') ||
148
- current_version.segments.first == release_version.segments.first)
149
- end
150
- end
151
- end
152
- end
153
-
154
- if ::Selenium::WebDriver::Service.respond_to? :driver_path=
155
- ::Selenium::WebDriver::Chrome::Service.driver_path = proc { ::Webdrivers::Chromedriver.update }
156
- else
157
- # v3.141.0 and lower
158
- module Selenium
159
- module WebDriver
160
- module Chrome
161
- def self.driver_path
162
- @driver_path ||= Webdrivers::Chromedriver.update
163
- end
164
- end
165
- end
166
- end
167
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require 'webdrivers/common'
5
+
6
+ module Webdrivers
7
+ class Chromedriver < Common
8
+ class << self
9
+ #
10
+ # Returns current chromedriver version.
11
+ #
12
+ # @return [Gem::Version]
13
+ def current_version
14
+ Webdrivers.logger.debug 'Checking current version'
15
+ return nil unless exists?
16
+
17
+ version = binary_version
18
+ return nil if version.nil?
19
+
20
+ # Matches 2.46, 2.46.628411 and 73.0.3683.75
21
+ normalize_version version[/\d+\.\d+(\.\d+)?(\.\d+)?/]
22
+ end
23
+
24
+ #
25
+ # Returns latest available chromedriver version.
26
+ #
27
+ # @return [Gem::Version]
28
+ def latest_version
29
+ @latest_version ||= begin
30
+ # Versions before 70 do not have a LATEST_RELEASE file
31
+ return normalize_version('2.41') if release_version < normalize_version('70')
32
+
33
+ latest_applicable = with_cache(file_name) { latest_point_release(release_version) }
34
+
35
+ Webdrivers.logger.debug "Latest version available: #{latest_applicable}"
36
+ normalize_version(latest_applicable)
37
+ end
38
+ end
39
+
40
+ #
41
+ # Returns currently installed Chrome/Chromium version.
42
+ #
43
+ # @return [Gem::Version]
44
+ def chrome_version
45
+ ver = send("chrome_on_#{System.platform}").chomp
46
+
47
+ raise VersionError, 'Failed to find Chrome binary or its version.' if ver.nil? || ver.empty?
48
+
49
+ Webdrivers.logger.debug "Browser version: #{ver}"
50
+ normalize_version ver[/\d+\.\d+\.\d+\.\d+/] # Google Chrome 73.0.3683.75 -> 73.0.3683.75
51
+ end
52
+
53
+ private
54
+
55
+ def latest_point_release(version)
56
+ normalize_version(Network.get(URI.join(base_url, "LATEST_RELEASE_#{version}")))
57
+ rescue NetworkError
58
+ msg = "Unable to find latest point release version for #{version}."
59
+ msg = begin
60
+ latest_release = normalize_version(Network.get(URI.join(base_url, 'LATEST_RELEASE')))
61
+ if version > latest_release
62
+ "#{msg} You appear to be using a non-production version of Chrome."
63
+ else
64
+ msg
65
+ end
66
+ rescue NetworkError
67
+ "#{msg} A network issue is preventing determination of latest chromedriver release."
68
+ end
69
+
70
+ msg = "#{msg} Please set `Webdrivers::Chromedriver.required_version = <desired driver version>` "\
71
+ 'to a known chromedriver version: https://chromedriver.storage.googleapis.com/index.html'
72
+
73
+ Webdrivers.logger.debug msg
74
+ raise VersionError, msg
75
+ end
76
+
77
+ def file_name
78
+ System.platform == 'win' ? 'chromedriver.exe' : 'chromedriver'
79
+ end
80
+
81
+ def base_url
82
+ 'https://chromedriver.storage.googleapis.com'
83
+ end
84
+
85
+ def download_url
86
+ return @download_url if @download_url
87
+
88
+ version = if required_version == EMPTY_VERSION
89
+ latest_version
90
+ else
91
+ normalize_version(required_version)
92
+ end
93
+
94
+ file_name = System.platform == 'win' ? 'win32' : "#{System.platform}64"
95
+ url = "#{base_url}/#{version}/chromedriver_#{file_name}.zip"
96
+ Webdrivers.logger.debug "chromedriver URL: #{url}"
97
+ @download_url = url
98
+ end
99
+
100
+ # Returns release version from the currently installed Chrome version
101
+ #
102
+ # @example
103
+ # 73.0.3683.75 -> 73.0.3683
104
+ def release_version
105
+ chrome = normalize_version(chrome_version)
106
+ normalize_version(chrome.segments[0..2].join('.'))
107
+ end
108
+
109
+ def chrome_on_win
110
+ if browser_binary
111
+ Webdrivers.logger.debug "Browser executable: '#{browser_binary}'"
112
+ return System.call("powershell (Get-ItemProperty '#{browser_binary}').VersionInfo.ProductVersion").strip
113
+ end
114
+
115
+ # Workaround for Google Chrome when using Jruby on Windows.
116
+ # @see https://github.com/titusfortner/webdrivers/issues/41
117
+ if RUBY_PLATFORM == 'java'
118
+ ver = 'powershell (Get-Item -Path ((Get-ItemProperty "HKLM:\\Software\\Microsoft' \
119
+ "\\Windows\\CurrentVersion\\App` Paths\\chrome.exe\").\\'(default)\\'))" \
120
+ '.VersionInfo.ProductVersion'
121
+ return System.call(ver).strip
122
+ end
123
+
124
+ # Default to Google Chrome
125
+ reg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe'
126
+ executable = System.call("powershell (Get-ItemProperty '#{reg}' -Name '(default)').'(default)'").strip
127
+ Webdrivers.logger.debug "Browser executable: '#{executable}'"
128
+ ps = "(Get-Item (Get-ItemProperty '#{reg}').'(default)').VersionInfo.ProductVersion"
129
+ System.call("powershell #{ps}").strip
130
+ end
131
+
132
+ def chrome_on_linux
133
+ if browser_binary
134
+ Webdrivers.logger.debug "Browser executable: '#{browser_binary}'"
135
+ return System.call("#{Shellwords.escape browser_binary} --product-version").strip
136
+ end
137
+
138
+ # Default to Google Chrome
139
+ executable = System.call('which google-chrome').strip
140
+ Webdrivers.logger.debug "Browser executable: '#{executable}'"
141
+ System.call("#{executable} --product-version").strip
142
+ end
143
+
144
+ def chrome_on_mac
145
+ if browser_binary
146
+ Webdrivers.logger.debug "Browser executable: '#{browser_binary}'"
147
+ return System.call("#{Shellwords.escape browser_binary} --version").strip
148
+ end
149
+
150
+ # Default to Google Chrome
151
+ executable = Shellwords.escape '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
152
+ Webdrivers.logger.debug "Browser executable: #{executable}"
153
+ System.call("#{executable} --version").strip
154
+ end
155
+
156
+ #
157
+ # Returns user defined browser executable path from Selenium::WebDrivers::Chrome#path.
158
+ #
159
+ def browser_binary
160
+ # For Chromium, Brave, or whatever else
161
+ Selenium::WebDriver::Chrome.path
162
+ end
163
+
164
+ def sufficient_binary?
165
+ super && current_version && (current_version < normalize_version('70.0.3538') ||
166
+ current_version.segments.first == release_version.segments.first)
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
173
+ ::Selenium::WebDriver::Chrome::Service.driver_path = proc { ::Webdrivers::Chromedriver.update }
174
+ else
175
+ # v3.141.0 and lower
176
+ module Selenium
177
+ module WebDriver
178
+ module Chrome
179
+ def self.driver_path
180
+ @driver_path ||= Webdrivers::Chromedriver.update
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -1,157 +1,191 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rubygems/package'
4
- require 'zip'
5
- require 'webdrivers/logger'
6
- require 'webdrivers/network'
7
- require 'webdrivers/system'
8
- require 'selenium-webdriver'
9
-
10
- module Webdrivers
11
- class ConnectionError < StandardError
12
- end
13
-
14
- class VersionError < StandardError
15
- end
16
-
17
- class << self
18
- attr_accessor :proxy_addr, :proxy_port, :proxy_user, :proxy_pass, :install_dir
19
-
20
- attr_writer :cache_time
21
-
22
- def cache_time
23
- @cache_time || 0
24
- end
25
-
26
- def logger
27
- @logger ||= Webdrivers::Logger.new
28
- end
29
-
30
- def configure
31
- yield self
32
- end
33
-
34
- def net_http_ssl_fix
35
- raise 'Webdrivers.net_http_ssl_fix is no longer available.' \
36
- ' Please see https://github.com/titusfortner/webdrivers#ssl_connect-errors.'
37
- end
38
- end
39
-
40
- class Common
41
- class << self
42
- attr_writer :required_version
43
- attr_reader :cache_warning
44
-
45
- def version
46
- Webdrivers.logger.deprecate("#{self.class}#version", "#{self.class}#required_version")
47
- required_version
48
- end
49
-
50
- def version=(version)
51
- Webdrivers.logger.deprecate("#{self.class}#version=", "#{self.class}#required_version=")
52
- self.required_version = version
53
- end
54
-
55
- def required_version
56
- normalize_version @required_version
57
- end
58
-
59
- def update
60
- if correct_binary?
61
- Webdrivers.logger.debug 'The required webdriver version is already on the system'
62
- return driver_path
63
- end
64
-
65
- remove
66
- System.download(download_url, driver_path)
67
- end
68
-
69
- def desired_version
70
- old = "#{self.class}#desired_version"
71
- new = "#{self.class}#required_version or #{self.class}#latest_version"
72
- Webdrivers.logger.deprecate(old, new)
73
-
74
- desired_version == EMPTY_VERSION ? latest_version : normalize_version(desired_version)
75
- end
76
-
77
- def remove
78
- @download_url = nil
79
- @latest_version = nil
80
- System.delete "#{System.install_dir}/#{file_name.gsub('.exe', '')}.version"
81
- System.delete driver_path
82
- end
83
-
84
- def download
85
- Webdrivers.logger.deprecate('#download', '#update')
86
- System.download(download_url, driver_path)
87
- end
88
-
89
- def binary
90
- Webdrivers.logger.deprecate('#binary', '#driver_path')
91
- driver_path
92
- end
93
-
94
- def driver_path
95
- File.join System.install_dir, file_name
96
- end
97
-
98
- private
99
-
100
- def download_url
101
- @download_url ||= if required_version == EMPTY_VERSION
102
- downloads[downloads.keys.max]
103
- else
104
- downloads[normalize_version(required_version)]
105
- end
106
- end
107
-
108
- def exists?
109
- System.exists? driver_path
110
- end
111
-
112
- def correct_binary?
113
- current_version == if required_version == EMPTY_VERSION
114
- latest_version
115
- else
116
- normalize_version(required_version)
117
- end
118
- rescue ConnectionError
119
- driver_path if sufficient_binary?
120
- end
121
-
122
- def sufficient_binary?
123
- exists?
124
- end
125
-
126
- def normalize_version(version)
127
- Gem::Version.new(version.to_s)
128
- end
129
-
130
- def binary_version
131
- version = System.call("#{driver_path} --version")
132
- Webdrivers.logger.debug "Current version of #{driver_path} is #{version}"
133
- version
134
- rescue Errno::ENOENT
135
- Webdrivers.logger.debug "No Such File or Directory: #{driver_path}"
136
- nil
137
- end
138
-
139
- def with_cache(file_name)
140
- if System.valid_cache?(file_name)
141
- normalize_version System.cached_version(file_name)
142
- else
143
- unless cache_warning
144
- Webdrivers.logger.warn 'Driver caching is turned off in this version, but will be '\
145
- 'enabled by default in 4.x. Set the value with `Webdrivers#cache_time=` in seconds'
146
- @cache_warning = true
147
- end
148
- version = yield
149
- System.cache_version(file_name, version)
150
- normalize_version version
151
- end
152
- end
153
-
154
- EMPTY_VERSION = Gem::Version.new('')
155
- end
156
- end
157
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems/package'
4
+ require 'zip'
5
+ require 'webdrivers/logger'
6
+ require 'webdrivers/network'
7
+ require 'webdrivers/system'
8
+ require 'selenium-webdriver'
9
+
10
+ module Webdrivers
11
+ class ConnectionError < StandardError
12
+ end
13
+
14
+ class VersionError < StandardError
15
+ end
16
+
17
+ class NetworkError < StandardError
18
+ end
19
+
20
+ class << self
21
+ attr_accessor :proxy_addr, :proxy_port, :proxy_user, :proxy_pass, :install_dir
22
+
23
+ attr_writer :cache_time
24
+
25
+ #
26
+ # Returns the amount of time (Seconds) the gem waits between two update checks.
27
+ #
28
+ def cache_time
29
+ @cache_time || 0
30
+ end
31
+
32
+ def logger
33
+ @logger ||= Webdrivers::Logger.new
34
+ end
35
+
36
+ #
37
+ # Provides a convenient way to configure the gem.
38
+ #
39
+ # @example Configure proxy and cache_time
40
+ # Webdrivers.configure do |config|
41
+ # config.proxy_addr = 'myproxy_address.com'
42
+ # config.proxy_port = '8080'
43
+ # config.proxy_user = 'username'
44
+ # config.proxy_pass = 'password'
45
+ # config.cache_time = 604_800 # 7 days
46
+ # end
47
+ #
48
+ def configure
49
+ yield self
50
+ end
51
+
52
+ def net_http_ssl_fix
53
+ raise 'Webdrivers.net_http_ssl_fix is no longer available.' \
54
+ ' Please see https://github.com/titusfortner/webdrivers#ssl_connect-errors.'
55
+ end
56
+ end
57
+
58
+ class Common
59
+ class << self
60
+ attr_writer :required_version
61
+ attr_reader :cache_warning
62
+
63
+ def version
64
+ Webdrivers.logger.deprecate("#{self.class}#version", "#{self.class}#required_version")
65
+ required_version
66
+ end
67
+
68
+ def version=(version)
69
+ Webdrivers.logger.deprecate("#{self.class}#version=", "#{self.class}#required_version=")
70
+ self.required_version = version
71
+ end
72
+
73
+ #
74
+ # Returns the user defined required version.
75
+ #
76
+ # @return [Gem::Version]
77
+ def required_version
78
+ normalize_version @required_version
79
+ end
80
+
81
+ #
82
+ # Triggers an update check.
83
+ #
84
+ # @return [String] Path to the driver binary.
85
+ def update
86
+ if correct_binary?
87
+ msg = required_version != EMPTY_VERSION ? 'The required webdriver version' : 'A working webdriver version'
88
+ Webdrivers.logger.debug "#{msg} is already on the system"
89
+ return driver_path
90
+ end
91
+
92
+ remove
93
+ System.download(download_url, driver_path)
94
+ end
95
+
96
+ def desired_version
97
+ old = "#{self.class}#desired_version"
98
+ new = "#{self.class}#required_version or #{self.class}#latest_version"
99
+ Webdrivers.logger.deprecate(old, new)
100
+
101
+ desired_version == EMPTY_VERSION ? latest_version : normalize_version(desired_version)
102
+ end
103
+
104
+ #
105
+ # Deletes the existing driver binary.
106
+ #
107
+ def remove
108
+ @download_url = nil
109
+ @latest_version = nil
110
+ System.delete "#{System.install_dir}/#{file_name.gsub('.exe', '')}.version"
111
+ System.delete driver_path
112
+ end
113
+
114
+ def download
115
+ Webdrivers.logger.deprecate('#download', '#update')
116
+ System.download(download_url, driver_path)
117
+ end
118
+
119
+ def binary
120
+ Webdrivers.logger.deprecate('#binary', '#driver_path')
121
+ driver_path
122
+ end
123
+
124
+ #
125
+ # Returns path to the driver binary.
126
+ #
127
+ # @return [String]
128
+ def driver_path
129
+ File.join System.install_dir, file_name
130
+ end
131
+
132
+ private
133
+
134
+ def download_url
135
+ @download_url ||= if required_version == EMPTY_VERSION
136
+ downloads[downloads.keys.max]
137
+ else
138
+ downloads[normalize_version(required_version)]
139
+ end
140
+ end
141
+
142
+ def exists?
143
+ System.exists? driver_path
144
+ end
145
+
146
+ def correct_binary?
147
+ current_version == if required_version == EMPTY_VERSION
148
+ latest_version
149
+ else
150
+ normalize_version(required_version)
151
+ end
152
+ rescue ConnectionError, VersionError
153
+ driver_path if sufficient_binary?
154
+ end
155
+
156
+ def sufficient_binary?
157
+ exists?
158
+ end
159
+
160
+ def normalize_version(version)
161
+ Gem::Version.new(version.to_s)
162
+ end
163
+
164
+ def binary_version
165
+ version = System.call("#{driver_path} --version")
166
+ Webdrivers.logger.debug "Current version of #{driver_path} is #{version}"
167
+ version
168
+ rescue Errno::ENOENT
169
+ Webdrivers.logger.debug "No Such File or Directory: #{driver_path}"
170
+ nil
171
+ end
172
+
173
+ def with_cache(file_name)
174
+ if System.valid_cache?(file_name)
175
+ normalize_version System.cached_version(file_name)
176
+ else
177
+ unless cache_warning
178
+ Webdrivers.logger.warn 'Driver caching is turned off in this version, but will be '\
179
+ 'enabled by default in 4.x. Set the value with `Webdrivers#cache_time=` in seconds'
180
+ @cache_warning = true
181
+ end
182
+ version = yield
183
+ System.cache_version(file_name, version)
184
+ normalize_version version
185
+ end
186
+ end
187
+
188
+ EMPTY_VERSION = Gem::Version.new('')
189
+ end
190
+ end
191
+ end