Finished CSV feature
parent
501dc417d0
commit
04b55d8b32
|
|
@ -1,5 +1,6 @@
|
||||||
# Squib CHANGELOG
|
# Squib CHANGELOG
|
||||||
|
|
||||||
|
# Added a `csv` command that works just like `xslx`. Uses Ruby's CSV inside, with some extra checking and warnings.
|
||||||
# Custom layouts now support loading & merging multiple Yaml files! Updated README, docs, and sample to document it.
|
# Custom layouts now support loading & merging multiple Yaml files! Updated README, docs, and sample to document it.
|
||||||
# Built-in layouts! Currently we support `hand.yml` and `playing-card.yml`. Documented in the `layouts.rb` sample.
|
# Built-in layouts! Currently we support `hand.yml` and `playing-card.yml`. Documented in the `layouts.rb` sample.
|
||||||
# `text` now returns the ink extent rectangle of the rendered text. Updated docs and sample to document it.
|
# `text` now returns the ink extent rectangle of the rendered text. Updated docs and sample to document it.
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -4,7 +4,7 @@ Squib is a Ruby [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) for
|
||||||
* A concise set of rules for laying out your cards
|
* A concise set of rules for laying out your cards
|
||||||
* Loading PNGs and SVGs using [Cairo](http://cairographics.org/)
|
* Loading PNGs and SVGs using [Cairo](http://cairographics.org/)
|
||||||
* Complex text rendering using [Pango](http://www.pango.org/)
|
* Complex text rendering using [Pango](http://www.pango.org/)
|
||||||
* Reading `.xlsx` files
|
* Reading `xlsx` and `csv` files
|
||||||
* Basic shape drawing
|
* Basic shape drawing
|
||||||
* Rendering decks to PNGs and PDFs
|
* Rendering decks to PNGs and PDFs
|
||||||
* Data-driven layouts
|
* Data-driven layouts
|
||||||
|
|
@ -255,6 +255,14 @@ See the `custom_config` sample found [here](https://github.com/andymeneely/squib
|
||||||
|
|
||||||
{include:file:samples/custom_config.rb}
|
{include:file:samples/custom_config.rb}
|
||||||
|
|
||||||
|
## Importing from Excel and CSV
|
||||||
|
|
||||||
|
Squib supports importing data from `xlsx` files and `csv` files. These methods are column-based, which means that they assume you have a header row in your table, and that header row will define the column. Squib will return a `Hash` of `Arrays` correspoding to each row. Warnings are thrown on things like duplicate columns. See the `excel.rb` and the `csv_import.rb` sample found [here](https://github.com/andymeneely/squib/tree/master/samples/).
|
||||||
|
|
||||||
|
{include:file:samples/excel.rb}
|
||||||
|
|
||||||
|
Of course, you can always import your game data other ways using just Ruby. There's nothing special about Squib's methods other than their convenience.
|
||||||
|
|
||||||
## Making Squib Verbose
|
## Making Squib Verbose
|
||||||
|
|
||||||
By default, Squib's logger is set to WARN, but more fine-grained logging is embedded in the code. To set the logger, just put this at the top of your script:
|
By default, Squib's logger is set to WARN, but more fine-grained logging is embedded in the code. To set the logger, just put this at the top of your script:
|
||||||
|
|
|
||||||
|
|
@ -63,13 +63,26 @@ module Squib
|
||||||
def csv(opts = {})
|
def csv(opts = {})
|
||||||
opts = Squib::SYSTEM_DEFAULTS.merge(opts)
|
opts = Squib::SYSTEM_DEFAULTS.merge(opts)
|
||||||
opts = Squib::InputHelpers.fileify(opts)
|
opts = Squib::InputHelpers.fileify(opts)
|
||||||
hash = {}
|
table = CSV.read(opts[:file], headers: true, converters: :numeric)
|
||||||
csv = CSV.open(opts[:file], headers: true, converters: :numeric).read
|
check_duplicate_csv_headers(table)
|
||||||
|
hash = Hash.new
|
||||||
|
table.headers.each do |header|
|
||||||
|
hash[header.to_s] ||= table[header]
|
||||||
|
end
|
||||||
return hash
|
return hash
|
||||||
end
|
end
|
||||||
module_function :csv
|
module_function :csv
|
||||||
|
|
||||||
|
# Check if the given CSV table has duplicate columns, and throw a warning
|
||||||
|
# @api private
|
||||||
|
def check_duplicate_csv_headers(table)
|
||||||
|
if table.headers.size != table.headers.uniq.size
|
||||||
|
dups = table.headers.select{|e| table.headers.count(e) > 1 }
|
||||||
|
Squib.logger.warn "CSV duplicated the following column keys: #{dups.join(',')}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :check_duplicate_csv_headers
|
||||||
|
|
||||||
class Deck
|
class Deck
|
||||||
|
|
||||||
# Convenience call on deck goes to the module function
|
# Convenience call on deck goes to the module function
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,8 @@ Squib::Deck.new(cards: 2) do
|
||||||
# You can also specify the sheet, starting at 0
|
# You can also specify the sheet, starting at 0
|
||||||
data = xlsx file: 'sample.xlsx', sheet: 2
|
data = xlsx file: 'sample.xlsx', sheet: 2
|
||||||
|
|
||||||
save format: :png, prefix: 'sample_excel_'
|
save format: :png, prefix: 'sample_csv_'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# CSV is also a Squib-module-level function, so this also works:
|
||||||
|
data = Squib.csv file: 'sample.csv'
|
||||||
|
|
@ -2,11 +2,30 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe Squib::Deck do
|
describe Squib::Deck do
|
||||||
context '#csv' do
|
context '#csv' do
|
||||||
# it 'loads basic csv data' do
|
it 'loads basic csv data' do
|
||||||
# expect(Squib.csv(file: csv_file('basic.csv'))).to eq({
|
expect(Squib.csv(file: csv_file('basic.csv'))).to eq({
|
||||||
# 'h1' => [1, 3],
|
'h1' => [1, 3],
|
||||||
# 'h2' => [2, 4]
|
'h2' => [2, 4]
|
||||||
# })
|
})
|
||||||
# end
|
end
|
||||||
|
|
||||||
|
it 'collapses duplicate columns and warns' do
|
||||||
|
expect(Squib.logger).to receive(:warn)
|
||||||
|
.with('CSV duplicated the following column keys: h1,h1')
|
||||||
|
expect(Squib.csv(file: csv_file('dup_cols.csv'))).to eq({
|
||||||
|
'h1' => [1, 3],
|
||||||
|
'h2' => [5, 7],
|
||||||
|
'H2' => [6, 8],
|
||||||
|
'h3' => [9, 10],
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'handles spaces properly' do
|
||||||
|
expect(Squib.csv(file: csv_file('with_spaces.csv'))).to eq({
|
||||||
|
'With Spaces' => ['a b c ', 3],
|
||||||
|
'h2' => [2, 4],
|
||||||
|
'h3' => [3, nil]
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
h1,h1,h2,H2,h3
|
||||||
|
1,2,5,6,9
|
||||||
|
3,4,7,8,10
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
With Spaces,h2,h3
|
||||||
|
a b c , 2,3
|
||||||
|
3 ,4
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=1.0, @green=1.0, @blue=1.0>])
|
||||||
|
cairo: paint([])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=1.0, @green=1.0, @blue=1.0>])
|
||||||
|
cairo: paint([])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=0.0, @green=0.0, @blue=0.0>])
|
||||||
|
cairo: translate([250, 55])
|
||||||
|
cairo: rotate([0])
|
||||||
|
cairo: translate([-250, -55])
|
||||||
|
cairo: move_to([250, 55])
|
||||||
|
pango: font_description=([])
|
||||||
|
pango: text=([""])
|
||||||
|
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||||
|
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||||
|
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||||
|
pango: justify=([false])
|
||||||
|
pango: spacing=([0])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: show_pango_layout([MockDouble])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=0.0, @green=0.0, @blue=0.0>])
|
||||||
|
cairo: translate([250, 55])
|
||||||
|
cairo: rotate([0])
|
||||||
|
cairo: translate([-250, -55])
|
||||||
|
cairo: move_to([250, 55])
|
||||||
|
pango: font_description=([])
|
||||||
|
pango: text=([""])
|
||||||
|
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||||
|
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||||
|
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||||
|
pango: justify=([false])
|
||||||
|
pango: spacing=([0])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: show_pango_layout([MockDouble])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=0.0, @green=0.0, @blue=0.0>])
|
||||||
|
cairo: translate([65, 65])
|
||||||
|
cairo: rotate([0])
|
||||||
|
cairo: translate([-65, -65])
|
||||||
|
cairo: move_to([65, 65])
|
||||||
|
pango: font_description=([])
|
||||||
|
pango: text=([""])
|
||||||
|
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||||
|
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||||
|
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||||
|
pango: justify=([false])
|
||||||
|
pango: spacing=([0])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: show_pango_layout([MockDouble])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=0.0, @green=0.0, @blue=0.0>])
|
||||||
|
cairo: translate([65, 65])
|
||||||
|
cairo: rotate([0])
|
||||||
|
cairo: translate([-65, -65])
|
||||||
|
cairo: move_to([65, 65])
|
||||||
|
pango: font_description=([])
|
||||||
|
pango: text=([""])
|
||||||
|
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||||
|
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||||
|
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||||
|
pango: justify=([false])
|
||||||
|
pango: spacing=([0])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: show_pango_layout([MockDouble])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=0.0, @green=0.0, @blue=0.0>])
|
||||||
|
cairo: translate([65, 600])
|
||||||
|
cairo: rotate([0])
|
||||||
|
cairo: translate([-65, -600])
|
||||||
|
cairo: move_to([65, 600])
|
||||||
|
pango: font_description=([])
|
||||||
|
pango: text=([""])
|
||||||
|
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||||
|
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||||
|
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||||
|
pango: justify=([false])
|
||||||
|
pango: spacing=([0])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: show_pango_layout([MockDouble])
|
||||||
|
cairo: restore([])
|
||||||
|
cairo: save([])
|
||||||
|
cairo: set_source_color([#<Cairo::Color::RGB: @alpha=1.0, @red=0.0, @green=0.0, @blue=0.0>])
|
||||||
|
cairo: translate([65, 600])
|
||||||
|
cairo: rotate([0])
|
||||||
|
cairo: translate([-65, -600])
|
||||||
|
cairo: move_to([65, 600])
|
||||||
|
pango: font_description=([])
|
||||||
|
pango: text=([""])
|
||||||
|
pango: wrap=([#<Pango::Layout::WrapMode word-char>])
|
||||||
|
pango: ellipsize=([#<Pango::Layout::EllipsizeMode end>])
|
||||||
|
pango: alignment=([#<Pango::Layout::Alignment left>])
|
||||||
|
pango: justify=([false])
|
||||||
|
pango: spacing=([0])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: update_pango_layout([MockDouble])
|
||||||
|
cairo: show_pango_layout([MockDouble])
|
||||||
|
cairo: restore([])
|
||||||
|
surface: write_to_png(["_output/sample_csv_0.png"])
|
||||||
|
surface: write_to_png(["_output/sample_csv_1.png"])
|
||||||
|
|
@ -54,6 +54,7 @@ describe "Squib samples" do
|
||||||
draw_shapes.rb
|
draw_shapes.rb
|
||||||
colors.rb
|
colors.rb
|
||||||
excel.rb
|
excel.rb
|
||||||
|
csv_import.rb
|
||||||
portrait-landscape.rb
|
portrait-landscape.rb
|
||||||
tgc_proofs.rb
|
tgc_proofs.rb
|
||||||
ranges.rb
|
ranges.rb
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue