proforma 1.0.0.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|