remote_table 0.2.11 → 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -14,8 +14,8 @@ begin
14
14
  gem.add_dependency 'fastercsv', '>=1.5.0'
15
15
  gem.add_dependency 'activesupport', '>=2.3.4'
16
16
  gem.add_dependency 'slither', '>=0.99.3'
17
+ gem.add_dependency 'nokogiri', '>=1.4.1'
17
18
  gem.require_path = "lib"
18
- gem.files.include %w(lib/remote_table) unless gem.files.empty? # seems to fail once it's in the wild
19
19
  gem.rdoc_options << '--line-numbers' << '--inline-source'
20
20
  gem.requirements << 'curl'
21
21
  gem.rubyforge_project = "remotetable"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.11
1
+ 0.2.12
data/lib/remote_table.rb CHANGED
@@ -15,6 +15,7 @@ end if ActiveSupport::VERSION::MAJOR == 3
15
15
  require 'fastercsv'
16
16
  require 'slither'
17
17
  require 'roo'
18
+ require 'nokogiri'
18
19
  require 'remote_table/transform'
19
20
  require 'remote_table/request'
20
21
  require 'remote_table/package'
@@ -24,6 +25,7 @@ require 'remote_table/file/fixed_width'
24
25
  require 'remote_table/file/roo_spreadsheet'
25
26
  require 'remote_table/file/ods'
26
27
  require 'remote_table/file/xls'
28
+ require 'remote_table/file/html'
27
29
 
28
30
  class RemoteTable
29
31
  attr_accessor :request, :package, :file, :transform
@@ -4,6 +4,8 @@ class RemoteTable
4
4
  attr_accessor :encoding
5
5
  attr_accessor :path
6
6
  attr_accessor :keep_blank_rows
7
+ attr_accessor :row_xpath
8
+ attr_accessor :column_xpath
7
9
 
8
10
  def initialize(bus)
9
11
  @filename = bus[:filename]
@@ -19,22 +21,11 @@ class RemoteTable
19
21
  @schema_name = bus[:schema_name]
20
22
  @trap = bus[:trap]
21
23
  @encoding = bus[:encoding] || 'UTF-8'
24
+ @row_xpath = bus[:row_xpath]
25
+ @column_xpath = bus[:column_xpath]
22
26
  extend "RemoteTable::#{format.to_s.camelcase}".constantize
23
27
  end
24
28
 
25
- class << self
26
- # http://santanatechnotes.blogspot.com/2005/12/matching-iso-8859-1-strings-with-ruby.html
27
- def convert_to_utf8(str, encoding)
28
- if encoding == 'UTF-8' or encoding == 'UTF8'
29
- str.toutf8 # just in case
30
- else
31
- @_iconv ||= Hash.new
32
- @_iconv[encoding] ||= Iconv.new 'UTF-8', encoding
33
- @_iconv[encoding].iconv(str).toutf8
34
- end
35
- end
36
- end
37
-
38
29
  def tabulate(path)
39
30
  define_fixed_width_schema! if format == :fixed_width and schema.is_a?(Array) # TODO move to generic subclass callback
40
31
  self.path = path
@@ -98,6 +89,7 @@ class RemoteTable
98
89
  extname = ::File.extname(filename).gsub('.', '')
99
90
  return :csv if extname.blank?
100
91
  format = [ :xls, :ods ].detect { |i| i == extname.to_sym }
92
+ format = :html if extname =~ /\Ahtm/
101
93
  format = :csv if format.blank?
102
94
  format
103
95
  end
@@ -35,7 +35,7 @@ class RemoteTable
35
35
  private
36
36
 
37
37
  def fastercsv_options
38
- fastercsv_options = { :skip_blanks => !keep_blank_rows, :header_converters => lambda { |k| RemoteTable::File.convert_to_utf8 k.to_s, encoding } }
38
+ fastercsv_options = { :skip_blanks => !keep_blank_rows }
39
39
  if headers == false
40
40
  fastercsv_options.merge!(:headers => nil)
41
41
  else
@@ -0,0 +1,36 @@
1
+ class RemoteTable
2
+ module Html
3
+ def each_row(&block)
4
+ backup_file!
5
+ convert_file_to_utf8!
6
+ html_headers = (headers.is_a?(Array)) ? headers : nil
7
+ Nokogiri::HTML(unescaped_html_without_soft_hyphens, nil, 'UTF-8').xpath(row_xpath).each do |row|
8
+ values = row.xpath(column_xpath).map { |td| td.content.gsub(/\s+/, ' ').strip }
9
+ if html_headers.nil?
10
+ html_headers = values
11
+ next
12
+ end
13
+ hash = zip html_headers, values
14
+ yield hash if keep_blank_rows or hash.any? { |k, v| v.present? }
15
+ end
16
+ ensure
17
+ restore_file!
18
+ end
19
+
20
+ private
21
+
22
+ # http://snippets.dzone.com/posts/show/406
23
+ def zip(keys, values)
24
+ hash = Hash.new
25
+ keys.zip(values) { |k,v| hash[k]=v }
26
+ hash
27
+ end
28
+
29
+ # should we be doing this in ruby?
30
+ def unescaped_html_without_soft_hyphens
31
+ str = CGI.unescapeHTML IO.read(path)
32
+ str.gsub! /&shy;/, ''
33
+ str
34
+ end
35
+ end
36
+ end
data/remote_table.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{remote_table}
8
- s.version = "0.2.11"
8
+ s.version = "0.2.12"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Seamus Abshere", "Andy Rossmeissl"]
12
- s.date = %q{2010-04-19}
12
+ s.date = %q{2010-04-22}
13
13
  s.description = %q{Remotely open and parse Excel XLS, ODS, CSV and fixed-width tables.}
