csv_plus_plus 0.1.2 → 0.2.0

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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -5
  3. data/{CHANGELOG.md → docs/CHANGELOG.md} +25 -0
  4. data/lib/csv_plus_plus/a1_reference.rb +202 -0
  5. data/lib/csv_plus_plus/benchmarked_compiler.rb +70 -20
  6. data/lib/csv_plus_plus/cell.rb +29 -41
  7. data/lib/csv_plus_plus/cli.rb +53 -80
  8. data/lib/csv_plus_plus/cli_flag.rb +71 -71
  9. data/lib/csv_plus_plus/color.rb +32 -7
  10. data/lib/csv_plus_plus/compiler.rb +98 -66
  11. data/lib/csv_plus_plus/entities/ast_builder.rb +30 -39
  12. data/lib/csv_plus_plus/entities/boolean.rb +26 -10
  13. data/lib/csv_plus_plus/entities/builtins.rb +66 -24
  14. data/lib/csv_plus_plus/entities/date.rb +42 -6
  15. data/lib/csv_plus_plus/entities/entity.rb +17 -69
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +44 -0
  17. data/lib/csv_plus_plus/entities/function.rb +34 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +49 -10
  19. data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
  20. data/lib/csv_plus_plus/entities/number.rb +30 -11
  21. data/lib/csv_plus_plus/entities/reference.rb +77 -0
  22. data/lib/csv_plus_plus/entities/runtime_value.rb +43 -13
  23. data/lib/csv_plus_plus/entities/string.rb +23 -7
  24. data/lib/csv_plus_plus/entities.rb +7 -16
  25. data/lib/csv_plus_plus/error/cli_error.rb +17 -0
  26. data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
  27. data/lib/csv_plus_plus/error/error.rb +25 -2
  28. data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -12
  29. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +34 -12
  30. data/lib/csv_plus_plus/error/modifier_validation_error.rb +21 -27
  31. data/lib/csv_plus_plus/error/positional_error.rb +15 -0
  32. data/lib/csv_plus_plus/error/writer_error.rb +8 -0
  33. data/lib/csv_plus_plus/error.rb +5 -1
  34. data/lib/csv_plus_plus/error_formatter.rb +111 -0
  35. data/lib/csv_plus_plus/google_api_client.rb +25 -10
  36. data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
  37. data/lib/csv_plus_plus/lexer/tokenizer.rb +58 -17
  38. data/lib/csv_plus_plus/lexer.rb +64 -1
  39. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +1 -0
  40. data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
  41. data/lib/csv_plus_plus/modifier/expand.rb +78 -0
  42. data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
  43. data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
  44. data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
  45. data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
  46. data/lib/csv_plus_plus/modifier.rb +89 -160
  47. data/lib/csv_plus_plus/options/file_options.rb +49 -0
  48. data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
  49. data/lib/csv_plus_plus/options/options.rb +97 -0
  50. data/lib/csv_plus_plus/options.rb +34 -77
  51. data/lib/csv_plus_plus/parser/cell_value.tab.rb +66 -67
  52. data/lib/csv_plus_plus/parser/code_section.tab.rb +86 -83
  53. data/lib/csv_plus_plus/parser/modifier.tab.rb +57 -53
  54. data/lib/csv_plus_plus/reader/csv.rb +50 -0
  55. data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
  56. data/lib/csv_plus_plus/reader/reader.rb +27 -0
  57. data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
  58. data/lib/csv_plus_plus/reader.rb +14 -0
  59. data/lib/csv_plus_plus/row.rb +53 -12
  60. data/lib/csv_plus_plus/runtime/graph.rb +68 -0
  61. data/lib/csv_plus_plus/runtime/position.rb +242 -0
  62. data/lib/csv_plus_plus/runtime/references.rb +115 -0
  63. data/lib/csv_plus_plus/runtime/runtime.rb +132 -0
  64. data/lib/csv_plus_plus/runtime/scope.rb +280 -0
  65. data/lib/csv_plus_plus/runtime.rb +34 -191
  66. data/lib/csv_plus_plus/source_code.rb +71 -0
  67. data/lib/csv_plus_plus/template.rb +71 -39
  68. data/lib/csv_plus_plus/version.rb +2 -1
  69. data/lib/csv_plus_plus/writer/csv.rb +37 -8
  70. data/lib/csv_plus_plus/writer/excel.rb +25 -5
  71. data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -13
  72. data/lib/csv_plus_plus/writer/google_sheets.rb +29 -85
  73. data/lib/csv_plus_plus/writer/google_sheets_builder.rb +179 -0
  74. data/lib/csv_plus_plus/writer/merger.rb +31 -0
  75. data/lib/csv_plus_plus/writer/open_document.rb +21 -2
  76. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +140 -42
  77. data/lib/csv_plus_plus/writer/writer.rb +42 -0
  78. data/lib/csv_plus_plus/writer.rb +79 -10
  79. data/lib/csv_plus_plus.rb +47 -18
  80. metadata +50 -21
  81. data/lib/csv_plus_plus/can_define_references.rb +0 -88
  82. data/lib/csv_plus_plus/can_resolve_references.rb +0 -8
  83. data/lib/csv_plus_plus/data_validation.rb +0 -138
  84. data/lib/csv_plus_plus/entities/cell_reference.rb +0 -60
  85. data/lib/csv_plus_plus/entities/variable.rb +0 -25
  86. data/lib/csv_plus_plus/error/syntax_error.rb +0 -58
  87. data/lib/csv_plus_plus/expand.rb +0 -20
  88. data/lib/csv_plus_plus/google_options.rb +0 -27
  89. data/lib/csv_plus_plus/graph.rb +0 -62
  90. data/lib/csv_plus_plus/lexer/lexer.rb +0 -85
  91. data/lib/csv_plus_plus/references.rb +0 -68
  92. data/lib/csv_plus_plus/scope.rb +0 -196
  93. data/lib/csv_plus_plus/validated_modifier.rb +0 -164
  94. data/lib/csv_plus_plus/writer/base_writer.rb +0 -20
  95. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +0 -147
  96. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -77
  97. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -1,147 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './google_sheet_modifier'
