squib 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +9 -0
  4. data/README.md +38 -33
  5. data/Rakefile +1 -1
  6. data/bin/squib +6 -6
  7. data/lib/squib.rb +8 -8
  8. data/lib/squib/api/background.rb +3 -3
  9. data/lib/squib/api/data.rb +5 -6
  10. data/lib/squib/api/image.rb +13 -10
  11. data/lib/squib/api/save.rb +4 -5
  12. data/lib/squib/api/settings.rb +4 -4
  13. data/lib/squib/api/shapes.rb +20 -20
  14. data/lib/squib/api/text.rb +11 -11
  15. data/lib/squib/api/units.rb +4 -4
  16. data/lib/squib/card.rb +5 -5
  17. data/lib/squib/commands/new.rb +5 -5
  18. data/lib/squib/constants.rb +10 -10
  19. data/lib/squib/deck.rb +24 -22
  20. data/lib/squib/graphics/background.rb +3 -3
  21. data/lib/squib/graphics/image.rb +13 -6
  22. data/lib/squib/graphics/save_doc.rb +13 -11
  23. data/lib/squib/graphics/save_images.rb +3 -3
  24. data/lib/squib/graphics/shapes.rb +9 -8
  25. data/lib/squib/graphics/text.rb +61 -59
  26. data/lib/squib/input_helpers.rb +13 -13
  27. data/lib/squib/progress.rb +4 -4
  28. data/lib/squib/project_template/Gemfile +1 -1
  29. data/lib/squib/project_template/deck.rb +3 -3
  30. data/lib/squib/version.rb +6 -2
  31. data/samples/autoscale_font.rb +4 -4
  32. data/samples/basic.rb +6 -7
  33. data/samples/cairo_access.rb +27 -0
  34. data/samples/colors.rb +2 -2
  35. data/samples/custom-layout.yml +5 -5
  36. data/samples/custom_config.rb +4 -4
  37. data/samples/draw_shapes.rb +8 -8
  38. data/samples/hello_world.rb +2 -3
  39. data/samples/load_images.rb +6 -1
  40. data/samples/portrait-landscape.rb +7 -7
  41. data/samples/ranges.rb +13 -14
  42. data/samples/save_pdf.rb +2 -2
  43. data/samples/text_options.rb +17 -17
  44. data/samples/tgc_proofs.rb +3 -3
  45. data/samples/use_layout.rb +3 -3
  46. data/spec/api/api_text_spec.rb +11 -17
  47. data/spec/commands/new_spec.rb +10 -10
  48. data/spec/data/easy-circular-extends.yml +1 -1
  49. data/spec/data/hard-circular-extends.yml +2 -2
  50. data/spec/data/multi-extends-single-entry.yml +3 -3
  51. data/spec/data/multi-level-extends.yml +1 -1
  52. data/spec/data/no-extends.yml +2 -2
  53. data/spec/data/pre-extends.yml +1 -1
  54. data/spec/data/self-circular-extends.yml +1 -1
  55. data/spec/data/single-extends.yml +1 -1
  56. data/spec/data/single-level-multi-extends.yml +1 -1
  57. data/spec/deck_spec.rb +62 -62
  58. data/spec/graphics/graphics_images_spec.rb +79 -0
  59. data/spec/graphics/graphics_save_doc_spec.rb +66 -0
  60. data/spec/graphics/graphics_shapes_spec.rb +74 -0
  61. data/spec/graphics/graphics_text_spec.rb +135 -0
  62. data/spec/input_helpers_spec.rb +61 -40
  63. data/spec/samples_run_spec.rb +6 -6
  64. data/spec/spec_helper.rb +32 -1
  65. data/squib.gemspec +21 -21
  66. metadata +22 -14
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'squib'
3
+
4
+ describe Squib::Deck, '#save_pdf' do
5
+
6
+ def expect_card_place(x, y)
7
+ expect(@context).to receive(:set_source)
8
+ .with(instance_of(Cairo::ImageSurface), -37, -37)
9
+ .once # trim the card
10
+ expect(@context).to receive(:paint).once # paint trimmed card
11
+ expect(@context).to receive(:set_source) # place the card
12
+ .with(instance_of(Cairo::ImageSurface), x, y).once
13
+ expect(@context).to receive(:paint).once # paint placed card
14
+ end
15
+
16
+ context 'typical inputs' do
17
+
18
+ before(:each) do
19
+ @context = double(Cairo::Context)
20
+ allow(Cairo::PDFSurface).to receive(:new).and_return(nil) #don't create the file
21
+ end
22
+
23
+ it 'make all the expected calls on a smoke test' do
24
+ num_cards = 9
25
+ args = { file: 'foo.pdf', dir: '_out', margin: 75, gap: 5, trim: 37 }
26
+ deck = Squib::Deck.new(cards: num_cards, width: 825, height: 1125)
27
+ mock_squib_logger(@old_logger) do
28
+ expect(Squib.logger).to receive(:debug).thrice
29
+ expect(Cairo::Context).to receive(:new).and_return(@context).exactly(num_cards + 1).times
30
+ expect(deck).to receive(:dirify) { |arg| arg } #don't create the dir
31
+
32
+ expect_card_place(75, 75)
33
+ expect_card_place(831, 75)
34
+ expect_card_place(1587, 75)
35
+ expect_card_place(2343, 75)
36
+ expect_card_place(75, 1131)
37
+ expect_card_place(831, 1131)
38
+ expect_card_place(1587, 1131)
39
+ expect_card_place(2343, 1131)
40
+ expect(@context).to receive(:show_page).once
41
+ expect_card_place(75, 75)
42
+
43
+ deck.save_pdf(args)
44
+ end
45
+ end
46
+
47
+ it 'only does the three cards on a limited range' do
48
+ num_cards = 9
49
+ args = { range: 2..4, file: 'foo.pdf', dir: '_out', margin: 75, gap: 5, trim: 37 }
50
+ deck = Squib::Deck.new(cards: num_cards, width: 825, height: 1125)
51
+ mock_squib_logger(@old_logger) do
52
+ expect(Squib.logger).to receive(:debug).thrice
53
+ expect(Cairo::Context).to receive(:new).and_return(@context).exactly(4).times
54
+ expect(deck).to receive(:dirify) { |arg| arg } #don't create the dir
55
+
56
+ expect_card_place(75, 75)
57
+ expect_card_place(831, 75)
58
+ expect_card_place(1587, 75)
59
+
60
+ deck.save_pdf(args)
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'squib'
3
+
4
+ describe Squib::Card do
5
+
6
+ def expect_stroke(fill_color, stroke_color, stroke_width)
7
+ expect(@context).to receive(:set_source_color).with(stroke_color).once
8
+ expect(@context).to receive(:set_line_width).with(stroke_width).once
9
+ expect(@context).to receive(:stroke).once
10
+ expect(@context).to receive(:set_source_color).with(fill_color).once
11
+ expect(@context).to receive(:fill).once
12
+ end
13
+
14
+ before(:each) do
15
+ @deck = double(Squib::Deck)
16
+ @context = double(Cairo::Context)
17
+ allow(Cairo::Context).to receive(:new).and_return(@context)
18
+ end
19
+
20
+ context 'rect' do
21
+ it 'make all the expected calls on a smoke test' do
22
+ expect(@context).to receive(:save).once
23
+ expect(@context).to receive(:rounded_rectangle).with(37, 38, 50, 100, 10, 15).twice
24
+ expect_stroke('#fff', '#f00', 2.0)
25
+ expect(@context).to receive(:restore).once
26
+
27
+ card = Squib::Card.new(@deck, 100, 150)
28
+ # rect(x, y, width, height, x_radius, y_radius,
29
+ # fill_color, stroke_color, stroke_width)
30
+ card.rect(37, 38, 50, 100, 10, 15, '#fff', '#f00', 2.0)
31
+ end
32
+ end
33
+
34
+ context 'circle' do
35
+ it 'make all the expected calls on a smoke test' do
36
+ expect(@context).to receive(:save).once
37
+ expect(@context).to receive(:circle).with(37, 38, 100).twice
38
+ expect_stroke('#fff', '#f00', 2.0)
39
+ expect(@context).to receive(:restore).once
40
+
41
+ card = Squib::Card.new(@deck, 100, 150)
42
+ # circle(x, y, radius,
43
+ # fill_color, stroke_color, stroke_width)
44
+ card.circle(37, 38, 100, '#fff', '#f00', 2.0)
45
+ end
46
+ end
47
+
48
+ context 'triangle' do
49
+ it 'make all the expected calls on a smoke test' do
50
+ expect(@context).to receive(:save).once
51
+ expect(@context).to receive(:triangle).with(1, 2, 3, 4, 5, 6).twice
52
+ expect_stroke('#fff', '#f00', 2.0)
53
+ expect(@context).to receive(:restore).once
54
+
55
+ card = Squib::Card.new(@deck, 100, 150)
56
+ card.triangle(1, 2, 3, 4, 5, 6, '#fff', '#f00', 2.0)
57
+ end
58
+ end
59
+
60
+ context 'line' do
61
+ it 'make all the expected calls on a smoke test' do
62
+ expect(@context).to receive(:save).once
63
+ expect(@context).to receive(:move_to).with(1, 2).once
64
+ expect(@context).to receive(:line_to).with(3, 4).once
65
+ expect(@context).to receive(:set_source_color).with('#fff').once
66
+ expect(@context).to receive(:set_line_width).with(2.0).once
67
+ expect(@context).to receive(:stroke).once
68
+ expect(@context).to receive(:restore).once
69
+
70
+ card = Squib::Card.new(@deck, 100, 150)
71
+ card.line(1, 2, 3, 4, '#fff', 2.0)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+ require 'squib'
3
+
4
+ describe Squib::Card, '#text' do
5
+
6
+ context 'typical inputs' do
7
+ before(:each) do
8
+ @deck = double(Squib::Deck)
9
+ @context = double(Cairo::Context)
10
+ @layout = double(Pango::Layout)
11
+ allow(Cairo::Context).to receive(:new).and_return(@context)
12
+ end
13
+
14
+ it 'make all the expected calls on a smoke test' do
15
+ mock_squib_logger(@old_logger) do
16
+ expect(Squib.logger).to receive(:debug).once
17
+ expect(@context).to receive(:save).once
18
+ expect(@context).to receive(:set_source_color).once
19
+ expect(@context).to receive(:move_to).with(10, 15).once
20
+ expect(@context).to receive(:translate).with(-10, -15).once
21
+ expect(@context).to receive(:rotate).with(0.0).once
22
+ expect(@context).to receive(:translate).with(10, 15).once
23
+ expect(@context).to receive(:create_pango_layout).once.and_return(@layout)
24
+ expect(@layout).to receive(:font_description=).with(Pango::FontDescription.new('Sans 12')).once
25
+ expect(@layout).to receive(:text=).with('foo').once
26
+ expect(@layout).to receive(:width=).with(20 * Pango::SCALE).once
27
+ expect(@layout).to receive(:height=).with(25 * Pango::SCALE).once
28
+ expect(@layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_NONE).once
29
+ expect(@layout).to receive(:alignment=).with(Pango::Layout::ALIGN_LEFT).once
30
+ expect(@layout).to receive(:justify=).with(false).once
31
+ expect(@layout).to receive(:spacing=).with(1.0 * Pango::SCALE).once
32
+ expect(@context).to receive(:update_pango_layout).once
33
+ expect(@layout).to receive(:height).once.and_return(25)
34
+ expect(@layout).to receive(:extents).once.and_return([0,0])
35
+ expect(@context).to receive(:update_pango_layout).once
36
+ expect(@context).to receive(:show_pango_layout).once
37
+ expect(@context).to receive(:restore).once
38
+
39
+ card = Squib::Card.new(@deck, 100, 150)
40
+ # text(str, font, font_size, color,
41
+ # x, y, width, height,
42
+ # markup, justify, wrap, ellipsize,
43
+ # spacing, align, valign, hint, angle)
44
+ card.text('foo', 'Sans 12', nil, '#abc',
45
+ 10, 15, 20, 25,
46
+ nil, false, false, false,
47
+ 1.0, :left, :top, nil, 0.0)
48
+ end
49
+ end
50
+ end
51
+
52
+ context 'convenience lookups' do
53
+ before(:each) do
54
+ @deck = double(Squib::Deck)
55
+ @context = double(Cairo::Context).as_null_object
56
+ @layout = double(Pango::Layout).as_null_object
57
+ @extents = double("extents")
58
+ allow(Cairo::Context).to receive(:new).and_return(@context)
59
+ expect(@context).to receive(:create_pango_layout).once.and_return(@layout)
60
+ end
61
+
62
+ it 'aligns right with strings' do
63
+ card = Squib::Card.new(@deck, 100, 150)
64
+ expect(@layout).to receive(:alignment=).with(Pango::Layout::ALIGN_RIGHT).once
65
+ card.text('foo', 'Sans 12', nil, '#abc',
66
+ 10, 15, 20, 50,
67
+ nil, false, false, false,
68
+ 1.0, 'right', :top, nil, 0.0)
69
+ end
70
+
71
+ it 'aligns center with strings' do
72
+ card = Squib::Card.new(@deck, 100, 150)
73
+ expect(@layout).to receive(:alignment=).with(Pango::Layout::ALIGN_CENTER).once
74
+ card.text('foo', 'Sans 12', nil, '#abc',
75
+ 10, 15, 20, 50,
76
+ nil, false, false, false,
77
+ 1.0, 'center', :top, nil, 0.0)
78
+ end
79
+
80
+ it 'sets wrap to char with string char' do
81
+ card = Squib::Card.new(@deck, 100, 150)
82
+ expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_CHAR).once
83
+ card.text('foo', 'Sans 12', nil, '#abc',
84
+ 10, 15, 20, 50,
85
+ nil, false, 'char', false,
86
+ 1.0, :left, :top, nil, 0.0)
87
+ end
88
+
89
+ it 'sets wrap to word with word string' do
90
+ card = Squib::Card.new(@deck, 100, 150)
91
+ expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD).once
92
+ card.text('foo', 'Sans 12', nil, '#abc',
93
+ 10, 15, 20, 50,
94
+ nil, false, 'word', false,
95
+ 1.0, :left, :top, nil, 0.0)
96
+ end
97
+
98
+ it 'sets wrap to word_char with symbol word_char' do
99
+ card = Squib::Card.new(@deck, 100, 150)
100
+ expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD_CHAR).once
101
+ card.text('foo', 'Sans 12', nil, '#abc',
102
+ 10, 15, 20, 50,
103
+ nil, false, :word_char, false,
104
+ 1.0, :left, :top, nil, 0.0)
105
+ end
106
+
107
+ it 'sets wrap to word_char with true' do
108
+ card = Squib::Card.new(@deck, 100, 150)
109
+ expect(@layout).to receive(:wrap=).with(Pango::Layout::WRAP_WORD_CHAR).once
110
+ card.text('foo', 'Sans 12', nil, '#abc',
111
+ 10, 15, 20, 50,
112
+ nil, false, true, false,
113
+ 1.0, :left, :top, nil, 0.0)
114
+ end
115
+
116
+ it 'sets ellipsize to start properly' do
117
+ card = Squib::Card.new(@deck, 100, 150)
118
+ expect(@layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_START).once
119
+ card.text('foo', 'Sans 12', nil, '#abc',
120
+ 10, 15, 20, 50,
121
+ nil, false, true, :start,
122
+ 1.0, :left, :top, nil, 0.0)
123
+ end
124
+
125
+ it 'sets ellipsize to middle properly' do
126
+ card = Squib::Card.new(@deck, 100, 150)
127
+ expect(@layout).to receive(:ellipsize=).with(Pango::Layout::ELLIPSIZE_MIDDLE).once
128
+ card.text('foo', 'Sans 12', nil, '#abc',
129
+ 10, 15, 20, 50,
130
+ nil, false, true, 'middle',
131
+ 1.0, :left, :top, nil, 0.0)
132
+ end
133
+
134
+ end
135
+ end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'squib'
2
3
  require 'squib/input_helpers'