14
14
  s.email = %q{seamus@abshere.net}
15
15
  s.extra_rdoc_files = [
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  "lib/remote_table/file.rb",
29
29
  "lib/remote_table/file/csv.rb",
30
30
  "lib/remote_table/file/fixed_width.rb",
31
+ "lib/remote_table/file/html.rb",
31
32
  "lib/remote_table/file/ods.rb",
32
33
  "lib/remote_table/file/roo_spreadsheet.rb",
33
34
  "lib/remote_table/file/xls.rb",
@@ -59,17 +60,20 @@ Gem::Specification.new do |s|
59
60
  s.add_runtime_dependency(%q<fastercsv>, [">= 1.5.0"])
60
61
  s.add_runtime_dependency(%q<activesupport>, [">= 2.3.4"])
61
62
  s.add_runtime_dependency(%q<slither>, [">= 0.99.3"])
63
+ s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.1"])
62
64
  else
63
65
  s.add_dependency(%q<roo>, ["= 1.3.11"])
64
66
  s.add_dependency(%q<fastercsv>, [">= 1.5.0"])
65
67
  s.add_dependency(%q<activesupport>, [">= 2.3.4"])
66
68
  s.add_dependency(%q<slither>, [">= 0.99.3"])
69
+ s.add_dependency(%q<nokogiri>, [">= 1.4.1"])
67
70
  end
68
71
  else
69
72
  s.add_dependency(%q<roo>, ["= 1.3.11"])
70
73
  s.add_dependency(%q<fastercsv>, [">= 1.5.0"])
71
74
  s.add_dependency(%q<activesupport>, [">= 2.3.4"])
72
75
  s.add_dependency(%q<slither>, [">= 0.99.3"])
76
+ s.add_dependency(%q<nokogiri>, [">= 1.4.1"])
73
77
  end
74
78
  end
75
79
 
@@ -50,230 +50,246 @@ class RemoteTableTest < Test::Unit::TestCase
50
50
  ]
51
51
  end
52
52
 
53
- should "open an XLS inside a zip file" do
54
- t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/02data.zip', :filename => 'guide_jan28.xls')
55
- assert_equal 'ACURA', t.rows.first['Manufacturer']
56
- assert_equal 'NSX', t.rows.first['carline name']
57
- assert_equal 'VOLVO', t.rows.last['Manufacturer']
58
- assert_equal 'V70 XC AWD', t.rows.last['carline name']
53
+ if ENV['NEW'] == 'true'
59
54
  end
60
55
 
61
- should "not have indifferent string/symbol hash access" do
62
- t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/02data.zip', :filename => 'guide_jan28.xls')
63
- assert_equal 'ACURA', t.rows.first['Manufacturer']
64
- assert_equal nil, t.rows.first[:Manufacturer]
65
- end
56
+ if ENV['OLD'] == 'true'
57
+ should "read an HTML table made with frontpage" do
58
+ t = RemoteTable.new :url => "http://www.faa.gov/air_traffic/publications/atpubs/CNT/5-2-E.htm",
59
+ :encoding => 'US-ASCII',
60
+ :row_xpath => '//table/tr[2]/td/table/tr',
61
+ :column_xpath => 'td'
62
+ assert_equal 'E110', t.rows.first['Designator']
63
+ assert_equal 'EMBRAER', t.rows.first['Manufacturer']
64
+ assert_equal 'EZKC', t.rows.last['Designator']
65
+ assert_equal 'EZ King Cobra', t.rows.last['Model']
66
+ end
67
+
68
+ should "open an XLS inside a zip file" do
69
+ t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/02data.zip', :filename => 'guide_jan28.xls')
70
+ assert_equal 'ACURA', t.rows.first['Manufacturer']
71
+ assert_equal 'NSX', t.rows.first['carline name']
72
+ assert_equal 'VOLVO', t.rows.last['Manufacturer']
73
+ assert_equal 'V70 XC AWD', t.rows.last['carline name']
74
+ end
75
+
76
+ should "not have indifferent string/symbol hash access" do
77
+ t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/02data.zip', :filename => 'guide_jan28.xls')
78
+ assert_equal 'ACURA', t.rows.first['Manufacturer']
79
+ assert_equal nil, t.rows.first[:Manufacturer]
80
+ end
66
81
 
67
- should "hash rows without paying attention to order" do
68
- x = ActiveSupport::OrderedHash.new
69
- x[:a] = 1
70
- x[:b] = 2
82
+ should "hash rows without paying attention to order" do
83
+ x = ActiveSupport::OrderedHash.new
84
+ x[:a] = 1
85
+ x[:b] = 2
71
86
 
72
- y = ActiveSupport::OrderedHash.new
73
- y[:b] = 2
74
- y[:a] = 1
87
+ y = ActiveSupport::OrderedHash.new
88
+ y[:b] = 2
89
+ y[:a] = 1
75
90
 
76
- assert Marshal.dump(x) != Marshal.dump(y)
77
- assert RemoteTable::Transform.row_hash(x) == RemoteTable::Transform.row_hash(y)
78
- end
91
+ assert Marshal.dump(x) != Marshal.dump(y)
92
+ assert RemoteTable::Transform.row_hash(x) == RemoteTable::Transform.row_hash(y)
93
+ end
79
94
 
80
- should "open a Google Docs url (as a CSV)" do
81
- t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=t5HM1KbaRngmTUbntg8JwPA')
82
- assert_equal 'Gulf Coast', t.rows.first['PAD district name']
83
- assert_equal 'AL', t.rows.first['State']
84
- assert_equal 'Rocky Mountain', t.rows.last['PAD district name']
85
- assert_equal 'WY', t.rows.last['State']
86
- end
95
+ should "open a Google Docs url (as a CSV)" do
96
+ t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=t5HM1KbaRngmTUbntg8JwPA')
97
+ assert_equal 'Gulf Coast', t.rows.first['PAD district name']
98
+ assert_equal 'AL', t.rows.first['State']
99
+ assert_equal 'Rocky Mountain', t.rows.last['PAD district name']
100
+ assert_equal 'WY', t.rows.last['State']
101
+ end
87
102
 