4
-
5
- module CSVPlusPlus
6
- module Writer
7
- # Given +rows+ from a +Template+, build requests compatible with Google Sheets Ruby API
8
- # rubocop:disable Metrics/ClassLength
9
- class GoogleSheetBuilder
10
- # @param current_sheet_values
11
- def initialize(current_sheet_values:, sheet_id:, rows:, column_index: 0, row_index: 0)
12
- @current_sheet_values = current_sheet_values
13
- @sheet_id = sheet_id
14
- @rows = rows
15
- @column_index = column_index
16
- @row_index = row_index
17
- end
18
-
19
- # Build a Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest
20
- #
21
- # @return [Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest]
22
- def batch_update_spreadsheet_request
23
- build_batch_request(@rows)
24
- end
25
-
26
- private
27
-
28
- def set_extended_value_type!(extended_value, value)
29
- v = value || ''
30
- if v.start_with?('=')
31
- extended_value.formula_value = value
32
- elsif v.match(/^-?[\d.]+$/)
33
- extended_value.number_value = value
34
- elsif v.downcase == 'true' || v.downcase == 'false'
35
- extended_value.boolean_value = value
36
- else
37
- extended_value.string_value = value
38
- end
39
- end
40
-
41
- def build_cell_format(mod)
42
- ::Google::Apis::SheetsV4::CellFormat.new.tap do |cf|
43
- cf.text_format = mod.text_format
44
-
45
- cf.horizontal_alignment = mod.halign
46
- cf.vertical_alignment = mod.valign
47
- cf.background_color = mod.color
48
- cf.number_format = mod.numberformat
49
- end
50
- end
51
-
52
- def grid_range_for_cell(cell)
53
- ::Google::Apis::SheetsV4::GridRange.new(
54
- sheet_id: @sheet_id,
55
- start_column_index: cell.index,
56
- end_column_index: cell.index + 1,
57
- start_row_index: cell.row_index,
58
- end_row_index: cell.row_index + 1
59
- )
60
- end
61
-
62
- def current_value(row_index, cell_index)
63
- @current_sheet_values[row_index][cell_index]
64
- rescue ::StandardError
65
- nil
66
- end
67
-
68
- def build_cell_value(cell)
69
- ::Google::Apis::SheetsV4::ExtendedValue.new.tap do |xv|
70
- value =
71
- if cell.value.nil?
72
- current_value(cell.row_index, cell.index)
73
- else
74
- cell.to_csv
75
- end
76
-
77
- set_extended_value_type!(xv, value)
78
- end
79
- end
80
-
81
- def build_cell_data(cell)
82
- mod = ::CSVPlusPlus::Writer::GoogleSheetModifier.new(cell.modifier)
83
-
84
- ::Google::Apis::SheetsV4::CellData.new.tap do |cd|
85
- cd.user_entered_format = build_cell_format(mod)
86
- cd.note = mod.note if mod.note
87
-
88
- # XXX apply data validation
89
- cd.user_entered_value = build_cell_value(cell)
90
- end
91
- end
92
-
93
- def build_row_data(row)
94
- ::Google::Apis::SheetsV4::RowData.new(values: row.cells.map { |cell| build_cell_data(cell) })
95
- end
96
-
97
- def build_update_cells_request(rows)
98
- ::Google::Apis::SheetsV4::UpdateCellsRequest.new(
99
- fields: '*',
100
- start: ::Google::Apis::SheetsV4::GridCoordinate.new(
101
- sheet_id: @sheet_id,
102
- column_index: @column_index,
103
- row_index: @row_index
104
- ),
105
- rows: rows.map { |row| build_row_data(row) }
106
- )
107
- end
108
-
109
- def build_border(cell)
110
- mod = ::CSVPlusPlus::Writer::GoogleSheetModifier.new(cell.modifier)
111
- border = mod.border
112
-
113
- ::Google::Apis::SheetsV4::UpdateBordersRequest.new(
114
- top: mod.border_along?(:top) ? border : nil,
115
- right: mod.border_along?(:right) ? border : nil,
116
- left: mod.border_along?(:left) ? border : nil,
117
- bottom: mod.border_along?(:bottom) ? border : nil,
118
- range: grid_range_for_cell(cell)
119
- )
120
- end
121
-
122
- def build_update_borders_request(cell)
123
- ::Google::Apis::SheetsV4::Request.new(update_borders: build_border(cell))
124
- end
125
-
126
- def chunked_requests(rows)
127
- rows.each_slice(1000).to_a.map do |chunked_rows|
128
- ::Google::Apis::SheetsV4::Request.new(update_cells: build_update_cells_request(chunked_rows))
129
- end
130
- end
131
-
132
- def build_batch_request(rows)
133
- ::Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new.tap do |bu|
134
- bu.requests = chunked_requests(rows)
135
-
136
- rows.each do |row|
137
- row.cells.filter { |c| c.modifier.any_border? }
138
- .each do |cell|
139
- bu.requests << build_update_borders_request(cell)
140
- end
141
- end
142
- end
143
- end
144
- end
145
- # rubocop:enable Metrics/ClassLength
146
- end
147
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- module Writer
5
- # Decorate a Modifier so it can be written to the Google Sheets API
6
- class GoogleSheetModifier < ::SimpleDelegator
7
- # Format the border for Google Sheets
8
- #
9
- # @return [Google::Apis::SheetsV4::Border]
10
- def border
11
- # TODO: allow different border styles per side
12
- ::Google::Apis::SheetsV4::Border.new(
13
- color: bordercolor&.to_s || '#000000',
14
- style: borderstyle&.to_s || 'solid'
15
- )
16
- end
17
-
18
- # Format the color for Google Sheets
19
- #
20
- # @return [Google::Apis::SheetsV4::Color]
21
- def color
22
- google_sheets_color(super) if super
23
- end
24
-
25
- # Format the fontcolor for Google Sheets
26
- #
27
- # @return [Google::Apis::SheetsV4::Color]
28
- def fontcolor
29
- google_sheets_color(super) if super
30
- end
31
-
32
- # Format the halign for Google Sheets
33
- #
34
- # @return [String]
35
- def halign
36
- super&.to_s&.upcase
37
- end
38
-
39
- # Format the numberformat for Google Sheets
40
- #
41
- # @return [::Google::Apis::SheetsV4::NumberFormat]
42
- def numberformat
43
- ::Google::Apis::SheetsV4::NumberFormat.new(type: super) if super
44
- end
45
-
46
- # Builds a SheetsV4::TextFormat with the underlying Modifier
47
- #
48
- # @return [::Google::Apis::SheetsV4::TextFormat]
49
- def text_format
50
- ::Google::Apis::SheetsV4::TextFormat.new(
51
- bold: formatted?(:bold) || nil,
52
- italic: formatted?(:italic) || nil,
53
- strikethrough: formatted?(:strikethrough) || nil,
54
- underline: formatted?(:underline) || nil,
55
- font_family: fontfamily,
56
- font_size: fontsize,
57
- foreground_color: fontcolor
58
- )
59
- end
60
-
61
- # Format the valign for Google Sheets
62
- def valign
63
- super&.to_s&.upcase
64
- end
65
-
66
- private
67
-
68
- def google_sheets_color(color)
69
- ::Google::Apis::SheetsV4::Color.new(
70
- red: color.red_percent,
71
- green: color.green_percent,
72
- blue: color.blue_percent
73
- )
74
- end
75
- end
76
- end
77
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- module Writer
5
- # Build a RubyXL-decorated Modifier class adds some support for Excel
6
- class RubyXLModifier < ::SimpleDelegator
7
- # https://www.rubydoc.info/gems/rubyXL/RubyXL/NumberFormats
8
- # https://support.microsoft.com/en-us/office/number-format-codes-5026bbd6-04bc-48cd-bf33-80f18b4eae68
9
- NUM_FMT_IDS = {
10
- currency: 5,
11
- date: 14,
12
- date_time: 22,
13
- number: 1,
14
- percent: 9,
15
- text: 49,
16
- time: 21,
17
- scientific: 48
18
- }.freeze
19
- private_constant :NUM_FMT_IDS
20
-
21
- # https://www.rubydoc.info/gems/rubyXL/2.3.0/RubyXL
22
- # ST_BorderStyle = %w{ none thin medium dashed dotted thick double hair mediumDashed dashDot mediumDashDot
23
- # dashDotDot slantDashDot }
24
- BORDER_STYLES = {
25
- dashed: 'dashed',
26
- dotted: 'dotted',
27
- double: 'double',
28
- solid: 'thin',
29
- solid_medium: 'medium',
30
- solid_thick: 'thick'
31
- }.freeze
32
- private_constant :BORDER_STYLES
33
-
34
- # The excel-specific border weight
35
- #
36
- # @return [Integer]
37
- def border_weight
38
- return unless borderstyle
39
-
40
- # rubocop:disable Lint/ConstantResolution
41
- BORDER_STYLES[borderstyle.to_sym]
42
- # rubocop:enable Lint/ConstantResolution
43
- end
44
-
45
- # The excel-specific number format code
46
- #
47
- # @return [String]
48
- def number_format_code
49
- return unless numberformat
50
-
51
- ::RubyXL::NumberFormats::DEFAULT_NUMBER_FORMATS.find_by_format_id(
52
- # rubocop:disable Lint/ConstantResolution
53
- NUM_FMT_IDS[numberformat.to_sym]
54
- # rubocop:enable Lint/ConstantResolution
55
- ).format_code
56
- end
57
- end
58
- end
59
- end