proforma 1.0.0.pre.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +8 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +105 -0
- data/Guardfile +16 -0
- data/LICENSE +7 -0
- data/README.md +328 -0
- data/bin/console +11 -0
- data/bin/render +68 -0
- data/lib/proforma.rb +38 -0
- data/lib/proforma/compiling.rb +12 -0
- data/lib/proforma/compiling/aggregation.rb +62 -0
- data/lib/proforma/compiling/compilable.rb +21 -0
- data/lib/proforma/compiling/counter.rb +35 -0
- data/lib/proforma/core_ext/hash.rb +21 -0
- data/lib/proforma/document.rb +38 -0
- data/lib/proforma/hash_evaluator.rb +40 -0
- data/lib/proforma/model_factory.rb +38 -0
- data/lib/proforma/modeling.rb +21 -0
- data/lib/proforma/modeling/banner.rb +64 -0
- data/lib/proforma/modeling/collection.rb +34 -0
- data/lib/proforma/modeling/data_table.rb +117 -0
- data/lib/proforma/modeling/data_table/aggregator.rb +43 -0
- data/lib/proforma/modeling/data_table/column.rb +94 -0
- data/lib/proforma/modeling/generic_container.rb +57 -0
- data/lib/proforma/modeling/grouping.rb +40 -0
- data/lib/proforma/modeling/header.rb +18 -0
- data/lib/proforma/modeling/pane.rb +40 -0
- data/lib/proforma/modeling/pane/column.rb +68 -0
- data/lib/proforma/modeling/pane/line.rb +42 -0
- data/lib/proforma/modeling/separator.rb +19 -0
- data/lib/proforma/modeling/spacer.rb +19 -0
- data/lib/proforma/modeling/table.rb +52 -0
- data/lib/proforma/modeling/table/cell.rb +49 -0
- data/lib/proforma/modeling/table/row.rb +24 -0
- data/lib/proforma/modeling/table/section.rb +23 -0
- data/lib/proforma/modeling/text.rb +37 -0
- data/lib/proforma/modeling/types.rb +10 -0
- data/lib/proforma/modeling/types/align.rb +22 -0
- data/lib/proforma/plain_text_renderer.rb +106 -0
- data/lib/proforma/prototype.rb +28 -0
- data/lib/proforma/template.rb +65 -0
- data/lib/proforma/type_factory.rb +44 -0
- data/lib/proforma/version.rb +12 -0
- data/proforma.gemspec +34 -0
- data/spec/fixtures/snapshots/custom_table.yml +55 -0
- data/spec/fixtures/snapshots/user_details.yml +101 -0
- data/spec/fixtures/snapshots/user_list.yml +143 -0
- data/spec/proforma/hash_evaluator_spec.rb +26 -0
- data/spec/proforma/modeling/table/cell_spec.rb +26 -0
- data/spec/proforma/modeling/table/row_spec.rb +42 -0
- data/spec/proforma_spec.rb +230 -0
- data/spec/spec_helper.rb +25 -0
- metadata +211 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Proforma
|
11
|
+
module Modeling
|
12
|
+
# A general purpose grouping of modeling objects. When compiled, they will each be
|
13
|
+
# compiled once then their output(s) are combined and flattened.
|
14
|
+
class Collection
|
15
|
+
acts_as_hashable
|
16
|
+
|
17
|
+
attr_writer :children
|
18
|
+
|
19
|
+
def initialize(children: [])
|
20
|
+
@children = ModelFactory.array(children)
|
21
|
+
end
|
22
|
+
|
23
|
+
def children
|
24
|
+
Array(@children)
|
25
|
+
end
|
26
|
+
|
27
|
+
def compile(data, evaluator)
|
28
|
+
children.map do |section|
|
29
|
+
section.respond_to?(:compile) ? section.compile(data, evaluator) : section
|
30
|
+
end.flatten
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'data_table/aggregator'
|
11
|
+
require_relative 'data_table/column'
|
12
|
+
|
13
|
+
module Proforma
|
14
|
+
module Modeling
|
15
|
+
# A table that understands how to be compiled against a data source.
|
16
|
+
class DataTable
|
17
|
+
include Compiling::Compilable
|
18
|
+
acts_as_hashable
|
19
|
+
|
20
|
+
attr_accessor :property
|
21
|
+
|
22
|
+
attr_writer :aggregators,
|
23
|
+
:columns,
|
24
|
+
:empty_message
|
25
|
+
|
26
|
+
def initialize(
|
27
|
+
aggregators: [],
|
28
|
+
columns: [],
|
29
|
+
empty_message: '',
|
30
|
+
property: nil
|
31
|
+
)
|
32
|
+
@aggregators = Aggregator.array(aggregators)
|
33
|
+
@columns = Column.array(columns)
|
34
|
+
@empty_message = empty_message
|
35
|
+
@property = property
|
36
|
+
end
|
37
|
+
|
38
|
+
def empty_message
|
39
|
+
@empty_message.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def aggregators
|
43
|
+
Array(@aggregators)
|
44
|
+
end
|
45
|
+
|
46
|
+
def columns
|
47
|
+
Array(@columns)
|
48
|
+
end
|
49
|
+
|
50
|
+
def compile(data, evaluator)
|
51
|
+
records = array(evaluator.value(data, property))
|
52
|
+
|
53
|
+
return Text.new(value: empty_message) if show_empty_message?(records)
|
54
|
+
|
55
|
+
meta_data = make_aggregator_meta_data(records, evaluator)
|
56
|
+
|
57
|
+
Table.new(
|
58
|
+
body: make_body(records, evaluator),
|
59
|
+
footer: make_footer(meta_data, evaluator),
|
60
|
+
header: make_header({}, evaluator)
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def make_aggregator_meta_data(records, evaluator)
|
67
|
+
Compiling::Aggregation.new(aggregators, evaluator).add(records).to_hash
|
68
|
+
end
|
69
|
+
|
70
|
+
def footer?
|
71
|
+
columns.select(&:footer?).any?
|
72
|
+
end
|
73
|
+
|
74
|
+
def header?
|
75
|
+
columns.select(&:header?).any?
|
76
|
+
end
|
77
|
+
|
78
|
+
def show_empty_message?(records)
|
79
|
+
records.empty? && !empty_message.empty?
|
80
|
+
end
|
81
|
+
|
82
|
+
def make_footer(data, evaluator)
|
83
|
+
make(footer?, :compile_footer_cell, data, evaluator)
|
84
|
+
end
|
85
|
+
|
86
|
+
def make_header(data, evaluator)
|
87
|
+
make(header?, :compile_header_cell, data, evaluator)
|
88
|
+
end
|
89
|
+
|
90
|
+
def make(visible, row_compile_method, data, evaluator)
|
91
|
+
return Table::Section.new unless visible
|
92
|
+
|
93
|
+
rows = [
|
94
|
+
make_row(row_compile_method, data, evaluator)
|
95
|
+
]
|
96
|
+
|
97
|
+
Table::Section.new(rows: rows)
|
98
|
+
end
|
99
|
+
|
100
|
+
def make_body(records, evaluator)
|
101
|
+
rows = records.map do |record|
|
102
|
+
make_row(:compile_body_cell, record, evaluator)
|
103
|
+
end
|
104
|
+
|
105
|
+
Table::Section.new(rows: rows)
|
106
|
+
end
|
107
|
+
|
108
|
+
def make_row(method, record, evaluator)
|
109
|
+
cells = columns.map do |column|
|
110
|
+
column.send(method, record, evaluator)
|
111
|
+
end
|
112
|
+
|
113
|
+
Table::Row.new(cells: cells)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Proforma
|
11
|
+
module Modeling
|
12
|
+
class DataTable
|
13
|
+
# An Aggregator is a table aggregator function configuration.
|
14
|
+
class Aggregator
|
15
|
+
acts_as_hashable
|
16
|
+
|
17
|
+
module Function
|
18
|
+
SUM = :sum
|
19
|
+
AVE = :ave
|
20
|
+
end
|
21
|
+
include Function
|
22
|
+
|
23
|
+
attr_accessor :property
|
24
|
+
|
25
|
+
attr_writer :function, :name
|
26
|
+
|
27
|
+
def initialize(function: SUM, name: '', property: nil)
|
28
|
+
@function = function
|
29
|
+
@name = name
|
30
|
+
@property = property
|
31
|
+
end
|
32
|
+
|
33
|
+
def function
|
34
|
+
@function || SUM
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
@name.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Proforma
|
11
|
+
module Modeling
|
12
|
+
class DataTable
|
13
|
+
# An explicit table column that understands how to compile header, body, and footer cells
|
14
|
+
# from records.
|
15
|
+
class Column
|
16
|
+
include Modeling::Types::Align
|
17
|
+
include Compiling::Compilable
|
18
|
+
acts_as_hashable
|
19
|
+
|
20
|
+
attr_writer :align,
|
21
|
+
:body,
|
22
|
+
:footer,
|
23
|
+
:header,
|
24
|
+
:width
|
25
|
+
|
26
|
+
def initialize(
|
27
|
+
align: LEFT,
|
28
|
+
body: '',
|
29
|
+
footer: '',
|
30
|
+
header: '',
|
31
|
+
width: nil
|
32
|
+
)
|
33
|
+
@align = align
|
34
|
+
@body = body
|
35
|
+
@footer = footer
|
36
|
+
@header = header
|
37
|
+
@width = width
|
38
|
+
end
|
39
|
+
|
40
|
+
def align
|
41
|
+
@align || LEFT
|
42
|
+
end
|
43
|
+
|
44
|
+
def body
|
45
|
+
@body.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def footer
|
49
|
+
@footer.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def header
|
53
|
+
@header.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def width
|
57
|
+
@width ? @width.to_f : nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def compile_header_cell(record, evaluator)
|
61
|
+
Modeling::Table::Cell.new(
|
62
|
+
align: align,
|
63
|
+
text: evaluator.text(record, header),
|
64
|
+
width: width
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def compile_body_cell(record, evaluator)
|
69
|
+
Modeling::Table::Cell.new(
|
70
|
+
align: align,
|
71
|
+
text: evaluator.text(record, body),
|
72
|
+
width: width
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def compile_footer_cell(record, evaluator)
|
77
|
+
Modeling::Table::Cell.new(
|
78
|
+
align: align,
|
79
|
+
text: evaluator.text(record, footer),
|
80
|
+
width: width
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def footer?
|
85
|
+
!footer.to_s.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
def header?
|
89
|
+
!header.to_s.empty?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Proforma
|
11
|
+
module Modeling
|
12
|
+
# This can be used as a super-class for models which are basically containers for
|
13
|
+
# simple arrays of elements
|
14
|
+
class GenericContainer
|
15
|
+
def initialize(child_key, child_class, children = [])
|
16
|
+
raise ArgumentError, 'child_key is required' unless child_key
|
17
|
+
raise ArgumentError, 'child_class is required' unless child_class
|
18
|
+
|
19
|
+
@child_class = child_class
|
20
|
+
@child_key = child_key.to_s.to_sym
|
21
|
+
@children = child_class.array(children)
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to_missing?(method_name, include_private = false)
|
25
|
+
method_name.to_s == child_key.to_s ||
|
26
|
+
method_name.to_s == child_setter_name ||
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(method_name, *args, &block)
|
31
|
+
if method_name.to_s == child_key.to_s
|
32
|
+
Array(children)
|
33
|
+
elsif method_name.to_s == child_setter_name
|
34
|
+
@children = args
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def compile(data, evaluator)
|
41
|
+
compiled_children = children.map { |child| child.compile(data, evaluator) }
|
42
|
+
|
43
|
+
args = {}.tap { |hash| hash[child_key] = compiled_children }
|
44
|
+
|
45
|
+
self.class.new(args)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :child_class, :child_key, :children
|
51
|
+
|
52
|
+
def child_setter_name
|
53
|
+
"#{child_key}="
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Proforma
|
11
|
+
module Modeling
|
12
|
+
# A grouping is an inverted collection, meaning, it iterates each child once per record
|
13
|
+
# instead of only one time. It also provides a mechanic to traverse data to tap
|
14
|
+
# nested child data (through the property attribute.)
|
15
|
+
class Grouping
|
16
|
+
include Compiling::Compilable
|
17
|
+
acts_as_hashable
|
18
|
+
|
19
|
+
attr_accessor :property
|
20
|
+
|
21
|
+
attr_writer :children
|
22
|
+
|
23
|
+
def initialize(children: [], property: nil)
|
24
|
+
@children = ModelFactory.array(children)
|
25
|
+
@property = property
|
26
|
+
end
|
27
|
+
|
28
|
+
def children
|
29
|
+
Array(@children)
|
30
|
+
end
|
31
|
+
|
32
|
+
def compile(data, evaluator)
|
33
|
+
records = array(evaluator.value(data, property))
|
34
|
+
|
35
|
+
records.map { |record| Collection.new(children: children).compile(record, evaluator) }
|
36
|
+
.flatten
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'text'
|
11
|
+
|
12
|
+
module Proforma
|
13
|
+
module Modeling
|
14
|
+
# A Header is an object representation of an important text-based title.
|
15
|
+
class Header < Text
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'pane/column'
|
11
|
+
require_relative 'pane/line'
|
12
|
+
|
13
|
+
module Proforma
|
14
|
+
module Modeling
|
15
|
+
# Think of a pane like a pivoted table. It has columns but not in the same respect as
|
16
|
+
# a table's columns. For a pane, it makes up a vertical section. Each column (section)
|
17
|
+
# then has a number of lines which holds the label:value pairs that facilitates data
|
18
|
+
# rendering.
|
19
|
+
class Pane
|
20
|
+
include Compiling::Compilable
|
21
|
+
acts_as_hashable
|
22
|
+
|
23
|
+
attr_writer :columns
|
24
|
+
|
25
|
+
def initialize(columns: [])
|
26
|
+
@columns = Column.array(columns)
|
27
|
+
end
|
28
|
+
|
29
|
+
def columns
|
30
|
+
Array(@columns)
|
31
|
+
end
|
32
|
+
|
33
|
+
def compile(data, evaluator)
|
34
|
+
compiled_columns = columns.map { |column| column.compile(data, evaluator) }
|
35
|
+
|
36
|
+
self.class.new(columns: compiled_columns)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|