88
- should "open a Google Docs url as a CSV without headers" do
89
- t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=t5HM1KbaRngmTUbntg8JwPA', :skip => 1, :headers => false)
90
- assert_equal 'AL', t.rows.first[0]
91
- assert_equal 'Gulf Coast', t.rows.first[4]
92
- assert_equal 'WY', t.rows.last[0]
93
- assert_equal 'Rocky Mountain', t.rows.last[4]
94
- end
103
+ should "open a Google Docs url as a CSV without headers" do
104
+ t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=t5HM1KbaRngmTUbntg8JwPA', :skip => 1, :headers => false)
105
+ assert_equal 'AL', t.rows.first[0]
106
+ assert_equal 'Gulf Coast', t.rows.first[4]
107
+ assert_equal 'WY', t.rows.last[0]
108
+ assert_equal 'Rocky Mountain', t.rows.last[4]
109
+ end
95
110
 
96
- should "send form data, follow redirects and use a filename glob" do
97
- url = 'http://www.transtats.bts.gov/DownLoad_Table.asp?Table_ID=293&Has_Group=3&Is_Zipped=0'
98
- form_data = 'UserTableName=T_100_Segment__All_Carriers&DBShortName=Air_Carriers&RawDataTable=T_T100_SEGMENT_ALL_CARRIER&sqlstr=+SELECT+DEPARTURES_SCHEDULED%2CDEPARTURES_PERFORMED%2CPAYLOAD%2CSEATS%2CPASSENGERS%2CFREIGHT%2CMAIL%2CDISTANCE%2CRAMP_TO_RAMP%2CAIR_TIME%2CUNIQUE_CARRIER%2CAIRLINE_ID%2CUNIQUE_CARRIER_NAME%2CUNIQUE_CARRIER_ENTITY%2CREGION%2CCARRIER%2CCARRIER_NAME%2CCARRIER_GROUP%2CCARRIER_GROUP_NEW%2CORIGIN%2CORIGIN_CITY_NAME%2CORIGIN_CITY_NUM%2CORIGIN_STATE_ABR%2CORIGIN_STATE_FIPS%2CORIGIN_STATE_NM%2CORIGIN_COUNTRY%2CORIGIN_COUNTRY_NAME%2CORIGIN_WAC%2CDEST%2CDEST_CITY_NAME%2CDEST_CITY_NUM%2CDEST_STATE_ABR%2CDEST_STATE_FIPS%2CDEST_STATE_NM%2CDEST_COUNTRY%2CDEST_COUNTRY_NAME%2CDEST_WAC%2CAIRCRAFT_GROUP%2CAIRCRAFT_TYPE%2CAIRCRAFT_CONFIG%2CYEAR%2CQUARTER%2CMONTH%2CDISTANCE_GROUP%2CCLASS%2CDATA_SOURCE+FROM++T_T100_SEGMENT_ALL_CARRIER+WHERE+Month+%3D1+AND+YEAR%3D2008&varlist=DEPARTURES_SCHEDULED%2CDEPARTURES_PERFORMED%2CPAYLOAD%2CSEATS%2CPASSENGERS%2CFREIGHT%2CMAIL%2CDISTANCE%2CRAMP_TO_RAMP%2CAIR_TIME%2CUNIQUE_CARRIER%2CAIRLINE_ID%2CUNIQUE_CARRIER_NAME%2CUNIQUE_CARRIER_ENTITY%2CREGION%2CCARRIER%2CCARRIER_NAME%2CCARRIER_GROUP%2CCARRIER_GROUP_NEW%2CORIGIN%2CORIGIN_CITY_NAME%2CORIGIN_CITY_NUM%2CORIGIN_STATE_ABR%2CORIGIN_STATE_FIPS%2CORIGIN_STATE_NM%2CORIGIN_COUNTRY%2CORIGIN_COUNTRY_NAME%2CORIGIN_WAC%2CDEST%2CDEST_CITY_NAME%2CDEST_CITY_NUM%2CDEST_STATE_ABR%2CDEST_STATE_FIPS%2CDEST_STATE_NM%2CDEST_COUNTRY%2CDEST_COUNTRY_NAME%2CDEST_WAC%2CAIRCRAFT_GROUP%2CAIRCRAFT_TYPE%2CAIRCRAFT_CONFIG%2CYEAR%2CQUARTER%2CMONTH%2CDISTANCE_GROUP%2CCLASS%2CDATA_SOURCE&grouplist=&suml=&sumRegion=&filter1=title%3D&filter2=title%3D&geo=All%A0&time=January&timename=Month&GEOGRAPHY=All&XYEAR=2008&FREQUENCY=1&AllVars=All&VarName=DEPARTURES_SCHEDULED&VarDesc=DepScheduled&VarType=Num&VarName=DEPARTURES_PERFORMED&VarDesc=DepPerformed&VarType=Num&VarName=PAYLOAD&VarDesc=Payload&VarType=Num&VarName=SEATS&VarDesc=Seats&VarType=Num&VarName=PASSENGERS&VarDesc=Passengers&VarType=Num&VarName=FREIGHT&VarDesc=Freight&VarType=Num&VarName=MAIL&VarDesc=Mail&VarType=Num&VarName=DISTANCE&VarDesc=Distance&VarType=Num&VarName=RAMP_TO_RAMP&VarDesc=RampToRamp&VarType=Num&VarName=AIR_TIME&VarDesc=AirTime&VarType=Num&VarName=UNIQUE_CARRIER&VarDesc=UniqueCarrier&VarType=Char&VarName=AIRLINE_ID&VarDesc=AirlineID&VarType=Num&VarName=UNIQUE_CARRIER_NAME&VarDesc=UniqueCarrierName&VarType=Char&VarName=UNIQUE_CARRIER_ENTITY&VarDesc=UniqCarrierEntity&VarType=Char&VarName=REGION&VarDesc=CarrierRegion&VarType=Char&VarName=CARRIER&VarDesc=Carrier&VarType=Char&VarName=CARRIER_NAME&VarDesc=CarrierName&VarType=Char&VarName=CARRIER_GROUP&VarDesc=CarrierGroup&VarType=Num&VarName=CARRIER_GROUP_NEW&VarDesc=CarrierGroupNew&VarType=Num&VarName=ORIGIN&VarDesc=Origin&VarType=Char&VarName=ORIGIN_CITY_NAME&VarDesc=OriginCityName&VarType=Char&VarName=ORIGIN_CITY_NUM&VarDesc=OriginCityNum&VarType=Num&VarName=ORIGIN_STATE_ABR&VarDesc=OriginState&VarType=Char&VarName=ORIGIN_STATE_FIPS&VarDesc=OriginStateFips&VarType=Char&VarName=ORIGIN_STATE_NM&VarDesc=OriginStateName&VarType=Char&VarName=ORIGIN_COUNTRY&VarDesc=OriginCountry&VarType=Char&VarName=ORIGIN_COUNTRY_NAME&VarDesc=OriginCountryName&VarType=Char&VarName=ORIGIN_WAC&VarDesc=OriginWac&VarType=Num&VarName=DEST&VarDesc=Dest&VarType=Char&VarName=DEST_CITY_NAME&VarDesc=DestCityName&VarType=Char&VarName=DEST_CITY_NUM&VarDesc=DestCityNum&VarType=Num&VarName=DEST_STATE_ABR&VarDesc=DestState&VarType=Char&VarName=DEST_STATE_FIPS&VarDesc=DestStateFips&VarType=Char&VarName=DEST_STATE_NM&VarDesc=DestStateName&VarType=Char&VarName=DEST_COUNTRY&VarDesc=DestCountry&VarType=Char&VarName=DEST_COUNTRY_NAME&VarDesc=DestCountryName&VarType=Char&VarName=DEST_WAC&VarDesc=DestWac&VarType=Num&VarName=AIRCRAFT_GROUP&VarDesc=AircraftGroup&VarType=Num&VarName=AIRCRAFT_TYPE&VarDesc=AircraftType&VarType=Char&VarName=AIRCRAFT_CONFIG&VarDesc=AircraftConfig&VarType=Num&VarName=YEAR&VarDesc=Year&VarType=Num&VarName=QUARTER&VarDesc=Quarter&VarType=Num&VarName=MONTH&VarDesc=Month&VarType=Num&VarName=DISTANCE_GROUP&VarDesc=DistanceGroup&VarType=Num&VarName=CLASS&VarDesc=Class&VarType=Char&VarName=DATA_SOURCE&VarDesc=DataSource&VarType=Char'
99
- t = RemoteTable.new :url => url, :form_data => form_data, :compression => :zip, :glob => '/*.csv'
100
- assert_equal 'United States of America', t.rows.first['DEST_COUNTRY_NAME']
101
- end
111
+ should "send form data, follow redirects and use a filename glob" do
112
+ url = 'http://www.transtats.bts.gov/DownLoad_Table.asp?Table_ID=293&Has_Group=3&Is_Zipped=0'
113
+ form_data = 'UserTableName=T_100_Segment__All_Carriers&DBShortName=Air_Carriers&RawDataTable=T_T100_SEGMENT_ALL_CARRIER&sqlstr=+SELECT+DEPARTURES_SCHEDULED%2CDEPARTURES_PERFORMED%2CPAYLOAD%2CSEATS%2CPASSENGERS%2CFREIGHT%2CMAIL%2CDISTANCE%2CRAMP_TO_RAMP%2CAIR_TIME%2CUNIQUE_CARRIER%2CAIRLINE_ID%2CUNIQUE_CARRIER_NAME%2CUNIQUE_CARRIER_ENTITY%2CREGION%2CCARRIER%2CCARRIER_NAME%2CCARRIER_GROUP%2CCARRIER_GROUP_NEW%2CORIGIN%2CORIGIN_CITY_NAME%2CORIGIN_CITY_NUM%2CORIGIN_STATE_ABR%2CORIGIN_STATE_FIPS%2CORIGIN_STATE_NM%2CORIGIN_COUNTRY%2CORIGIN_COUNTRY_NAME%2CORIGIN_WAC%2CDEST%2CDEST_CITY_NAME%2CDEST_CITY_NUM%2CDEST_STATE_ABR%2CDEST_STATE_FIPS%2CDEST_STATE_NM%2CDEST_COUNTRY%2CDEST_COUNTRY_NAME%2CDEST_WAC%2CAIRCRAFT_GROUP%2CAIRCRAFT_TYPE%2CAIRCRAFT_CONFIG%2CYEAR%2CQUARTER%2CMONTH%2CDISTANCE_GROUP%2CCLASS%2CDATA_SOURCE+FROM++T_T100_SEGMENT_ALL_CARRIER+WHERE+Month+%3D1+AND+YEAR%3D2008&varlist=DEPARTURES_SCHEDULED%2CDEPARTURES_PERFORMED%2CPAYLOAD%2CSEATS%2CPASSENGERS%2CFREIGHT%2CMAIL%2CDISTANCE%2CRAMP_TO_RAMP%2CAIR_TIME%2CUNIQUE_CARRIER%2CAIRLINE_ID%2CUNIQUE_CARRIER_NAME%2CUNIQUE_CARRIER_ENTITY%2CREGION%2CCARRIER%2CCARRIER_NAME%2CCARRIER_GROUP%2CCARRIER_GROUP_NEW%2CORIGIN%2CORIGIN_CITY_NAME%2CORIGIN_CITY_NUM%2CORIGIN_STATE_ABR%2CORIGIN_STATE_FIPS%2CORIGIN_STATE_NM%2CORIGIN_COUNTRY%2CORIGIN_COUNTRY_NAME%2CORIGIN_WAC%2CDEST%2CDEST_CITY_NAME%2CDEST_CITY_NUM%2CDEST_STATE_ABR%2CDEST_STATE_FIPS%2CDEST_STATE_NM%2CDEST_COUNTRY%2CDEST_COUNTRY_NAME%2CDEST_WAC%2CAIRCRAFT_GROUP%2CAIRCRAFT_TYPE%2CAIRCRAFT_CONFIG%2CYEAR%2CQUARTER%2CMONTH%2CDISTANCE_GROUP%2CCLASS%2CDATA_SOURCE&grouplist=&suml=&sumRegion=&filter1=title%3D&filter2=title%3D&geo=All%A0&time=January&timename=Month&GEOGRAPHY=All&XYEAR=2008&FREQUENCY=1&AllVars=All&VarName=DEPARTURES_SCHEDULED&VarDesc=DepScheduled&VarType=Num&VarName=DEPARTURES_PERFORMED&VarDesc=DepPerformed&VarType=Num&VarName=PAYLOAD&VarDesc=Payload&VarType=Num&VarName=SEATS&VarDesc=Seats&VarType=Num&VarName=PASSENGERS&VarDesc=Passengers&VarType=Num&VarName=FREIGHT&VarDesc=Freight&VarType=Num&VarName=MAIL&VarDesc=Mail&VarType=Num&VarName=DISTANCE&VarDesc=Distance&VarType=Num&VarName=RAMP_TO_RAMP&VarDesc=RampToRamp&VarType=Num&VarName=AIR_TIME&VarDesc=AirTime&VarType=Num&VarName=UNIQUE_CARRIER&VarDesc=UniqueCarrier&VarType=Char&VarName=AIRLINE_ID&VarDesc=AirlineID&VarType=Num&VarName=UNIQUE_CARRIER_NAME&VarDesc=UniqueCarrierName&VarType=Char&VarName=UNIQUE_CARRIER_ENTITY&VarDesc=UniqCarrierEntity&VarType=Char&VarName=REGION&VarDesc=CarrierRegion&VarType=Char&VarName=CARRIER&VarDesc=Carrier&VarType=Char&VarName=CARRIER_NAME&VarDesc=CarrierName&VarType=Char&VarName=CARRIER_GROUP&VarDesc=CarrierGroup&VarType=Num&VarName=CARRIER_GROUP_NEW&VarDesc=CarrierGroupNew&VarType=Num&VarName=ORIGIN&VarDesc=Origin&VarType=Char&VarName=ORIGIN_CITY_NAME&VarDesc=OriginCityName&VarType=Char&VarName=ORIGIN_CITY_NUM&VarDesc=OriginCityNum&VarType=Num&VarName=ORIGIN_STATE_ABR&VarDesc=OriginState&VarType=Char&VarName=ORIGIN_STATE_FIPS&VarDesc=OriginStateFips&VarType=Char&VarName=ORIGIN_STATE_NM&VarDesc=OriginStateName&VarType=Char&VarName=ORIGIN_COUNTRY&VarDesc=OriginCountry&VarType=Char&VarName=ORIGIN_COUNTRY_NAME&VarDesc=OriginCountryName&VarType=Char&VarName=ORIGIN_WAC&VarDesc=OriginWac&VarType=Num&VarName=DEST&VarDesc=Dest&VarType=Char&VarName=DEST_CITY_NAME&VarDesc=DestCityName&VarType=Char&VarName=DEST_CITY_NUM&VarDesc=DestCityNum&VarType=Num&VarName=DEST_STATE_ABR&VarDesc=DestState&VarType=Char&VarName=DEST_STATE_FIPS&VarDesc=DestStateFips&VarType=Char&VarName=DEST_STATE_NM&VarDesc=DestStateName&VarType=Char&VarName=DEST_COUNTRY&VarDesc=DestCountry&VarType=Char&VarName=DEST_COUNTRY_NAME&VarDesc=DestCountryName&VarType=Char&VarName=DEST_WAC&VarDesc=DestWac&VarType=Num&VarName=AIRCRAFT_GROUP&VarDesc=AircraftGroup&VarType=Num&VarName=AIRCRAFT_TYPE&VarDesc=AircraftType&VarType=Char&VarName=AIRCRAFT_CONFIG&VarDesc=AircraftConfig&VarType=Num&VarName=YEAR&VarDesc=Year&VarType=Num&VarName=QUARTER&VarDesc=Quarter&VarType=Num&VarName=MONTH&VarDesc=Month&VarType=Num&VarName=DISTANCE_GROUP&VarDesc=DistanceGroup&VarType=Num&VarName=CLASS&VarDesc=Class&VarType=Char&VarName=DATA_SOURCE&VarDesc=DataSource&VarType=Char'
114
+ t = RemoteTable.new :url => url, :form_data => form_data, :compression => :zip, :glob => '/*.csv'
115
+ assert_equal 'United States of America', t.rows.first['DEST_COUNTRY_NAME']
116
+ end
102
117
 