3
4
 
4
5
  class DummyDeck
@@ -6,20 +7,12 @@ class DummyDeck
6
7
  attr_accessor :layout, :cards, :custom_colors
7
8
  end
8
9
 
9
- module Squib
10
- def logger=(l)
11
- @logger = l
12
- end
13
- module_function 'logger='
14
- end
15
-
16
10
  describe Squib::InputHelpers do
17
11
 
18
-
19
12
  before(:each) do
20
13
  @deck = DummyDeck.new
21
14
  @deck.layout = {
22
- 'blah' => {x: 25},
15
+ 'blah' => {x: 25},
23
16
  'apples' => {x: 35},
24
17
  'oranges' => {y: 45},
25
18
  }
@@ -28,90 +21,118 @@ describe Squib::InputHelpers do
28
21
  end
29
22
 
30
23
  context '#layoutify' do
31
- it "warns on the logger when the layout doesn't exist" do
32
- @old_logger = Squib.logger
33
- Squib.logger = instance_double(Logger)
34
- expect(Squib.logger).to receive(:warn).with("Layout entry 'foo' does not exist.").twice
35
- expect(Squib.logger).to receive(:debug)
36
- expect(@deck.send(:layoutify, {layout: :foo})).to eq({layout: [:foo,:foo]})
37
- Squib.logger = @old_logger
24
+ it 'warns on the logger when the layout does not exist' do
25
+ mock_squib_logger(@old_logger) do
26
+ expect(Squib.logger).to receive(:warn).with("Layout entry 'foo' does not exist.").twice
27
+ expect(Squib.logger).to receive(:debug)
28
+ expect(@deck.send(:layoutify, {layout: :foo})).to eq({layout: [:foo,:foo]})
29
+ end
38
30
  end
