Module: U3d::Utils
- Defined in:
- lib/u3d/utils.rb
Overview
Several different utility methods rubocop:disable Metrics/ModuleLength
Constant Summary collapse
- CSIDL_LOCAL_APPDATA =
Regex to capture each part of a version string (0.0.0x0)
0x001c
- UNITY_VERSION_REGEX =
/(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:(\w)(?:(\d+))?)?/.freeze
Class Method Summary collapse
-
.download_file(path, url, size: nil) ⇒ Object
size a hint of the expected size.
- .ensure_dir(dir) ⇒ Object
- .file_exists_not_empty?(path) ⇒ Boolean
- .final_url(url, redirect_limit: 10) ⇒ Object
- .follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block) ⇒ Object
-
.get_ssl(url, redirect_limit: 10, request_headers: {}) ⇒ Object
FIXME: alias deprecated.
- .get_url_content_length(url) ⇒ Object
- .get_write_access(dir) ⇒ Object
- .hashfile(file_path, blocksize: 65_536) ⇒ Object
- .http_request_class(method, uri) ⇒ Object
- .page_content(url, redirect_limit: 10, request_headers: {}) ⇒ Object
- .parse_unity_version(version) ⇒ Object
- .pretty_filesize(filesize) ⇒ Object
-
.print_progress(current, total, started_at) ⇒ Object
if total is nil (unknown, falls back to print_progress_nosize).
- .print_progress_nosize(current, started_at) ⇒ Object
-
.strings(path) ⇒ Object
Ruby implementation of binutils strings.
- .windows_fileversion(info_key, path) ⇒ Object
- .windows_local_appdata ⇒ Object
- .windows_path(path) ⇒ Object
Class Method Details
.download_file(path, url, size: nil) ⇒ Object
size a hint of the expected size
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/u3d/utils.rb', line 94 def download_file(path, url, size: nil) File.open(path, 'wb') do |f| uri = URI(url) current = 0 last_print_update = 0 print_progress = UI.interactive? || U3dCore::Globals.verbose? Net::HTTP.start(uri.host, uri.port, http_opts(use_ssl: uri.scheme == 'https')) do |http| request = Net::HTTP::Get.new uri http.request request do |response| begin # override with actual results, this should help with # innacurrate declared sizes, especially on Windows platform size = Integer(response['Content-Length']) rescue ArgumentError UI.verbose 'Unable to get length of file in download' end started_at = Time.now.to_i - 1 response.read_body do |segment| f.write(segment) current += segment.length # wait for Net::HTTP buffer on slow networks # FIXME revisits, this slows down download on fast network # sleep 0.08 # adjust to reduce CPU next unless print_progress print_progress_now = Time.now.to_f - last_print_update > 0.5 # force printing when done downloading print_progress_now = true if !print_progress_now && size && current >= size next unless print_progress_now last_print_update = Time.now.to_f Utils.print_progress(current, size, started_at) print "\n" unless UI.interactive? end end end print "\n" if print_progress end end |
.ensure_dir(dir) ⇒ Object
157 158 159 |
# File 'lib/u3d/utils.rb', line 157 def ensure_dir(dir) FileUtils.mkpath(dir) unless File.directory?(dir) end |
.file_exists_not_empty?(path) ⇒ Boolean
318 319 320 |
# File 'lib/u3d/utils.rb', line 318 def file_exists_not_empty?(path) File.file?(path) && File.size(path).positive? end |
.final_url(url, redirect_limit: 10) ⇒ Object
39 40 41 42 43 |
# File 'lib/u3d/utils.rb', line 39 def final_url(url, redirect_limit: 10) follow_redirects(url, redirect_limit: redirect_limit, http_method: :head) do |request, _response| request.uri.to_s end end |
.follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/u3d/utils.rb', line 56 def follow_redirects(url, redirect_limit: 10, http_method: :get, request_headers: {}, &block) raise 'Too many redirections' if redirect_limit.zero? response = nil request = nil uri = URI(url) begin use_ssl = /^https/.match(url) Net::HTTP.start(uri.host, uri.port, http_opts(use_ssl: use_ssl)) do |http| request = http_request_class http_method, uri request_headers.each do |k, v| request[k] = v end response = http.request request end rescue OpenSSL::OpenSSLError => e UI.error 'SSL has faced an error, you may want to check our README to fix it' raise e end case response when Net::HTTPSuccess yield(request, response) when Net::HTTPRedirection UI.verbose "Redirected to #{response['location']}" follow_redirects(response['location'], redirect_limit: redirect_limit - 1, http_method: http_method, request_headers: request_headers, &block) else raise "Request failed with status #{response.code}" end end |
.get_ssl(url, redirect_limit: 10, request_headers: {}) ⇒ Object
FIXME: alias deprecated
46 47 48 |
# File 'lib/u3d/utils.rb', line 46 def get_ssl(url, redirect_limit: 10, request_headers: {}) page_content(url, redirect_limit: redirect_limit, request_headers: request_headers) end |
.get_url_content_length(url) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/u3d/utils.rb', line 134 def get_url_content_length(url) UI.verbose "get_url_content_length #{url}" uri = URI(url) size = nil Net::HTTP.start(uri.host, uri.port, http_opts) do |http| response = http.request_head url size = Integer(response['Content-Length']) end UI.verbose "get_url_content_length #{url}: #{size}" size end |
.get_write_access(dir) ⇒ Object
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/u3d/utils.rb', line 161 def get_write_access(dir) if U3dCore::Helper. == :win yield else stat_command = case U3dCore::Helper. when :linux "stat -c \"%U,%a\" #{dir}" when :mac "stat -f \"%Su,%A\" #{dir}" end owner, access = U3dCore::CommandExecutor.execute(command: stat_command, admin: false).strip.split(',') current_user = U3dCore::CommandExecutor.execute(command: 'whoami', admin: false) U3dCore::CommandExecutor.execute(command: "chown #{current_user}: #{dir}", admin: true) U3dCore::CommandExecutor.execute(command: "chmod u+w #{dir}", admin: true) begin yield ensure U3dCore::CommandExecutor.execute(command: "chown #{owner}: #{dir}", admin: true) U3dCore::CommandExecutor.execute(command: "chmod #{access} #{dir}", admin: true) end end end |
.hashfile(file_path, blocksize: 65_536) ⇒ Object
146 147 148 149 150 151 152 153 154 155 |
# File 'lib/u3d/utils.rb', line 146 def hashfile(file_path, blocksize: 65_536) require 'digest' raise ArgumentError, 'Not a file' unless File.file?(file_path) md5 = Digest::MD5.new File.open(file_path, 'r') do |f| md5 << f.read(blocksize) until f.eof? end md5.hexdigest end |
.http_request_class(method, uri) ⇒ Object
86 87 88 89 90 91 |
# File 'lib/u3d/utils.rb', line 86 def http_request_class(method, uri) return Net::HTTP::Get.new uri if method == :get return Net::HTTP::Head.new uri if method == :head raise "Unknown method #{method}" end |
.page_content(url, redirect_limit: 10, request_headers: {}) ⇒ Object
50 51 52 53 54 |
# File 'lib/u3d/utils.rb', line 50 def page_content(url, redirect_limit: 10, request_headers: {}) follow_redirects(url, redirect_limit: redirect_limit, request_headers: request_headers) do |_request, response| response.body end end |
.parse_unity_version(version) ⇒ Object
206 207 208 209 210 211 212 213 |
# File 'lib/u3d/utils.rb', line 206 def parse_unity_version(version) ver = UNITY_VERSION_REGEX.match(version) if ver.nil? raise ArgumentError, "Version (#{version}) does not match the Unity "\ 'version format 0.0.0x0' end [ver[1], ver[2], ver[3], ver[4], ver[5]] end |
.pretty_filesize(filesize) ⇒ Object
289 290 291 |
# File 'lib/u3d/utils.rb', line 289 def pretty_filesize(filesize) Filesize.from("#{filesize.round} B").pretty end |
.print_progress(current, total, started_at) ⇒ Object
if total is nil (unknown, falls back to print_progress_nosize)
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/u3d/utils.rb', line 185 def print_progress(current, total, started_at) if total.nil? print_progress_nosize(current, started_at) return end ratio = [current.to_f / total, 1.0].min percent = (ratio * 100.0).round(1) arrow = (ratio * 20.0).floor time_spent = Time.now.to_i - started_at print("\r[") print('=' * [arrow - 1, 0].max) print('>') print('.' * (20 - arrow)) print("] #{pretty_filesize(current)}/#{pretty_filesize(total)} (#{percent}% at #{pretty_filesize(current.to_f / time_spent)}/s) ") end |
.print_progress_nosize(current, started_at) ⇒ Object
201 202 203 204 |
# File 'lib/u3d/utils.rb', line 201 def print_progress_nosize(current, started_at) time_spent = Time.now.to_i - started_at print("\r>#{pretty_filesize(current)} downloaded at #{pretty_filesize(current.to_f / time_spent)}/s) ") end |
.strings(path) ⇒ Object
Ruby implementation of binutils strings
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/u3d/utils.rb', line 299 def strings(path) min = 4 Enumerator.new do |y| File.open(path, "rb") do |f| s = "" f.each_char do |c| if c =~ /[[:print:]]/ # is there a cleaner way to do this check? s += c next else y.yield s if s.length >= min s = "" end end y.yield s if s.length >= min end end end |
.windows_fileversion(info_key, path) ⇒ Object
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/u3d/utils.rb', line 234 def windows_fileversion(info_key, path) require "fiddle" version_dll = Fiddle.dlopen('version.dll') kernel32 = Fiddle.dlopen('kernel32.dll') get_file_version_info_size = Fiddle::Function.new(version_dll['GetFileVersionInfoSize'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_LONG) get_file_version_info = Fiddle::Function.new(version_dll['GetFileVersionInfo'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) # FIXME: TYPE_INT => TYPE_LONG?? ver_query_value = Fiddle::Function.new(version_dll['VerQueryValue'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) rtl_move_memory = Fiddle::Function.new(kernel32['RtlMoveMemory'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG], Fiddle::TYPE_INT) file = "#{path.tr('/', '\\')}\u0000" s = [0].pack('L') version_size = get_file_version_info_size.call(file, s) raise StandardError if version_size.zero? version_info = ' ' * version_size version_ok = get_file_version_info.call(file, 0, version_size, version_info) raise StandardError if version_ok.zero? # TODO: use GetLastError # hack, giving up using VerQueryValue for now. rstring = version_info.unpack('v*').map { |c| c.chr if c < 256 } * '' r = /#{info_key}..(.*?)\000/.match(rstring) # puts "#{info_key} = #{r ? r[1] : '??'}" return r ? r[1] : nil # rubocop:disable Lint/UnreachableCode # hardcoding lang codepage struct_path = "\\StringFileInfo\\040904b0\\#{info_key}" addr = [0].pack('L') size = [0].pack('L') query_ok = ver_query_value.call(version_info, "#{struct_path}\u0000", addr, size) raise StandardError if query_ok.zero? raddr = addr.unpack1('L') rsize = size.unpack1('L') # this is not working right now, getting seg faults and other low level issues puts "Size: #{raddr} #{rsize}" fixed_info = Array.new(rsize, 0).pack('L*') query_ok = rtl_move_memory.call(fixed_info, raddr, fixed_info.length) raise StandardError if query_ok.zero? info = fixed_info.unpack('L*') file_version = [info[4], info[3], info[6], info[5]] product_version = [info[8], info[7], info[10], info[9]] [file_version, product_version] # rubocop:enable Lint/UnreachableCode rescue StandardError => e UI.error("Failure to find '#{info_key}' under '#{path}': #{e}") UI.error(e.backtrace) nil end |
.windows_local_appdata ⇒ Object
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/u3d/utils.rb', line 215 def windows_local_appdata require "fiddle" shell32 = Fiddle.dlopen('shell32') getdir = Fiddle::Function.new(shell32['SHGetFolderPath'], [Fiddle::TYPE_LONG, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_LONG) windir = ' ' * 261 result = getdir.call(0, CSIDL_LOCAL_APPDATA, 0, 0, windir) raise "Unable to get Local Appdata directory, returned with value #{result}" unless result.zero? windir.rstrip! windir = windir.encode("UTF-8", Encoding.find('filesystem')) windir = File.(windir.rstrip) return windir if Dir.exist? windir raise "Local Appdata retrieved (#{windir}) is not correct" end |
.windows_path(path) ⇒ Object
293 294 295 296 |
# File 'lib/u3d/utils.rb', line 293 def windows_path(path) UI.deprecated("Use U3dCore::Helper.windows_path") U3dCore::Helper.windows_path(path) end |