103
- should "take the last of values if the header is duplicated" do
104
- t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tujrgUOwDSLWb-P4KCt1qBg')
105
- assert_equal '2', t.rows.first['dup_header']
106
- end
118
+ should "take the last of values if the header is duplicated" do
119
+ t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=tujrgUOwDSLWb-P4KCt1qBg')
120
+ assert_equal '2', t.rows.first['dup_header']
121
+ end
107
122
 
108
- should "respect field order in CSVs without headers" do
109
- t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=t5HM1KbaRngmTUbntg8JwPA', :skip => 1, :headers => false)
110
- last_k = -1
111
- saw_string = false
112
- t.rows.each do |row|
113
- row.each do |k, v|
114
- if k.is_a?(Fixnum) and last_k.is_a?(Fixnum)
115
- assert !saw_string
116
- assert k > last_k
123
+ should "respect field order in CSVs without headers" do
124
+ t = RemoteTable.new(:url => 'http://spreadsheets.google.com/pub?key=t5HM1KbaRngmTUbntg8JwPA', :skip => 1, :headers => false)
125
+ last_k = -1
126
+ saw_string = false
127
+ t.rows.each do |row|
128
+ row.each do |k, v|
129
+ if k.is_a?(Fixnum) and last_k.is_a?(Fixnum)
130
+ assert !saw_string
131
+ assert k > last_k
132
+ end
133
+ last_k = k
134
+ saw_string = k.is_a?(String)
117
135
  end