39
31
 
40
- it "applies the layout in a normal situation" do
32
+ it 'applies the layout in a normal situation' do
41
33
  expect(@deck.send(:layoutify, {layout: :blah})).to \
42
- eq({layout: [:blah, :blah], x: [25, 25]})
34
+ eq({layout: [:blah, :blah], x: [25, 25]})
43
35
  end
44
36
 
45
- it "applies two different layouts for two different situations" do
37
+ it 'applies two different layouts for two different situations' do
46
38
  expect(@deck.send(:layoutify, {layout: ['blah', 'apples']})).to \
47
- eq({layout: ['blah','apples'], x: [25, 35]})
39
+ eq({layout: ['blah','apples'], x: [25, 35]})
48
40
  end
49
41
 
50
- it "still has nils when not applied two different layouts differ in structure" do
42
+ it 'still has nils when not applied two different layouts differ in structure' do
51
43
  expect(@deck.send(:layoutify, {layout: ['apples', 'oranges']})).to \
52
- eq({layout: ['apples','oranges'], x: [35], y: [nil, 45]})
44
+ eq({layout: ['apples','oranges'], x: [35], y: [nil, 45]})
53
45
  #...this might behavior that is hard to debug for users. Trying to come up with a warning or something...
54
46
  end
55
47
 
