From f140ff32fa25abc5924a8793c48d19dea2333ded Mon Sep 17 00:00:00 2001 From: Andy Meneely Date: Sat, 15 Apr 2017 21:25:34 -0400 Subject: [PATCH] Cleanup and tests of yaml method --- CHANGELOG.md | 3 +- docs/dsl/yaml.rst | 73 +++++++++++++++++++++++++++++++++++++++ lib/squib/api/data.rb | 22 ++++++++---- spec/api/api_data_spec.rb | 40 +++++++++++++++++++++ spec/data/yaml/basic.yml | 6 ++++ spec/data/yaml/nilly.yml | 3 ++ spec/data/yaml/qty.yml | 4 +++ spec/spec_helper.rb | 4 +++ 8 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 docs/dsl/yaml.rst create mode 100644 spec/data/yaml/basic.yml create mode 100644 spec/data/yaml/nilly.yml create mode 100644 spec/data/yaml/qty.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index f729c3c..b6ba38a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ Squib follows [semantic versioning](http://semver.org). Features: * `circle` method now supports various `arc` options, so you can draw partial circles (#211) by @sparr * `save_sheet` method now supports `rtl` or "right-to-left", for easier duplex printing of backs (#204, #208) by @sparr +* `yaml` method for reading in data, much like `csv` and `xlsx` by @blinks -Special thanks to @sparr for all of his work!! +Special thanks to @sparr and @blinks for all of their work!! ## v0.13.2 / 2017-01-27 diff --git a/docs/dsl/yaml.rst b/docs/dsl/yaml.rst new file mode 100644 index 0000000..52381ba --- /dev/null +++ b/docs/dsl/yaml.rst @@ -0,0 +1,73 @@ +yaml +==== + +Pulls deck data from a YAML files into a ``Squib::DataFrame`` (essentially a hash of arrays). + +Parsing uses Ruby's built-in Yaml package. + +The ``yaml`` method is a member of ``Squib::Deck``, but it is also available outside of the Deck DSL with ``Squib.yaml()``. This allows a construction like:: + + data = Squib.Yaml file: 'data.yml' + Squib::Deck.new(cards: data['name'].size) do + end + +The Yaml file format assumes that the entire deck is an array, then each element of the array is a hash. Every key encountered in that hash will translate to a "column" in the data frame. If a key exists in one card and not in another, then it defaults to ``nil``. + +.. warning:: + + Case matters in your Yaml keys. + +Options +------- + +file + default: ``'deck.yml'`` + + the YAML-formatted file to open. Opens relative to the current directory. If ``data`` is set, this option is overridden. + +data + default: ``nil`` + + when set, method will parse this Yaml data instead of reading the file. + +explode + default: ``'qty'`` + + Quantity explosion will be applied to the column this name. For example, rows in the csv with a ``'qty'`` of 3 will be duplicated 3 times. + + +Individual Pre-processing +------------------------- + +The ``yaml`` method also takes in a block that will be executed for each cell in your data. This is useful for processing individual cells, like putting a dollar sign in front of dollars, or converting from a float to an integer. The value of the block will be what is assigned to that cell. For example:: + + resource_data = Squib.yaml(file: 'sample.yaml') do |header, value| + case header + when 'Cost' + "$#{value}k" # e.g. "3" becomes "$3k" + else + value # always return the original value if you didn't do anything to it + end + end + +Examples +-------- + + +To get the sample Excel files, go to `its source `_ + +.. literalinclude:: ../../samples/data/_csv.rb + :language: ruby + :linenos: + +Here's the sample.csv + +.. literalinclude:: ../../samples/data/sample.csv + :language: text + :linenos: + +Here's the quantity_explosion.csv + +.. literalinclude:: ../../samples/data/quantity_explosion.csv + :language: text + :linenos: diff --git a/lib/squib/api/data.rb b/lib/squib/api/data.rb index f142027..a834c98 100644 --- a/lib/squib/api/data.rb +++ b/lib/squib/api/data.rb @@ -67,19 +67,27 @@ module Squib return explode_quantities(hash, import.explode) end module_function :csv - + # DSL method. See http://squib.readthedocs.io def yaml(opts = {}) input = Args::InputFile.new(file: 'deck.yml').load!(opts) import = Args::Import.new.load!(opts) - s = YAML.load_file(input.file[0]) + yml = YAML.load_file(input.file[0]) data = Squib::DataFrame.new # Get a universal list of keys to ensure everything is covered. - keys = s.map {|c| c.keys}.flatten.uniq - # Initialize the data frame; why is [] not the default value? - keys.each {|k| data[k] = [] } - # Load all cards into the frame, nil value if key isn't set. - s.each {|card| keys.each {|k| data[k] << card[k] } } + keys = yml.map { |c| c.keys}.flatten.uniq + keys.each { |k| data[k] = [] } #init arrays + yml.each do |card| + # nil value if key isn't set. + keys.each { |k| data[k] << card[k] } + end + if block_given? + data.each do |header, col| + col.map! do |val| + yield(header, val) + end + end + end explode_quantities(data, import.explode) end module_function :yaml diff --git a/spec/api/api_data_spec.rb b/spec/api/api_data_spec.rb index 4993eb9..074482e 100644 --- a/spec/api/api_data_spec.rb +++ b/spec/api/api_data_spec.rb @@ -169,4 +169,44 @@ describe Squib::Deck do end end + + context '#yaml' do + it 'loads basic data' do + expect(Squib.yaml(file: yaml_file('basic.yml')).to_h).to eq({ + 'Name' => %w(Larry Curly Mo), + 'Number' => [4.0, 5.0, 6.0], # numbers get auto-converted to integers + }) + end + + it 'explodes quantities' do + expect(Squib.yaml(explode: 'qty', file: yaml_file('qty.yml')).to_h).to eq({ + 'name' => %w(ha ha he), + 'qty' => [2, 2, 1], + }) + end + + it 'handles silence' do + expect(Squib.yaml(file: yaml_file('nilly.yml')).to_h).to eq({ + 'name' => %w(foo bar), + 'desc' => [nil, 'Hello'], + }) + end + + it 'yields to block when given' do + data = Squib.yaml(file: yaml_file('basic.yml')) do |header, value| + case header + when 'Name' + 'he' + when 'Number' + value * 2 + else + 'ha' + end + end + expect(data.to_h).to eq({ + 'Name' => %w(he he he), + 'Number' => [8.0, 10.0, 12.0], + }) + end + end end diff --git a/spec/data/yaml/basic.yml b/spec/data/yaml/basic.yml new file mode 100644 index 0000000..a23f26a --- /dev/null +++ b/spec/data/yaml/basic.yml @@ -0,0 +1,6 @@ +- Name: Larry + Number: 4.0 +- Name: Curly + Number: 5.0 +- Name: Mo + Number: 6.0 diff --git a/spec/data/yaml/nilly.yml b/spec/data/yaml/nilly.yml new file mode 100644 index 0000000..e7ac7ca --- /dev/null +++ b/spec/data/yaml/nilly.yml @@ -0,0 +1,3 @@ +- name: foo +- name: bar + desc: Hello diff --git a/spec/data/yaml/qty.yml b/spec/data/yaml/qty.yml new file mode 100644 index 0000000..6eeb25a --- /dev/null +++ b/spec/data/yaml/qty.yml @@ -0,0 +1,4 @@ +- name: ha + qty: 2 +- name: he + qty: 1 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8c69de9..cd9e39b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -46,6 +46,10 @@ def xlsx_file(file) "#{File.expand_path(File.dirname(__FILE__))}/data/xlsx/#{file}" end +def yaml_file(file) + "#{File.expand_path(File.dirname(__FILE__))}/data/yaml/#{file}" +end + def project_template(file) "#{File.expand_path(File.dirname(__FILE__))}/../lib/squib/project_template/#{file}" end