118
- last_k = k
119
- saw_string = k.is_a?(String)
120
136
  end
121
137
  end
122
- end
123
138
 
124
- %w{ csv ods xls }.each do |format|
125
- eval %{
126
- should "read #{format}" do
127
- t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.#{format}')
128
- # no blank headers
129
- assert t.rows.all? { |row| row.keys.all?(&:present?) }
130
- # correct values
131
- t.rows.each_with_index do |row, index|
132
- assert_equal row.except('row_hash'), @test2_rows[index]
139
+ %w{ csv ods xls }.each do |format|
140
+ eval %{
141
+ should "read #{format}" do
142
+ t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.#{format}')
143
+ # no blank headers
144
+ assert t.rows.all? { |row| row.keys.all?(&:present?) }
145
+ # correct values
146
+ t.rows.each_with_index do |row, index|
147
+ assert_equal row.except('row_hash'), @test2_rows[index]
148
+ end
133
149
  end
134
- end
135
150
 
136
- should "read #{format}, keeping blank rows" do
137
- t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.#{format}', :keep_blank_rows => true)
138
- # no blank headers
139
- assert t.rows.all? { |row| row.keys.all?(&:present?) }
140
- # correct values
141
- t.rows.each_with_index do |row, index|
142
- assert_equal row.except('row_hash'), @test2_rows_with_blanks[index]
151
+ should "read #{format}, keeping blank rows" do
152
+ t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.#{format}', :keep_blank_rows => true)
153
+ # no blank headers
154
+ assert t.rows.all? { |row| row.keys.all?(&:present?) }
155
+ # correct values
156
+ t.rows.each_with_index do |row, index|
157
+ assert_equal row.except('row_hash'), @test2_rows_with_blanks[index]
158
+ end
143
159
  end
144
- end
145
- }
146
- end
160
+ }
161
+ end
147
162
 
