Browse Source

Redesigning how config files are read, stored, and delegated

With the new design, we take the load off of deck.rb and simply delegate methods over to the new conf.rb. This means that things like `antialias` is now available as a method to the normal Squib scripts for easy checking (without being mutable). Squib::Conf also handles parsing and defaults, and any potential input validation we need to do in the future.

Typographer is also set up now as a deck-wide configuration. This may change in the future if we want typography customization per-command, although that seems like a strange use case.

Lots of tests for this one, and lots of cross-cutting concerns in this commit.

This commit also includes some tweaks to rspec tests, including tagging of slow tests for a `rake spec_fastonly`

Conflicts:
	spec/samples/samples_regression_spec.rb
	spec/spec_helper.rb
	squib.sublime-project
dev
Andy Meneely 11 years ago
parent
commit
923f346575
  1. 4
      Rakefile
  2. 4
      lib/squib/api/image.rb
  3. 35
      lib/squib/args/typographer.rb
  4. 10
      lib/squib/card.rb
  5. 114
      lib/squib/conf.rb
  6. 32
      lib/squib/constants.rb
  7. 60
      lib/squib/deck.rb
  8. 2
      lib/squib/graphics/save_doc.rb
  9. 6
      lib/squib/graphics/text.rb
  10. 4
      lib/squib/input_helpers.rb
  11. 1
      samples/custom-config.yml
  12. 2
      samples/custom_config.rb
  13. 29
      spec/conf_spec.rb
  14. 0
      spec/data/conf/basic.yml
  15. 1
      spec/data/conf/empty.yml
  16. 89
      spec/graphics/graphics_images_spec.rb
  17. 84
      spec/graphics/graphics_shapes_spec.rb
  18. 5
      spec/graphics/graphics_text_spec.rb
  19. 4
      spec/samples/samples_regression_spec.rb
  20. 8
      spec/spec_helper.rb
  21. 38
      squib.sublime-project

4
Rakefile

@ -20,6 +20,10 @@ end
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new(:spec_fastonly) do |t|
t.rspec_opts = "--tag ~slow"
end
task doc: [:yarddoc, :apply_google_analytics] task doc: [:yarddoc, :apply_google_analytics]
YARD::Rake::YardocTask.new(:yarddoc) do |t| YARD::Rake::YardocTask.new(:yarddoc) do |t|

4
lib/squib/api/image.rb

