class TableSetter::Table
Attributes
The Table
class handles processing the yaml processing and csv loading, through table fu
The Table
class handles processing the yaml processing and csv loading, through table fu
The Table
class handles processing the yaml processing and csv loading, through table fu
The Table
class handles processing the yaml processing and csv loading, through table fu
The Table
class handles processing the yaml processing and csv loading, through table fu
The Table
class handles processing the yaml processing and csv loading, through table fu
Public Class Methods
Returns all the tables in the table directory. Each table is deferred so accessing the @data attribute will throw and error.
# File lib/table_setter/table.rb, line 179 def all tables=[] Dir.glob("#{TableSetter.table_path}/*.yml").each do |file| table = new(File.basename(file, ".yml"), :defer => true) tables << table if table.live end tables end
Does a table with this slug exist?
# File lib/table_setter/table.rb, line 210 def exists?(slug) File.exists? table_path(slug) end
fresh_yaml_time
checks each file in the tables directory and returns the newest file's modification time – there's probably a more unix-y way to do this but for now this is plenty speedy.
# File lib/table_setter/table.rb, line 191 def fresh_yaml_time newest_file = Dir["#{TableSetter.table_path}/*.yml"].inject do |memo, obj| memo_time = File.new(File.expand_path memo).mtime obj_time = File.new(File.expand_path obj).mtime if memo_time > obj_time memo else obj end end File.new(newest_file).mtime end
A new Table
should accept a slug, mapped to a yaml in the tables directory, optionally you can defer loading of the table until you're ready to render it.
# File lib/table_setter/table.rb, line 14 def initialize(slug, opts={:defer => false}) options = indifferent_access YAML.load_file(Table.table_path(slug)) @table_opts = options[:table] @table_opts[:slug] = slug @deferred = opts[:defer] if !@deferred self.load end end
Convenience method for looking up by slug.
# File lib/table_setter/table.rb, line 205 def table_path(slug) "#{TableSetter.table_path}#{slug}.yml" end
Public Instance Methods
The csv_data
for the table fu instance is loaded either from the remote source or from a local file, depending on the keys present in the yaml file.
# File lib/table_setter/table.rb, line 41 def csv_data case when google_key || url then Curl::Easy.perform(uri).body_str when file then File.open(uri).read end end
# File lib/table_setter/table.rb, line 64 def faceted? !@facets.nil? end
hard_paginate instructs the app to render batches of a table.
# File lib/table_setter/table.rb, line 74 def hard_paginate? @table_opts[:hard_paginate] == true end
The load method handles the actual request either to the file system or remote url. It performs the requested data manipulations form the yml file after the data has been loaded. We're keeping this explicit to control against unnecessary http requests.
# File lib/table_setter/table.rb, line 27 def load if @table_opts[:column_options] @table_opts[:column_options]['style'] ||= {} end @data = TableFu.new(csv_data, @table_opts[:column_options] || {}) if @table_opts[:faceting] @data.col_opts[:ignored] = [@table_opts[:faceting][:facet_by]] @facets = @data.faceted_by @table_opts[:faceting][:facet_by] end @data.delete_rows! @table_opts[:dead_rows] if @table_opts[:dead_rows] end
We magically need access to the top level keys like google_key, or uri for the other methods. It's a bit dangerous because everything returns nil otherwise. At some point we should eval and create methods at boot time.
# File lib/table_setter/table.rb, line 114 def method_missing(method) if @table_opts[method] @table_opts[method] end end
paginate uses TableFu's only! method to batch the table. It also computes the page attributes which are nil and meaningless otherwise.
# File lib/table_setter/table.rb, line 85 def paginate!(curr_page) return if !hard_paginate? @page = curr_page.to_i raise ArgumentError if @page < 1 || @page > total_pages adj_page = @page - 1 > 0 ? @page - 1 : 0 @prev_page = adj_page > 0 ? adj_page : nil @next_page = page < total_pages ? (@page + 1) : nil @data.only!(adj_page * per_page..(@page * per_page - 1)) end
The number of rows per page. Defaults to 20
# File lib/table_setter/table.rb, line 79 def per_page @table_opts[:per_page] || 20 end
A convienence method to return the sort array for table setter.
# File lib/table_setter/table.rb, line 103 def sort_array if @data.sorted_by @data.sorted_by.inject([]) do |memo, (key, value)| memo << [@data.columns.index(key), value == 'descending' ? 1 : 0] end end end
A table isn't sortable by tablesorter if it's either faceted or multi-page paginated.
# File lib/table_setter/table.rb, line 69 def sortable? !faceted? && !hard_paginate? end
The total pages we'll have. We need to calculate it before paginate, so that we still have the full @data.rows.length
# File lib/table_setter/table.rb, line 98 def total_pages @total_pages ||= (@data.rows.length / per_page.to_f).ceil end
The real updated_at
of a Table
instance is the newer modification time of the csv file or the yaml file. Updates to either resource should break the cache.
# File lib/table_setter/table.rb, line 59 def updated_at csv_time = google_key.nil? ? modification_time(uri) : google_modification_time (csv_time > yaml_time ? csv_time : yaml_time).to_s end
Returns a usable uri based on what sort of input we have.
# File lib/table_setter/table.rb, line 49 def uri case when google_key then "https://docs.google.com/spreadsheet/pub?key=#{google_key}&output=csv&single=true&gid=0" when url then url when file then File.expand_path("#{TableSetter.table_path}#{file}") end end
Private Instance Methods
Returns the google modification time of the spreadsheet. The public urls don't set the last-modified header on anything, so we have to do a little dance to find out when exactly the spreadsheet was last modified. The od part of the feed url changes at whim, so we'll need to keep an eye on it. Another problem is that curb doesn't feel like parsing headers, so since a head request from google is pretty lightweight we can get away with using Net:HTTP. If for whatever reason the google modification time is busted we'll the yaml modified time.
# File lib/table_setter/table.rb, line 128 def google_modification_time local_url = URI.parse "http://spreadsheets.google.com/feeds/list/#{google_key}/od6/public/basic" web_modification_time local_url end
Enable string or symbol key access to col_opts from sinatra.
# File lib/table_setter/table.rb, line 160 def indifferent_access(params) params = indifferent_hash.merge(params) params.each do |key, value| next unless value.is_a?(Hash) params[key] = indifferent_access(value) end end
Duplicate a hash's keys and convert them into symbols.
# File lib/table_setter/table.rb, line 169 def indifferent_hash Hash.new {|hash,key| hash[key.to_s] if Symbol === key } end
Dispatches to web_modification_time
if we're dealing with a url, otherwise just stats the local file.
# File lib/table_setter/table.rb, line 145 def modification_time(path) is_uri = URI.parse(path) if !is_uri.host.nil? return web_modification_time is_uri end File.new(path).mtime end
Returns the last-modified time from the remote server. Assumes the remote server knows how to do this. Returns the epoch if the remote is dense.
# File lib/table_setter/table.rb, line 135 def web_modification_time(local_url) resp = nil Net::HTTP.start(local_url.host, 80) do |http| resp = http.head(local_url.path) end resp['Last-Modified'].nil? ? Time.at(0) : Time.parse(resp['Last-Modified']) end
The modification time of this Table's yaml file.
# File lib/table_setter/table.rb, line 154 def yaml_time modification_time(Table.table_path(slug)) end