148
- should "read fixed width correctly" do
149
- t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.fixed_width.txt',
150
- :format => :fixed_width,
151
- :skip => 1,
152
- :schema => [[ 'header4', 10, { :type => :string } ],
153
- [ 'spacer', 1 ],
154
- [ 'header5', 10, { :type => :string } ],
155
- [ 'spacer', 12 ],
156
- [ 'header6', 10, { :type => :string } ]])
163
+ should "read fixed width correctly" do
164
+ t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.fixed_width.txt',
165
+ :format => :fixed_width,
166
+ :skip => 1,
167
+ :schema => [[ 'header4', 10, { :type => :string } ],
168
+ [ 'spacer', 1 ],
169
+ [ 'header5', 10, { :type => :string } ],
170
+ [ 'spacer', 12 ],
171
+ [ 'header6', 10, { :type => :string } ]])
157
172
 
158
- # no blank headers
159
- assert t.rows.all? { |row| row.keys.all?(&:present?) }
160
- # correct values
161
- t.rows.each_with_index do |row, index|
162
- assert_equal row.except('row_hash'), @test2_rows[index]
173
+ # no blank headers
174
+ assert t.rows.all? { |row| row.keys.all?(&:present?) }
175
+ # correct values
176
+ t.rows.each_with_index do |row, index|
177
+ assert_equal row.except('row_hash'), @test2_rows[index]
178
+ end
163
179
  end
164
- end
165
180
 
166
- should "read fixed width correctly, keeping blank rows" do
167
- t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.fixed_width.txt',
168
- :format => :fixed_width,
169
- :keep_blank_rows => true,
170
- :skip => 1,
171
- :schema => [[ 'header4', 10, { :type => :string } ],
172
- [ 'spacer', 1 ],
173
- [ 'header5', 10, { :type => :string } ],
174
- [ 'spacer', 12 ],
175
- [ 'header6', 10, { :type => :string } ]])
181
+ should "read fixed width correctly, keeping blank rows" do
182
+ t = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/test2.fixed_width.txt',
183
+ :format => :fixed_width,
184
+ :keep_blank_rows => true,
185
+ :skip => 1,
186
+ :schema => [[ 'header4', 10, { :type => :string } ],
187
+ [ 'spacer', 1 ],
188
+ [ 'header5', 10, { :type => :string } ],
189
+ [ 'spacer', 12 ],
190
+ [ 'header6', 10, { :type => :string } ]])
176
191
 
