Class: Webcache::DiskCache

Inherits:
Object
  • Object
show all
Defined in:
lib/webget/webcache_disk.rb,
lib/webget/webcache_disk-rewrite.rb

Overview

todo/check - change to Disk - why? why not?

Instance Method Summary collapse

Instance Method Details

#_body_path(url) ⇒ Object



8
# File 'lib/webget/webcache_disk.rb', line 8

def _body_path( url ) "#{Webcache.root}/#{url_to_path( url )}"; end

#_meta_path(url) ⇒ Object



9
# File 'lib/webget/webcache_disk.rb', line 9

def _meta_path( url ) "#{Webcache.root}/#{url_to_path( url )}.meta.txt"; end

#_read_utf8(path) ⇒ Object



194
195
196
197
198
199
200
201
# File 'lib/webget/webcache_disk.rb', line 194

def _read_utf8( path )
##  note - by default ruby on windows (automagically)
##          translates \r\n newlines to \n (universal/unix-style)
##                  when read files!!!!
##   note - only handles \r\n  (not "legacy" mac classic-style \r)

  File.open( path, 'r:utf-8' ) {|f| f.read }
end

#_write_utf8(path, text) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/webget/webcache_disk.rb', line 203

def _write_utf8( path, text )
##  write out utf8 (always use "universal" newlines on any platform)
##    todo / fix -   add  universial or such to open too ?
##
## fix: newlines - always use "unix" style" - why? why not?
## fix:  use :newline => :universal option? translates to univeral "\n"
##
##
##  The universal_newline: true flag forces Ruby to look through the string and
##   convert both Windows-style (\r\n) and old Mac-style (\r) newlines
##  into the standard Unix newline (\n)
##
##  File.open("output.txt", "w", universal_newline: true) do |file|
##     file.write(content)
##  end
##
##  mixed_string = "Line one\r\nLine two\rLine three\n"
##
##   Converts all \r\n and \r into \n
##     clean_string = mixed_string.encode(universal_newline: true)
##
##  Why Use encode Instead of gsub?
##  While many developers use regular expressions like .gsub(/\r\n?/, "\n"),
##   using .encode is highly preferred because:
##  Edge-case Safety: It is an internal, optimized C-level implementation
##   that handles mixed and broken newline edges perfectly.
##  Encoding Preservation: It seamlessly preserves the existing character encoding
##    (e.g., UTF-8) of your string.
##
##  was -  text  = text.gsub( "\r\n", "\n" )
##
##  note - by default ruby on windows (automagically) translates newlines to \r\n (crlf)!!!
##          thus, always use/ add universal_newline flag!!!

  File.open( path, 'w:utf-8', universal_newline: true ) do |f|
      f.write( text )
  end
end

#cached?(url) ⇒ Boolean Also known as: exist?

Returns:

  • (Boolean)


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/webget/webcache_disk.rb', line 12

def cached?( url )
  body_path = _body_path( url )
  exist =  File.exist?( body_path )

=begin
##  not really working - check back later
###   The catch on Windows
##   On Windows, File.realpath does NOT normalize casing to the on-disk canonical case.
##
## note - on windows - file.exist? is case-insensitive
##         use the strict: true flag if you want to enforce case-sensitive exists checks on windows!!!
##   On Windows, File.realpath returns the "true" path
##    as stored on the disk with the correct casing.
##    If the path you provide doesn't match the casing of the real path,
##     you know the match was case-insensitive.
  if exist && strict
   exist =  File.realpath(body_path) == File.expand_path(body_path)
  end
=end

  exist
end

#read(url) ⇒ Object

fix-fix-fix change to read_txt/read_text/read_html plus add read_blob/read_bin(ary) !!!



42
43
44
# File 'lib/webget/webcache_disk.rb', line 42

def read( url )
  _read_utf8(_body_path( url ))
end

#read_csv(url) ⇒ Object



54
55
56
57
58
# File 'lib/webget/webcache_disk.rb', line 54

