proforma 1.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +8 -0
  3. data/.gitignore +4 -0
  4. data/.rubocop.yml +11 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +20 -0
  7. data/CHANGELOG.md +7 -0
  8. data/Gemfile +5 -0
  9. data/Gemfile.lock +105 -0
  10. data/Guardfile +16 -0
  11. data/LICENSE +7 -0
  12. data/README.md +328 -0
  13. data/bin/console +11 -0
  14. data/bin/render +68 -0
  15. data/lib/proforma.rb +38 -0
  16. data/lib/proforma/compiling.rb +12 -0
  17. data/lib/proforma/compiling/aggregation.rb +62 -0
  18. data/lib/proforma/compiling/compilable.rb +21 -0
  19. data/lib/proforma/compiling/counter.rb +35 -0
  20. data/lib/proforma/core_ext/hash.rb +21 -0
  21. data/lib/proforma/document.rb +38 -0
  22. data/lib/proforma/hash_evaluator.rb +40 -0
  23. data/lib/proforma/model_factory.rb +38 -0
  24. data/lib/proforma/modeling.rb +21 -0
  25. data/lib/proforma/modeling/banner.rb +64 -0
  26. data/lib/proforma/modeling/collection.rb +34 -0
  27. data/lib/proforma/modeling/data_table.rb +117 -0
  28. data/lib/proforma/modeling/data_table/aggregator.rb +43 -0
  29. data/lib/proforma/modeling/data_table/column.rb +94 -0
  30. data/lib/proforma/modeling/generic_container.rb +57 -0
  31. data/lib/proforma/modeling/grouping.rb +40 -0
  32. data/lib/proforma/modeling/header.rb +18 -0
  33. data/lib/proforma/modeling/pane.rb +40 -0
  34. data/lib/proforma/modeling/pane/column.rb +68 -0
  35. data/lib/proforma/modeling/pane/line.rb +42 -0
  36. data/lib/proforma/modeling/separator.rb +19 -0
  37. data/lib/proforma/modeling/spacer.rb +19 -0
  38. data/lib/proforma/modeling/table.rb +52 -0
  39. data/lib/proforma/modeling/table/cell.rb +49 -0
  40. data/lib/proforma/modeling/table/row.rb +24 -0
  41. data/lib/proforma/modeling/table/section.rb +23 -0
  42. data/lib/proforma/modeling/text.rb +37 -0
  43. data/lib/proforma/modeling/types.rb +10 -0
  44. data/lib/proforma/modeling/types/align.rb +22 -0
  45. data/lib/proforma/plain_text_renderer.rb +106 -0
  46. data/lib/proforma/prototype.rb +28 -0
  47. data/lib/proforma/template.rb +65 -0
  48. data/lib/proforma/type_factory.rb +44 -0
  49. data/lib/proforma/version.rb +12 -0
  50. data/proforma.gemspec +34 -0
  51. data/spec/fixtures/snapshots/custom_table.yml +55 -0
  52. data/spec/fixtures/snapshots/user_details.yml +101 -0
  53. data/spec/fixtures/snapshots/user_list.yml +143 -0
  54. data/spec/proforma/hash_evaluator_spec.rb +26 -0
  55. data/spec/proforma/modeling/table/cell_spec.rb +26 -0
  56. data/spec/proforma/modeling/table/row_spec.rb +42 -0
  57. data/spec/proforma_spec.rb +230 -0
  58. data/spec/spec_helper.rb +25 -0
  59. 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