csv_plus_plus 0.1.2 → 0.2.0

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