def read_csv( url )
  txt = _read_utf8(_body_path( url ))
  data = CsvHash.parse( txt )
  data
end

#read_json(url) ⇒ Object



48
49
50
51
52
# File 'lib/webget/webcache_disk.rb', line 48

def read_json( url )
  txt = _read_utf8(_body_path( url ))
  data = JSON.parse( txt )
  data
end

#read_meta(url) ⇒ Object



61
62
63
64
65
# File 'lib/webget/webcache_disk.rb', line 61

def read_meta( url )
  txt = _read_utf8(_meta_path( url ))
  data = Headers.parse( txt )
  data
end

#record(url, response, format:) ⇒ Object

add more save / put / etc. aliases - why? why not? rename to record_html - why? why not?



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/webget/webcache_disk.rb', line 73

def record( url, response, format: )

  ###
  ## note - encoding_user MUST be passed along with response (wrapper) obj
  ##           e.g.  response._encoding_user = encoding ??
  ##                   see Webget.page|text|dataset|etc.


  ## todo/check - use rel_path or local_path or such??
  save_path = url_to_path( url )

  body_path = _body_path( url )
  meta_path = _meta_path( url )   ## is _body_path + ".meta.txt"

  ## make sure path exits
  FileUtils.mkdir_p( File.dirname( body_path ) )


  puts "[cache] saving #{body_path}..."

  ## todo/check: verify content-type - why? why not?
  ## note - for now respone.text always assume (converted) to utf8!!!!!!!!!

  if format == 'json'
    _write_utf8( body_path, JSON.pretty_generate( response.json ))
    x_encoding        = nil   ## for now do not track; always assume  UTF-8
    x_encoding_source = nil
    x_encoding_valid  = nil
    x_ascii_only      = nil
    x_8bit            = nil
    x_utf8_replace    = nil
  else   ## html,  txt or csv
    _write_utf8( body_path, response.text )

    x_encoding        = response._text_encoding
    x_encoding_source = response._text_encoding_source
    x_encoding_valid  = response._text_encoding_valid  # true|false or nil (undef)
    x_ascii_only      = response._text_ascii_only
    x_8bit            = response._text_8bit
    x_utf8_replace    = response._text_utf8_replace
  end


  ### fix-fix-fix  -- add support for binary/image formats
  ##     e.g. bin|gif|jpg|etc  - why? why not?

  ####
  ## get file size in bytes
  ##     or use File.stat( body_path ).size (using File::Stat) ??
  x_size    = File.size( body_path )



    ## todo/check:
    ##  do headers also need to converted (like text) if encoding is NOT utf-8 ???


    #### add our own custom headers first!!
    ##     change x-save to x-filename or ??
    ##     change to x-7bit-only or x-ascii7bit or x-ascii7bit-only or ??
    ##     change x-size to x-bytesize or ??
    ##     change x-8bit  to ???

    ### start w/ comment line
    ###   uncomment - http status - why? why not?
    buf = String.new
    buf << "# fetched on #{Time.now.utc}\n"
    buf << "# HTTP/#{response.version} #{response.status.code} #{response.status.message}\n"
    buf << "\n"

    buf << "x-url: #{url}\n"
    buf << "x-encoding: #{x_encoding}\n"                 if x_encoding
    buf << "x-encoding-source: #{x_encoding_source}\n"   if x_encoding_source
    buf << "x-encoding-valid: #{x_encoding_valid}\n"     if x_encoding_valid
    buf << "x-ascii-only: #{x_ascii_only}\n"             if x_ascii_only
    buf << "x-8bit: #{x_8bit}\n"                         if x_8bit
    buf << "x-utf8-replace: #{x_utf8_replace}\n"         if x_utf8_replace
    buf << "x-save: #{save_path}\n"
    buf << "x-size: #{x_size}\n"
    buf << "x-format: #{format}\n"      ## e.g. json|html|csv|etc.
    buf << "\n"

    # iterate all response headers
    response.headers.each do |key, value|
      buf << "#{key}: #{value}\n"
    end

    _write_utf8( meta_path, buf )