56
- it "also looks up based on strings" do
48
+ it 'also looks up based on strings' do
57
49
  expect(@deck.send(:layoutify, {layout: 'blah'})).to \
58
- eq({layout: ['blah','blah'], x: [25, 25]})
50
+ eq({layout: ['blah','blah'], x: [25, 25]})
59
51
  end
60
52
 
61
53
  end
62
54
 
63
55
  context '#rangeify' do
64
- it "must be within the card size range" do
56
+ it 'must be within the card size range' do
65
57
  expect{@deck.send(:rangeify, {range: 2..3})}.to \
66
58
  raise_error(ArgumentError, '2..3 is outside of deck range of 0..1')
67
59
  end
68
60
 
69
- it "cannot be nil" do
61
+ it 'cannot be nil' do
70
62
  expect{@deck.send(:rangeify, {range: nil})}.to \
71
63
  raise_error(RuntimeError, 'Range cannot be nil')
72
64
  end
73
65
 
74
- it "defaults to a range of all cards if :all" do
66
+ it 'defaults to a range of all cards if :all' do
75
67
  expect(@deck.send(:rangeify, {range: :all})).to eq({range: 0..1})
76
68
  end
77
69
  end
78
70
 
79
- context "#fileify" do
80
- it "should throw an error if the file doesn't exist" do
71
+ context '#fileify' do
72
+ it 'should throw an error if the file does not exist' do
81
73
  expect{@deck.send(:fileify, {file: 'nonexist.txt'}, true)}.to \
