juno-report 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.
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +197 -0
- data/Rakefile +1 -0
- data/juno-report.gemspec +21 -0
- data/lib/juno-report.rb +32 -0
- data/lib/juno-report/pdf.rb +234 -0
- data/lib/juno-report/pdf/behaviors.rb +18 -0
- data/lib/juno-report/report_object.rb +9 -0
- data/lib/juno-report/version.rb +3 -0
- metadata +73 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
tmp
|
|
2
|
+
spec/reports
|
|
3
|
+
|
|
4
|
+
*.gem
|
|
5
|
+
InstalledFiles
|
|
6
|
+
*.rbc
|
|
7
|
+
.config
|
|
8
|
+
rdoc
|
|
9
|
+
test/version_tmp
|
|
10
|
+
lib/bundler/man
|
|
11
|
+
.bundle
|
|
12
|
+
pkg
|
|
13
|
+
coverage
|
|
14
|
+
.yardoc
|
|
15
|
+
junoreport.sublime-project
|
|
16
|
+
junoreport.sublime-workspace
|
|
17
|
+
# YARD artifacts
|
|
18
|
+
test/tmp
|
|
19
|
+
_yardoc
|
|
20
|
+
doc/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2012 Edson Júnior
|
|
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,197 @@
|
|
|
1
|
+
# Juno Report
|
|
2
|
+
|
|
3
|
+
Cry with Report Generation? Nevermore!
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'juno-report'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install juno-report
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
The generating reports is based on a YAML file with all rules, with the fields and their settings, divided by sections.
|
|
22
|
+
|
|
23
|
+
### Body section
|
|
24
|
+
|
|
25
|
+
Represents the records which will be iterated to report generating
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
# example.yml
|
|
29
|
+
body:
|
|
30
|
+
settings: {posY: 40, height: 25}
|
|
31
|
+
fields:
|
|
32
|
+
test_field1: [10]
|
|
33
|
+
test_field2: [220]
|
|
34
|
+
test_field3: [430, {column: "Named Test Field 3"}]
|
|
35
|
+
footer:
|
|
36
|
+
label_total: [10, {value: "Total"}]
|
|
37
|
+
totalizer: [220, {behavior: count}]
|
|
38
|
+
test_field3: [430, {behavior: sum}]
|
|
39
|
+
```
|
|
40
|
+
The [body] section ***must*** have three rules:
|
|
41
|
+
|
|
42
|
+
* `settings`: Defines some configurations for body, like their height and ypos.
|
|
43
|
+
* `fields`: Describes each field of record to be iterated.
|
|
44
|
+
* `footer`: Drawn at the end of all printed records and calculates fields according behavior parameter.
|
|
45
|
+
|
|
46
|
+
Each of these rules receives a array, where the first position is an integer representing the field horizontal position and
|
|
47
|
+
the second position is a hash with some configurations.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
##### Settings
|
|
51
|
+
|
|
52
|
+
* `height`: Set the of each row at report [Integer].
|
|
53
|
+
* `posY`: Relative vertical position of last row at report [Integer].
|
|
54
|
+
* `groups`: Describes which groups will be printed (More bellow) [Array].
|
|
55
|
+
|
|
56
|
+
##### Fields
|
|
57
|
+
|
|
58
|
+
* `size`: Font size of the field [Integer].
|
|
59
|
+
* `align`: Defines the text alignment for each value [left|center|right].
|
|
60
|
+
* `font`: Supports all font type provided by Prawn gem (See more in http://rubydoc.info/gems/prawn/Prawn/Font/AFM).
|
|
61
|
+
* `style`: Stylistic variations of a font [bold|italic].
|
|
62
|
+
* `value`: Fixed text to be printed [string].
|
|
63
|
+
* `column`: The header are "humanized" automatically, but you can set other value manually [string].
|
|
64
|
+
|
|
65
|
+
##### Footer
|
|
66
|
+
|
|
67
|
+
* `behavior`: Specify a function to be performed, sending as parameter the fieldname value [string].
|
|
68
|
+
* `label`: Preppends a text to fieldname value specified [string].
|
|
69
|
+
* `value`: Fixed text to be printed (fieldname value will be ignored) [string].
|
|
70
|
+
|
|
71
|
+
With theses configurations already is possible generate a basic report, without groups feature.
|
|
72
|
+
For this we need call the generate method on JunoReport module:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# test.rb
|
|
76
|
+
require 'juno-report'
|
|
77
|
+
|
|
78
|
+
data = [
|
|
79
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 50},
|
|
80
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 7},
|
|
81
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 10},
|
|
82
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 5},
|
|
83
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 2},
|
|
84
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 4},
|
|
85
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 2", :test_field3 => 24}
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
JunoReport::generate(data, :report => 'example')
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The first parameter must be a set of hash or objects which represent the report data. And the second parameter is a hash
|
|
92
|
+
with the report settings, that can be:
|
|
93
|
+
|
|
94
|
+
* `report`: The source of all rules. Must be a YAML file [string].
|
|
95
|
+
* `type`: Specify if the report will be writed on the disc or returned to the caller such a stream [:file|:stream]
|
|
96
|
+
* `filename`: Defines the report name which will be writed on disc. If not specified, the default name is "report.pdf" [string].
|
|
97
|
+
|
|
98
|
+
### Page section
|
|
99
|
+
|
|
100
|
+
You may want to print a title every time that a page is created. You simply insert a [page] section.
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# example.yml
|
|
104
|
+
page:
|
|
105
|
+
fields:
|
|
106
|
+
title1: [260, {size: 24, align: center, value: "Test Report"}]
|
|
107
|
+
subtitle1: [260, {size: 20, posY: 20, align: center, value: "Generated by Juno Report"}]
|
|
108
|
+
body:
|
|
109
|
+
settings: {posY: 40, height: 25}
|
|
110
|
+
fields:
|
|
111
|
+
test_field1: [10]
|
|
112
|
+
test_field2: [220]
|
|
113
|
+
test_field3: [430, {column: "Named Test Field 3"}]
|
|
114
|
+
footer:
|
|
115
|
+
label_total: [10, {value: "Total"}]
|
|
116
|
+
totalizer: [220, {behavior: count}]
|
|
117
|
+
test_field3: [430, {behavior: sum}]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Groups section
|
|
121
|
+
|
|
122
|
+
For each item in groups parameter at body section you should create a section with same name.
|
|
123
|
+
This section represents the header configurations to every time that the group is printed and behaves like [body] section.
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
# example.yml
|
|
127
|
+
page:
|
|
128
|
+
fields:
|
|
129
|
+
title1: [260, {size: 24, align: center, value: "Test Report"}]
|
|
130
|
+
subtitle1: [260, {size: 20, posY: 20, align: center, value: "Generated by Juno Report"}]
|
|
131
|
+
body:
|
|
132
|
+
settings: {posY: 40, height: 30, groups: [group_field1, group_field2]}
|
|
133
|
+
fields:
|
|
134
|
+
test_field1: [10]
|
|
135
|
+
test_field2: [220]
|
|
136
|
+
test_field3: [420, {column: "Named Test Field 3"}]
|
|
137
|
+
footer:
|
|
138
|
+
label_total: [10, {value: "Total"}]
|
|
139
|
+
totalizer1: [220, {behavior: count}]
|
|
140
|
+
test_field3: [420, {behavior: sum}]
|
|
141
|
+
group_field1:
|
|
142
|
+
settings: {posY: 30, height: 10}
|
|
143
|
+
fields:
|
|
144
|
+
group_field1: [10, size: 25]
|
|
145
|
+
footer:
|
|
146
|
+
group_field1: [10, {label: "Total "}]
|
|
147
|
+
totalizer1: [220, {behavior: count}]
|
|
148
|
+
test_field3: [420, {behavior: sum}]
|
|
149
|
+
group_field2:
|
|
150
|
+
settings: {posY: 30, height: 25}
|
|
151
|
+
fields:
|
|
152
|
+
group_field2: [10, size: 17]
|
|
153
|
+
footer:
|
|
154
|
+
group_field2: [10, {label: "Total "}]
|
|
155
|
+
totalizer1: [220, {behavior: count}]
|
|
156
|
+
test_field3: [420, {behavior: sum}]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Every time that a "group field" value changes, the group will be printed.
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# test.rb
|
|
163
|
+
require 'juno-report'
|
|
164
|
+
|
|
165
|
+
data = [
|
|
166
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 1", :test_field3 => 50, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 1'},
|
|
167
|
+
{:test_field1 => 'Test Value 2', :test_field2 => "Test Value 2", :test_field3 => 16, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 1'},
|
|
168
|
+
{:test_field1 => 'Test Value 5', :test_field2 => "Test Value 3", :test_field3 => 7, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 1'},
|
|
169
|
+
{:test_field1 => 'Test Value 3', :test_field2 => "Test Value 9", :test_field3 => 10, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 2'},
|
|
170
|
+
{:test_field1 => 'Test Value 3', :test_field2 => "Test Value 2", :test_field3 => 4, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 2'},
|
|
171
|
+
{:test_field1 => 'Test Value 9', :test_field2 => "Test Value 4", :test_field3 => 10, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 2'},
|
|
172
|
+
{:test_field1 => 'Test Value 7', :test_field2 => "Test Value 5", :test_field3 => 5, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 3'},
|
|
173
|
+
{:test_field1 => 'Test Value 3', :test_field2 => "Test Value 5", :test_field3 => 2, :group_field1 => 'Group 1', :group_field2 => 'Subgroup 3'},
|
|
174
|
+
{:test_field1 => 'Test Value 3', :test_field2 => "Test Value 2", :test_field3 => 27, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 1'},
|
|
175
|
+
{:test_field1 => 'Test Value 3', :test_field2 => "Test Value 5", :test_field3 => 2, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 1'},
|
|
176
|
+
{:test_field1 => 'Test Value 0', :test_field2 => "Test Value 4", :test_field3 => 13, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 1'},
|
|
177
|
+
{:test_field1 => 'Test Value 4', :test_field2 => "Test Value 7", :test_field3 => 7, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 1'},
|
|
178
|
+
{:test_field1 => 'Test Value 1', :test_field2 => "Test Value 3", :test_field3 => 28, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 1'},
|
|
179
|
+
{:test_field1 => 'Test Value 4', :test_field2 => "Test Value 5", :test_field3 => 4, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 2'},
|
|
180
|
+
{:test_field1 => 'Test Value 5', :test_field2 => "Test Value 6", :test_field3 => 24, :group_field1 => 'Group 2', :group_field2 => 'Subgroup 2'}
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
JunoReport::generate(data, :report => 'example', :filename => 'juno-report.pdf')
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Contributors
|
|
187
|
+
|
|
188
|
+
2. Edson Júnior (http://github.com/ebfjunior)
|
|
189
|
+
|
|
190
|
+
## Contributing
|
|
191
|
+
|
|
192
|
+
1. Fork it
|
|
193
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
194
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
|
195
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
196
|
+
5. Create new Pull Request
|
|
197
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
data/juno-report.gemspec
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require 'juno-report/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |gem|
|
|
6
|
+
gem.name = "juno-report"
|
|
7
|
+
gem.version = JunoReport::VERSION
|
|
8
|
+
gem.platform = Gem::Platform::RUBY
|
|
9
|
+
gem.authors = ["Edson Júnior"]
|
|
10
|
+
gem.email = ["ejunior.batista@gmail.com"]
|
|
11
|
+
gem.description = "A simple, but efficient, report genarator yaml based"
|
|
12
|
+
gem.summary = "Juno Reports generates reports with the minimum configuration and effort"
|
|
13
|
+
gem.homepage = "http://github.com/ebfjunior/juno-report"
|
|
14
|
+
|
|
15
|
+
gem.files = `git ls-files`.split("\n")
|
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
18
|
+
gem.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
gem.add_dependency "prawml"
|
|
21
|
+
end
|
data/lib/juno-report.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "juno-report/version"
|
|
2
|
+
require "juno-report/pdf"
|
|
3
|
+
require "juno-report/pdf/behaviors"
|
|
4
|
+
require "prawml"
|
|
5
|
+
|
|
6
|
+
module JunoReport
|
|
7
|
+
autoload :ReportObject, 'juno-report/report_object'
|
|
8
|
+
|
|
9
|
+
def self.generate(collection, options)
|
|
10
|
+
rules = "#{options[:report]}.yml"
|
|
11
|
+
|
|
12
|
+
defaults = {
|
|
13
|
+
:page_layout => :portrait
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pdf = Prawml::PDF.new rules, defaults.merge(options)
|
|
17
|
+
|
|
18
|
+
pdf.extend JunoReport::Pdf
|
|
19
|
+
report = pdf.generate(collection)
|
|
20
|
+
|
|
21
|
+
options[:type] ||= :file
|
|
22
|
+
|
|
23
|
+
if options[:type].eql? :file
|
|
24
|
+
report.render_file (options[:filename] || "report.pdf")
|
|
25
|
+
elsif options[:type].eql? :stream
|
|
26
|
+
return report
|
|
27
|
+
else
|
|
28
|
+
raise "Type options must be :file or :stream."
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
module JunoReport
|
|
2
|
+
module Pdf
|
|
3
|
+
|
|
4
|
+
#Responsible for generate a report, based on rules passed as parameter in Juno::Report::generate.
|
|
5
|
+
#Juno Reports has support groups, just by especifying them at the rules file.
|
|
6
|
+
#Receives a collection as parameter, which should be a Array of records of the report.
|
|
7
|
+
def generate(collection)
|
|
8
|
+
@defaults = {
|
|
9
|
+
:style => :normal,
|
|
10
|
+
:size => 12,
|
|
11
|
+
:align => :left,
|
|
12
|
+
:format => false,
|
|
13
|
+
:font => 'Times-Roman',
|
|
14
|
+
:type => :text,
|
|
15
|
+
:color => '000000',
|
|
16
|
+
:fixed => false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get_sections
|
|
20
|
+
set_pos_y
|
|
21
|
+
|
|
22
|
+
collection = [collection] unless collection.is_a?(Array)
|
|
23
|
+
print_section :page unless @sections[:page].nil?
|
|
24
|
+
set_pos_y (@sections[:body][:settings][:posY] || 0)
|
|
25
|
+
@current_groups = {}
|
|
26
|
+
@footers = {}
|
|
27
|
+
|
|
28
|
+
unless @sections[:groups].empty?
|
|
29
|
+
reset_groups_values
|
|
30
|
+
else
|
|
31
|
+
draw_columns
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
initialize_footer_values
|
|
35
|
+
can_print_footer = false
|
|
36
|
+
|
|
37
|
+
collection.each do |record|
|
|
38
|
+
@record = record.is_a?(Hash) ? ReportObject.new(record) : record #Convert the hash on a Object to futurely extend a module
|
|
39
|
+
|
|
40
|
+
headers_to_print, headers_height = calculate_header
|
|
41
|
+
|
|
42
|
+
unless headers_to_print.empty?
|
|
43
|
+
draw_footer headers_to_print, @sections[:groups] if can_print_footer
|
|
44
|
+
if @posY - headers_height < 2*@sections[:body][:settings][:height]
|
|
45
|
+
new_page
|
|
46
|
+
else
|
|
47
|
+
headers_to_print.each { |group| print_section group, @record, true }
|
|
48
|
+
draw_columns
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
can_print_footer = true
|
|
52
|
+
|
|
53
|
+
update_footer_values
|
|
54
|
+
print_section :body, @record
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
draw_footer(@sections[:body][:settings][:groups].collect {|group| group.to_sym}, @sections[:groups]) if has_groups?
|
|
58
|
+
draw_footer [:body], @sections
|
|
59
|
+
|
|
60
|
+
@pdf
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
protected
|
|
64
|
+
|
|
65
|
+
#Creates a new page, restarting the vertical position of the pointer.
|
|
66
|
+
#Print the whole header for the current groups and the columns of the report.
|
|
67
|
+
def new_page
|
|
68
|
+
@pdf.start_new_page
|
|
69
|
+
set_pos_y
|
|
70
|
+
print_section :page unless @sections[:page].nil?
|
|
71
|
+
set_pos_y (@sections[:body][:settings][:posY] || 0)
|
|
72
|
+
@current_groups.each do |field, value|
|
|
73
|
+
print_section field.to_sym, @record, true
|
|
74
|
+
end
|
|
75
|
+
draw_columns
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
#Generic function to print a section like :body, :page or the group sections.
|
|
79
|
+
def print_section(section_name, values = nil, group = false)
|
|
80
|
+
section = !group ? @sections[section_name] : @sections[:groups][section_name]
|
|
81
|
+
set_pos_y(section[:settings][:posY] || 0) unless section_name.eql?(:body) || section[:settings].nil?
|
|
82
|
+
new_page if @posY < 30
|
|
83
|
+
|
|
84
|
+
section[:fields].each do |field, settings|
|
|
85
|
+
symbolize! settings[1] unless settings[1].nil?
|
|
86
|
+
set_pos_y settings[1][:posY] unless settings[1].nil? || settings[1][:posY].nil?
|
|
87
|
+
settings = [settings[0], @posY, (@defaults.merge (settings[1] || { }))]
|
|
88
|
+
settings[2][:style] = settings[2][:style].to_sym
|
|
89
|
+
set_options settings[2]
|
|
90
|
+
|
|
91
|
+
value = settings[2][:value].nil? ? (values.respond_to?(field) ? values.send(field) : "") : settings[2][:value]
|
|
92
|
+
draw_text value, settings
|
|
93
|
+
end
|
|
94
|
+
set_pos_y (section[:settings][:height]) unless section[:settings].nil? || section[:settings][:height].nil?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
#Print a horizontal line with the whole width of the page.
|
|
98
|
+
def draw_line(y)
|
|
99
|
+
width = @options[:page_layout] == :portrait ? 530 : 770
|
|
100
|
+
@pdf.stroke { @pdf.horizontal_line 0, width, :at => y }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#Update the pointer vertical position to the specified value or 'zero' if the parameter is nil.
|
|
104
|
+
#Obs: Prawn pointer is decrescent, in other words, the left-top corner position is (0, 750). For
|
|
105
|
+
#semantic purposes, we set the same corner as (0, 0).
|
|
106
|
+
def set_pos_y(posY = nil)
|
|
107
|
+
height = @options[:page_layout] == :portrait ? 750 : 520
|
|
108
|
+
@posY = height if @posY.nil?
|
|
109
|
+
@posY = posY.nil? ? height : @posY - posY
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
#Convert to symbol all hash keys, recursively.
|
|
113
|
+
def symbolize! hash
|
|
114
|
+
hash.symbolize_keys!
|
|
115
|
+
hash.values.select{|v| v.is_a? Hash}.each{|h| symbolize!(h)}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#Convert the structure of the rules to facilitate the generating proccess.
|
|
119
|
+
def get_sections
|
|
120
|
+
symbolize! @rules
|
|
121
|
+
raise "[body] section on YAML file is needed to generate the report." if @rules[:body].nil?
|
|
122
|
+
@sections = {:page => @rules[:page], :body => @rules[:body], :groups => {}}
|
|
123
|
+
@sections[:body][:settings][:groups].each { |group| @sections[:groups][group.to_sym] = @rules[group.to_sym] } if has_groups?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
#@current_groups storages the value for all groups. When a value is changed, the header is printed.
|
|
127
|
+
#This function set nil value for every item in @current_groups if the parameter is not passed. Otherwise,
|
|
128
|
+
#only the forward groups will be cleaned to avoid conflict problems with others groups.
|
|
129
|
+
def reset_groups_values current_group = nil
|
|
130
|
+
groups = @sections[:body][:settings][:groups]
|
|
131
|
+
groups.each_with_index do |group, idx|
|
|
132
|
+
@current_groups[group] = nil if current_group.nil? || groups.index(current_group.to_s) <= idx
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
#Calculates the headers which must be printed before print the current record.
|
|
138
|
+
#The function also returns the current header height to create a new page if the
|
|
139
|
+
#page remaining space is smaller than (header + a record height)
|
|
140
|
+
def calculate_header
|
|
141
|
+
headers = []
|
|
142
|
+
height = 0
|
|
143
|
+
@current_groups.each do |field, value|
|
|
144
|
+
if @record.send(field) != value
|
|
145
|
+
reset_groups_values field
|
|
146
|
+
|
|
147
|
+
headers << field.to_sym
|
|
148
|
+
height += @sections[:groups][field.to_sym][:settings][:height] + @sections[:groups][field.to_sym][:settings][:posY]
|
|
149
|
+
|
|
150
|
+
@current_groups[field] = @record.send(field)
|
|
151
|
+
end
|
|
152
|
+
end unless @current_groups.empty?
|
|
153
|
+
[headers, height]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
#Create a structure to calculate the footer values for all groups. Appends the footer body to total values too.
|
|
157
|
+
def initialize_footer_values
|
|
158
|
+
@sections[:body][:settings][:groups].each do |group|
|
|
159
|
+
current_footer = {}
|
|
160
|
+
@sections[:groups][group.to_sym][:footer].each { |field, settings| current_footer[field] = nil } unless @sections[:groups][group.to_sym][:footer].nil?
|
|
161
|
+
@footers[group.to_sym] = current_footer unless current_footer.empty?
|
|
162
|
+
end if has_groups?
|
|
163
|
+
raise "The report must have at least a footer on body section" if @sections[:body][:footer].nil?
|
|
164
|
+
current_footer = {}
|
|
165
|
+
@sections[:body][:footer].each { |field, settings| current_footer[field] = nil }
|
|
166
|
+
@footers[:body] = current_footer unless current_footer.empty?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
#Call the function that calculates the footer values for all groups and the total body footer, with
|
|
170
|
+
#different source for each
|
|
171
|
+
def update_footer_values
|
|
172
|
+
@sections[:body][:settings][:groups].reverse_each do |group|
|
|
173
|
+
calculate_footer_values group, @sections[:groups][group.to_sym][:footer]
|
|
174
|
+
end if has_groups?
|
|
175
|
+
calculate_footer_values :body, @sections[:body][:footer]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
#Returns the values to the group passed as parameter. If :behavior setting is used, so a
|
|
179
|
+
#function in [lib/pdf/behaviors.rb] calculates the value of current field, else the report
|
|
180
|
+
#method is called
|
|
181
|
+
def calculate_footer_values group, source
|
|
182
|
+
@footers[group.to_sym].each do |field, value|
|
|
183
|
+
footer_rule = source[field]
|
|
184
|
+
symbolize! footer_rule[1]
|
|
185
|
+
unless footer_rule[1][:behavior].nil?
|
|
186
|
+
@footers[group.to_sym][field] = JunoReport::Pdf::Behaviors.send footer_rule[1][:behavior].to_sym, value, (@record.respond_to?(field) ? @record.send(field) : nil)
|
|
187
|
+
else
|
|
188
|
+
@footers[group.to_sym][field] = footer_rule[1][:value] || (footer_rule[1][:label].to_s + @record.send(field))
|
|
189
|
+
end unless @footers[group.to_sym].nil? || footer_rule[1].nil?
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
#Print the footers according to the groups and source specified
|
|
194
|
+
def draw_footer footers_to_print, source
|
|
195
|
+
footers_to_print.reverse_each do |group|
|
|
196
|
+
draw_line(@posY + @sections[:body][:settings][:height]/2)
|
|
197
|
+
source[group][:footer].each do |field, settings|
|
|
198
|
+
settings = [settings[0], @posY, (@defaults.merge (settings[1] || { }).symbolize_keys!)]
|
|
199
|
+
settings[2][:style] = settings[2][:style].to_sym
|
|
200
|
+
set_options settings[2]
|
|
201
|
+
draw_text @footers[group][field], settings
|
|
202
|
+
end
|
|
203
|
+
draw_line(@posY - @sections[:body][:settings][:height]/4)
|
|
204
|
+
set_pos_y @sections[:body][:settings][:height]
|
|
205
|
+
|
|
206
|
+
reset_footer group
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
#Resets the footer to next groups
|
|
211
|
+
def reset_footer(group); @footers[group].each { |field, value| @footers[group][field] = nil }; end
|
|
212
|
+
|
|
213
|
+
#Based on the Key names of the :body section at the rules, the function draw columns with
|
|
214
|
+
#baselines on the top and bottom of the header.
|
|
215
|
+
def draw_columns
|
|
216
|
+
@sections[:body][:fields].each do |field, settings|
|
|
217
|
+
settings = [settings[0], @posY, (@defaults.merge (settings[1] || { }).symbolize_keys!)]
|
|
218
|
+
settings[2][:style] = settings[2][:style].to_sym
|
|
219
|
+
set_options settings[2]
|
|
220
|
+
draw_line(@posY + @sections[:body][:settings][:height]/2)
|
|
221
|
+
field = settings[2][:column] || field.to_s.split('_').inject('') do |str, part|
|
|
222
|
+
str << part.camelize << " "
|
|
223
|
+
end
|
|
224
|
+
draw_text field, settings
|
|
225
|
+
end
|
|
226
|
+
draw_line(@posY - @sections[:body][:settings][:height]/2)
|
|
227
|
+
set_pos_y @sections[:body][:settings][:height]
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def has_groups?
|
|
231
|
+
!@sections[:body][:settings][:groups].nil?
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module JunoReport
|
|
2
|
+
module Pdf
|
|
3
|
+
module Behaviors
|
|
4
|
+
def self.sum old_value, new_value
|
|
5
|
+
value = (old_value.to_f + new_value.to_f).to_s
|
|
6
|
+
(/^[0-9]+(?=\.)/.match value).nil? ? value : value[/^[0-9]+(?=\.)/]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.substract old_value, new_value
|
|
10
|
+
old_value.to_f - new_value.to_f
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.count old_value, new_value = nil
|
|
14
|
+
old_value.to_i + 1
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class ReportObject
|
|
2
|
+
def initialize(hash)
|
|
3
|
+
hash.each do |k,v|
|
|
4
|
+
self.instance_variable_set("@#{k}", v)
|
|
5
|
+
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
|
|
6
|
+
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: juno-report
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.5
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Edson Júnior
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-02-22 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: prawml
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
30
|
+
description: A simple, but efficient, report genarator yaml based
|
|
31
|
+
email:
|
|
32
|
+
- ejunior.batista@gmail.com
|
|
33
|
+
executables: []
|
|
34
|
+
extensions: []
|
|
35
|
+
extra_rdoc_files: []
|
|
36
|
+
files:
|
|
37
|
+
- .gitignore
|
|
38
|
+
- Gemfile
|
|
39
|
+
- Gemfile.lock
|
|
40
|
+
- LICENSE.txt
|
|
41
|
+
- README.md
|
|
42
|
+
- Rakefile
|
|
43
|
+
- juno-report.gemspec
|
|
44
|
+
- lib/juno-report.rb
|
|
45
|
+
- lib/juno-report/pdf.rb
|
|
46
|
+
- lib/juno-report/pdf/behaviors.rb
|
|
47
|
+
- lib/juno-report/report_object.rb
|
|
48
|
+
- lib/juno-report/version.rb
|
|
49
|
+
homepage: http://github.com/ebfjunior/juno-report
|
|
50
|
+
licenses: []
|
|
51
|
+
post_install_message:
|
|
52
|
+
rdoc_options: []
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
none: false
|
|
57
|
+
requirements:
|
|
58
|
+
- - ! '>='
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
none: false
|
|
63
|
+
requirements:
|
|
64
|
+
- - ! '>='
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0'
|
|
67
|
+
requirements: []
|
|
68
|
+
rubyforge_project:
|
|
69
|
+
rubygems_version: 1.8.23
|
|
70
|
+
signing_key:
|
|
71
|
+
specification_version: 3
|
|
72
|
+
summary: Juno Reports generates reports with the minimum configuration and effort
|
|
73
|
+
test_files: []
|