thousand_island 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 389fb55f938dbc9612ecbdab028eb4c6f7a1ba6b
4
- data.tar.gz: ef8e4cb13f7628a49c8f61af335686b6bf9596e6
3
+ metadata.gz: 7c954cf1458e611ac4249a9b3a39d8136db0cf14
4
+ data.tar.gz: 02b295ba89ca8e95219343ef9414ef648752c4d7
5
5
  SHA512:
6
- metadata.gz: a720ae6858b1a2716f90b110aa5686023302add7de08f1e0929ca1e0da7b98877ac1c8d58dd6d10c7e0ea07530da3a3c5798fd9359dc64d9863e9bd2a9ba17a5
7
- data.tar.gz: 12528531c5d4239969608d7ca0e0d6480494d84234293515a3ac587e16996b780ae2cd28fed86d85098209f48e58f1c3d8eed77ad275db5ab3939e0184f25355
6
+ metadata.gz: 8e616580434d106f2629097376c9b97eb602d07e0cd47bd22ca4e081834d48241d025ac9c75569f6aad7d9e109343c6634a95bdcb0ce65656f34669e19234ce9
7
+ data.tar.gz: 5a010d76d9bdd237127e6efd91937b218021b41dd358f0aba9b5d2a8aa3c98f4276b50fc56a63187c5dadbc6758ae694f57e29136c9fe9ed8dfd8ed41f62bb12
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.2
3
+ - 2.1.4
4
4
  - 2.0.0
5
5
  - 1.9.3
6
6
  script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Thousand Island
2
- ### Dressing for Prawn
2
+ **Dressing for Prawn**
3
3
 