82
74
  raise_error(RuntimeError,"File #{File.expand_path('nonexist.txt')} does not exist!")
83
75
  end
84
76
  end
85
77
 
86
- context "#dir" do
87
- it "should raise an error if the directory does not exist" do
78
+ context '#dirify' do
79
+ it 'should raise an error if the directory does not exist' do
88
80
  expect{@deck.send(:dirify, {dir: 'nonexist'}, :dir, false)}.to \
89
81
  raise_error(RuntimeError,"'nonexist' does not exist!")
90
82
  end
83
+
84
+ it 'should warn and make a directory creation is allowed' do
85
+ opts = {dir: 'tocreate'}
86
+ Dir.chdir(output_dir) do
87
+ FileUtils.rm_rf('tocreate', secure: true)
88
+ mock_squib_logger(@old_logger) do
89
+ expect(Squib.logger).to receive(:warn).with("Dir 'tocreate' does not exist, creating it.").once
90
+ expect(@deck.send(:dirify, opts, :dir, true)).to eq(opts)
91
+ expect(Dir.exists? 'tocreate').to be true
92
+ end
93
+ end
94
+ end
95
+
91
96
  end
92
97
 
93
- context "#colorify" do
94
- it "should parse if nillable" do
98
+ context '#colorify' do
99
+ it 'should parse if nillable' do
95
100
  color = @deck.send(:colorify, {color: ['#fff']}, true)[:color]
