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
data/lib/csv_plus_plus.rb CHANGED
@@ -1,66 +1,95 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
4
+ require 'sorbet-runtime'
5
+
3
6
  require 'benchmark'
4
7
  require 'csv'
5
8
  require 'fileutils'
6
9
  require 'google/apis/drive_v3'
7
10
  require 'google/apis/sheets_v4'
8
11
  require 'googleauth'
12
+ require 'optparse'
9
13
  require 'pathname'
10
14
  require 'rubyXL'
11
15
  require 'rubyXL/convenience_methods'
12
16
  require 'set'
17
+ require 'strscan'
13
18
  require 'tempfile'
14
19
 
20
+ require_relative 'csv_plus_plus/a1_reference'
21
+ require_relative 'csv_plus_plus/google_api_client'
22
+ require_relative 'csv_plus_plus/options'
23
+ require_relative 'csv_plus_plus/runtime/position'
24
+ require_relative 'csv_plus_plus/source_code'
25
+
26
+ require_relative 'csv_plus_plus/cli_flag'
15
27
  require_relative 'csv_plus_plus/entities'
16
28
  require_relative 'csv_plus_plus/error'
29
+ require_relative 'csv_plus_plus/error_formatter'
17
30
 
18
- require_relative 'csv_plus_plus/cell'
19
31
  require_relative 'csv_plus_plus/cli'
32
+ require_relative 'csv_plus_plus/runtime'
33
+
34
+ require_relative 'csv_plus_plus/cell'
20
35
  require_relative 'csv_plus_plus/color'
36
+ require_relative 'csv_plus_plus/modifier'
37
+
38
+ require_relative 'csv_plus_plus/parser/cell_value.tab'
39
+ require_relative 'csv_plus_plus/parser/code_section.tab'
40
+ require_relative 'csv_plus_plus/parser/modifier.tab'
21
41
 
22
42
  require_relative 'csv_plus_plus/compiler'
23
- require_relative 'csv_plus_plus/runtime'
24
43
 
25
44
  require_relative 'csv_plus_plus/lexer'
26
- require_relative 'csv_plus_plus/lexer/tokenizer'
27
- require_relative 'csv_plus_plus/modifier'
28
- require_relative 'csv_plus_plus/options'
29
- require_relative 'csv_plus_plus/parser/modifier.tab'
45
+ require_relative 'csv_plus_plus/reader'
30
46
  require_relative 'csv_plus_plus/row'
31
47
  require_relative 'csv_plus_plus/template'
32
- require_relative 'csv_plus_plus/validated_modifier'
33
48
  require_relative 'csv_plus_plus/writer'
34
49
 
50
+ require_relative 'csv_plus_plus/benchmarked_compiler'
51
+
35
52
  # A programming language for writing rich CSV files
36
53
  module CSVPlusPlus
54
+ extend ::T::Sig
55
+
56
+ sig { params(source_code: ::CSVPlusPlus::SourceCode, options: ::CSVPlusPlus::Options::Options).void }
37
57
  # Parse the input into a +Template+ and write it to the desired format
38
58
  #
39
- # @param input [String] The csvpp input to compile
40
- # @param filename [String, nil] The filename the input was read from. +nil+ if it is read from stdin.
59
+ # @param source_code [SourceCode] The source code being compiled
41
60
  # @param options [Options] The various options to compile with
42
- def self.apply_template_to_sheet!(input, filename, options)
43
- warn(options.verbose_summary) if options.verbose
61
+ def self.cli_compile(source_code, options)
62
+ runtime = ::CSVPlusPlus::Runtime.new(source_code:)
44
63
 
45
- runtime = ::CSVPlusPlus::Runtime.new(input:, filename:)
64
+ warn(options.verbose_summary) if options.verbose
46
65
 
47
66
  ::CSVPlusPlus::Compiler.with_compiler(options:, runtime:) do |compiler|
48
67
  template = compiler.compile_template
49
-
50
68
  warn(template.verbose_summary) if options.verbose
51
69
 
52
- write_template(template, compiler, options)
70
+ write_template(template:, compiler:, options:)
53
71
  end
72
+ rescue ::StandardError => e
73
+ ::CSVPlusPlus::ErrorFormatter.new(runtime: ::T.must(runtime), options:).handle_error(e)
74
+ # the caller will exit(1)
75
+ raise(e)
54
76
  end
55
77
 
78
+ sig do
79
+ params(
80
+ compiler: ::CSVPlusPlus::Compiler,
81
+ options: ::CSVPlusPlus::Options::Options,
82
+ template: ::CSVPlusPlus::Template
83
+ ).void
84
+ end
56
85
  # Write the results (and possibly make a backup) of a compiled +template+
57
86
  #
58
- # @param template [Template] The compiled template
59
87
  # @param compiler [Compiler] The compiler currently in use
60
88
  # @param options [Options] The options we're running with
61
- def self.write_template(template, compiler, options)
62
- compiler.outputting! do
63
- output = ::CSVPlusPlus::Writer.writer(options)
89
+ # @param template [Template] The compiled template
90
+ def self.write_template(compiler:, options:, template:)
91
+ compiler.outputting! do |position|
92
+ output = ::CSVPlusPlus::Writer.writer(options, position)
64
93
  output.write_backup if options.backup
65
94
  output.write(template)
66
95
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_plus_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Carroll
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-20 00:00:00.000000000 Z
11
+ date: 2023-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-apis-drive_v3
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sorbet-runtime
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.5'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.5'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -118,71 +132,86 @@ executables:
118
132
  extensions: []
119
133
  extra_rdoc_files: []
120
134
  files:
121
- - CHANGELOG.md
122
135
  - README.md
123
136
  - bin/csv++
124
137
  - bin/csvpp
138
+ - docs/CHANGELOG.md
125
139
  - lib/csv_plus_plus.rb
140
+ - lib/csv_plus_plus/a1_reference.rb
126
141
  - lib/csv_plus_plus/benchmarked_compiler.rb
127
- - lib/csv_plus_plus/can_define_references.rb
128
- - lib/csv_plus_plus/can_resolve_references.rb
129
142
  - lib/csv_plus_plus/cell.rb
130
143
  - lib/csv_plus_plus/cli.rb
131
144
  - lib/csv_plus_plus/cli_flag.rb
132
145
  - lib/csv_plus_plus/color.rb
133
146
  - lib/csv_plus_plus/compiler.rb
134
- - lib/csv_plus_plus/data_validation.rb
135
147
  - lib/csv_plus_plus/entities.rb
136
148
  - lib/csv_plus_plus/entities/ast_builder.rb
137
149
  - lib/csv_plus_plus/entities/boolean.rb
138
150
  - lib/csv_plus_plus/entities/builtins.rb
139
- - lib/csv_plus_plus/entities/cell_reference.rb
140
151
  - lib/csv_plus_plus/entities/date.rb
141
152
  - lib/csv_plus_plus/entities/entity.rb
153
+ - lib/csv_plus_plus/entities/entity_with_arguments.rb
142
154
  - lib/csv_plus_plus/entities/function.rb
143
155
  - lib/csv_plus_plus/entities/function_call.rb
156
+ - lib/csv_plus_plus/entities/has_identifier.rb
144
157
  - lib/csv_plus_plus/entities/number.rb
158
+ - lib/csv_plus_plus/entities/reference.rb
145
159
  - lib/csv_plus_plus/entities/runtime_value.rb
146
160
  - lib/csv_plus_plus/entities/string.rb
147
- - lib/csv_plus_plus/entities/variable.rb
148
161
  - lib/csv_plus_plus/error.rb
162
+ - lib/csv_plus_plus/error/cli_error.rb
163
+ - lib/csv_plus_plus/error/compiler_error.rb
149
164
  - lib/csv_plus_plus/error/error.rb
150
165
  - lib/csv_plus_plus/error/formula_syntax_error.rb
151
166
  - lib/csv_plus_plus/error/modifier_syntax_error.rb
152
167
  - lib/csv_plus_plus/error/modifier_validation_error.rb
153
- - lib/csv_plus_plus/error/syntax_error.rb
168
+ - lib/csv_plus_plus/error/positional_error.rb
154
169
  - lib/csv_plus_plus/error/writer_error.rb
155
- - lib/csv_plus_plus/expand.rb
170
+ - lib/csv_plus_plus/error_formatter.rb
156
171
  - lib/csv_plus_plus/google_api_client.rb
157
- - lib/csv_plus_plus/google_options.rb
158
- - lib/csv_plus_plus/graph.rb
159
172
  - lib/csv_plus_plus/lexer.rb
160
- - lib/csv_plus_plus/lexer/lexer.rb
173
+ - lib/csv_plus_plus/lexer/racc_lexer.rb
161
174
  - lib/csv_plus_plus/lexer/tokenizer.rb
162
175
  - lib/csv_plus_plus/modifier.rb
163
176
  - lib/csv_plus_plus/modifier/conditional_formatting.rb
177
+ - lib/csv_plus_plus/modifier/data_validation.rb
178
+ - lib/csv_plus_plus/modifier/expand.rb
179
+ - lib/csv_plus_plus/modifier/google_sheet_modifier.rb
180
+ - lib/csv_plus_plus/modifier/modifier.rb
181
+ - lib/csv_plus_plus/modifier/modifier_validator.rb
182
+ - lib/csv_plus_plus/modifier/rubyxl_modifier.rb
164
183
  - lib/csv_plus_plus/options.rb
184
+ - lib/csv_plus_plus/options/file_options.rb
185
+ - lib/csv_plus_plus/options/google_sheets_options.rb
186
+ - lib/csv_plus_plus/options/options.rb
165
187
  - lib/csv_plus_plus/parser/cell_value.tab.rb
166
188
  - lib/csv_plus_plus/parser/code_section.tab.rb
167
189
  - lib/csv_plus_plus/parser/modifier.tab.rb
168
- - lib/csv_plus_plus/references.rb
190
+ - lib/csv_plus_plus/reader.rb
191
+ - lib/csv_plus_plus/reader/csv.rb
192
+ - lib/csv_plus_plus/reader/google_sheets.rb
193
+ - lib/csv_plus_plus/reader/reader.rb
194
+ - lib/csv_plus_plus/reader/rubyxl.rb
169
195
  - lib/csv_plus_plus/row.rb
170
196
  - lib/csv_plus_plus/runtime.rb
171
- - lib/csv_plus_plus/scope.rb
197
+ - lib/csv_plus_plus/runtime/graph.rb
198
+ - lib/csv_plus_plus/runtime/position.rb
199
+ - lib/csv_plus_plus/runtime/references.rb
200
+ - lib/csv_plus_plus/runtime/runtime.rb
201
+ - lib/csv_plus_plus/runtime/scope.rb
202
+ - lib/csv_plus_plus/source_code.rb
172
203
  - lib/csv_plus_plus/template.rb
173
- - lib/csv_plus_plus/validated_modifier.rb
174
204
  - lib/csv_plus_plus/version.rb
175
205
  - lib/csv_plus_plus/writer.rb
176
- - lib/csv_plus_plus/writer/base_writer.rb
177
206
  - lib/csv_plus_plus/writer/csv.rb
178
207
  - lib/csv_plus_plus/writer/excel.rb
179
208
  - lib/csv_plus_plus/writer/file_backer_upper.rb
180
- - lib/csv_plus_plus/writer/google_sheet_builder.rb
181
- - lib/csv_plus_plus/writer/google_sheet_modifier.rb
182
209
  - lib/csv_plus_plus/writer/google_sheets.rb
210
+ - lib/csv_plus_plus/writer/google_sheets_builder.rb
211
+ - lib/csv_plus_plus/writer/merger.rb
183
212
  - lib/csv_plus_plus/writer/open_document.rb
184
213
  - lib/csv_plus_plus/writer/rubyxl_builder.rb
185
- - lib/csv_plus_plus/writer/rubyxl_modifier.rb
214
+ - lib/csv_plus_plus/writer/writer.rb
186
215
  homepage: https://github.com/patrickomatic/csv-plus-plus
187
216
  licenses:
188
217
  - MIT
@@ -192,7 +221,7 @@ metadata:
192
221
  github_repo: git://github.com/patrickomatic/csv-plus-plus
193
222
  homepage_uri: https://github.com/patrickomatic/csv-plus-plus
194
223
  source_code_uri: https://github.com/patrickomatic/csv-plus-plus
195
- changelog_uri: https://github.com/patrickomatic/csv-plus-plus/blob/main/CHANGELOG.md
224
+ changelog_uri: https://github.com/patrickomatic/csv-plus-plus/blob/main/docs/CHANGELOG.md
196
225
  rubygems_mfa_required: 'true'
197
226
  post_install_message:
198
227
  rdoc_options: []
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- # Methods for classes that need to manage +@variables+ and +@functions+
5
- module CanDefineReferences
6
- # Define a (or re-define an existing) variable
7
- #
8
- # @param id [String, Symbol] The identifier for the variable
9
- # @param entity [Entity] The value (entity) the variable holds
10
- def def_variable(id, entity)
11
- variables[id.to_sym] = entity
12
- end
13
-
14
- # Define (or re-define existing) variables
15
- #
16
- # @param variables [Hash<Symbol, Variable>] Variables to define
17
- def def_variables(vars)
18
- vars.each { |id, entity| def_variable(id, entity) }
19
- end
20
-
21
- # Define a (or re-define an existing) function
22
- #
23
- # @param id [String, Symbol] The identifier for the function
24
- # @param entity [Entities::Function] The defined function
25
- def def_function(id, entity)
26
- functions[id.to_sym] = entity
27
- end
28
-
29
- # Is the variable defined?
30
- #
31
- # @param var_id [Symbol, String] The identifier of the variable
32
- #
33
- # @return [boolean]
34
- def defined_variable?(var_id)
35
- variables.key?(var_id.to_sym)
36
- end
37
-
38
- # Is the function defined?
39
- #
40
- # @param fn_id [Symbol, String] The identifier of the function
41
- #
42
- # @return [boolean]
43
- def defined_function?(fn_id)
44
- functions.key?(fn_id.to_sym)
45
- end
46
-
47
- # Provide a summary of the functions and variables compiled (to show in verbose mode)
48
- #
49
- # @return [String]
50
- def verbose_summary
51
- <<~SUMMARY
52
- # Code Section Summary
53
-
54
- ## Resolved Variables
55
-
56
- #{variable_summary}
57
-
58
- ## Functions
59
-
60
- #{function_summary}
61
- SUMMARY
62
- end
63
-
64
- private
65
-
66
- def variables
67
- @variables ||= {}
68
- end
69
-
70
- def functions
71
- @functions ||= {}
72
- end
73
-
74
- def variable_summary
75
- return '(no variables defined)' if variables.empty?
76
-
77
- variables.map { |k, v| "#{k} := #{v}" }
78
- .join("\n")
79
- end
80
-
81
- def function_summary
82
- return '(no functions defined)' if functions.empty?
83
-
84
- functions.map { |k, f| "#{k}: #{f}" }
85
- .join("\n")
86
- end
87
- end
88
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- # Methods for classes that need to resolve references
5
- module CanResolveReferences
6
- # TODO
7
- end
8
- end
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- # A validation on a cell value. Used to support the `validate=` modifier directive. This is mostly based on the
5
- # Google Sheets API spec which can be seen here:
6
- #
7
- # {https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/other#ConditionType}
8
- #
9
- # @attr_reader arguments [Array<::String>] The parsed arguments as required by the condition.
10
- # @attr_reader condition [Symbol] The condition (:blank, :text_eq, :date_before, etc.)
11
- # @attr_reader invalid_reason [::String, nil] If set, the reason why this modifier is not valid.
12
- class DataValidation
13
- attr_reader :arguments, :condition, :invalid_reason
14
-
15
- # @param value [::String] The value to parse as a data validation
16
- def initialize(value)
17
- condition, args = unquote(value).split(/\s*:\s*/)
18
- @arguments = unquote(args || '').split(/\s+/)
19
- @condition = condition.to_sym
20
-
21
- validate!
22
- end
23
-
24
- # Each data validation represented by (+condition+) has their own require
25
- # @return [boolean]
26
- def valid?
27
- @invalid_reason.nil?
28
- end
29
-
30
- protected
31
-
32
- def unquote(str)
33
- # TODO: I'm pretty sure this isn't sufficient and we need to deal with the backslashes
34
- str.gsub(/^['\s]*|['\s]*$/, '')
35
- end
36
-
37
- def invalid!(reason)
38
- @invalid_reason = reason
39
- end
40
-
41
- def a_number(arg)
42
- Float(arg)
43
- rescue ::ArgumentError
44
- invalid!("Requires a number but given: #{arg}")
45
- end
46
-
47
- def a1_notation(arg)
48
- return arg if ::CSVPlusPlus::Entities::CellReference.valid_cell_reference?(arg)
49
- end
50
-
51
- def a_date(arg, allow_relative_date: false)
52
- return arg if ::CSVPlusPlus::Entities::Date.valid_date?(arg)
53
-
54
- if allow_relative_date
55
- a_relative_date(arg)
56
- else
57
- invalid!("Requires a date but given: #{arg}")
58
- end
59
- end
60
-
61
- def a_relative_date(arg)
62
- return arg if %w[past_month past_week past_year yesterday today tomorrow].include?(arg.downcase)
63
-
64
- invalid!('Requires a relative date: past_month, past_week, past_year, yesterday, today or tomorrow')
65
- end
66
-
67
- def no_args
68
- return if @arguments.empty?
69
-
70
- invalid!("Requires no arguments but #{@arguments.length} given: #{@arguments}")
71
- end
72
-
73
- def one_arg
74
- return @arguments[0] if @arguments.length == 1
75
-
76
- invalid!("Requires only one argument but #{@arguments.length} given: #{@arguments}")
77
- end
78
-
79
- def one_arg_or_more
80
- return @arguments if @arguments.length.positive?
81
-
82
- invalid!("Requires at least one argument but #{@arguments.length} given: #{@arguments}")
83
- end
84
-
85
- def two_dates
86
- return @arguments if @arguments.length == 2 && a_date(@arguments[0]) && a_date(@arguments[1])
87
-
88
- invalid!("Requires exactly two dates but given: #{@arguments}")
89
- end
90
-
91
- def two_numbers
92
- return @arguments if @arguments.length == 2 && a_number(@arguments[0]) && a_number(@arguments[1])
93
-
94
- invalid!("Requires exactly two numbers but given: #{@arguments}")
95
- end
96
-
97
- # validate_boolean is a weird one because it can have 0, 1 or 2 @arguments - all of them must be (true | false)
98
- def validate_boolean
99
- return @arguments if @arguments.empty?
100
-
101
- converted_args = @arguments.map(&:strip).map(&:downcase)
102
- return @arguments if [1, 2].include?(@arguments.length) && converted_args.all? do |arg|
103
- %w[true false].include?(arg)
104
- end
105
-
106
- invalid!("Requires 0, 1 or 2 arguments and they all must be either 'true' or 'false'. Received: #{arguments}")
107
- end
108
-
109
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
110
- def validate!
111
- case condition.to_sym
112
- when :blank, :date_is_valid, :not_blank, :text_is_email, :text_is_url
113
- no_args
114
- when :text_contains, :text_ends_with, :text_eq, :text_not_contains, :text_starts_with
115
- one_arg
116
- when :date_after, :date_before, :date_on_or_after, :date_on_or_before
117
- a_date(one_arg, allow_relative_date: true)
118
- when :date_eq, :date_not_eq
119
- a_date(one_arg)
120
- when :date_between, :date_not_between
121
- two_dates
122
- when :one_of_range
123
- a1_notation(one_arg)
124
- when :custom_formula, :one_of_list, :text_not_eq
125
- one_arg_or_more
126
- when :number_eq, :number_greater, :number_greater_than_eq, :number_less, :number_less_than_eq, :number_not_eq
127
- a_number(one_arg)
128
- when :number_between, :number_not_between
129
- two_numbers
130
- when :boolean
131
- validate_boolean
132
- else
133
- invalid!('Not a recognized data validation directive')
134
- end
135
- end
136
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
137
- end
138
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './ast_builder'
4
- require_relative './entity'
5
-
6
- module CSVPlusPlus
7
- module Entities
8
- # A reference to a cell
9
- #
10
- # @attr_reader cell_reference [String] The cell reference in A1 format
11
- class CellReference < Entity
12
- attr_reader :cell_reference
13
-
14
- A1_NOTATION_REGEXP = /(['\w]+!)?\w+:\w+/
15
- public_constant :A1_NOTATION_REGEXP
16
-
17
- # Create a +CellReference+ to the given indexes
18
- #
19
- # @param cell_index [Integer] The current cell index
20
- # @param row_index [Integer] The current row index
21
- #
22
- # @return [CellReference]
23
- def self.from_index(cell_index:, row_index:)
24
- return unless row_index || cell_index
25
-
26
- # I can't just extend this class due to circular references :(
27
- ::Class.new.extend(::CSVPlusPlus::Entities::ASTBuilder).ref(cell_index:, row_index:)
28
- end
29
-
30
- # Does the given +cell_reference_string+ conform to a valid cell reference?
31
- #
32
- # {https://developers.google.com/sheets/api/guides/concepts}
33
- #
34
- # @param cell_reference_string [::String] The string to check if it is a valid cell reference (we assume it's in
35
- # A1 notation but maybe can support R1C1)
36
- #
37
- # @return [boolean]
38
- def self.valid_cell_reference?(cell_reference_string)
39
- !(cell_reference_string =~ ::CSVPlusPlus::Entities::CellReference::A1_NOTATION_REGEXP).nil?
40
- end
41
-
42
- # @param cell_reference [String] The cell reference in A1 format
43
- def initialize(cell_reference)
44
- super(:cell_reference)
45
-
46
- @cell_reference = cell_reference
47
- end
48
-
49
- # @return [::String]
50
- def to_s
51
- @cell_reference
52
- end
53
-
54
- # @return [Boolean]
55
- def ==(other)
56
- super && @cell_reference == other.cell_reference
57
- end
58
- end
59
- end
60
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- module Entities
5
- # TODO: get rid of this I think - everything will just be References
6
- #
7
- # A reference to a variable
8
- class Variable < Entity
9
- # @param id [Symbol] The identifier of the variable
10
- def initialize(id)
11
- super(:variable, id:)
12
- end
13
-
14
- # @return [String]
15
- def to_s
16
- "$$#{@id}"
17
- end
18
-
19
- # @return [boolean]
20
- def ==(other)
21
- super && id == other.id
22
- end
23
- end
24
- end
25
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- module Error
5
- # An error that can be thrown for various syntax errors
6
- class SyntaxError < ::CSVPlusPlus::Error::Error
7
- # @param runtime [Runtime] The current runtime
8
- # @param wrapped_error [StandardError] The underlying error that caused the syntax error. For example a
9
- # Racc::ParseError that was thrown
10
- def initialize(runtime, wrapped_error: nil)
11
- @runtime = runtime
12
- @wrapped_error = wrapped_error
13
-
14
- super()
15
- end
16
-
17
- # @return [String]
18
- def to_s
19
- to_trace
20
- end
21
-
22
- # Output a verbose user-helpful string that references the current runtime
23
- def to_verbose_trace
24
- warn(@wrapped_error.full_message) if @wrapped_error
25
- warn(@wrapped_error.backtrace) if @wrapped_error
26
- to_trace
27
- end
28
-
29
- # Output a user-helpful string that references the runtime state
30
- #
31
- # @return [String]
32
- def to_trace
33
- "#{message_prefix}#{cell_index} #{error_message}"
34
- end
35
-
36
- private
37
-
38
- def cell_index
39
- row_index = @runtime.row_index
40
- if @runtime.cell_index
41
- "[#{row_index},#{@runtime.cell_index}]"
42
- elsif row_index
43
- "[#{row_index}]"
44
- else
45
- ''
46
- end
47
- end
48
-
49
- def message_prefix
50
- line_number = @runtime.line_number
51
- filename = @runtime.filename
52
-
53
- line_str = line_number ? ":#{line_number}" : ''
54
- "#{filename}#{line_str}"
55
- end
56
- end
57
- end
58
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- Expand =
5
- ::Struct.new(:repetitions) do
6
- # Does this infinitely expand?
7
- #
8
- # @return [boolean]
9
- def infinite?
10
- repetitions.nil?
11
- end
12
-
13
- # @return [::String]
14
- def to_s
15
- "Expand #{repetitions || 'infinity'}"
16
- end
17
- end
18
-
19
- public_constant :Expand
20
- end