thousand_island 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/Guardfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +363 -0
- data/Rakefile +1 -0
- data/lib/thousand_island.rb +21 -0
- data/lib/thousand_island/builder.rb +153 -0
- data/lib/thousand_island/components.rb +4 -0
- data/lib/thousand_island/components/base.rb +43 -0
- data/lib/thousand_island/components/body.rb +18 -0
- data/lib/thousand_island/components/footer.rb +85 -0
- data/lib/thousand_island/components/header.rb +24 -0
- data/lib/thousand_island/style_sheet.rb +112 -0
- data/lib/thousand_island/template.rb +280 -0
- data/lib/thousand_island/utilities/style_hash.rb +20 -0
- data/lib/thousand_island/utilities/utilities.rb +58 -0
- data/lib/thousand_island/version.rb +3 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/thousand_island/builder_spec.rb +106 -0
- data/spec/thousand_island/components/base_spec.rb +68 -0
- data/spec/thousand_island/components/body_spec.rb +37 -0
- data/spec/thousand_island/components/footer_spec.rb +57 -0
- data/spec/thousand_island/components/header_spec.rb +40 -0
- data/spec/thousand_island/style_sheet_spec.rb +44 -0
- data/spec/thousand_island/template_spec.rb +150 -0
- data/spec/thousand_island/utilities/style_hash_spec.rb +29 -0
- data/spec/thousand_island/utilities/utilities_spec.rb +82 -0
- data/spec/thousand_island_spec.rb +3 -0
- data/thousand_island.gemspec +30 -0
- metadata +200 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
module Components
|
3
|
+
describe Base do
|
4
|
+
let(:prawn_doc) { instance_double('Prawn::Document') }
|
5
|
+
let(:component) { described_class.new(prawn_doc) }
|
6
|
+
let(:test_block) { lambda { |a| a } }
|
7
|
+
|
8
|
+
it 'merges defaults and passed options on new' do
|
9
|
+
options = { op_one: 1, op_two: 2 }
|
10
|
+
component = described_class.new(prawn_doc, options)
|
11
|
+
expected = component.defaults.merge(options)
|
12
|
+
expect(component.options).to eq(expected)
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'interactions with Prawn' do
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
allow(prawn_doc).to receive(:bounding_box).and_yield
|
19
|
+
allow(prawn_doc).to receive(:repeat).and_yield
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'render all repeats the component' do
|
23
|
+
allow(component).to receive(:render)
|
24
|
+
expect(prawn_doc).to receive(:repeat).with(:all)
|
25
|
+
component.render_all
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'the block' do
|
29
|
+
it 'yields when render' do
|
30
|
+
expect{ |doc| component.render(&doc) }.to raise_error(NotImplementedError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'is passed to render when render_all' do
|
34
|
+
expect(component).to receive(:render)
|
35
|
+
component.render_all(&test_block)
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'is passed by draw to' do
|
39
|
+
it 'render_all when repeated?' do
|
40
|
+
allow(component).to receive(:repeated?) { true }
|
41
|
+
expect(component).to receive(:render_all)
|
42
|
+
component.draw(&test_block)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'render when not repeated?' do
|
46
|
+
allow(component).to receive(:repeated?) { false }
|
47
|
+
expect(component).to receive(:render)
|
48
|
+
component.draw(&test_block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises on render' do
|
55
|
+
expect{ component.render }.to raise_error(NotImplementedError)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'gets class defaults for instance' do
|
59
|
+
expect(component.defaults).to eq(described_class.defaults)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'defaults to not repeated?' do
|
63
|
+
expect(component.repeated?).to be false
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
module Components
|
3
|
+
describe Body do
|
4
|
+
let(:prawn_doc) { instance_double('Prawn::Document') }
|
5
|
+
let(:bounds) { double(:bounds) }
|
6
|
+
let(:body) { described_class.new(prawn_doc) }
|
7
|
+
|
8
|
+
context 'interactions with Prawn' do
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
allow(prawn_doc).to receive(:bounding_box).and_yield
|
12
|
+
allow(bounds).to receive(:height) { std_doc_height }
|
13
|
+
allow(bounds).to receive(:width) { std_doc_width }
|
14
|
+
allow(prawn_doc).to receive(:bounds) { bounds }
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sizes the box' do
|
18
|
+
h = body.options[:height]
|
19
|
+
t = body.options[:top]
|
20
|
+
expect(prawn_doc).to receive(:bounding_box).with([0, t], width: std_doc_width, height: h)
|
21
|
+
body.render
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def std_doc_height
|
28
|
+
840
|
29
|
+
end
|
30
|
+
|
31
|
+
def std_doc_width
|
32
|
+
595
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
module Components
|
3
|
+
describe Footer do
|
4
|
+
let(:prawn_doc) { instance_double('Prawn::Document') }
|
5
|
+
let(:bounds) { double(:bounds) }
|
6
|
+
let(:footer) { described_class.new(prawn_doc) }
|
7
|
+
|
8
|
+
it '#repeated? uses the option value' do
|
9
|
+
footer.options[:repeated] = true
|
10
|
+
expect(footer.repeated?).to be true
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'Page Numbering' do
|
14
|
+
context 'uses the correct string when options are' do
|
15
|
+
it 'set' do
|
16
|
+
expected = 'Page <page> of <total>'
|
17
|
+
footer.options[:numbering_string] = expected
|
18
|
+
expect(footer.numbering_string).to eq(expected)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'not set' do
|
22
|
+
expect(footer.numbering_string).to eq('<page>')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'interactions with Prawn' do
|
29
|
+
|
30
|
+
before :each do
|
31
|
+
allow(prawn_doc).to receive(:bounding_box).and_yield
|
32
|
+
allow(bounds).to receive(:width) { std_doc_width }
|
33
|
+
allow(prawn_doc).to receive(:bounds) { bounds }
|
34
|
+
allow(prawn_doc).to receive(:number_pages)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'sizes the box' do
|
38
|
+
h = footer.options[:height]
|
39
|
+
expect(prawn_doc).to receive(:bounding_box).with([0, h], width: std_doc_width, height: h)
|
40
|
+
footer.render
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'renders the page numbers' do
|
44
|
+
expect(prawn_doc).to receive(:number_pages).with(footer.numbering_string, footer.options[:numbering_options])
|
45
|
+
footer.render
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def std_doc_width
|
52
|
+
595
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
module Components
|
3
|
+
describe Header do
|
4
|
+
let(:prawn_doc) { instance_double('Prawn::Document') }
|
5
|
+
let(:bounds) { double(:bounds) }
|
6
|
+
let(:header) { described_class.new(prawn_doc) }
|
7
|
+
|
8
|
+
it '#repeated? uses the option value' do
|
9
|
+
header.options[:repeated] = true
|
10
|
+
expect(header.repeated?).to be true
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'interactions with Prawn' do
|
14
|
+
|
15
|
+
before :each do
|
16
|
+
allow(prawn_doc).to receive(:bounding_box).and_yield
|
17
|
+
allow(bounds).to receive(:height) { std_doc_height }
|
18
|
+
allow(bounds).to receive(:width) { std_doc_width }
|
19
|
+
allow(prawn_doc).to receive(:bounds) { bounds }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sizes the box' do
|
23
|
+
h = header.options[:height]
|
24
|
+
expect(prawn_doc).to receive(:bounding_box).with([0, std_doc_height], width: std_doc_width, height: h)
|
25
|
+
header.render
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def std_doc_height
|
31
|
+
840
|
32
|
+
end
|
33
|
+
|
34
|
+
def std_doc_width
|
35
|
+
595
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
describe StyleSheet do
|
3
|
+
let(:subject) do
|
4
|
+
dummy = Class.new
|
5
|
+
dummy.send(:include, described_class)
|
6
|
+
dummy.new
|
7
|
+
end
|
8
|
+
let(:expected_styles) { [:h1, :h2, :h3, :h4, :h5, :h6, :body, :footer] }
|
9
|
+
|
10
|
+
it 'builds the available styles array' do
|
11
|
+
expect(subject.available_styles).to match_array(expected_styles)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has a style method for every style' do
|
15
|
+
expected_styles.each do |s|
|
16
|
+
expect(subject.send("#{s}_style")).to be_a(StyleHash)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'merges body_style with custom for h1' do
|
21
|
+
expected = {
|
22
|
+
size: 10 * 1.8,
|
23
|
+
style: :bold,
|
24
|
+
align: :left,
|
25
|
+
leading: 8,
|
26
|
+
inline_format: true,
|
27
|
+
color: '000000',
|
28
|
+
}
|
29
|
+
expect(subject.h1_style).to eq(expected)
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'extends the Hash with' do
|
33
|
+
it 'font_size' do
|
34
|
+
style = subject.h1_style
|
35
|
+
expect(style[:font_size]).to eq(style[:size])
|
36
|
+
end
|
37
|
+
it 'styles' do
|
38
|
+
style = subject.h3_style
|
39
|
+
expect(style[:styles]).to eq([style[:style]])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
describe Template do
|
3
|
+
|
4
|
+
context 'setup' do
|
5
|
+
it { expect(subject.pdf).to be_a(Prawn::Document) }
|
6
|
+
|
7
|
+
it 'sets the options with merged defaults and passed options' do
|
8
|
+
options = { op_one: 1, op_two: 2 }
|
9
|
+
defaults = subject.send(:defaults)
|
10
|
+
component_defaults = subject.send(:component_defaults)
|
11
|
+
deep_merger = ThousandIsland::Utilities::DeepMerge
|
12
|
+
expect(deep_merger).to receive(:merge_options).with(options, [], defaults, component_defaults)
|
13
|
+
subject.send(:setup_document_options, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'method missing' do
|
17
|
+
expect{ subject.made_up_method }.to raise_error(NoMethodError)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'Style Sheet' do
|
23
|
+
context 'adds method to template' do
|
24
|
+
it 'h1' do
|
25
|
+
expect(subject.respond_to?(:h1)).to be true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'h2' do
|
29
|
+
expect(subject.respond_to?(:h2)).to be true
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'h3' do
|
33
|
+
expect(subject.respond_to?(:h3)).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'adds text to the pdf' do
|
39
|
+
pdf = instance_double('Prawn::Document')
|
40
|
+
allow(subject).to receive(:pdf) { pdf }
|
41
|
+
expected = 'Test Text'
|
42
|
+
style = subject.h1_style
|
43
|
+
expect(pdf).to receive(:text).with(expected, style)
|
44
|
+
subject.h1 expected
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not add an h9 method' do
|
48
|
+
expect(subject.respond_to?(:h9)).to be false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'Header' do
|
53
|
+
let(:header) { instance_double('ThousandIsland::Components::Header') }
|
54
|
+
|
55
|
+
before :each do
|
56
|
+
allow(header).to receive(:draw)
|
57
|
+
allow(subject).to receive(:header_obj) { header }
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'calls draw on the Header component' do
|
61
|
+
expect(header).to receive(:draw)
|
62
|
+
subject.draw_header
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'does not call header_content if not exists' do
|
66
|
+
expect(subject).to_not receive(:header_content)
|
67
|
+
subject.draw_header
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'calls header_content when it exists' do
|
71
|
+
described_class.send(:define_method, :header_content) {}
|
72
|
+
template = described_class.new
|
73
|
+
expect(template).to receive(:header_content)
|
74
|
+
template.draw_header
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'calculates space' do
|
78
|
+
it 'on when rendering' do
|
79
|
+
subject.pdf_options[:header][:height] = 30
|
80
|
+
subject.pdf_options[:header][:bottom_padding] = 10
|
81
|
+
expect(subject.send(:header_space)).to eq(40)
|
82
|
+
end
|
83
|
+
it 'on when not rendering' do
|
84
|
+
subject.pdf_options[:header][:render] = false
|
85
|
+
expect(subject.send(:header_space)).to eq(0)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'Footer' do
|
91
|
+
let(:footer) { instance_double('ThousandIsland::Components::Footer') }
|
92
|
+
|
93
|
+
before :each do
|
94
|
+
allow(footer).to receive(:draw)
|
95
|
+
allow(subject).to receive(:footer_obj) { footer }
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'calls draw on the Footer component' do
|
99
|
+
expect(footer).to receive(:draw)
|
100
|
+
subject.draw_footer
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'does not call footer_content if not exists' do
|
104
|
+
expect(subject).to_not receive(:footer_content)
|
105
|
+
subject.draw_footer
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'calls footer_content when it exists' do
|
109
|
+
described_class.send(:define_method, :footer_content) {}
|
110
|
+
template = described_class.new
|
111
|
+
expect(template).to receive(:footer_content)
|
112
|
+
template.draw_footer
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'calculates space' do
|
116
|
+
it 'on when rendering' do
|
117
|
+
subject.pdf_options[:footer][:height] = 30
|
118
|
+
subject.pdf_options[:footer][:top_padding] = 10
|
119
|
+
expect(subject.send(:footer_space)).to eq(40)
|
120
|
+
end
|
121
|
+
it 'on when not rendering' do
|
122
|
+
subject.pdf_options[:footer][:render] = false
|
123
|
+
expect(subject.send(:footer_space)).to eq(0)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'Body' do
|
129
|
+
let(:body) { instance_double('ThousandIsland::Components::Body') }
|
130
|
+
|
131
|
+
before :each do
|
132
|
+
allow(body).to receive(:draw)
|
133
|
+
allow(subject).to receive(:body_obj) { body }
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'does not call body if not exists' do
|
137
|
+
expect(subject).to_not receive(:body_content)
|
138
|
+
subject.draw_body
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'calls body when it exists' do
|
142
|
+
described_class.send(:define_method, :body_content) {}
|
143
|
+
template = described_class.new
|
144
|
+
expect(template).to receive(:body_content)
|
145
|
+
template.draw_body
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
describe StyleHash do
|
3
|
+
let(:opts) { {size: 1, style: 2, other: 3} }
|
4
|
+
let(:subject) { described_class.new(opts) }
|
5
|
+
|
6
|
+
it 'is a Hash' do
|
7
|
+
expect(subject).to be_a(Hash)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns nil as default key' do
|
11
|
+
expect(subject[:made_up_key]).to be nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'sets the passed options' do
|
15
|
+
expect(subject[:size]).to eq(1)
|
16
|
+
expect(subject[:style]).to eq(2)
|
17
|
+
expect(subject[:other]).to eq(3)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'gets size value for the font_size key' do
|
21
|
+
expect(subject[:font_size]).to eq(1)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'gets style value for the styles key' do
|
25
|
+
expect(subject[:styles]).to eq([2])
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ThousandIsland
|
2
|
+
module Utilities
|
3
|
+
|
4
|
+
describe DeepMerge do
|
5
|
+
|
6
|
+
let(:default) {{
|
7
|
+
header:{
|
8
|
+
height: 33,
|
9
|
+
repeated: true,
|
10
|
+
},
|
11
|
+
footer: {
|
12
|
+
height: 33,
|
13
|
+
repeated: true,
|
14
|
+
numbering_options: {
|
15
|
+
align: :right,
|
16
|
+
start_count_at: 1,
|
17
|
+
},
|
18
|
+
number_pages: true,
|
19
|
+
numbering_string: '<page>',
|
20
|
+
style: {
|
21
|
+
font_size: 23
|
22
|
+
},
|
23
|
+
},
|
24
|
+
}}
|
25
|
+
let(:options) {{
|
26
|
+
header: {
|
27
|
+
height: 33,
|
28
|
+
repeated: false,
|
29
|
+
},
|
30
|
+
footer: {
|
31
|
+
height: 40,
|
32
|
+
repeated: true,
|
33
|
+
numbering_options: {
|
34
|
+
align: :right,
|
35
|
+
start_count_at: 2,
|
36
|
+
},
|
37
|
+
},
|
38
|
+
}}
|
39
|
+
let(:over) {{
|
40
|
+
header: {
|
41
|
+
height: 50,
|
42
|
+
repeated: true,
|
43
|
+
},
|
44
|
+
footer: {
|
45
|
+
height: 50,
|
46
|
+
repeated: false,
|
47
|
+
numbering_options: {
|
48
|
+
align: :left,
|
49
|
+
},
|
50
|
+
style: {
|
51
|
+
font_size: 5
|
52
|
+
},
|
53
|
+
},
|
54
|
+
}}
|
55
|
+
|
56
|
+
it 'deep merges the hashes intelligently' do
|
57
|
+
expected = {
|
58
|
+
header: {
|
59
|
+
height: 50,
|
60
|
+
repeated: true,
|
61
|
+
},
|
62
|
+
footer: {
|
63
|
+
height: 50,
|
64
|
+
repeated: false,
|
65
|
+
numbering_options: {
|
66
|
+
align: :left,
|
67
|
+
start_count_at: 2,
|
68
|
+
},
|
69
|
+
number_pages: true,
|
70
|
+
numbering_string: '<page>',
|
71
|
+
style: {
|
72
|
+
font_size: 5
|
73
|
+
},
|
74
|
+
},
|
75
|
+
body: {},
|
76
|
+
}
|
77
|
+
final = described_class.merge_options(over, options, default)
|
78
|
+
expect(final).to eq(expected)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|