177
- # no blank headers
178
- assert t.rows.all? { |row| row.keys.all?(&:present?) }
179
- # correct values
180
- t.rows.each_with_index do |row, index|
181
- assert_equal row.except('row_hash'), @test2_rows_with_blanks[index]
192
+ # no blank headers
193
+ assert t.rows.all? { |row| row.keys.all?(&:present?) }
194
+ # correct values
195
+ t.rows.each_with_index do |row, index|
196
+ assert_equal row.except('row_hash'), @test2_rows_with_blanks[index]
197
+ end
182
198
  end
183
- end
184
199
 
185
- should "have the same row hash across formats" do
186
- csv = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.csv')
187
- ods = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.ods')
188
- xls = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.xls')
189
- fixed_width = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.fixed_width.txt',
190
- :format => :fixed_width,
191
- :skip => 1,
192
- :schema => [[ 'header1', 10, { :type => :string } ],
193
- [ 'spacer', 1 ],
194
- [ 'header2', 10, { :type => :string } ],
195
- [ 'spacer', 12 ],
196
- [ 'header3', 10, { :type => :string } ]])
200
+ should "have the same row hash across formats" do
201
+ csv = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.csv')
202
+ ods = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.ods')
203
+ xls = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.xls')
204
+ fixed_width = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.fixed_width.txt',
205
+ :format => :fixed_width,
206
+ :skip => 1,
207
+ :schema => [[ 'header1', 10, { :type => :string } ],
208
+ [ 'spacer', 1 ],
209
+ [ 'header2', 10, { :type => :string } ],
210
+ [ 'spacer', 12 ],
211
+ [ 'header3', 10, { :type => :string } ]])
197
212
 
198
- csv2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.csv')
199
- ods2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.ods')
200
- xls2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.xls')
201
- fixed_width2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.fixed_width.txt',
202
- :format => :fixed_width,
203
- :skip => 1,
204
- :schema => [[ 'spacer', 11 ],
205
- [ 'header2', 10, { :type => :string } ],
206
- [ 'spacer', 1 ],
207
- [ 'header3', 10, { :type => :string } ],
208
- [ 'spacer', 1 ],
209
- [ 'header1', 10, { :type => :string } ]])
213
+ csv2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.csv')
214
+ ods2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.ods')
215
+ xls2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.xls')
216
+ fixed_width2 = RemoteTable.new(:url => 'http://cloud.github.com/downloads/seamusabshere/remote_table/remote_table_row_hash_test.alternate_order.fixed_width.txt',
217
+ :format => :fixed_width,
218
+ :skip => 1,
219
+ :schema => [[ 'spacer', 11 ],
220
+ [ 'header2', 10, { :type => :string } ],
221
+ [ 'spacer', 1 ],
222
+ [ 'header3', 10, { :type => :string } ],
223
+ [ 'spacer', 1 ],
224
+ [ 'header1', 10, { :type => :string } ]])
210
225
 
211
226
 
212
- reference = csv.rows[0]['row_hash']
227
+ reference = csv.rows[0]['row_hash']
213
228
 
214
- # same row hashes
215
- assert_equal reference, ods.rows[0]['row_hash']
216
- assert_equal reference, xls.rows[0]['row_hash']
217
- assert_equal reference, fixed_width.rows[0]['row_hash']
218
- # same row hashes with different order
219
- assert_equal reference, csv2.rows[0]['row_hash']
220
- assert_equal reference, ods2.rows[0]['row_hash']
221
- assert_equal reference, xls2.rows[0]['row_hash']
222
- assert_equal reference, fixed_width2.rows[0]['row_hash']
223
- end
229
+ # same row hashes
230
+ assert_equal reference, ods.rows[0]['row_hash']
231
+ assert_equal reference, xls.rows[0]['row_hash']
232
+ assert_equal reference, fixed_width.rows[0]['row_hash']
233
+ # same row hashes with different order
234
+ assert_equal reference, csv2.rows[0]['row_hash']
235
+ assert_equal reference, ods2.rows[0]['row_hash']
236
+ assert_equal reference, xls2.rows[0]['row_hash']
237
+ assert_equal reference, fixed_width2.rows[0]['row_hash']
238
+ end
224
239
 
225
- should "open an ODS" do
226
- t = RemoteTable.new(:url => 'http://www.worldmapper.org/data/opendoc/2_worldmapper_data.ods', :sheet => 'Data', :keep_blank_rows => true)
240
+ should "open an ODS" do
241
+ t = RemoteTable.new(:url => 'http://www.worldmapper.org/data/opendoc/2_worldmapper_data.ods', :sheet => 'Data', :keep_blank_rows => true)
227
242
 
228
- assert_equal 'Central Africa', t.rows[5]['name']
229
- assert_equal 99, t.rows[5]['MAP DATA population (millions) 2002'].to_i
230
- end
243
+ assert_equal 'Central Africa', t.rows[5]['name']
244
+ assert_equal 99, t.rows[5]['MAP DATA population (millions) 2002'].to_i
245
+ end
231
246
 
232
- should "open a CSV inside a zip file" do
233
- t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/98guide6.zip', :filename => '98guide6.csv')
234
- assert_equal 'ACURA', t.rows.first['Manufacturer']
235
- assert_equal 'NSX', t.rows.first['carline name']
236
- assert_equal 'TOYOTA', t.rows.last['Manufacturer']
237
- assert_equal 'RAV4 SOFT TOP 4WD', t.rows.last['carline name']
238
- end
247
+ should "open a CSV inside a zip file" do
248
+ t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/98guide6.zip', :filename => '98guide6.csv')
249
+ assert_equal 'ACURA', t.rows.first['Manufacturer']
250
+ assert_equal 'NSX', t.rows.first['carline name']
251
+ assert_equal 'TOYOTA', t.rows.last['Manufacturer']
252
+ assert_equal 'RAV4 SOFT TOP 4WD', t.rows.last['carline name']
253
+ end
239
254
 