4
- [![GitHub version](https://badge.fury.io/gh/colinweight%2Fthousand_island.svg)](http://badge.fury.io/gh/colinweight%2Fthousand_island) [![Code Climate](https://codeclimate.com/github/colinweight/thousand_island/badges/gpa.svg)](https://codeclimate.com/github/colinweight/thousand_island) [![Test Coverage](https://codeclimate.com/github/colinweight/thousand_island/badges/coverage.svg)](https://codeclimate.com/github/colinweight/thousand_island) [![Build Status](https://travis-ci.org/colinweight/thousand_island.svg?branch=master)](https://travis-ci.org/colinweight/thousand_island) [![Inline docs](http://inch-ci.org/github/colinweight/thousand_island.png?branch=master)](http://inch-ci.org/github/colinweight/thousand_island)
4
+ [![Gem Version](https://badge.fury.io/rb/thousand_island.svg)](http://badge.fury.io/rb/thousand_island) [![Code Climate](https://codeclimate.com/github/colinweight/thousand_island/badges/gpa.svg)](https://codeclimate.com/github/colinweight/thousand_island) [![Test Coverage](https://codeclimate.com/github/colinweight/thousand_island/badges/coverage.svg)](https://codeclimate.com/github/colinweight/thousand_island) [![Build Status](https://travis-ci.org/colinweight/thousand_island.svg?branch=master)](https://travis-ci.org/colinweight/thousand_island) [![Inline docs](http://inch-ci.org/github/colinweight/thousand_island.png?branch=master)](http://inch-ci.org/github/colinweight/thousand_island)
5
5
 
6
6
 
7
7
  [Prawn](https://github.com/prawnpdf/prawn) is awesome. It has some amazing functionality, and you can get anything that's in your head onto a PDF document with some Ruby code. For me though, as wonderful as that is, I normally only need a repeating header and footer, and then some text and maybe a table in between them. This is where **Thousand Island** comes in. A few simple commands should get you set up with a template that you can use application wide, and then all you need to worry about is getting the right content into the document.
@@ -229,7 +229,7 @@ def show
229
229
  If your controller for getting the data for a PDF is that simple, then you're pretty lucky. Normally we're going to want a PDF file to render a few things at once, so you might build a service object that formats the data, and use as follows:
230
230
 
231
231
  ```ruby
232
- def show
232
+ def show
233
233
  respond_to do |format|
234
234
  format.html do
235
235
  @thing_for_html_view = Thing.find(params[:id])
@@ -255,6 +255,87 @@ These are only suggestions, as you can probably tell there is nothing tying you
255
255
  ```
256
256
  However, that kind of logic seems beyond the scope of the Builder, and should proabably be in the consumer of your Builder class, rather than the builder itself.
257
257
 
258
+ ## Tables
259
+
260
+ Quite often a pdf will need to render tabular data. It makes sense if there is a common table styling for all PDFs in your application (although it's not compulsory!). Thousand Island has a Table class, and a TableStyle class.
261
+
262
+ > Note: **Thousand Island** has been designed to handle the most common scenarios, and therefore cannot cover every possibility and still keep it simple. If there's anything you can't do with a ThousandIsland::Table, you may need to use Prawn's standard table rendering. There's nothing stopping your Builder rendering a table without using any of the Table implementation in **Thousand Island**. Check out the [Prawn Table Manual](http://prawnpdf.org/prawn-table-manual.pdf) for the full capabilities.
263
+
264
+ ### Creating a Table
265
+ There are three main components of a table, header body and footer
266
+ #### Header
267
+ Header rows are optional. By doing nothing, your table will have no header row/s. To have a header in your table, you have several options:
268
+ ```ruby
269
+ # Straightforward single row, no builder logic required
270
+ header_rows = [['Column One', 'Column Two', 'Column Three']]
271
+
272
+ # Build gradually (You can use Prawn native syntax for span and formatting)
273
+ header_rows << [{content: 'First Two Columns', colspan: 2}, 'Third']
274
+ header_rows << ['Column One', 'Column Two', 'Column Three']
275
+
276
+ ```
277
+
278
+ By default **Thousand Island** takes all the rows in the <code>header_rows</code> and formats them as a repeating header. If you don't want a header, don't put anything in <code>header_rows</code>. If you don't want the header to repeat across pages, then set:
279
+ ```ruby
280
+ header_repeat: false
281
+ ```
282
+ in either the <code>settings</code> method (which returns a hash specific to this Table class) or by setting it in you TableSettings custom class.
283
+
284
+ #### Body
285
+ Similar to <code>header_rows</code>, <code>body_rows</code> can be set in one go or build gradually. Given the nature of the content of a table, it is more likely you'll have a method in that iterates through a collection of objects and builds the table according to the requirements.
286
+ The way you build the Array of nested Array for the body is up to you. Three suggestions:
287
+
288
+ - **Simple Data**: Simply set the <code>body_rows</code> as an Array of Arrays anywhere that's convenient
289
+ - **Slightly Complex**: Iterate over a collection craeting and Array of "cellable" objects for Prawn, and add to <code>body_rows</code> as you go (<code>body_rows << newly_built_row</code>). You can do this in your Builder and pass it to the Table, or pass objects from your Builder to your Table so that the Table can deal with it.
290
+ - **Complex Real Life**: Create a ServiceObject that knows everything it needs to know about how to build the rows. In some situations, a table row might need to represent multiple objects and the logic may be complex. Keeping the row building away from your Builder or Table classess keeps things simple.
291
+
292
+ #### Footer
293
+ Works in the same way as the header, except you use <code>footer_rows</code>. The only difference is that the footer does not repeat.
294
+
295
+ #### Table Options
296
+ A PrawnTable is very configurable. The goal of the Table component of **Thousand Island** is to make the configuration reusable throughout your application. There is a TableSettings class (see below) that you can subclass and use as a configuration object, but you can also override those settings for a specific table by setting a compatible Hash in your Table class like so:
297
+ ```ruby
298
+ def settings
299
+ {
300
+ header_repeat: false
301
+ }
302
+ end
303
+ ```
304
+
305
+ ### Table Settings
306
+ You can use the defaults, or create your own <code>TableSettings</code> class as below:
307
+ ```ruby
308
+ class MyTableSettings
309
+ def table_options
310
+ {...}
311
+ end
312
+ end
313
+ ```
314
+ All the settings in the <code>table_options</code> Hash will be merged with the defaults and made available to any Table that uses your custom TableSettings class.
315
+
316
+ To get one of your Table subclasses to use your custom TableSettings subclass, do the following:
317
+ ```ruby
318
+ class MyTable < ThousandIsland::Table
319
+ uses_settings MyTableSettings
320
+
321
+ ...
322
+
323
+ end
324
+ ```
325
+
326
+ ### Using Tables in your Builder
327
+ Anywhere in your Builder, you can do:
328
+ ```ruby
329
+ table = table_with MyTable
330
+ ```
331
+ This initialises the Table class and gets it ready to build the body rows. You can then use it where appropriate to render the table in your PDF document:
332
+ ```ruby
333
+ table.draw
334
+
335
+ # To override any of the settins either in the table or the TableSettings being used, just pass them as a Hash to the draw method:
336
+ table.draw(header_repeat: false)
337
+ ```
338
+
258
339
  ## Default Styles
259
340
  Out of the box, ThousandIsland gives you some generic styles with default values. Override any of the values in your custom Stylesheet, or your Template. Create your own entirely new style in either of those places too, and get the magic method for free.
260
341
 
@@ -262,94 +343,117 @@ The default styles are:
262
343
  ##### body
263
344
  ```ruby
264
345
  {
265
- :size => 10, # Inherited from default_style
266
- :style => :normal, # Inherited from default_style
267
- :align => :left, # Inherited from default_style
268
- :leading => 1, # Inherited from default_style
269
- :inline_format => true, # Inherited from default_style
270
- :color => "000000" # Inherited from default_style
346
+ size: 10, # Inherited from default_style
347
+ style: :normal, # Inherited from default_style
348
+ align: :left, # Inherited from default_style
349
+ leading: 1, # Inherited from default_style
350
+ inline_format: true, # Inherited from default_style
351
+ color: "000000" # Inherited from default_style
271
352
  }
272
353
  ```
273
354
  ##### h1
274
355
  ```ruby
275
356
  {
276
- :size => 18, # Calcuated as 1.8 * default_style[:size]
277
- :style => :bold,
278
- :align => :left, # Inherited from default_style
279
- :leading => 8,
280
- :inline_format => true, # Inherited from default_style
281
- :color => "000000" # Inherited from default_style
357
+ size: 18, # Calcuated as 1.8 * default_style[:size]
358
+ style: :bold,
359
+ align: :left, # Inherited from default_style
360
+ leading: 8,
361
+ inline_format: true, # Inherited from default_style
362
+ color: "000000" # Inherited from default_style
282
363
  }
283
364
  ```
284
365
  ##### h2
285
366
  ```ruby
286
367
  {
287
- :size => 15, # Calcuated as 1.5 * default_style[:size]
288
- :style => :bold,
289
- :align => :left, # Inherited from default_style
290
- :leading => 4,
291
- :inline_format => true, # Inherited from default_style
292
- :color => "000000" # Inherited from default_style
368
+ size: 15, # Calcuated as 1.5 * default_style[:size]
369
+ style: :bold,
370
+ align: :left, # Inherited from default_style
371
+ leading: 4,
372
+ inline_format: true, # Inherited from default_style
373
+ color: "000000" # Inherited from default_style
293
374
  }
294
375
  ```
295
376
  ##### h3
296
377
  ```ruby
297
378
  {
298
- :size => 14, # Calcuated as 1.4 * default_style[:size]
299
- :style => :bold,
300
- :align => :left, # Inherited from default_style
301
- :leading => 4,
302
- :inline_format => true, # Inherited from default_style
303
- :color => "000000" # Inherited from default_style
379
+ size: 14, # Calcuated as 1.4 * default_style[:size]
380
+ style: :bold,
381
+ align: :left, # Inherited from default_style
382
+ leading: 4,
383
+ inline_format: true, # Inherited from default_style
384
+ color: "000000" # Inherited from default_style
304
385
  }
305
386
  ```
306
387
  ##### h4
307
388
  ```ruby
308
389
  {
309
- :size => 11, # Calcuated as 1.1 * default_style[:size]
310
- :style => :bold_italic,
311
- :align => :left, # Inherited from default_style
312
- :leading => 4,
313
- :inline_format => true, # Inherited from default_style
314
- :color => "000000" # Inherited from default_style
390
+ size: 11, # Calcuated as 1.1 * default_style[:size]
391
+ style: :bold_italic,
392
+ align: :left, # Inherited from default_style
393
+ leading: 4,
394
+ inline_format: true, # Inherited from default_style
395
+ color: "000000" # Inherited from default_style
315
396
  }
316
397
  ```
317
398
  ##### h5
318
399
  ```ruby
319
400
  {
320
- :size => 10, # Calcuated as 1 * default_style[:size]
321
- :style => :normal, # Inherited from default_style
322
- :align => :left, # Inherited from default_style
323
- :leading => 4,
324
- :inline_format => true, # Inherited from default_style
325
- :color => "000000" # Inherited from default_style
401
+ size: 10, # Calcuated as 1 * default_style[:size]
402
+ style: :normal, # Inherited from default_style
403
+ align: :left, # Inherited from default_style
404
+ leading: 4,
405
+ inline_format: true, # Inherited from default_style
406
+ color: "000000" # Inherited from default_style
326
407
  }
327
408
  ```
328
409
  ##### h6
329
410
  ```ruby
330
411
  {
331
- :size => 8.5, # Calcuated as 0.85 * default_style[:size]
332
- :style => :italic,
333
- :align => :left, # Inherited from default_style
334
- :leading => 4,
335
- :inline_format => true, # Inherited from default_style
336
- :color => "000000" # Inherited from default_style
412
+ size: 8.5, # Calcuated as 0.85 * default_style[:size]
413
+ style: :italic,
414
+ align: :left, # Inherited from default_style
415
+ leading: 4,
416
+ inline_format: true, # Inherited from default_style
417
+ color: "000000" # Inherited from default_style
337
418
  }
338
419
  ```
339
420
  ##### footer
340
421
  ```ruby
341
422
  {
342
- :size => 0.8, # Calcuated as 0.8 * default_style[:size]
343
- :style => :normal,
344
- :align => :left, # Inherited from default_style
345
- :leading => 1, # Inherited from default_style
346
- :inline_format => true, # Inherited from default_style
347
- :color => "666666"
423
+ size: 0.8, # Calcuated as 0.8 * default_style[:size]
424
+ style: :normal,
425
+ align: :left, # Inherited from default_style
426
+ leading: 1, # Inherited from default_style
427
+ inline_format: true, # Inherited from default_style
428
+ color: "666666"
348
429
  }
349
430
  ```
431
+ ## Default Table Settings
432
+ Override any of these settings (or the nested settings) using the following techniques (in order of precedence):
350
433
 
434
+ - Builder: <code>table.draw(_your_hash_)</code>
435
+ - Table class: <code>settings</code> method that returns a hash
436
+ - TableSettings class: <code>table_settings</code> method that returns a hash
437
+
438
+ ### The full list of defaults
439
+ ```ruby
440
+ {
441
+ position: :center,
442
+ width: pdf.bounds.width, # Equates to full page width
443
+ cell_style: {
444
+ borders: [:top, :bottom],
445
+ border_width: 0.5,
446
+ inline_format: true,
447
+ size: 10
448
+ },
449
+ header_repeat: true,
450
+ header_format: {
451
+ align: :center,
452
+ font_style: :bold
453
+ }
454
+ }
455
+ ```
351
456
  ## To come...
352
- - Easy (and repeatable) Table formatting
353
457
  - Easy list rendering and styling (including nested lists)
354
458
  - More flexibility in the Footer layout
355
459
  - (Possibly) Command line functions to create common subclass files
@@ -1,19 +1,22 @@
1
- require "thousand_island/version"
1
+ require 'thousand_island/version'
2
2
 
3
- require "prawn"
3
+ require 'prawn'
4
+ require 'prawn/table'
4
5
 
5
- require "thousand_island/style_sheet"
6
- require "thousand_island/template"
7
- require "thousand_island/builder"
6
+ require 'thousand_island/style_sheet'
7
+ require 'thousand_island/template'
8
+ require 'thousand_island/builder'
9
+ require 'thousand_island/table'
10
+ require 'thousand_island/table_settings'
8
11
 
9
- require "thousand_island/components"
10
- require "thousand_island/components/base"
11
- require "thousand_island/components/header"
12
- require "thousand_island/components/footer"
13
- require "thousand_island/components/body"
12
+ require 'thousand_island/components'
13
+ require 'thousand_island/components/base'
14
+ require 'thousand_island/components/header'
15
+ require 'thousand_island/components/footer'
16
+ require 'thousand_island/components/body'
14
17
 
15
- require "thousand_island/utilities/utilities"
16
- require "thousand_island/utilities/style_hash"
18
+ require 'thousand_island/utilities/utilities'
19
+ require 'thousand_island/utilities/style_hash'
17
20
 
18
21
  module ThousandIsland
19
22
  Error = Class.new(StandardError)
@@ -107,6 +107,10 @@ module ThousandIsland
107
107
  {}
108
108
  end
109
109
 
110
+ def table_with(klass)
111
+ klass.new(pdf)
112
+ end
113
+
110
114
  private
111
115
 
112
116
  def draw_header
@@ -36,9 +36,7 @@ module ThousandIsland
36
36
  def col2
37
37
  start = col1_width
38
38
  pdf.bounding_box([start, box_height], width: col2_width, height: box_height) do
39
- options[:style].each do |k,v|
40
- pdf.send(k, v) if pdf.respond_to?(k)
41
- end if options[:style]
39
+ inject_style
42
40
  yield if block_given?
43
41
  end
44
42
  end
@@ -62,6 +60,10 @@ module ThousandIsland
62
60
  options[:repeated]
63
61
  end
64
62
 
63
+ def inject_style
64
+ options[:style].each { |k,v| pdf.send(k, v) if pdf.respond_to?(k) } if options[:style]
65
+ end
66
+
65
67
  def self.defaults
66
68
  {
67
69
  height: 33,
@@ -0,0 +1,119 @@
1
+ module ThousandIsland
2
+ # The Table class can be used to set the definition of your table, such as
3
+ # format and headings. You then inject the data from your Builder so that
4
+ # it can be rendered.
5
+ # You can declare a TableSettings class that you wish to use, otherwise
6
+ # the default will be used. To set a Table Setting class, add the following
7
+ # in your class body:
8
+ #
9
+ # uses_style MyTableSettings
10
+ #
11
+ class Table
12
+
13
+ attr_reader :pdf
14
+ attr_accessor :table_options
15
+
16
+ class << self
17
+ attr_writer :table_settings_klass
18
+
19
+ def table_settings_klass
20
+ @table_settings_klass ||= TableSettings
21
+ end
22
+
23
+ def uses_settings(klass)
24
+ self.table_settings_klass = klass
25
+ end
26
+ end
27
+
28
+ def initialize(pdf)
29
+ @pdf = pdf
30
+ @table_options = {}
31
+ end
32
+
33
+ def settings
34
+ {}
35
+ end
36
+
37
+ def draw(options={})
38
+
39
+ @table_options = merged_options(options)
40
+
41
+ table_options[:header] = prawn_header_setting
42
+
43
+ pdf.table(table_data, options_for_prawn) do |t|
44
+ t.row(0..num_header_rows - 1).font_style = table_options[:header_format][:font_style] unless header_rows.empty?
45
+ t.row(0..num_header_rows - 1).align = table_options[:header_format][:align] unless header_rows.empty?
46
+ t.row(-1..(0 - num_footer_rows)).font_style = :bold unless footer_rows.empty?
47
+ yield(t) if block_given?
48
+ end
49
+ end
50
+
51
+ def body_rows=(row_array)
52
+ raise ArgumentError.new('table_rows must be an array') unless row_array.is_a?(Array)
53
+ @body_rows = row_array
54
+ end
55
+
56
+ def body_rows
57
+ @body_rows ||= []
58
+ end
59
+
60
+ def header_rows=(row_array)
61
+ raise ArgumentError.new('header_cells must be an array') unless row_array.is_a?(Array)
62
+ @header_rows = row_array
63
+ end
64
+
65
+ def header_rows
66
+ @header_rows ||= []
67
+ end
68
+
69
+ def footer_rows=(row_array)
70
+ raise ArgumentError.new('footer_rows must be an array') unless row_array.is_a?(Array)
71
+ @footer_rows = row_array
72
+ end
73
+
74
+ def footer_rows
75
+ @footer_rows ||= []
76
+ end
77
+
78
+
79
+ private
80
+
81
+ def table_data
82
+ data = body_rows
83
+ header_rows.reverse_each{ |row| data.unshift(row) }
84
+ footer_rows.each{ |row| data << row }
85
+ data
86
+ end
87
+
88
+ def num_footer_rows
89
+ footer_rows.size
90
+ end
91
+
92
+ def num_header_rows
93
+ header_rows.size
94
+ end
95
+
96
+
97
+ def prawn_header_setting
98
+ return false if header_rows.empty? || table_options[:header_repeat] == false
99
+ header_rows.size
100
+ end
101
+
102
+ def options_for_prawn
103
+ table_options.select{ |k| [:cell_style, :header, :column_widths, :position, :width, :row_colors, ].include?(k)}
104
+ end
105
+
106
+ def merged_options(options={})
107
+ deep_merger.merge_options(options, settings, table_settings.settings)
108
+ end
109
+
110
+ def table_settings
111
+ @table_settings||= self.class.table_settings_klass.new(pdf, settings)
112
+ end
113
+
114
+ def deep_merger
115
+ @deep_merger ||= Utilities::DeepMerge::TableOptions
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,58 @@
1
+ module ThousandIsland
2
+ # The TableSettings class is where you set up styling rules that can be used by your Table class. You may create a class that inherits from TableSettings, and then use it in any of your tables. You can sub-class your TableStyles so you may define a master style for your app, but then have derived styles for special situations.
3
+ #
4
+ class TableSettings
5
+
6
+ attr_reader :pdf, :overrides
7
+
8
+ def initialize(pdf, overrides={})
9
+ @pdf = pdf
10
+ @overrides = overrides
11
+ end
12
+
13
+ #@TODO override this one!!! Do the docs...
14
+ def table_settings
15
+ {}
16
+ end
17
+
18
+ def settings
19
+ default_options.merge(table_settings.merge(overrides))
20
+ end
21
+
22
+ def default_options
23
+ {
24
+ width: pdf.bounds.width,
25
+ cell_style: cell_styles,
26
+ position: :center,
27
+ header_format: header_format,
28
+ header_repeat: true,
29
+ footer_format: footer_format
30
+ }
31
+ end
32
+
33
+ def cell_styles
34
+ {
35
+ borders: [:top, :bottom],
36
+ border_width: 0.5,
37
+ inline_format: true,
38
+ size: 10
39
+ }
40
+ end
41
+
42
+ def header_format
43
+ {
44
+ align: :center,
45
+ font_style: :bold
46
+ }
47
+
48
+ end
49
+
50
+ def footer_format
51
+ {
52
+ font_style: :bold
53
+ }
54
+ end
55
+
56
+
57
+ end
58
+ end
@@ -181,13 +181,13 @@ module ThousandIsland
181
181
  end
182
182
 
183
183
  def header_space
184
- return (pdf_options[:header][:height] + pdf_options[:header][:bottom_padding]) if pdf_options[:header][:render]
185
- 0
184
+ return 0 unless pdf_options[:header][:render]
185
+ pdf_options[:header][:height] + pdf_options[:header][:bottom_padding]
186
186
  end
187
187
 
188
188
  def footer_space
189
- return (pdf_options[:footer][:height] + pdf_options[:footer][:top_padding]) if pdf_options[:footer][:render]
190
- 0
189
+ return 0 unless pdf_options[:footer][:render]
190
+ pdf_options[:footer][:height] + pdf_options[:footer][:top_padding]
191
191
  end
192
192
 
193
193
 
@@ -237,7 +237,7 @@ module ThousandIsland
237
237
 
238
238
 
239
239
  def deep_merger
240
- @deep_merger ||= Utilities::DeepMerge
240
+ @deep_merger ||= Utilities::DeepMerge::TemplateOptions
241
241
  end
242
242
 
243
243
  # Called by method missing when a style is supplied with text, ie: h1 'Header'
@@ -3,53 +3,85 @@ module ThousandIsland
3
3
 
4
4
  module DeepMerge
5
5
 
6
- #Take a number of hashes and merge them into one, respecting
7
- # the structure and nesting according to the pdf options hash.
8
- # Hashes work in order of precedence, the first in the array
9
- # overrides, the second, etc.
10
- #
11
- # @param hashes [*Hash] A number of hashes to merge, in the order of precedence
12
- #
13
- # @return [Hash] the merged values
14
- def self.merge_options(*hashes)
15
- hashes.reverse!
6
+ def merge_for_key_and_nested_keys(key, keys, *hashes)
7
+ temp = {}
16
8
  merged = {}
17
- footer = merge_footer(*hashes)
18
- header = merge_header(*hashes)
19
- body = merge_body(*hashes)
20
9
  hashes.each do |h|
21
- merged.merge!(h)
10
+ keys.each do |k|
11
+ temp[k] = {} unless temp.has_key? k
12
+ temp[k].merge!(h[key][k]) if h[key] && h[key][k]
13
+ end
14
+ merged.merge!(h[key]) if h[key]
22
15
  end
23
- merged[:footer] = footer
24
- merged[:header] = header
25
- merged[:body] = body
26
- merged
16
+ merged.merge(temp)
27
17
  end
28
18
 
29
- def self.merge_footer(*hashes)
30
- keys = [:numbering_options, :style]
31
- merge_for_key_and_nested_keys(:footer, keys, *hashes)
32
- end
19
+ module TemplateOptions
20
+ extend ::ThousandIsland::Utilities::DeepMerge
33
21
 
34
- def self.merge_header(*hashes)
35
- merge_for_key_and_nested_keys(:header, [], *hashes)
36
- end
22
+ # Take a number of hashes used for Template Options and merge them
23
+ # into one, respecting the structure and nesting according to
24
+ # the pdf options hash. Hashes work in order of precedence, the
25
+ # first in the array overrides, the second, etc.
26
+ #
27
+ # @param hashes [*Hash] A number of hashes to merge, in the order of precedence
28
+ #
29
+ # @return [Hash] the merged values
30
+ def self.merge_options(*hashes)
31
+ hashes.reverse!
32
+ merged = {}
33
+ footer = merge_footer(*hashes)
34
+ header = merge_header(*hashes)
35
+ body = merge_body(*hashes)
36
+ hashes.each do |h|
37
+ merged.merge!(h)
38
+ end
39
+ merged[:footer] = footer
40
+ merged[:header] = header
41
+ merged[:body] = body
42
+ merged
43
+ end
44
+
45
+ def self.merge_footer(*hashes)
46
+ keys = [:numbering_options, :style]
47
+ merge_for_key_and_nested_keys(:footer, keys, *hashes)
48
+ end
37
49
 
38
- def self.merge_body(*hashes)
39
- merge_for_key_and_nested_keys(:body, [], *hashes)
50
+ def self.merge_header(*hashes)
51
+ merge_for_key_and_nested_keys(:header, [], *hashes)
52
+ end
53
+
54
+ def self.merge_body(*hashes)
55
+ merge_for_key_and_nested_keys(:body, [], *hashes)
56
+ end
40
57
  end
41
58
 
42
- def self.merge_for_key_and_nested_keys(key, keys, *hashes)
43
- temp = {}
44
- merged = {}
45
- hashes.each do |h|
46
- keys.each do |k|
47
- temp[k] = {} unless temp.has_key? k
48
- temp[k].merge!(h[key][k]) if h[key] && h[key][k]
59
+ module TableOptions
60
+ extend ::ThousandIsland::Utilities::DeepMerge
61
+
62
+ #Take a number of hashes used for Table Options and merge them
63
+ # into one, respecting the structure and nesting according to
64
+ # the pdf options hash. Hashes work in order of precedence, the
65
+ # first in the array overrides, the second, etc.
66
+ #
67
+ # @param hashes [*Hash] A number of hashes to merge, in the order of precedence
68
+ #
69
+ # @return [Hash] the merged values
70
+ def self.merge_options(*hashes)
71
+ hashes.reverse!
72
+ merged = {}
73
+ footer_format = merge_for_key_and_nested_keys(:footer_format, [], *hashes)
74
+ header_format = merge_for_key_and_nested_keys(:header_format, [], *hashes)
75
+ cell_style = merge_for_key_and_nested_keys(:cell_style, [], *hashes)
76
+ hashes.each do |h|
77
+ merged.merge!(h)
49
78
  end
50
- merged.merge!(h[key]) if h[key]
79
+ merged[:footer_format] = footer_format
80
+ merged[:header_format] = header_format
81
+ merged[:cell_style] = cell_style
82
+ merged
51
83
  end
52
- merged.merge(temp)
84
+
53
85
  end
54
86
 
55
87
  end
@@ -1,3 +1,3 @@
1
1
  module ThousandIsland
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "codeclimate-test-reporter"
2
2
  CodeClimate::TestReporter.start
3
3
 
4
+ require 'pdf/inspector'
4
5
  require 'thousand_island'
5
6
 
6
7
  RSpec.configure do |config|
@@ -29,14 +29,10 @@ module ThousandIsland
29
29
 
30
30
  before :each do
31
31
  allow(builder).to receive(:template) { template }
32
- # klass.send(:define_method, :header_content, ->{})
33
- # klass.send(:define_method, :body_content, ->{})
34
- # klass.send(:define_method, :footer_content, ->{})
35
32
  end
36
33
  context 'exists' do
37
34
 
38
35
  it 'header_content' do
39
- # allow(builder).to receive(:header_content)
40
36
  expect(builder).to receive(:header_content)
41
37
  builder.send(:draw_header)
42
38
  end
@@ -101,6 +97,12 @@ module ThousandIsland
101
97
  end
102
98
  end
103
99
  end
100
+
101
+ it 'table_with instantiates the class' do
102
+ dummy_table = double
103
+ expect(dummy_table).to receive(:new).with(subject.pdf)
104
+ subject.table_with dummy_table
105
+ end
104
106
  end
105
107
  end
106
108
  end
@@ -0,0 +1,107 @@
1
+ module ThousandIsland
2
+ describe Table do
3
+ let(:pdf) { Prawn::Document.new({:page_size => "A4", :page_layout => :portrait}) }
4
+
5
+ subject { described_class.new(pdf) }
6
+
7
+ context 'when TableStyle is not set' do
8
+ it 'sets default style' do
9
+ expect(described_class.table_settings_klass).to eq(TableSettings)
10
+ end
11
+ end
12
+
13
+ context 'when TableStyle is set' do
14
+ it 'is used' do
15
+ klass = described_class.dup
16
+ Dummy = Class.new
17
+ klass.uses_settings(Dummy)
18
+ expect(klass.table_settings_klass).to eq(Dummy)
19
+ end
20
+ end
21
+
22
+ describe '#header_rows' do
23
+ it 'defaults to an empty array' do
24
+ expect(subject.header_rows).to eq([])
25
+ end
26
+
27
+ it 'forces array' do
28
+ expect{ subject.header_rows = 'not an array' }.to raise_error(ArgumentError)
29
+ end
30
+
31
+ it 'can be set with an array' do
32
+ expect{ subject.header_rows = %w'it is an array' }.to_not raise_error
33
+ end
34
+ end
35
+
36
+ describe '#body_rows' do
37
+ it 'defaults to an empty array' do
38
+ expect(subject.body_rows).to eq([])
39
+ end
40
+
41
+ it 'forces array' do
42
+ expect{ subject.body_rows = 'not an array' }.to raise_error(ArgumentError)
43
+ end
44
+
45
+ it 'can be set with an array' do
46
+ expect{ subject.body_rows = %w'it is an array' }.to_not raise_error
47
+ end
48
+ end
49
+
50
+ describe '#footer_rows' do
51
+ it 'defaults to an empty array' do
52
+ expect(subject.footer_rows).to eq([])
53
+ end
54
+
55
+ it 'forces array' do
56
+ expect{ subject.footer_rows = 'not an array' }.to raise_error(ArgumentError)
57
+ end
58
+
59
+ it 'can be set with an array' do
60
+ expect{ subject.footer_rows = %w'it is an array' }.to_not raise_error
61
+ end
62
+ end
63
+
64
+ describe 'header' do
65
+ describe 'repeats' do
66
+ let(:body_rows) { [] }
67
+ let(:header_rows) { [[' ', { content: "Prefix", colspan: 2 } ], ['The Number', 'As', 'Bs'] ] }
68
+
69
+ before do
70
+ (1..50).each{ |n| body_rows.push( ["#{n}", "A#{n}", "B#{n}"] ) }
71
+ subject.body_rows = body_rows
72
+ end
73
+
74
+ it 'all rows' do
75
+ subject.header_rows = header_rows
76
+ draw_the_table
77
+ output = PDF::Inspector::Page.analyze(subject.pdf.render)
78
+ expect(output.pages[0][:strings][0..4]).to eq(output.pages[1][:strings][0..4])
79
+ end
80
+
81
+ it 'unless specified not to' do
82
+ subject.header_rows = header_rows
83
+ draw_the_table({header_repeat: false})
84
+ output = PDF::Inspector::Page.analyze(subject.pdf.render)
85
+ expect(output.pages[0][:strings][0..4]).to_not eq(output.pages[1][:strings][0..4])
86
+ end
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def draw_the_table(opts={})
93
+ subject.pdf.bounding_box( [0, std_doc_height], height: std_doc_height, width: std_doc_width ) do
94
+ subject.draw(opts)
95
+ end
96
+ end
97
+
98
+ def std_doc_height
99
+ 840
100
+ end
101
+
102
+ def std_doc_width
103
+ 595
104
+ end
105
+
106
+ end
107
+ end
@@ -8,7 +8,7 @@ module ThousandIsland
8
8
  options = { op_one: 1, op_two: 2 }
9
9
  defaults = subject.send(:defaults)
10
10
  component_defaults = subject.send(:component_defaults)
11
- deep_merger = ThousandIsland::Utilities::DeepMerge
11
+ deep_merger = ThousandIsland::Utilities::DeepMerge::TemplateOptions
12
12
  expect(deep_merger).to receive(:merge_options).with(options, [], defaults, component_defaults)
13
13
  subject.send(:setup_document_options, options)
14
14
  end
@@ -16,7 +16,6 @@ module ThousandIsland
16
16
  it 'method missing' do
17
17
  expect{ subject.made_up_method }.to raise_error(NoMethodError)
18
18
  end
19
-
20
19
  end
21
20
 
22
21
  context 'Style Sheet' do
@@ -1,60 +1,44 @@
1
1
  module ThousandIsland
2
2
  module Utilities
3
+ module DeepMerge
3
4
 
4
- describe DeepMerge do
5
+ describe TemplateOptions do
5
6
 
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
7
+
8
+ let(:default) {{
9
+ header:{
10
+ height: 33,
11
+ repeated: true,
22
12
  },
23
- },
24
- }}
25
- let(:options) {{
26
- header: {
13
+ footer: {
27
14
  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,
15
+ repeated: true,
16
+ numbering_options: {
17
+ align: :right,
18
+ start_count_at: 1,
19
+ },
20
+ number_pages: true,
21
+ numbering_string: '<page>',
22
+ style: {
23
+ font_size: 23
24
+ },
36
25
  },
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,
26
+ }}
27
+ let(:options) {{
28
+ header: {
29
+ height: 33,
30
+ repeated: false,
49
31
  },
50
- style: {
51
- font_size: 5
32
+ footer: {
33
+ height: 40,
34
+ repeated: true,
35
+ numbering_options: {
36
+ align: :right,
37
+ start_count_at: 2,
38
+ },
52
39
  },
53
- },
54
- }}
55
-
56
- it 'deep merges the hashes intelligently' do
57
- expected = {
40
+ }}
41
+ let(:over) {{
58
42
  header: {
59
43
  height: 50,
60
44
  repeated: true,
@@ -64,18 +48,96 @@ module ThousandIsland
64
48
  repeated: false,
65
49
  numbering_options: {
66
50
  align: :left,
67
- start_count_at: 2,
68
51
  },
69
- number_pages: true,
70
- numbering_string: '<page>',
71
52
  style: {
72
53
  font_size: 5
73
54
  },
74
55
  },
75
- body: {},
76
- }
77
- final = described_class.merge_options(over, options, default)
78
- expect(final).to eq(expected)
56
+ }}
57
+
58
+ it 'deep merges the hashes intelligently' do
59
+ expected = {
60
+ header: {
61
+ height: 50,
62
+ repeated: true,
63
+ },
64
+ footer: {
65
+ height: 50,
66
+ repeated: false,
67
+ numbering_options: {
68
+ align: :left,
69
+ start_count_at: 2,
70
+ },
71
+ number_pages: true,
72
+ numbering_string: '<page>',
73
+ style: {
74
+ font_size: 5
75
+ },
76
+ },
77
+ body: {},
78
+ }
79
+ final = described_class.merge_options(over, options, default)
80
+ expect(final).to eq(expected)
81
+ end
82
+ end
83
+
84
+ describe TableOptions do
85
+
86
+ let(:default) {{
87
+ header_repeat: true,
88
+ width: 600,
89
+ cell_style: {
90
+ borders: [:top, :bottom],
91
+ border_width: 0.5,
92
+ inline_format: true,
93
+ size: 10
94
+ },
95
+ position: :center,
96
+ header_format: {
97
+ align: :center,
98
+ font_style: :bold
99
+ },
100
+ footer_format: {
101
+ font_style: :bold
102
+ }
103
+ }}
104
+ let(:options) {{
105
+ header_format: {
106
+ align: :left,
107
+ },
108
+ header_repeat: false
109
+ }}
110
+ let(:over) {{
111
+ header_format: {
112
+ align: :right,
113
+ },
114
+ cell_style: {
115
+ size: 12
116
+ },
117
+ }}
118
+
119
+ it 'deep merges the hashes intelligently' do
120
+ expected = {
121
+ header_repeat: false,
122
+ width: 600,
123
+ cell_style: {
124
+ borders: [:top, :bottom],
125
+ border_width: 0.5,
126
+ inline_format: true,
127
+ size: 12
128
+ },
129
+ position: :center,
130
+ header_format: {
131
+ align: :right,
132
+ font_style: :bold
133
+ },
134
+ footer_format: {
135
+ font_style: :bold
136
+ }
137
+ }
138
+ final = described_class.merge_options(over, options, default)
139
+ expect(final).to eq(expected)
140
+ end
79
141
  end
80
142
  end
81
143
  end
@@ -4,27 +4,30 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'thousand_island/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "thousand_island"
7
+ spec.name = 'thousand_island'
8
8
  spec.version = ThousandIsland::VERSION
9
- spec.authors = ["Colin Weight"]
10
- spec.email = ["colin@colinweight.com.au"]
9
+ spec.authors = ['Colin Weight']
10
+ spec.email = ['colin@colinweight.com.au']
11
11
  spec.description = %q{Dressing for Prawn}
12
12
  spec.summary = %q{A library that helps with common layout elements in PDFs.}
13
- spec.homepage = "https://github.com/colinweight/thousand_island"
14
- spec.license = "MIT"
13
+ spec.homepage = 'https://github.com/colinweight/thousand_island'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'prawn', '~> 1.2.1'
21
+ spec.required_ruby_version = '>= 1.9.3'
22
+ spec.add_dependency 'prawn', '~> 1.3.0'
23
+ spec.add_dependency 'prawn-table', '~> 0.2.1'
22
24
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec", "~> 3.0"
26
- spec.add_development_dependency "guard", "~> 2.6.1"
27
- spec.add_development_dependency "guard-rspec", "~> 4.3.1"
28
- spec.add_development_dependency "yard", "~> 0.8.7"
29
- spec.add_development_dependency "codeclimate-test-reporter"
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'guard', '~> 2.6.1'
29
+ spec.add_development_dependency 'guard-rspec', '~> 4.3.1'
30
+ spec.add_development_dependency 'pdf-inspector', '~> 1.2.0'
31
+ spec.add_development_dependency 'yard', '~> 0.8.7'
32
+ spec.add_development_dependency 'codeclimate-test-reporter'
30
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thousand_island
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Colin Weight
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-11 00:00:00.000000000 Z
11
+ date: 2014-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prawn
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.1
19
+ version: 1.3.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.1
26
+ version: 1.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: prawn-table
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.1
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,20 @@ dependencies:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: 4.3.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: pdf-inspector
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.2.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.2.0
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: yard
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +173,8 @@ files:
145
173
  - lib/thousand_island/components/footer.rb
146
174
  - lib/thousand_island/components/header.rb
147
175
  - lib/thousand_island/style_sheet.rb
176
+ - lib/thousand_island/table.rb
177
+ - lib/thousand_island/table_settings.rb
148
178
  - lib/thousand_island/template.rb
149
179
  - lib/thousand_island/utilities/style_hash.rb
150
180
  - lib/thousand_island/utilities/utilities.rb
@@ -156,6 +186,7 @@ files:
156
186
  - spec/thousand_island/components/footer_spec.rb
157
187
  - spec/thousand_island/components/header_spec.rb
158
188
  - spec/thousand_island/style_sheet_spec.rb
189
+ - spec/thousand_island/table_spec.rb
159
190
  - spec/thousand_island/template_spec.rb
160
191
  - spec/thousand_island/utilities/style_hash_spec.rb
161
192
  - spec/thousand_island/utilities/utilities_spec.rb
@@ -173,7 +204,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
173
204
  requirements:
174
205
  - - ">="
175
206
  - !ruby/object:Gem::Version
176
- version: '0'
207
+ version: 1.9.3
177
208
  required_rubygems_version: !ruby/object:Gem::Requirement
178
209
  requirements:
179
210
  - - ">="
@@ -193,6 +224,7 @@ test_files:
193
224
  - spec/thousand_island/components/footer_spec.rb
194
225
  - spec/thousand_island/components/header_spec.rb
195
226
  - spec/thousand_island/style_sheet_spec.rb
227
+ - spec/thousand_island/table_spec.rb
196
228
  - spec/thousand_island/template_spec.rb
197
229
  - spec/thousand_island/utilities/style_hash_spec.rb
198
230
  - spec/thousand_island/utilities/utilities_spec.rb