96
101
  expect(color.to_a[0].to_a).to eq([1.0, 1.0, 1.0, 1.0])
97
102
  end
98
103
 
99
- it "raises and error if the color doesn't exist" do
104
+ it 'raises and error if the color does not exist' do
100
105
  expect{ @deck.send(:colorify, {color: [:nonexist]}, false) }.to \
101
- raise_error(ArgumentError, "unknown color name: nonexist")
106
+ raise_error(ArgumentError, 'unknown color name: nonexist')
102
107
  end
103
108
 
104
- it "pulls from config's custom colors" do
105
- @deck.custom_colors['foo'] = "#abc"
109
+ it 'pulls from custom colors in the config' do
110
+ @deck.custom_colors['foo'] = '#abc'
106
111
  expect(@deck.send(:colorify, {color: [:foo]}, false)[:color][0].to_s).to \
107
112
  eq('#AABBCCFF')
108
113
  end
109
114
 
110
- it "pulls from config's custom colors even when a string" do
111
- @deck.custom_colors['foo'] = "#abc"
115
+ it 'pulls custom colors even when a string' do
116
+ @deck.custom_colors['foo'] = '#abc'
112
117
  expect(@deck.send(:colorify, {color: ['foo']}, false)[:color][0].to_s).to \
113
118
  eq('#AABBCCFF')
114
119
  end
115
120
  end
116
121
 
117
- end
122
+ context '#rotateify' do
123
+ it 'computes a clockwise rotate properly' do
124
+ opts = @deck.send(:rotateify, {rotate: :clockwise})
125
+ expect(opts).to eq({ :angle => 0.5 * Math::PI,
126
+ :rotate => :clockwise
127
+ })
128
+ end
129
+
130
+ it 'computes a counter-clockwise rotate properly' do
131
+ opts = @deck.send(:rotateify, {rotate: :counterclockwise})
132
+ expect(opts).to eq({ :angle => 1.5 * Math::PI,
133
+ :rotate => :counterclockwise
134
+ })
135
+ end
136
+ end
137
+
138
+ end