240
- should "open a fixed-width file with an inline schema inside a zip file" do
241
- t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/00data.zip',
242
- :filename => 'Gd6-dsc.txt',
243
- :format => :fixed_width,
244
- :crop => 21..26, # inclusive
245
- :cut => '2-',
246
- :select => lambda { |row| /\A[A-Z]/.match row['code'] },
247
- :schema => [[ 'code', 2, { :type => :string } ],
248
- [ 'spacer', 2 ],
249
- [ 'name', 52, { :type => :string } ]])
250
- assert_equal 'regular grade gasoline (octane number of 87)', t.rows.first['name']
251
- assert_equal 'R', t.rows.first['code']
252
- assert_equal 'electricity', t.rows.last['name']
253
- assert_equal 'El', t.rows.last['code']
254
- end
255
+ should "open a fixed-width file with an inline schema inside a zip file" do
256
+ t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/00data.zip',
257
+ :filename => 'Gd6-dsc.txt',
258
+ :format => :fixed_width,
259
+ :crop => 21..26, # inclusive
260
+ :cut => '2-',
261
+ :select => lambda { |row| /\A[A-Z]/.match row['code'] },
262
+ :schema => [[ 'code', 2, { :type => :string } ],
263
+ [ 'spacer', 2 ],
264
+ [ 'name', 52, { :type => :string } ]])
265
+ assert_equal 'regular grade gasoline (octane number of 87)', t.rows.first['name']
266
+ assert_equal 'R', t.rows.first['code']
267
+ assert_equal 'electricity', t.rows.last['name']
268
+ assert_equal 'El', t.rows.last['code']
269
+ end
255
270
 
256
- should "open an XLS with a parser" do
257
- ma_1990_01 = {"month"=>1, "cost"=>"54.0", "locatable"=>"Massachusetts (State)", "year"=>1990}
258
- ga_1990_01 = {"month"=>1, "cost"=>"50.7", "locatable"=>"Georgia (State)", "year"=>1990}
271
+ should "open an XLS with a parser" do
272
+ ma_1990_01 = {"month"=>1, "cost"=>"54.0", "locatable"=>"Massachusetts (State)", "year"=>1990}
273
+ ga_1990_01 = {"month"=>1, "cost"=>"50.7", "locatable"=>"Georgia (State)", "year"=>1990}
259
274
 
260
- t = RemoteTable.new(:url => 'http://tonto.eia.doe.gov/dnav/pet/xls/PET_PRI_RESID_A_EPPR_PTA_CPGAL_M.xls',
261
- :transform => { :class => FuelOilParser })
275
+ t = RemoteTable.new(:url => 'http://tonto.eia.doe.gov/dnav/pet/xls/PET_PRI_RESID_A_EPPR_PTA_CPGAL_M.xls',
276
+ :transform => { :class => FuelOilParser })
262
277
 
263
- assert t.rows.include?(ma_1990_01)
264
- assert t.rows.include?(ga_1990_01)
265
- end
278
+ assert t.rows.include?(ma_1990_01)
279
+ assert t.rows.include?(ga_1990_01)
280
+ end
266
281
 
267
- should "provide a row_hash on demand" do
268
- t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/00data.zip',
269
- :filename => 'Gd6-dsc.txt',
270
- :format => :fixed_width,
271
- :crop => 21..26, # inclusive
272
- :cut => '2-',
273
- :select => lambda { |row| /\A[A-Z]/.match row['code'] },
274
- :schema => [[ 'code', 2, { :type => :string } ],
275
- [ 'spacer', 2 ],
276
- [ 'name', 52, { :type => :string } ]])
277
- assert_equal 'a8a5d7f17b56772723c657eb62b0f238', t.rows.first['row_hash']
282
+ should "provide a row_hash on demand" do
283
+ t = RemoteTable.new(:url => 'http://www.fueleconomy.gov/FEG/epadata/00data.zip',
284
+ :filename => 'Gd6-dsc.txt',
285
+ :format => :fixed_width,
286
+ :crop => 21..26, # inclusive
287
+ :cut => '2-',
288
+ :select => lambda { |row| /\A[A-Z]/.match row['code'] },
289
+ :schema => [[ 'code', 2, { :type => :string } ],
290
+ [ 'spacer', 2 ],
291
+ [ 'name', 52, { :type => :string } ]])
292
+ assert_equal 'a8a5d7f17b56772723c657eb62b0f238', t.rows.first['row_hash']
293
+ end
278
294
  end
279
295
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 11
9
- version: 0.2.11
8
+ - 12
9
+ version: 0.2.12
10
10
  platform: ruby
11
11
  authors:
12
12
  - Seamus Abshere
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-04-19 00:00:00 -04:00
18
+ date: 2010-04-22 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -74,6 +74,20 @@ dependencies:
74
74
  version: 0.99.3
75
75
  type: :runtime
76
76
  version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: nokogiri
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 1
86
+ - 4
87
+ - 1
88
+ version: 1.4.1
89
+ type: :runtime
90
+ version_requirements: *id005
77
91
  description: Remotely open and parse Excel XLS, ODS, CSV and fixed-width tables.
78
92
  email: seamus@abshere.net
79
93
  executables: []
@@ -95,6 +109,7 @@ files:
95
109
  - lib/remote_table/file.rb
96
110
  - lib/remote_table/file/csv.rb
97
111
  - lib/remote_table/file/fixed_width.rb
112
+ - lib/remote_table/file/html.rb
98
113
  - lib/remote_table/file/ods.rb
99
114
  - lib/remote_table/file/roo_spreadsheet.rb
100
115
  - lib/remote_table/file/xls.rb