end

#rewrite_path(host, req_path) ⇒ Object

todo/fix - make rewrite_path configurable "pipeline" lets you auto-add more rewriters and keep this code "generic" move rewriters "downstream" into "userland" ??



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
# File 'lib/webget/webcache_disk-rewrite.rb', line 11

def rewrite_path( host, req_path )

        ### special "prettify" rule for weltfussball
    ##   /eng-league-one-2019-2020/  => /eng-league-one-2019-2020.html

    ### todo/fix - move rules downstream to user - why? why not?

    if host.include?( 'uefa.com' ) ||
       host.include?( 'kicker.de' ) ||
       host.include?( 'kicker.at' )
      if req_path.end_with?( '/' )
        req_path = "#{req_path[0..-2]}.html"
      else
        puts "ERROR: expected request_uri for >#{host}< ending with '/'; got: >#{req_path}<"
        exit 1
      end
    elsif host.include?( 'weltfussball.de' ) ||
          host.include?( 'worldfootball.net' )
          if req_path.end_with?( '/' )
             req_path = "#{req_path[0..-2]}.html"
          else
            puts "ERROR: expected request_uri for >#{host}< ending with '/'; got: >#{req_path}<"
            exit 1
          end
    elsif host.include?( 'tipp3.at' )
      req_path = req_path.sub( '.jsp', '' )  # shorten - cut off .jsp extension

      ##   change ? to -I-
      ##   change = to ~
      ##   Example:
      ##   sportwetten/classicresults.jsp?oddsetProgramID=888
      ##     =>
      ##   sportwetten/classicresults-I-oddsetProgramID~888
      req_path = req_path.gsub( '?', '-I-' )
                         .gsub( '=', '~')

      req_path = "#{req_path}.html"
    elsif host.include?( 'fbref.com' )
      req_path = req_path.sub( 'en/', '' )      # shorten - cut off en/
      req_path = "#{req_path}.html"             # auto-add html extension
    elsif host.include?( 'football-data.co.uk' )
      req_path = req_path.sub( 'mmz4281/', '' )  # shorten - cut off mmz4281/
      req_path = req_path.sub( 'new/', '' )      # shorten - cut off new/
    elsif host.include?( 'football-data.org' )
      ##  req_path = req_path.sub( 'v2/', '' )  # shorten - cut off v2/

      ## flattern - make a file path - for auto-save
      ##   change ? to -I-
      ##   change / to ~~
      ##   change = to ~
      req_path = req_path.gsub( '?', '-I-' )
                         .gsub( '/', '~~' )
                         .gsub( '=', '~')

      req_path = "#{req_path}.json"
    elsif host.include?( 'api-sports.io' )
      req_path = req_path.gsub( '?', '-I-' )
                         .gsub( '&', '~~' )   ### check if & present?
                         .gsub( '=', '~')

      req_path = "#{req_path}.json"
    elsif host.include?( 'api.cryptokitties.co' )
      ## for now always auto-add .json extensions e.g.
      ##     kitties/1   => kitties/1.json
      ##     cattributes => cattributes.json
      req_path = "#{req_path}.json"
    else
      ## no special rule
    end

    req_path
end

#url_to_path(str) ⇒ Object

helpers



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/webget/webcache_disk.rb', line 172

def url_to_path( str )
  ## map url to file path
  uri = URI( str )       ## URI() same as URI.parse()

  ## note: ignore scheme (e.g. http/https)
  ##         and  post  (e.g. 80, 8080, etc.) for now
  ##    always downcase for now (internet domain is case insensitive)
  host_dir = uri.host.downcase

  ## "/this/is/everything?query=params"
  ##   cut-off leading slash and
  ##    convert query ? =
  ##   check if [1..] is sames as [1..-1]
  req_path =   rewrite_path( host_dir, uri.request_uri[1..] )


  page_path = "#{host_dir}/#{req_path}"
  page_path
end