@ -22,7 +22,7 @@ module Squib
# @api public # @api public
def png(opts = {}) def png(opts = {})
opts = needs(opts, [:range, :files, :x, :y, :width, :height, :alpha, :layout, :blend, :angle, :mask]) opts = needs(opts, [:range, :files, :x, :y, :width, :height, :alpha, :layout, :blend, :angle, :mask])
Dir.chdir(@img_dir) do Dir.chdir(img_dir) do
@progress_bar.start('Loading PNG(s)', opts[:range].size) do |bar| @progress_bar.start('Loading PNG(s)', opts[:range].size) do |bar|
opts[:range].each do |i| opts[:range].each do |i|
@cards[i].png(opts[:file][i], @cards[i].png(opts[:file][i],
@ -57,7 +57,7 @@ module Squib
# @api public # @api public
def svg(opts = {}) def svg(opts = {})
p = needs(opts,[:range, :files, :svgid, :force_svgid, :x, :y, :width, :height, :layout, :alpha, :blend, :angle, :mask]) p = needs(opts,[:range, :files, :svgid, :force_svgid, :x, :y, :width, :height, :layout, :alpha, :blend, :angle, :mask])
Dir.chdir(@img_dir) do Dir.chdir(img_dir) do
@progress_bar.start('Loading SVG(s)', p[:range].size) do |bar| @progress_bar.start('Loading SVG(s)', p[:range].size) do |bar|
p[:range].each do |i| p[:range].each do |i|
unless p[:force_id][i] && p[:id][i].to_s.empty? unless p[:force_id][i] && p[:id][i].to_s.empty?

35
lib/squib/args/typographer.rb

@ -3,13 +3,16 @@ module Squib
module Args module Args
class Typographer class Typographer
def initialize(config = CONFIG_DEFAULTS) def initialize(config = Conf::DEFAULTS)
@config = config %w(lsquote ldquote rsquote rdquote smart_quotes
em_dash en_dash ellipsis).each do |var|
instance_variable_set("@#{var}", config[var])
end
end end
def process(str) def process(str)
str = explicit_replacements(str) str = explicit_replacements(str.to_s)
str = smart_quotes(str) if @config['smart_quotes'] str = smart_quotes(str) if @smart_quotes
str str
end end
@ -53,58 +56,58 @@ module Squib
# Straightforward replace # Straightforward replace
def left_curly(str) def left_curly(str)
str.gsub('``', @config['ldquote']) str.gsub('``', @ldquote)
end end
# Straightforward replace # Straightforward replace
def right_curly(str) def right_curly(str)
str.gsub(%{''}, @config['rdquote']) str.gsub(%{''}, @rdquote)
end end
# A quote between two letters is an apostraphe # A quote between two letters is an apostraphe
def apostraphize(str) def apostraphize(str)
str.gsub(/(\w)(\')(\w)/, '\1' + @config['rsquote'] + '\3') str.gsub(/(\w)(\')(\w)/, '\1' + @rsquote + '\3')
end end
# Straightforward replace # Straightforward replace
def ellipsificate(str) def ellipsificate(str)
str.gsub('...', @config['ellipsis']) str.gsub('...', @ellipsis)
end end
# Straightforward replace # Straightforward replace
def en_dash(str) def en_dash(str)
str.gsub('--', @config['en_dash']) str.gsub('--', @en_dash)
end end
# Straightforward replace # Straightforward replace
def em_dash(str) def em_dash(str)
str.gsub('---', @config['em_dash']) str.gsub('---', @em_dash)
end end
# Quote next to non-whitespace curls # Quote next to non-whitespace curls
def right_double_quote(str) def right_double_quote(str)
str.gsub(/(\S)(\")/, '\1' + @config['rdquote']) str.gsub(/(\S)(\")/, '\1' + @rdquote)
end end
# Quote next to non-whitespace curls # Quote next to non-whitespace curls
def left_double_quote(str) def left_double_quote(str)
str.gsub(/(\")(\S)/, @config['ldquote'] + '\2') str.gsub(/(\")(\S)/, @ldquote + '\2')
end end
# Handle the cases where a double quote is next to a single quote # Handle the cases where a double quote is next to a single quote
def single_inside_double_quote(str) def single_inside_double_quote(str)
str.gsub(/(\")(\')(\S)/, @config['ldquote'] + @config['lsquote'] + '\3') str.gsub(/(\")(\')(\S)/, @ldquote + @lsquote + '\3')
.gsub(/(\")(\')(\S)/, '\1' + @config['rsquote'] + @config['rdquote']) .gsub(/(\")(\')(\S)/, '\1' + @rsquote + @rdquote)
end end
# Quote next to non-whitespace curls # Quote next to non-whitespace curls
def right_single_quote(str) def right_single_quote(str)
str.gsub(/(\S)(\')/, '\1' + @config['rsquote']) str.gsub(/(\S)(\')/, '\1' + @rsquote)
end end
# Quote next to non-whitespace curls # Quote next to non-whitespace curls
def left_single_quote(str) def left_single_quote(str)
str.gsub(/(\')(\S)/, @config['lsquote'] + '\2') str.gsub(/(\')(\S)/, @lsquote + '\2')
end end
end end

10
lib/squib/card.rb

@ -16,21 +16,21 @@ module Squib
# :nodoc: # :nodoc:
# @api private # @api private
def initialize(deck, width, height, backend=:memory, index=-1) def initialize(deck, width, height, index=-1)
@deck = deck @deck = deck
@width = width @width = width
@height = height @height = height
@backend = backend @backend = deck.backend
@svgfile = "#{deck.dir}/#{deck.prefix}#{deck.count_format % index}.svg" @svgfile = "#{deck.dir}/#{deck.prefix}#{deck.count_format % index}.svg"
@cairo_surface = make_surface(@svgfile, backend) @cairo_surface = make_surface(@svgfile, @backend)
@cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface)) @cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface))
@cairo_context.antialias = ANTIALIAS_OPTS[(@deck.antialias.downcase)] || 'subpixel' @cairo_context.antialias = deck.antialias
end end
# :nodoc: # :nodoc:
# @api private # @api private
def make_surface(svgfile, backend) def make_surface(svgfile, backend)
case backend case backend.downcase.to_sym
when :memory when :memory
Cairo::ImageSurface.new(@width, @height) Cairo::ImageSurface.new(@width, @height)
when :svg when :svg

114
lib/squib/conf.rb

@ -0,0 +1,114 @@
require 'squib'
require 'forwardable'
require 'squib/args/typographer'
module Squib
# @api private
class Conf
DEFAULTS = {
'antialias' => 'best',
'backend' => 'memory',
'count_format' => SYSTEM_DEFAULTS[:count_format],
'custom_colors' => {},
'dir' => SYSTEM_DEFAULTS[:dir],
'hint' => :none,
'img_dir' => '.',
'progress_bars' => false,
'ldquote' => "\u201C", # UTF8 chars
'rdquote' => "\u201D",
'lsquote' => "\u2018",
'rsquote' => "\u2019",
'em_dash' => "\u2014",
'en_dash' => "\u2013",
'ellipsis' => "\u2026",
'smart_quotes' => true,
'text_hint' => 'off',
}
#Translate the hints to the methods.
ANTIALIAS_OPTS = {
nil => 'subpixel',
'best' => 'subpixel',
'good' => 'gray',
'fast' => 'gray',
'gray' => 'gray',
'subpixel' => 'subpixel'
}
def initialize(config_hash = DEFAULTS)
@config_hash = config_hash
@typographer = Args::Typographer.new(config_hash)
normalize_antialias
end
# FIXME REMOVE THIS as part of refactoring
# Delegate [] to our hash
# @api private
# def [](key)
# @config_hash[key]
# end
# Load the configuration file, if exists, overriding hardcoded defaults
# @api private
def self.load(file)
yaml = {}
if File.exists? file
Squib::logger.info { " using config: #{file}" }
yaml = YAML.load_file(file) || {}
end
Conf.new(DEFAULTS.merge(yaml))
end
def to_s
"Conf: #{@config_hash.to_s}"
end
def img_dir
@config_hash['img_dir']
end
def text_hint
@config_hash['text_hint']
end
def progress_bars
@config_hash['progress_bars']
end
def typographer
@typographer
end
def dir
@config_hash['dir']
end
def prefix
@config_hash['prefix']
end
def count_format
@config_hash['count_format']
end
def antialias
@config_hash['antialias']
end
def backend
@config_hash['backend']
end
def custom_colors
@config_hash['custom_colors']
end
private
def normalize_antialias
@config_hash['antialias'] = ANTIALIAS_OPTS[@config_hash['antialias'].downcase.strip]
end
end
end

32
lib/squib/constants.rb

@ -67,38 +67,6 @@ module Squib
:y_radius => 0, :y_radius => 0,
} }
# Squib's configuration defaults
#
# @api public
CONFIG_DEFAULTS = {
'antialias' => 'best',
'backend' => 'memory',
'count_format' => SYSTEM_DEFAULTS[:count_format],
'custom_colors' => {},
'dir' => SYSTEM_DEFAULTS[:dir],
'dpi' => 300,
'hint' => :none,
'img_dir' => '.',
'progress_bar' => false,
'ldquote' => "\u201C", # UTF8 chars
'rdquote' => "\u201D",
'lsquote' => "\u2018",
'rsquote' => "\u2019",
'em_dash' => "\u2014",
'en_dash' => "\u2013",
'ellipsis' => "\u2026",
'smart_quotes' => true,
'text_hint' => 'off',
}
#Translate the hints to the methods.
ANTIALIAS_OPTS = {
'best' => 'subpixel',
'good' => 'gray',
'fast' => 'gray',
'gray' => 'gray',
'subpixel' => 'subpixel'
}
# These are parameters that are intended to be "expanded" across # These are parameters that are intended to be "expanded" across
# range if they are singletons. # range if they are singletons.
# #

60
lib/squib/deck.rb

@ -1,5 +1,6 @@
require 'yaml' require 'yaml'
require 'pp' require 'pp'
require 'forwardable'
require 'squib' require 'squib'
require 'squib/card' require 'squib/card'
require 'squib/progress' require 'squib/progress'
@ -7,6 +8,7 @@ require 'squib/input_helpers'
require 'squib/constants' require 'squib/constants'
require 'squib/layout_parser' require 'squib/layout_parser'
require 'squib/args/unit_conversion' require 'squib/args/unit_conversion'
require 'squib/conf'
# The project module # The project module
# #
@ -19,24 +21,19 @@ module Squib
class Deck class Deck
include Enumerable include Enumerable
include Squib::InputHelpers include Squib::InputHelpers
extend Forwardable
# :nodoc: # Attributes for the width, height (in pixels) and number of cards
# @api private # These are expected to be immuatble for the life of Deck
attr_reader :width, :height
# :nodoc:
# @api private
attr_reader :cards
# :nodoc:
# @api private # @api private
attr_reader :text_hint, :antialias attr_reader :width, :height, :cards
# Delegate these configuration options to the Squib::Conf object
def_delegators :conf, :antialias, :backend, :count_format, :custom_colors, :dir,
:img_dir, :prefix, :text_hint, :typographer
# :nodoc: # :nodoc:
# @api private # @api private
attr_reader :layout, :config, :quote_chars attr_reader :layout, :conf
attr_reader :dir, :prefix, :count_format
# Squib's constructor that sets the immutable properties. # Squib's constructor that sets the immutable properties.
# #
@ -58,24 +55,16 @@ module Squib
# @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)
@antialias = CONFIG_DEFAULTS['antialias']
@dpi = dpi @dpi = dpi
@font = SYSTEM_DEFAULTS[:default_font] @font = SYSTEM_DEFAULTS[:default_font]
@cards = [] @cards = []
@custom_colors = {} @custom_colors = {}
@img_dir = '.' @conf = Conf.load(config)
@progress_bar = Progress.new(false) @progress_bar = Progress.new(@conf.progress_bars) # FIXME this is evil. Using something different with @ and non-@
@text_hint = :off
@backend = :memory
@dir = SYSTEM_DEFAULTS[:dir]
@prefix = SYSTEM_DEFAULTS[:prefix]
@count_format = SYSTEM_DEFAULTS[:count_format]
@quote_chars = CONFIG_DEFAULTS.select {|k,v| %w(lsquote rsquote ldquote rdquote em_dash en_dash ellipsis smart_quotes).include?(k) }
show_info(config, layout) show_info(config, layout)
load_config(config)
@width = Args::UnitConversion.parse width, dpi @width = Args::UnitConversion.parse width, dpi
@height = Args::UnitConversion.parse height, dpi @height = Args::UnitConversion.parse height, dpi
cards.times{ |i| @cards << Squib::Card.new(self, @width, @height, @backend, i) } cards.times{ |i| @cards << Squib::Card.new(self, @width, @height, i) }
@layout = LayoutParser.load_layout(layout) @layout = LayoutParser.load_layout(layout)
if block_given? if block_given?
instance_eval(&block) # here we go. wheeeee! instance_eval(&block) # here we go. wheeeee!
@ -96,29 +85,6 @@ module Squib
@cards.each { |card| block.call(card) } @cards.each { |card| block.call(card) }
end end
# Load the configuration file, if exists, overriding hardcoded defaults
# @api private
def load_config(file)
if File.exists?(file) && config = YAML.load_file(file)
Squib::logger.info { " using config: #{file}" }
config = CONFIG_DEFAULTS.merge(config)
@dpi = config['dpi'].to_i
@text_hint = config['text_hint']
@progress_bar.enabled = config['progress_bars']
@custom_colors = config['custom_colors']
@img_dir = config['img_dir']
@backend = (config['backend'].to_s.downcase.strip == 'svg') ? :svg : :memory
@dir = config['dir']
@prefix = config['prefix']
@count_format = config['count_format']
@antialias = config['antialias']
@quote_chars ||= {}
%w(lsquote rsquote ldquote rdquote smart_quotes em_dash en_dash ellipsis).each do |key|
@quote_chars[key] = config[key]
end
end
end
# Use Logger to show more detail on the run # Use Logger to show more detail on the run
# :nodoc: # :nodoc:
# @api private # @api private

2
lib/squib/graphics/save_doc.rb

@ -33,7 +33,7 @@ module Squib
cc.translate(x,y) cc.translate(x,y)
cc.rectangle(p[:trim], p[:trim], card_width, card_height) cc.rectangle(p[:trim], p[:trim], card_width, card_height)
cc.clip cc.clip
case card.backend case card.backend.downcase.to_sym
when :memory when :memory
cc.set_source(card.cairo_surface, 0, 0) cc.set_source(card.cairo_surface, 0, 0)
cc.paint cc.paint

6
lib/squib/graphics/text.rb

@ -78,7 +78,7 @@ module Squib
def set_font_rendering_opts!(layout) def set_font_rendering_opts!(layout)
font_options = Cairo::FontOptions.new font_options = Cairo::FontOptions.new
font_options.antialias = ANTIALIAS_OPTS[(@deck.antialias.downcase)] || 'gray' font_options.antialias = Conf::ANTIALIAS_OPTS[(@deck.antialias || 'gray').downcase]
font_options.hint_metrics = 'on' # TODO make this configurable font_options.hint_metrics = 'on' # TODO make this configurable
font_options.hint_style = 'full' # TODO make this configurable font_options.hint_style = 'full' # TODO make this configurable
layout.context.font_options = font_options layout.context.font_options = font_options
@ -127,7 +127,7 @@ module Squib
while (key = next_embed(embed.rules.keys, clean_str)) != nil while (key = next_embed(embed.rules.keys, clean_str)) != nil
rule = embed.rules[key] rule = embed.rules[key]
spacing = rule[:width] * Pango::SCALE spacing = rule[:width] * Pango::SCALE
index = clean_str.index(key) index = clean_str.index(key)
index = clean_str[0..index].bytesize #convert to byte index (bug #57) index = clean_str[0..index].bytesize #convert to byte index (bug #57)
str = str.sub(key, "<span size=\"#{ZERO_WIDTH_CHAR_SIZE}\">a<span letter_spacing=\"#{spacing.to_i}\">a</span>a</span>") str = str.sub(key, "<span size=\"#{ZERO_WIDTH_CHAR_SIZE}\">a<span letter_spacing=\"#{spacing.to_i}\">a</span>a</span>")
layout.markup = str layout.markup = str
@ -164,7 +164,7 @@ module Squib
layout.font_description = font_desc layout.font_description = font_desc
layout.text = str layout.text = str
if markup if markup
str = Args::Typographer.new(@deck.quote_chars).process(layout.text) str = @deck.typographer.process(layout.text)
layout.markup = str layout.markup = str
end end

4
lib/squib/input_helpers.rb

@ -130,8 +130,8 @@ module Squib
def colorify(opts, nillable=false, key=:color) def colorify(opts, nillable=false, key=:color)
opts[key].each_with_index do |color, i| opts[key].each_with_index do |color, i|
unless nillable && color.nil? unless nillable && color.nil?
if @custom_colors.key? color.to_s if custom_colors.key? color.to_s
color = @custom_colors[color.to_s] color = custom_colors[color.to_s]
end end
opts[key][i] = color opts[key][i] = color
end end

1
samples/custom-config.yml

@ -1,4 +1,3 @@
dpi: 300
progress_bars: true progress_bars: true
text_hint: '#FF0000' text_hint: '#FF0000'
custom_colors: custom_colors:

2
samples/custom_config.rb

@ -4,7 +4,7 @@ Squib::Deck.new(config: 'custom-config.yml') do
# Custom color defined in our config # Custom color defined in our config
background color: :foo background color: :foo
# Hints are turned on in the config file # Hints can be turned on in the config file
text str: 'The Title', x: 0, y: 78, width: 825, text str: 'The Title', x: 0, y: 78, width: 825,
font: 'Arial 72', align: :center font: 'Arial 72', align: :center

29
spec/conf_spec.rb

@ -0,0 +1,29 @@
require 'squib'
require 'spec_helper'
describe Squib::Conf do
it 'parses the project template file just fine' do
conf = Squib::Conf.load(project_template('config.yml'))
expect(conf.backend).to eq(Squib::Conf::DEFAULTS['backend'])
end
it 'parses an empty file' do
conf = Squib::Conf.load(conf('empty.yml'))
expect(conf.backend).to eq(Squib::Conf::DEFAULTS['backend'])
end
it 'parses the sample custom config' do
conf = Squib::Conf.load(sample_file('custom-config.yml'))
expect(conf.progress_bars).to be true
expect(conf.text_hint).to eq '#FF0000'
expect(conf.custom_colors).to eq({ 'foo' => '#ccc' })
expect(conf.img_dir).to eq 'customconfig-imgdir'
end
it 'normalizes antialias automatically' do
expect(Squib::Conf::DEFAULTS['antialias']).to eq 'best'
expect(Squib::Conf.new.antialias).to eq 'subpixel'
end
end

0
spec/data/conf/basic.yml

1
spec/data/conf/empty.yml

@ -0,0 +1 @@
# Intentionally empty

89
spec/graphics/graphics_images_spec.rb

@ -3,80 +3,81 @@ require 'squib'
describe Squib::Card do describe Squib::Card do
before(:example) do let(:deck) { double(Squib::Deck) }
@deck = double(Squib::Deck) let(:context) { double(Cairo::Context) }
@context = double(Cairo::Context) let(:svg) { double(RSVG::Handle) }
@svg = double(RSVG::Handle) let(:png) { double(Cairo::ImageSurface) }
@png = double(Cairo::ImageSurface)
allow(Cairo::Context).to receive(:new).and_return(@context)
allow(Cairo::ImageSurface).to receive(:from_png).and_return(@png)
allow(Cairo::ImageSurface).to receive(:new).and_return(@png)
allow(RSVG::Handle).to receive(:new_from_file).and_return(@svg)
allow(@deck).to receive(:dir).and_return('_output')
allow(@deck).to receive(:count_format).and_return('%02d')
allow(@deck).to receive(:prefix).and_return('card_')
allow(@deck).to receive(:antialias).and_return('best')
before(:each) do
allow(Cairo::Context).to receive(:new).and_return(context)
allow(Cairo::ImageSurface).to receive(:from_png).and_return(png)
allow(Cairo::ImageSurface).to receive(:new).and_return(png)
allow(RSVG::Handle).to receive(:new_from_file).and_return(svg)
allow(deck).to receive(:dir).and_return('_output')
allow(deck).to receive(:count_format).and_return('%02d')
allow(deck).to receive(:prefix).and_return('card_')
allow(deck).to receive(:antialias).and_return('subpixel')
allow(deck).to receive(:backend).and_return('memory')
end end
context '#png' do context '#png' do
it 'makes all the expected calls on a smoke test' do it 'makes all the expected calls on a smoke test' do
expect(@context).to receive(:antialias=).with('subpixel') expect(context).to receive(:antialias=).with('subpixel')
expect(@context).to receive(:save).once expect(context).to receive(:save).once
expect(@context).to receive(:translate).with(-37, -38).once expect(context).to receive(:translate).with(-37, -38).once
expect(@context).to receive(:rotate).with(0.0).once expect(context).to receive(:rotate).with(0.0).once
expect(@context).to receive(:translate).with(37, 38).once expect(context).to receive(:translate).with(37, 38).once
expect(@context).to receive(:set_source).with(@png, 37, 38).once expect(context).to receive(:set_source).with(png, 37, 38).once
expect(@context).to receive(:paint).with(0.9).once expect(context).to receive(:paint).with(0.9).once
expect(@context).to receive(:restore).once expect(context).to receive(:restore).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
# png(file, x, y, alpha, blend, angle) # png(file, x, y, alpha, blend, angle)
card.png('foo.png', 37, 38, :native, :native, 0.9, :none, 0.0, nil) card.png('foo.png', 37, 38, :native, :native, 0.9, :none, 0.0, nil)
end end
it 'sets blend when needed' do it 'sets blend when needed' do
@context.as_null_object context.as_null_object
expect(@context).to receive(:operator=).with(:overlay).once expect(context).to receive(:operator=).with(:overlay).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
card.png('foo.png', 37, 38, :native, :native, 0.9, :overlay, 0.0, nil) card.png('foo.png', 37, 38, :native, :native, 0.9, :overlay, 0.0, nil)
end end
end end
context '#svg' do context '#svg' do
it 'makes all the expected calls on a smoke test' do it 'makes all the expected calls on a smoke test' do
expect(@svg).to receive(:width).and_return(100).twice expect(svg).to receive(:width).and_return(100).twice
expect(@svg).to receive(:height).and_return(100).twice expect(svg).to receive(:height).and_return(100).twice
expect(@context).to receive(:antialias=).with('subpixel').once expect(context).to receive(:antialias=).with('subpixel').once
expect(@context).to receive(:save).once expect(context).to receive(:save).once
expect(@context).to receive(:rotate).with(0.0).once expect(context).to receive(:rotate).with(0.0).once
expect(@context).to receive(:translate).with(37, 38).once expect(context).to receive(:translate).with(37, 38).once
expect(@context).to receive(:scale).with(1.0, 1.0).once expect(context).to receive(:scale).with(1.0, 1.0).once
expect(@context).to receive(:render_rsvg_handle).with(@svg, 'id').once expect(context).to receive(:render_rsvg_handle).with(svg, 'id').once
expect(@context).to receive(:restore).once expect(context).to receive(:restore).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
# svg(file, id, x, y, width, height, alpha, blend, angle) # svg(file, id, x, y, width, height, alpha, blend, angle)
card.svg('foo.png', 'id', 37, 38, :native, :native, 0.9, :none, 0.0, nil) card.svg('foo.png', 'id', 37, 38, :native, :native, 0.9, :none, 0.0, nil)
end end
it 'sets blend when needed' do it 'sets blend when needed' do
@context.as_null_object context.as_null_object
@svg.as_null_object svg.as_null_object
expect(@context).to receive(:operator=).with(:overlay).once expect(context).to receive(:operator=).with(:overlay).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
card.svg('foo.png', nil, 37, 38, :native, :native, 0.9, :overlay, 0.0, nil) card.svg('foo.png', nil, 37, 38, :native, :native, 0.9, :overlay, 0.0, nil)
end end
it 'sets width & height when needed' do it 'sets width & height when needed' do
@context.as_null_object context.as_null_object
expect(@svg).to receive(:width).and_return(100).once expect(svg).to receive(:width).and_return(100).once
expect(@svg).to receive(:height).and_return(100).once expect(svg).to receive(:height).and_return(100).once
expect(@context).to receive(:scale).with(2.0, 3.0).once expect(context).to receive(:scale).with(2.0, 3.0).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
card.svg('foo.png', nil, 37, 38, 200, 300, 0.9, :none, 0.0, nil) card.svg('foo.png', nil, 37, 38, 200, 300, 0.9, :none, 0.0, nil)
end end
end end

84
spec/graphics/graphics_shapes_spec.rb

@ -3,33 +3,35 @@ require 'squib'
describe Squib::Card do describe Squib::Card do
def expect_stroke(fill_color, stroke_color, stroke_width) let(:deck) { double(Squib::Deck) }
expect(@context).to receive(:set_source_color).with(stroke_color).once let(:cxt) { double(Cairo::Context) }
expect(@context).to receive(:set_line_width).with(stroke_width).once
expect(@context).to receive(:stroke).once def expect_stroke(cxt, fill_color, stroke_color, stroke_width)
expect(@context).to receive(:set_source_color).with(fill_color).once expect(cxt).to receive(:set_source_color).with(stroke_color).once
expect(@context).to receive(:fill).once expect(cxt).to receive(:set_line_width).with(stroke_width).once
expect(cxt).to receive(:stroke).once
expect(cxt).to receive(:set_source_color).with(fill_color).once
expect(cxt).to receive(:fill).once
end end
before(:each) do before(:each) do
@deck = double(Squib::Deck) allow(Cairo::Context).to receive(:new).and_return(cxt)
@context = double(Cairo::Context) allow(deck).to receive(:dir).and_return('_output')
allow(Cairo::Context).to receive(:new).and_return(@context) allow(deck).to receive(:count_format).and_return('%02d')
allow(@deck).to receive(:dir).and_return('_output') allow(deck).to receive(:prefix).and_return('card_')
allow(@deck).to receive(:count_format).and_return('%02d') allow(deck).to receive(:antialias).and_return('subpixel')
allow(@deck).to receive(:prefix).and_return('card_') allow(deck).to receive(:backend).and_return('memory')
allow(@deck).to receive(:antialias).and_return('best')
end end
context 'rect' do context 'rect' do
it 'make all the expected calls on a smoke test' do it 'make all the expected calls on a smoke test' do
expect(@context).to receive(:antialias=).with('subpixel') expect(cxt).to receive(:antialias=).with('subpixel')
expect(@context).to receive(:save).once expect(cxt).to receive(:save).once
expect(@context).to receive(:rounded_rectangle).with(37, 38, 50, 100, 10, 15).twice expect(cxt).to receive(:rounded_rectangle).with(37, 38, 50, 100, 10, 15).twice
expect_stroke('#fff', '#f00', 2.0) expect_stroke(cxt, '#fff', '#f00', 2.0)
expect(@context).to receive(:restore).once expect(cxt).to receive(:restore).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
# rect(x, y, width, height, x_radius, y_radius, # rect(x, y, width, height, x_radius, y_radius,
# fill_color, stroke_color, stroke_width) # fill_color, stroke_color, stroke_width)
card.rect(37, 38, 50, 100, 10, 15, '#fff', '#f00', 2.0) card.rect(37, 38, 50, 100, 10, 15, '#fff', '#f00', 2.0)
@ -38,14 +40,14 @@ describe Squib::Card do
context 'circle' do context 'circle' do
it 'make all the expected calls on a smoke test' do it 'make all the expected calls on a smoke test' do
expect(@context).to receive(:antialias=).with('subpixel') expect(cxt).to receive(:antialias=).with('subpixel')
expect(@context).to receive(:save).once expect(cxt).to receive(:save).once
expect(@context).to receive(:move_to).with(137, 38) expect(cxt).to receive(:move_to).with(137, 38)
expect(@context).to receive(:circle).with(37, 38, 100).twice expect(cxt).to receive(:circle).with(37, 38, 100).twice
expect_stroke('#fff', '#f00', 2.0) expect_stroke(cxt, '#fff', '#f00', 2.0)
expect(@context).to receive(:restore).once expect(cxt).to receive(:restore).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
# circle(x, y, radius, # circle(x, y, radius,
# fill_color, stroke_color, stroke_width) # fill_color, stroke_color, stroke_width)
card.circle(37, 38, 100, '#fff', '#f00', 2.0) card.circle(37, 38, 100, '#fff', '#f00', 2.0)
@ -54,29 +56,29 @@ describe Squib::Card do
context 'triangle' do context 'triangle' do
it 'make all the expected calls on a smoke test' do it 'make all the expected calls on a smoke test' do
expect(@context).to receive(:antialias=).with('subpixel') expect(cxt).to receive(:antialias=).with('subpixel')
expect(@context).to receive(:save).once expect(cxt).to receive(:save).once
expect(@context).to receive(:triangle).with(1, 2, 3, 4, 5, 6).twice expect(cxt).to receive(:triangle).with(1, 2, 3, 4, 5, 6).twice
expect_stroke('#fff', '#f00', 2.0) expect_stroke(cxt, '#fff', '#f00', 2.0)
expect(@context).to receive(:restore).once expect(cxt).to receive(:restore).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
card.triangle(1, 2, 3, 4, 5, 6, '#fff', '#f00', 2.0) card.triangle(1, 2, 3, 4, 5, 6, '#fff', '#f00', 2.0)
end end
end end
context 'line' do context 'line' do
it 'make all the expected calls on a smoke test' do it 'make all the expected calls on a smoke test' do
expect(@context).to receive(:antialias=).with('subpixel') expect(cxt).to receive(:antialias=).with('subpixel')
expect(@context).to receive(:save).once expect(cxt).to receive(:save).once
expect(@context).to receive(:move_to).with(1, 2).once expect(cxt).to receive(:move_to).with(1, 2).once
expect(@context).to receive(:line_to).with(3, 4).once expect(cxt).to receive(:line_to).with(3, 4).once
expect(@context).to receive(:set_source_color).with('#fff').once expect(cxt).to receive(:set_source_color).with('#fff').once
expect(@context).to receive(:set_line_width).with(2.0).once expect(cxt).to receive(:set_line_width).with(2.0).once
expect(@context).to receive(:stroke).once expect(cxt).to receive(:stroke).once
expect(@context).to receive(:restore).once expect(cxt).to receive(:restore).once
card = Squib::Card.new(@deck, 100, 150) card = Squib::Card.new(deck, 100, 150)
card.line(1, 2, 3, 4, '#fff', 2.0) card.line(1, 2, 3, 4, '#fff', 2.0)
end end
end end

5
spec/graphics/graphics_text_spec.rb

@ -16,6 +16,8 @@ describe Squib::Card, '#text' do
allow(deck).to receive(:count_format).and_return('%02d') allow(deck).to receive(:count_format).and_return('%02d')
allow(deck).to receive(:prefix).and_return('card_') allow(deck).to receive(:prefix).and_return('card_')
allow(deck).to receive(:antialias).and_return('best') allow(deck).to receive(:antialias).and_return('best')
allow(deck).to receive(:antialias).and_return('subpixel')
allow(deck).to receive(:backend).and_return('memory')
allow(layout).to receive(:context).and_return(pango_cxt) allow(layout).to receive(:context).and_return(pango_cxt)
end end
@ -71,7 +73,8 @@ describe Squib::Card, '#text' do
allow(deck).to receive(:dir).and_return('_output') allow(deck).to receive(:dir).and_return('_output')
allow(deck).to receive(:count_format).and_return('%02d') allow(deck).to receive(:count_format).and_return('%02d')
allow(deck).to receive(:prefix).and_return('card_') allow(deck).to receive(:prefix).and_return('card_')
allow(deck).to receive(:antialias).and_return('best') allow(deck).to receive(:antialias).and_return('subpixel')
allow(deck).to receive(:backend).and_return('memory')
end end
it 'aligns right with strings' do it 'aligns right with strings' do

4
spec/samples/samples_regression_spec.rb

@ -13,7 +13,7 @@ describe "Squib samples" do
end end
Dir["#{@SAMPLES_DIR}/**/*.rb"].each do |sample| Dir["#{@SAMPLES_DIR}/**/*.rb"].each do |sample|
it "should execute #{sample} with no errors" do it "should execute #{sample} with no errors", slow: true do
allow(Squib.logger).to receive(:warn) {} allow(Squib.logger).to receive(:warn) {}
allow(ProgressBar).to receive(:create).and_return(Squib::DoNothing.new) allow(ProgressBar).to receive(:create).and_return(Squib::DoNothing.new)
load sample load sample
@ -68,7 +68,7 @@ describe "Squib samples" do
tgc_proofs.rb tgc_proofs.rb
units.rb units.rb
).each do |sample| ).each do |sample|
it "has not changed for #{sample}" do it "has not changed for #{sample}", slow: true do
log = StringIO.new log = StringIO.new
mock_cairo(log) mock_cairo(log)
load sample load sample

8
spec/spec_helper.rb

@ -36,6 +36,14 @@ def xlsx_file(file)
"#{File.expand_path(File.dirname(__FILE__))}/data/xlsx/#{file}" "#{File.expand_path(File.dirname(__FILE__))}/data/xlsx/#{file}"
end end
def project_template(file)
"#{File.expand_path(File.dirname(__FILE__))}/../lib/squib/project_template/#{file}"
end
def conf(file)
"#{File.expand_path(File.dirname(__FILE__))}/data/conf/#{file}"
end
def overwrite_sample(sample_name, log) def overwrite_sample(sample_name, log)
# Use this to overwrite the regression with current state # Use this to overwrite the regression with current state
File.open(sample_regression_file(sample_name), 'w+:UTF-8') do |f| File.open(sample_regression_file(sample_name), 'w+:UTF-8') do |f|

38
squib.sublime-project vendored

@ -12,10 +12,14 @@
"name": "rake", "name": "rake",
"shell_cmd": "rake", "shell_cmd": "rake",
}, },
{ {
"name": "rake sanity", "name": "rake sanity",
"shell_cmd": "rake sanity", "shell_cmd": "rake sanity",
}, },
{
"name": "rake spec_fastonly",
"shell_cmd": "rake spec_fastonly",
},
{ {
"name": "rake run[text_options]", "name": "rake run[text_options]",
"shell_cmd": "rake run[text_options]", "shell_cmd": "rake run[text_options]",
@ -32,6 +36,10 @@
"name": "rake run[config_text_markup]", "name": "rake run[config_text_markup]",
"shell_cmd": "rake run[config_text_markup]", "shell_cmd": "rake run[config_text_markup]",
}, },
{
"name": "rake run[custom_config]",
"shell_cmd": "rake run[custom_config]",
},
{ {
"name": "rspec spec/samples/samples_regression_spec.rb", "name": "rspec spec/samples/samples_regression_spec.rb",
"shell_cmd": "rspec spec/samples/samples_regression_spec.rb", "shell_cmd": "rspec spec/samples/samples_regression_spec.rb",
@ -48,11 +56,33 @@
"working_dir": "${project_path:${folder}}" "working_dir": "${project_path:${folder}}"
}, },
{ {
"name": "rspec spec/api/api_data_spec.rb",
"shell_cmd": "rspec spec/api/api_data_spec.rb",
"working_dir": "${project_path:${folder}}" "working_dir": "${project_path:${folder}}"
}, },
{
"name": "rspec spec/graphics/graphics_shapes_spec.rb",
"shell_cmd": "rspec spec/graphics/graphics_shapes_spec.rb",
"working_dir": "${project_path:${folder}}"
},
{
"name": "rspec spec/conf_spec.rb",
"shell_cmd": "rspec spec/conf_spec.rb",
"working_dir": "${project_path:${folder}}"
},
{
"name": "rspec spec/graphics/graphics_images_spec.rb",
"shell_cmd": "rspec spec/graphics/graphics_images_spec.rb",
"working_dir": "${project_path:${folder}}"
},
{
"name": "rspec spec/graphics/graphics_shapes_spec.rb",
"shell_cmd": "rspec spec/graphics/graphics_shapes_spec.rb",
"working_dir": "${project_path:${folder}}"
},
{
"name": "rspec spec/conf_spec.rb",
"shell_cmd": "rspec spec/conf_spec.rb",
], },
]
} }

Loading…
Cancel
Save