thousand_island 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 389fb55f938dbc9612ecbdab028eb4c6f7a1ba6b
|
4
|
+
data.tar.gz: ef8e4cb13f7628a49c8f61af335686b6bf9596e6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a720ae6858b1a2716f90b110aa5686023302add7de08f1e0929ca1e0da7b98877ac1c8d58dd6d10c7e0ea07530da3a3c5798fd9359dc64d9863e9bd2a9ba17a5
|
7
|
+
data.tar.gz: 12528531c5d4239969608d7ca0e0d6480494d84234293515a3ac587e16996b780ae2cd28fed86d85098209f48e58f1c3d8eed77ad275db5ab3939e0184f25355
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.2
|
4
|
+
- 2.0.0
|
5
|
+
- 1.9.3
|
6
|
+
script: bundle exec rspec spec
|
7
|
+
env:
|
8
|
+
global:
|
9
|
+
secure: eswj1U2HJcBv8pIbk8TkBiry8GXQvFhIqaz/XYkvAC5pPDJrzTK9GpSjo2AkvRgck+H99ukzNmwd6Sn1cnnTE/9lsbSogXvBj588KOxivlQ03bZay2g0aeL35qtSnhmJ1e7G4+B3GRyqEYxHboIOKaKBNP93d9+0VVgmm+wfPPw=
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
5
|
+
# rspec may be run, below are examples of the most common uses.
|
6
|
+
# * bundler: 'bundle exec rspec'
|
7
|
+
# * bundler binstubs: 'bin/rspec'
|
8
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
9
|
+
# installed the spring binstubs per the docs)
|
10
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
11
|
+
# * 'just' rspec: 'rspec'
|
12
|
+
guard :rspec, cmd: 'bundle exec rspec -f doc' do
|
13
|
+
watch(%r{^spec/.+_spec\.rb$})
|
14
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
15
|
+
watch(%r{^lib/thousand_island/(.+)\.rb$}) { |m| "spec/thousand_island/#{m[1]}_spec.rb" }
|
16
|
+
watch(%r{^lib/thousand_island/utilities/(.+)\.rb$}) { |m| "spec/thousand_island/utilities/#{m[1]}_spec.rb" }
|
17
|
+
watch('spec/spec_helper.rb') { "spec" }
|
18
|
+
|
19
|
+
# Turnip features and steps
|
20
|
+
# watch(%r{^spec/acceptance/(.+)\.feature$})
|
21
|
+
# watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
22
|
+
end
|
23
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Colin Weight
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,363 @@
|
|
1
|
+
# Thousand Island
|
2
|
+
### Dressing for Prawn
|
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)
|
5
|
+
|
6
|
+
|
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.
|
8
|
+
|
9
|
+
> Note: ThousandIsland is not meant to be a substitute for learning Prawn, you will get more out of it if you do. The excellent [Prawn Manual](http://prawnpdf.org/manual.pdf) is a great place to start.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'thousand_island'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install thousand_island
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
ThousandIsland uses the following classes to get the job done:
|
28
|
+
|
29
|
+
**ThousandIsland::Template** - The common layout and formatting for your pdfs live in here
|
30
|
+
|
31
|
+
**ThousandIsland::Builder** - Subclass the Builder for your actual business logic for the pdf itself
|
32
|
+
|
33
|
+
**ThousandIsland::StyleSheet** - A mixin for defining common styles to be used by your Template
|
34
|
+
|
35
|
+
A suggested directory structure for a Rails app is as follows, but it's entirely up to you:
|
36
|
+
|
37
|
+
```
|
38
|
+
app/
|
39
|
+
└── pdf_builders/
|
40
|
+
├── my_builder.rb
|
41
|
+
└── templates/
|
42
|
+
├── my_template.rb
|
43
|
+
├── my_style_sheet.rb
|
44
|
+
```
|
45
|
+
For a non-Rails application, the `lib` directory can be used instead of the `app` directory, but it's all up to you.
|
46
|
+
|
47
|
+
### Creating a Template
|
48
|
+
|
49
|
+
The Template class is where you can define elements that may be common to all (or some) documents within your application. It is likely that a common style will be required, so defining it in a Template and then using that Template subclass in any custom Builders DRYs up your pdf generation, as well as allowing for easy restyling across the whole application.
|
50
|
+
|
51
|
+
Typically, the Template subclass would define the settings for the Prawn Document, as well as the settings for the header and footer. Add your own or override any existing settings in the `settings` method. Any options passed into the constructor as a Hash will be merged with these settings, and the defaults.
|
52
|
+
|
53
|
+
Content for the header and footer will be defined in the methods `header_content` and `footer_content`. These methods are passed as a block when the pdf is rendered. Any standard Prawn methods may be used (including bounding boxes or any other layout tools). In addition, any of the styles from the `StyleSheet` can be applied as helper methods. For instance, the default style sheet has a `h1_style` method that returns a ThousandIsland::StyleHash, so in your code you can use:
|
54
|
+
```ruby
|
55
|
+
h1 "My Document Header"
|
56
|
+
```
|
57
|
+
and Prawn will render the text in the style set in the `h1_style` ThousandIsland::StyleHash.
|
58
|
+
In addition to the supplied style methods, you can create a custom method:
|
59
|
+
```ruby
|
60
|
+
def magic_style
|
61
|
+
ThousandIsland::StyleHash.new({
|
62
|
+
size: 15
|
63
|
+
style: bold
|
64
|
+
})
|
65
|
+
end
|
66
|
+
```
|
67
|
+
As long as the method ends in the word "_style" and returns a Hash, you magically get to do this:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
magic "My magic text is bold and size 15!!"
|
71
|
+
```
|
72
|
+
|
73
|
+
The method may return a standard Hash, but it is safer to return a ThousandIsland::StyleHash, as this dynamically duplicates a few keys to accommodate using the style in normal Prawn text methods as well as formatted text boxes, which use a slightly different convention. You don't have to worry about that if you use the ThousandIsland::StyleHash.
|
74
|
+
|
75
|
+
Alternatively, your method could do this:
|
76
|
+
```ruby
|
77
|
+
def magic_style
|
78
|
+
h1_style.merge({
|
79
|
+
size: 15
|
80
|
+
style: bold
|
81
|
+
})
|
82
|
+
end
|
83
|
+
```
|
84
|
+
The following is an example of a custom template that subclasses
|
85
|
+
ThousandIsland::Template -
|
86
|
+
```ruby
|
87
|
+
class MyTemplate < ThousandIsland::Template
|
88
|
+
include MyCustomStyleSheet # optional
|
89
|
+
|
90
|
+
# settings here are merged with and override the defaults
|
91
|
+
def settings
|
92
|
+
{
|
93
|
+
header: {
|
94
|
+
height: 55,
|
95
|
+
render:true,
|
96
|
+
repeated: true
|
97
|
+
},
|
98
|
+
footer: {
|
99
|
+
render:true,
|
100
|
+
height: 9,
|
101
|
+
numbering_string: 'Page <page> of <total>',
|
102
|
+
repeated: true
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def header_content
|
108
|
+
# Standard Prawn syntax
|
109
|
+
pdf.image "#{pdf_images_path}/company_logo.png", height: 30
|
110
|
+
end
|
111
|
+
|
112
|
+
def footer_content
|
113
|
+
# Using the magic method we get from the footer_style
|
114
|
+
footer "www.mycompanyurl.com"
|
115
|
+
end
|
116
|
+
|
117
|
+
def pdf_images_path
|
118
|
+
# How you go about this sort of thing is entirely up to you
|
119
|
+
"#{Rails.root}/app/assets/pdf_images"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
```
|
123
|
+
>Note: The Footer is a three column layout, with the numbering on the right column and the content defined here in the middle. More flexibility will be added in a later version.
|
124
|
+
|
125
|
+
Optional:
|
126
|
+
Add a `body_content` method to add content before whatever the Builder defines in it's method of the same name.
|
127
|
+
|
128
|
+
### StyleSheets
|
129
|
+
|
130
|
+
The StyleSheet is designed to be a mixin to the Template class. It may also be included into other modules to define custom StyleSheets.
|
131
|
+
|
132
|
+
Methods should return a StyleHash object rather than a vanilla Hash, as it has some customisation to help it work with Prawn. The default_style is used as the starting point for all other styles. For instance, the `default_style[:size]` value is multiplied in the heading styles, so changing the default style size value will have a cascading effect. Check the source for the default values and override as preferred.
|
133
|
+
|
134
|
+
An example of a custom StyleSheet:
|
135
|
+
```ruby
|
136
|
+
module MyStyleSheet
|
137
|
+
include ThousandIsland::StyleSheet
|
138
|
+
|
139
|
+
def default_style
|
140
|
+
super.merge({
|
141
|
+
size: 12,
|
142
|
+
color: '222222'
|
143
|
+
})
|
144
|
+
end
|
145
|
+
|
146
|
+
def h1_style
|
147
|
+
super.merge({ align: :center })
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
### Creating a Builder
|
153
|
+
|
154
|
+
Your Builder class is where you will put the necessary logic for rendering the final pdf. It's up to you how you get the data into the Builder. It will depend on the complexity. You might just pass an Invoice object (`MyBuilder.new(invoice))`) or you may have a bunch of methods that are called by an external object to get the data where it needs to be.
|
155
|
+
|
156
|
+
You must declare which Template class you will be using. Failing to do so will raise a `TemplateRequiredError` when you call the build method. Declare the template with the following in the main class body:
|
157
|
+
```ruby
|
158
|
+
uses_template MyTemplate
|
159
|
+
```
|
160
|
+
|
161
|
+
Your Builder can have a `filename` method, which will help a Rails Controller or other class determine the name to use to send the file to the browser or save to the filesystem (or both). Without this method it will have a default name, so you may choose to put the naming logic for your file elsewhere, it's up to you.
|
162
|
+
|
163
|
+
You must have a `body_content` method that takes no arguments (or the pdf will be empty!). This is the method that is passed around internally in order for Prawn to render what is in the method. You can use raw Prawn syntax, or any of the style magic methods to render to the pdf. You may also call other methods from your `body_content` method, and use Prawn syntax and magic methods in those too.
|
164
|
+
|
165
|
+
A Builder example might be:
|
166
|
+
```ruby
|
167
|
+
class MyBuilder < ThousandIsland::Builder
|
168
|
+
uses_template MyTemplate
|
169
|
+
|
170
|
+
attr_reader :data
|
171
|
+
|
172
|
+
def initialize(data={})
|
173
|
+
@data = data
|
174
|
+
# do something with the data...
|
175
|
+
end
|
176
|
+
|
177
|
+
def filename
|
178
|
+
"Document#{data.id_number}"
|
179
|
+
end
|
180
|
+
|
181
|
+
def body_content
|
182
|
+
# call custom methods, magic methods or call Prawn methods directly:
|
183
|
+
h1 'Main Heading'
|
184
|
+
display_info
|
185
|
+
body 'Main text in here...'
|
186
|
+
end
|
187
|
+
|
188
|
+
# Custom method called by body_content
|
189
|
+
def display_info
|
190
|
+
body "Written by: #{data.author}"
|
191
|
+
pdf.image data.avatar, height: 20
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
Finally, to get the finished pdf from your Builder, call the `build` method like so:
|
197
|
+
```ruby
|
198
|
+
pdf = my_builder.build
|
199
|
+
```
|
200
|
+
|
201
|
+
Optional:
|
202
|
+
|
203
|
+
Define a `header_content` method to add content below whatever is defined in the Template. This will be repeated according to the header settings in the Template.
|
204
|
+
|
205
|
+
Define a `footer_content` method to add content above whatever is defined in the Template. This will be repeated according to the footer settings in the Template.
|
206
|
+
|
207
|
+
Define a `settings` method that returns a Hash. This will be passed to the Template class and will override any of the Template default settings.
|
208
|
+
|
209
|
+
#### Using the Builder in a Rails Application
|
210
|
+
|
211
|
+
Your Controller can look something like this:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
def show
|
215
|
+
@thing = Thing.find(params[:id])
|
216
|
+
respond_to do |format|
|
217
|
+
format.html
|
218
|
+
format.pdf do
|
219
|
+
data = { thing: @thing } # How you structure this is up to you, it's your Class!!
|
220
|
+
builder = MyBuilder.new(data)
|
221
|
+
send_data builder.build, filename: builder.filename,
|
222
|
+
type: "application/pdf",
|
223
|
+
disposition: "inline" # Leave blank to render as a download
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
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
|
+
|
231
|
+
```ruby
|
232
|
+
def show
|
233
|
+
respond_to do |format|
|
234
|
+
format.html do
|
235
|
+
@thing_for_html_view = Thing.find(params[:id])
|
236
|
+
end
|
237
|
+
format.pdf do
|
238
|
+
# Tell the service object to do it's thing and return the Builder
|
239
|
+
builder = ThingPdfServiceObject.new(params)
|
240
|
+
send_data builder.build,
|
241
|
+
filename: builder.filename,
|
242
|
+
type: "application/pdf",
|
243
|
+
disposition: "inline" # Leave blank to render as a download
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
These are only suggestions, as you can probably tell there is nothing tying you down to a particular way of building and delivering the document. You might even want to save it to the file system, or upload to S3. You could override the build method if you wanted to:
|
250
|
+
```ruby
|
251
|
+
def build
|
252
|
+
pdf = super
|
253
|
+
# Save, upload, send or do whatever....
|
254
|
+
end
|
255
|
+
```
|
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
|
+
|
258
|
+
## Default Styles
|
259
|
+
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
|
+
|
261
|
+
The default styles are:
|
262
|
+
##### body
|
263
|
+
```ruby
|
264
|
+
{
|
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
|
271
|
+
}
|
272
|
+
```
|
273
|
+
##### h1
|
274
|
+
```ruby
|
275
|
+
{
|
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
|
282
|
+
}
|
283
|
+
```
|
284
|
+
##### h2
|
285
|
+
```ruby
|
286
|
+
{
|
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
|
293
|
+
}
|
294
|
+
```
|
295
|
+
##### h3
|
296
|
+
```ruby
|
297
|
+
{
|
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
|
304
|
+
}
|
305
|
+
```
|
306
|
+
##### h4
|
307
|
+
```ruby
|
308
|
+
{
|
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
|
315
|
+
}
|
316
|
+
```
|
317
|
+
##### h5
|
318
|
+
```ruby
|
319
|
+
{
|
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
|
326
|
+
}
|
327
|
+
```
|
328
|
+
##### h6
|
329
|
+
```ruby
|
330
|
+
{
|
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
|
337
|
+
}
|
338
|
+
```
|
339
|
+
##### footer
|
340
|
+
```ruby
|
341
|
+
{
|
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"
|
348
|
+
}
|
349
|
+
```
|
350
|
+
|
351
|
+
## To come...
|
352
|
+
- Easy (and repeatable) Table formatting
|
353
|
+
- Easy list rendering and styling (including nested lists)
|
354
|
+
- More flexibility in the Footer layout
|
355
|
+
- (Possibly) Command line functions to create common subclass files
|
356
|
+
|
357
|
+
## Contributing
|
358
|
+
|
359
|
+
1. Fork it
|
360
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
361
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
362
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
363
|
+
5. Create new Pull Request
|