From 00e8b591444566bce720f08b971de55d9dd3f650 Mon Sep 17 00:00:00 2001 From: Andy Meneely Date: Thu, 14 Aug 2014 17:02:28 -0400 Subject: [PATCH] Ugh, Yaml has a <<: merge key. facepalm: true --- README.md | 16 +++++-- lib/squib/deck.rb | 53 ++---------------------- samples/custom-layout.yml | 13 ++++-- spec/data/easy-circular-extends.yml | 6 --- spec/data/hard-circular-extends.yml | 9 ---- spec/data/multi-extends-single-entry.yml | 9 ++-- spec/data/multi-level-extends.yml | 10 ++--- spec/data/pre-extends.yml | 7 ---- spec/data/self-circular-extends.yml | 3 -- spec/data/single-extends.yml | 4 +- spec/data/single-level-multi-extends.yml | 6 +-- spec/deck_spec.rb | 41 ------------------ 12 files changed, 39 insertions(+), 138 deletions(-) delete mode 100644 spec/data/easy-circular-extends.yml delete mode 100644 spec/data/hard-circular-extends.yml delete mode 100644 spec/data/pre-extends.yml delete mode 100644 spec/data/self-circular-extends.yml diff --git a/README.md b/README.md index 6b91aef..baca0f4 100644 --- a/README.md +++ b/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. -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: @@ -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 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/) @@ -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: -* 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. * Custom colors keep you from hardcoding magic color strings everywhere. Custom colors are entered into the `config.yml` file. diff --git a/lib/squib/deck.rb b/lib/squib/deck.rb index 881ab20..88b74ba 100644 --- a/lib/squib/deck.rb +++ b/lib/squib/deck.rb @@ -49,7 +49,8 @@ module Squib # @param height: [Integer] the height of each card in pixels # @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 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. # @api public 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) cards.times{ @cards << Squib::Card.new(self, width, height) } load_config(config) - load_layout(layout) + @layout = YAML.load_file(layout) unless layout.nil? if block_given? instance_eval(&block) end @@ -98,54 +99,6 @@ module Squib 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 ### ################## diff --git a/samples/custom-layout.yml b/samples/custom-layout.yml index 409afd6..2042952 100644 --- a/samples/custom-layout.yml +++ b/samples/custom-layout.yml @@ -18,17 +18,22 @@ subtitle: height: 60 align: !ruby/symbol center 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 height: 125 y: 250 icon_left: - extends: icon + <<: *icon x: 150 icon_middle: - extends: icon + <<: *icon x: 350 y: 400 #overrides the y inherited from icon icon_right: - extends: icon + <<: *icon x: 550 diff --git a/spec/data/easy-circular-extends.yml b/spec/data/easy-circular-extends.yml deleted file mode 100644 index ccfc559..0000000 --- a/spec/data/easy-circular-extends.yml +++ /dev/null @@ -1,6 +0,0 @@ -a: - extends: b - x: 50 -b: - extends: a - x: 150 diff --git a/spec/data/hard-circular-extends.yml b/spec/data/hard-circular-extends.yml deleted file mode 100644 index 6ef16fc..0000000 --- a/spec/data/hard-circular-extends.yml +++ /dev/null @@ -1,9 +0,0 @@ -a: - extends: c - x: 50 -b: - extends: a - x: 150 -c: - extends: b - y: 250 \ No newline at end of file diff --git a/spec/data/multi-extends-single-entry.yml b/spec/data/multi-extends-single-entry.yml index 805c98b..0fb15cb 100644 --- a/spec/data/multi-extends-single-entry.yml +++ b/spec/data/multi-extends-single-entry.yml @@ -1,14 +1,13 @@ -aunt: +aunt: &aunt a: 101 b: 102 c: 103 -uncle: +uncle: &uncle x: 104 y: 105 b: 106 child: - extends: - - uncle - - aunt + <<: *uncle + <<: *aunt a: 107 x: 108 diff --git a/spec/data/multi-level-extends.yml b/spec/data/multi-level-extends.yml index 9f8867e..a37b5b5 100644 --- a/spec/data/multi-level-extends.yml +++ b/spec/data/multi-level-extends.yml @@ -1,10 +1,10 @@ -frame: +frame: &frame x: 38 y: 38 -title: - extends: frame +title: &title + <<: *frame y: 50 width: 100 -subtitle: - extends: title +subtitle: + <<: *title y: 150 \ No newline at end of file diff --git a/spec/data/pre-extends.yml b/spec/data/pre-extends.yml deleted file mode 100644 index 753cd5a..0000000 --- a/spec/data/pre-extends.yml +++ /dev/null @@ -1,7 +0,0 @@ -title: - extends: frame - y: 50 - width: 100 -frame: - x: 38 - y: 38 diff --git a/spec/data/self-circular-extends.yml b/spec/data/self-circular-extends.yml deleted file mode 100644 index f988d3b..0000000 --- a/spec/data/self-circular-extends.yml +++ /dev/null @@ -1,3 +0,0 @@ -a: - extends: a - x: 50 diff --git a/spec/data/single-extends.yml b/spec/data/single-extends.yml index 2788822..1019965 100644 --- a/spec/data/single-extends.yml +++ b/spec/data/single-extends.yml @@ -1,7 +1,7 @@ -frame: +frame: &frame x: 38 y: 38 title: - extends: frame + <<: *frame y: 50 width: 100 diff --git a/spec/data/single-level-multi-extends.yml b/spec/data/single-level-multi-extends.yml index 6cbd349..06ef128 100644 --- a/spec/data/single-level-multi-extends.yml +++ b/spec/data/single-level-multi-extends.yml @@ -1,12 +1,12 @@ -frame: +frame: &frame x: 38 y: 38 title: - extends: frame + <<: *frame y: 50 width: 100 title2: - extends: frame + <<: *frame x: 75 y: 150 width: 150 \ No newline at end of file diff --git a/spec/deck_spec.rb b/spec/deck_spec.rb index 43d7719..9f0f189 100644 --- a/spec/deck_spec.rb +++ b/spec/deck_spec.rb @@ -68,24 +68,6 @@ describe Squib::Deck do 'y' => 38, }, '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, 'y' => 50, 'width' => 100, @@ -102,13 +84,11 @@ describe Squib::Deck do 'y' => 38, }, 'title' => { - 'extends' => 'frame', 'x' => 38, 'y' => 50, 'width' => 100, }, 'title2' => { - 'extends' => 'frame', 'x' => 75, 'y' => 150, 'width' => 150, @@ -131,7 +111,6 @@ describe Squib::Deck do 'b' => 106, }, 'child' => { - 'extends' => ['uncle','aunt'], 'a' => 107, # my own 'b' => 102, # from the younger aunt 'c' => 103, # from aunt @@ -150,13 +129,11 @@ describe Squib::Deck do 'y' => 38, }, 'title' => { - 'extends' => 'frame', 'x' => 38, 'y' => 50, 'width' => 100, }, 'subtitle' => { - 'extends' => 'title', 'x' => 38, 'y' => 150, 'width' => 100, @@ -165,24 +142,6 @@ describe Squib::Deck do ) 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