Ugh, Yaml has a <<: merge key. facepalm: true
parent
6895e1276b
commit
00e8b59144
16
README.md
16
README.md
|
|
@ -163,7 +163,7 @@ Working with x-y coordinates all the time can be tiresome, and ideally everythin
|
||||||
|
|
||||||
To use a layout, set the `layout:` option on a `Deck.new` command to point to a YAML file. Any command that allows a `layout` option can be set with a Ruby symbol or String, and the command will then load the specified `x`, `y`, `width`, and `height`. The individual command can also override these options.
|
To use a layout, set the `layout:` option on a `Deck.new` command to point to a YAML file. Any command that allows a `layout` option can be set with a Ruby symbol or String, and the command will then load the specified `x`, `y`, `width`, and `height`. The individual command can also override these options.
|
||||||
|
|
||||||
Note: YAML is very finnicky about having tabs in the file. Use two spaces for indentation instead.
|
Note: YAML is very finnicky about having not allowing tabs. Use two spaces for indentation instead. If you get a `Psych` syntax error, this is likely the culprit. Indendation is also strongly enforced in Yaml too. See the [Yaml docs](http://www.yaml.org/YAML_for_ruby.html).
|
||||||
|
|
||||||
Layouts will override Squib's defaults, but are overriden by anything specified in the command itself. Thus, the order of precedence looks like this:
|
Layouts will override Squib's defaults, but are overriden by anything specified in the command itself. Thus, the order of precedence looks like this:
|
||||||
|
|
||||||
|
|
@ -171,7 +171,17 @@ Layouts will override Squib's defaults, but are overriden by anything specified
|
||||||
* If anything was not yet specified, use what was given in a layout (if a layout was specified in the command and the file was given to the Deck)
|
* If anything was not yet specified, use what was given in a layout (if a layout was specified in the command and the file was given to the Deck)
|
||||||
* If still anything was not yet specified, use what was given in Squib's defaults.
|
* If still anything was not yet specified, use what was given in Squib's defaults.
|
||||||
|
|
||||||
Layouts also allow for a special `extends` field that will copy all of the settings from another entry. Only a single level of extends are supported currently (contact the developer if you want multiple levels).
|
Since Layouts are in Yaml, we have the full power of that data format. One particular feature you should look into are ["merge keys"](http://www.yaml.org/YAML_for_ruby.html#merge_key). With merge keys, you can define base styles in one entry, then include those keys elsewhere. For example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
icon: &icon
|
||||||
|
width: 50
|
||||||
|
height: 50
|
||||||
|
icon_left
|
||||||
|
<<: *icon
|
||||||
|
x: 100
|
||||||
|
# The layout for icon_left will have the width/height from icon!
|
||||||
|
```
|
||||||
|
|
||||||
See the `use_layout` sample found [here](https://github.com/andymeneely/squib/tree/master/samples/)
|
See the `use_layout` sample found [here](https://github.com/andymeneely/squib/tree/master/samples/)
|
||||||
|
|
||||||
|
|
@ -196,7 +206,7 @@ See the `custom_config` sample found [here](https://github.com/andymeneely/squib
|
||||||
|
|
||||||
Squib tries to keep you DRY with the following features:
|
Squib tries to keep you DRY with the following features:
|
||||||
|
|
||||||
* Custom layouts allow you to specify various arguments in a separate file. This is great for x-y coordinates and alignment properties that would otherwise clutter up perfectly readable code. The `extends` takes this a step further and lets you specify base styles that can then be extended by other styles.
|
* Custom layouts allow you to specify various arguments in a separate file. This is great for x-y coordinates and alignment properties that would otherwise clutter up perfectly readable code. Yaml's "merge keys" takes this a step further and lets you specify base styles that can then be extended by other styles.
|
||||||
* Flexible ranges and array handling: the `range` parameter in Squib is very flexible, meaning that one `text` command can specify different text in different fonts, styles, colors, etc. for each card. If you find yourself doing multiple `text` command for the same field across different ranges of cards, there's probably a better way to condense.
|
* Flexible ranges and array handling: the `range` parameter in Squib is very flexible, meaning that one `text` command can specify different text in different fonts, styles, colors, etc. for each card. If you find yourself doing multiple `text` command for the same field across different ranges of cards, there's probably a better way to condense.
|
||||||
* Custom colors keep you from hardcoding magic color strings everywhere. Custom colors are entered into the `config.yml` file.
|
* Custom colors keep you from hardcoding magic color strings everywhere. Custom colors are entered into the `config.yml` file.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,8 @@ module Squib
|
||||||
# @param height: [Integer] the height of each card in pixels
|
# @param height: [Integer] the height of each card in pixels
|
||||||
# @param cards: [Integer] the number of cards in the deck
|
# @param cards: [Integer] the number of cards in the deck
|
||||||
# @param dpi: [Integer] the pixels per inch when rendering out to PDF or calculating using inches.
|
# @param dpi: [Integer] the pixels per inch when rendering out to PDF or calculating using inches.
|
||||||
# @param config: [String] the file used for global settings of this deck
|
# @param config: [String] the Yaml file used for global settings of this deck
|
||||||
|
# @param layout: [String] the Yaml file used for layouts.
|
||||||
# @param block [Block] the main body of the script.
|
# @param block [Block] the main body of the script.
|
||||||
# @api public
|
# @api public
|
||||||
def initialize(width: 825, height: 1125, cards: 1, dpi: 300, config: 'config.yml', layout: nil, &block)
|
def initialize(width: 825, height: 1125, cards: 1, dpi: 300, config: 'config.yml', layout: nil, &block)
|
||||||
|
|
@ -61,7 +62,7 @@ module Squib
|
||||||
@progress_bar = Squib::Progress.new(false)
|
@progress_bar = Squib::Progress.new(false)
|
||||||
cards.times{ @cards << Squib::Card.new(self, width, height) }
|
cards.times{ @cards << Squib::Card.new(self, width, height) }
|
||||||
load_config(config)
|
load_config(config)
|
||||||
load_layout(layout)
|
@layout = YAML.load_file(layout) unless layout.nil?
|
||||||
if block_given?
|
if block_given?
|
||||||
instance_eval(&block)
|
instance_eval(&block)
|
||||||
end
|
end
|
||||||
|
|
@ -98,54 +99,6 @@ module Squib
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Load the layout configuration file, if exists
|
|
||||||
# @api private
|
|
||||||
def load_layout(file)
|
|
||||||
return if file.nil?
|
|
||||||
@layout = {}
|
|
||||||
yml = YAML.load_file(file)
|
|
||||||
yml.each do |key, value|
|
|
||||||
@layout[key] = recurse_extends(yml, key, {})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Process the extends recursively
|
|
||||||
# :nodoc:
|
|
||||||
# @api private
|
|
||||||
def recurse_extends(yml, key, visited )
|
|
||||||
assert_not_visited(key, visited)
|
|
||||||
return yml[key] unless has_extends?(yml, key)
|
|
||||||
visited[key] = key
|
|
||||||
parent_keys = [yml[key]['extends']].flatten
|
|
||||||
h = {}
|
|
||||||
parent_keys.each do |parent_key|
|
|
||||||
from_extends = yml[key].merge(recurse_extends(yml, parent_key, visited)) do |key, child_val, parent_val|
|
|
||||||
child_val #child overrides parent when merging
|
|
||||||
end
|
|
||||||
h = h.merge(from_extends) do |key, older_sibling, younger_sibling|
|
|
||||||
younger_sibling #when two siblings have the same entry, the "younger" (lower one) overrides
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return h
|
|
||||||
end
|
|
||||||
|
|
||||||
# Does this layout entry have an extends field?
|
|
||||||
# i.e. is it a base-case or will it need recursion?
|
|
||||||
# :nodoc:
|
|
||||||
# @api private
|
|
||||||
def has_extends?(yml, key)
|
|
||||||
yml[key].key?('extends')
|
|
||||||
end
|
|
||||||
|
|
||||||
# Safeguard against malformed circular extends
|
|
||||||
# :nodoc:
|
|
||||||
# @api private
|
|
||||||
def assert_not_visited(key, visited)
|
|
||||||
if visited.key? key
|
|
||||||
raise "Invalid layout: circular extends with '#{key}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##################
|
##################
|
||||||
### PUBLIC API ###
|
### PUBLIC API ###
|
||||||
##################
|
##################
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,22 @@ subtitle:
|
||||||
height: 60
|
height: 60
|
||||||
align: !ruby/symbol center
|
align: !ruby/symbol center
|
||||||
valign: !ruby/symbol middle
|
valign: !ruby/symbol middle
|
||||||
icon:
|
|
||||||
|
# Yaml has this beautfiul feature for us: merge keys
|
||||||
|
# http://www.yaml.org/YAML_for_ruby.html#merge_key
|
||||||
|
# Define an alias with &foo, then use <<: *foo to include it
|
||||||
|
# Everything gets merged, with later merges overriding
|
||||||
|
icon: &icon
|
||||||
width: 125
|
width: 125
|
||||||
height: 125
|
height: 125
|
||||||
y: 250
|
y: 250
|
||||||
icon_left:
|
icon_left:
|
||||||
extends: icon
|
<<: *icon
|
||||||
x: 150
|
x: 150
|
||||||
icon_middle:
|
icon_middle:
|
||||||
extends: icon
|
<<: *icon
|
||||||
x: 350
|
x: 350
|
||||||
y: 400 #overrides the y inherited from icon
|
y: 400 #overrides the y inherited from icon
|
||||||
icon_right:
|
icon_right:
|
||||||
extends: icon
|
<<: *icon
|
||||||
x: 550
|
x: 550
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
a:
|
|
||||||
extends: b
|
|
||||||
x: 50
|
|
||||||
b:
|
|
||||||
extends: a
|
|
||||||
x: 150
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
a:
|
|
||||||
extends: c
|
|
||||||
x: 50
|
|
||||||
b:
|
|
||||||
extends: a
|
|
||||||
x: 150
|
|
||||||
c:
|
|
||||||
extends: b
|
|
||||||
y: 250
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
aunt:
|
aunt: &aunt
|
||||||
a: 101
|
a: 101
|
||||||
b: 102
|
b: 102
|
||||||
c: 103
|
c: 103
|
||||||
uncle:
|
uncle: &uncle
|
||||||
x: 104
|
x: 104
|
||||||
y: 105
|
y: 105
|
||||||
b: 106
|
b: 106
|
||||||
child:
|
child:
|
||||||
extends:
|
<<: *uncle
|
||||||
- uncle
|
<<: *aunt
|
||||||
- aunt
|
|
||||||
a: 107
|
a: 107
|
||||||
x: 108
|
x: 108
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
frame:
|
frame: &frame
|
||||||
x: 38
|
x: 38
|
||||||
y: 38
|
y: 38
|
||||||
title:
|
title: &title
|
||||||
extends: frame
|
<<: *frame
|
||||||
y: 50
|
y: 50
|
||||||
width: 100
|
width: 100
|
||||||
subtitle:
|
subtitle:
|
||||||
extends: title
|
<<: *title
|
||||||
y: 150
|
y: 150
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
title:
|
|
||||||
extends: frame
|
|
||||||
y: 50
|
|
||||||
width: 100
|
|
||||||
frame:
|
|
||||||
x: 38
|
|
||||||
y: 38
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
a:
|
|
||||||
extends: a
|
|
||||||
x: 50
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
frame:
|
frame: &frame
|
||||||
x: 38
|
x: 38
|
||||||
y: 38
|
y: 38
|
||||||
title:
|
title:
|
||||||
extends: frame
|
<<: *frame
|
||||||
y: 50
|
y: 50
|
||||||
width: 100
|
width: 100
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
frame:
|
frame: &frame
|
||||||
x: 38
|
x: 38
|
||||||
y: 38
|
y: 38
|
||||||
title:
|
title:
|
||||||
extends: frame
|
<<: *frame
|
||||||
y: 50
|
y: 50
|
||||||
width: 100
|
width: 100
|
||||||
title2:
|
title2:
|
||||||
extends: frame
|
<<: *frame
|
||||||
x: 75
|
x: 75
|
||||||
y: 150
|
y: 150
|
||||||
width: 150
|
width: 150
|
||||||
|
|
@ -68,24 +68,6 @@ describe Squib::Deck do
|
||||||
'y' => 38,
|
'y' => 38,
|
||||||
},
|
},
|
||||||
'title' => {
|
'title' => {
|
||||||
'extends' => 'frame',
|
|
||||||
'x' => 38,
|
|
||||||
'y' => 50,
|
|
||||||
'width' => 100,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "applies the extends regardless of order" do
|
|
||||||
d = Squib::Deck.new(layout: test_file('pre-extends.yml'))
|
|
||||||
expect(d.layout).to \
|
|
||||||
eq({'frame' => {
|
|
||||||
'x' => 38,
|
|
||||||
'y' => 38,
|
|
||||||
},
|
|
||||||
'title' => {
|
|
||||||
'extends' => 'frame',
|
|
||||||
'x' => 38,
|
'x' => 38,
|
||||||
'y' => 50,
|
'y' => 50,
|
||||||
'width' => 100,
|
'width' => 100,
|
||||||
|
|
@ -102,13 +84,11 @@ describe Squib::Deck do
|
||||||
'y' => 38,
|
'y' => 38,
|
||||||
},
|
},
|
||||||
'title' => {
|
'title' => {
|
||||||
'extends' => 'frame',
|
|
||||||
'x' => 38,
|
'x' => 38,
|
||||||
'y' => 50,
|
'y' => 50,
|
||||||
'width' => 100,
|
'width' => 100,
|
||||||
},
|
},
|
||||||
'title2' => {
|
'title2' => {
|
||||||
'extends' => 'frame',
|
|
||||||
'x' => 75,
|
'x' => 75,
|
||||||
'y' => 150,
|
'y' => 150,
|
||||||
'width' => 150,
|
'width' => 150,
|
||||||
|
|
@ -131,7 +111,6 @@ describe Squib::Deck do
|
||||||
'b' => 106,
|
'b' => 106,
|
||||||
},
|
},
|
||||||
'child' => {
|
'child' => {
|
||||||
'extends' => ['uncle','aunt'],
|
|
||||||
'a' => 107, # my own
|
'a' => 107, # my own
|
||||||
'b' => 102, # from the younger aunt
|
'b' => 102, # from the younger aunt
|
||||||
'c' => 103, # from aunt
|
'c' => 103, # from aunt
|
||||||
|
|
@ -150,13 +129,11 @@ describe Squib::Deck do
|
||||||
'y' => 38,
|
'y' => 38,
|
||||||
},
|
},
|
||||||
'title' => {
|
'title' => {
|
||||||
'extends' => 'frame',
|
|
||||||
'x' => 38,
|
'x' => 38,
|
||||||
'y' => 50,
|
'y' => 50,
|
||||||
'width' => 100,
|
'width' => 100,
|
||||||
},
|
},
|
||||||
'subtitle' => {
|
'subtitle' => {
|
||||||
'extends' => 'title',
|
|
||||||
'x' => 38,
|
'x' => 38,
|
||||||
'y' => 150,
|
'y' => 150,
|
||||||
'width' => 100,
|
'width' => 100,
|
||||||
|
|
@ -165,24 +142,6 @@ describe Squib::Deck do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails on a self-circular extends" do
|
|
||||||
file = test_file('self-circular-extends.yml')
|
|
||||||
expect { Squib::Deck.new(layout: file) }.to \
|
|
||||||
raise_error(RuntimeError, "Invalid layout: circular extends with 'a'")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "fails on a easy-circular extends" do
|
|
||||||
file = test_file('easy-circular-extends.yml')
|
|
||||||
expect { Squib::Deck.new(layout: file) }.to \
|
|
||||||
raise_error(RuntimeError, "Invalid layout: circular extends with 'a'")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "hard on a easy-circular extends" do
|
|
||||||
file = test_file('hard-circular-extends.yml')
|
|
||||||
expect { Squib::Deck.new(layout: file) }.to \
|
|
||||||
raise_error(RuntimeError, "Invalid layout: circular extends with 'a'")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue