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
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