csv_plus_plus 0.0.4 → 0.1.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +9 -3
  4. data/bin/csv++ +1 -37
  5. data/bin/csvpp +6 -0
  6. data/lib/csv_plus_plus/cell.rb +24 -8
  7. data/lib/csv_plus_plus/cli.rb +97 -0
  8. data/lib/csv_plus_plus/cli_flag.rb +10 -2
  9. data/lib/csv_plus_plus/code_section.rb +22 -3
  10. data/lib/csv_plus_plus/color.rb +19 -5
  11. data/lib/csv_plus_plus/google_api_client.rb +20 -0
  12. data/lib/csv_plus_plus/google_options.rb +6 -2
  13. data/lib/csv_plus_plus/graph.rb +0 -1
  14. data/lib/csv_plus_plus/language/ast_builder.rb +68 -0
  15. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +65 -0
  16. data/lib/csv_plus_plus/language/builtins.rb +46 -0
  17. data/lib/csv_plus_plus/language/cell_value.tab.rb +1 -2
  18. data/lib/csv_plus_plus/language/code_section.tab.rb +1 -2
  19. data/lib/csv_plus_plus/language/compiler.rb +74 -86
  20. data/lib/csv_plus_plus/language/entities/boolean.rb +5 -4
  21. data/lib/csv_plus_plus/language/entities/cell_reference.rb +10 -3
  22. data/lib/csv_plus_plus/language/entities/entity.rb +22 -6
  23. data/lib/csv_plus_plus/language/entities/function.rb +6 -4
  24. data/lib/csv_plus_plus/language/entities/function_call.rb +4 -3
  25. data/lib/csv_plus_plus/language/entities/number.rb +6 -4
  26. data/lib/csv_plus_plus/language/entities/runtime_value.rb +9 -8
  27. data/lib/csv_plus_plus/language/entities/string.rb +6 -4
  28. data/lib/csv_plus_plus/language/references.rb +22 -5
  29. data/lib/csv_plus_plus/language/runtime.rb +80 -22
  30. data/lib/csv_plus_plus/language/scope.rb +29 -38
  31. data/lib/csv_plus_plus/language/syntax_error.rb +10 -5
  32. data/lib/csv_plus_plus/lexer/lexer.rb +25 -12
  33. data/lib/csv_plus_plus/lexer/tokenizer.rb +35 -11
  34. data/lib/csv_plus_plus/modifier.rb +71 -8
  35. data/lib/csv_plus_plus/modifier.tab.rb +2 -2
  36. data/lib/csv_plus_plus/options.rb +17 -3
  37. data/lib/csv_plus_plus/row.rb +15 -4
  38. data/lib/csv_plus_plus/template.rb +10 -6
  39. data/lib/csv_plus_plus/version.rb +1 -1
  40. data/lib/csv_plus_plus/writer/base_writer.rb +0 -1
  41. data/lib/csv_plus_plus/writer/csv.rb +4 -1
  42. data/lib/csv_plus_plus/writer/excel.rb +5 -9
  43. data/lib/csv_plus_plus/writer/file_backer_upper.rb +58 -0
  44. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +8 -10
  45. data/lib/csv_plus_plus/writer/google_sheets.rb +22 -41
  46. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +23 -15
  47. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +15 -8
  48. data/lib/csv_plus_plus.rb +26 -4
  49. metadata +29 -7
@@ -17,6 +17,7 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 121)
17
17
 
18
18
  include ::CSVPlusPlus::Lexer
19
19
 
20
+ # @param cell_modifier
20
21
  def initialize(cell_modifier:, row_modifier:)
21
22
  super()
22
23
 
@@ -42,11 +43,10 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 121)
42
43
  'modifier'
43
44
  end
44
45
 
45
- def tokenizer(input)
46
+ def tokenizer
46
47
  ::CSVPlusPlus::Lexer::Tokenizer.new(
47
48
  catchall: /\w+/,
48
49
  ignore: /\s+/,
49
- input:,
50
50
  stop_fn: lambda do |scanner|
51
51
  return false unless scanner.scan(/\]\]/)
52
52
 
@@ -4,7 +4,16 @@ require_relative './cli_flag'
4
4
  require_relative './google_options'
5
5
 
6
6
  module CSVPlusPlus
7
- # The options a user can supply
7
+ # The options a user can supply (via CLI flags)
8
+ #
9
+ # @attr backup [boolean] Create a backup of the spreadsheet before writing
10
+ # @attr create_if_not_exists [boolean] Create the spreadsheet if it does not exist?
11
+ # @attr key_values [Hash] Additional variables that can be supplied to the template
12
+ # @attr offset [Array<Integer>] An [x, y] offset (array with two integers)
13
+ # @attr output_filename [String] The file to write our compiled results to
14
+ # @attr sheet_name [String] The name of the spreadsheet to write to
15
+ # @attr verbose [boolean] Include extra verbose output?
16
+ # @attr_reader google [GoogleOptions] Options that are specific to the Google Sheets writer
8
17
  class Options
9
18
  attr_accessor :backup, :create_if_not_exists, :key_values, :offset, :output_filename, :sheet_name, :verbose
10
19
  attr_reader :google
@@ -15,16 +24,20 @@ module CSVPlusPlus
15
24
  @create_if_not_exists = false
16
25
  @key_values = {}
17
26
  @verbose = false
18
- # TODO: switch to true? probably a safer choice
19
27
  @backup = false
20
28
  end
21
29
 
22
30
  # Set the Google Sheet ID
31
+ #
32
+ # @param sheet_id [String] The identifier used by Google's API to reference the sheet. You can find it in the URL
33
+ # for the sheet
34
+ # @return [String]
23
35
  def google_sheet_id=(sheet_id)
24
36
  @google = ::CSVPlusPlus::GoogleOptions.new(sheet_id)
25
37
  end
26
38
 
27
39
  # Returns an error string or nil if there are no validation problems
40
+ # @return [String, nil]
28
41
  def validate
29
42
  return if @google || @output_filename
30
43
 
@@ -32,6 +45,7 @@ module CSVPlusPlus
32
45
  end
33
46
 
34
47
  # Return a string with a verbose description of what we're doing with the options
48
+ # @return [String]
35
49
  def verbose_summary
36
50
  <<~SUMMARY
37
51
  #{summary_divider}
@@ -56,7 +70,7 @@ module CSVPlusPlus
56
70
  SUMMARY
57
71
  end
58
72
 
59
- # to_s
73
+ # @return [String]
60
74
  def to_s
61
75
  "Options(create_if_not_exists: #{@create_if_not_exists}, google: #{@google}, key_values: #{@key_values}, " \
62
76
  "offset: #{@offset}, sheet_name: #{@sheet_name}, verbose: #{@verbose})"
@@ -4,37 +4,48 @@ require_relative 'cell'
4
4
  require_relative 'modifier.tab'
5
5
 
6
6
  module CSVPlusPlus
7
- ##
8
7
  # A row of a template
8
+ #
9
+ # @attr_reader cells [Array<Cell>]
10
+ # @attr_reader index [Integer] The index of this row
11
+ # @attr_reader modifier [Modifier] The modifier to apply to all cells in this row
9
12
  class Row
10
13
  attr_reader :cells, :index, :modifier
11
14
 
12
- # initialize
15
+ # @param index [Integer] The index of this row (starts at 0)
16
+ # @param cells [Array<Cell>] The cells belonging to this row
17
+ # @param modifier [Modifier] The modifier to apply to all cells in this row
13
18
  def initialize(index, cells, modifier)
14
19
  @cells = cells
15
20
  @modifier = modifier
16
21
  @index = index
17
22
  end
18
23
 
19
- # Set the row index. And update the index of all affected cells
24
+ # Set the row's +index+ and update the +row_index+ of all affected cells
25
+ #
26
+ # @param index [Integer] The index of this row (starts at 0)
20
27
  def index=(index)
21
28
  @index = index
22
29
  @cells.each { |cell| cell.row_index = index }
23
30
  end
24
31
 
25
32
  # How much this row will expand itself, if at all (0)
33
+ #
34
+ # @return [Integer]
26
35
  def expand_amount
27
36
  return 0 unless @modifier.expand
28
37
 
29
38
  @modifier.expand.repetitions || (1000 - @index)
30
39
  end
31
40
 
32
- # to_s
41
+ # @return [String]
33
42
  def to_s
34
43
  "Row(index: #{index}, modifier: #{modifier}, cells: #{cells})"
35
44
  end
36
45
 
37
46
  # Return a deep copy of this row
47
+ #
48
+ # @return [Row]
38
49
  def deep_clone
39
50
  ::Marshal.load(::Marshal.dump(self))
40
51
  end
@@ -2,21 +2,23 @@
2
2
 
3
3
  module CSVPlusPlus
4
4
  # Contains the flow and data from a code section and CSV section
5
+ #
6
+ # @attr_reader rows [Array<Row>] The +Row+s that comprise this +Template+
5
7
  class Template
6
- attr_reader :rows, :scope
8
+ attr_reader :rows
7
9
 
8
- # initialize
9
- def initialize(rows:, scope:)
10
+ # @param rows [Array<Row>] The +Row+s that comprise this +Template+
11
+ def initialize(rows:)
10
12
  @rows = rows
11
- @scope = scope
12
13
  end
13
14
 
14
- # to_s
15
+ # @return [String]
15
16
  def to_s
16
- "Template(rows: #{@rows}, scope: #{@scope})"
17
+ "Template(rows: #{@rows})"
17
18
  end
18
19
 
19
20
  # Apply any expand= modifiers to the parsed template
21
+ # @return [Array<Row>]
20
22
  def expand_rows!
21
23
  expanded_rows = []
22
24
  row_index = 0
@@ -32,6 +34,8 @@ module CSVPlusPlus
32
34
  end
33
35
 
34
36
  # Make sure that the template has a valid amount of infinite expand modifiers
37
+ #
38
+ # @param runtime [Runtime] The compiler's current runtime
35
39
  def validate_infinite_expands(runtime)
36
40
  infinite_expand_rows = @rows.filter { |r| r.modifier.expand&.infinite? }
37
41
  return unless infinite_expand_rows.length > 1
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- VERSION = '0.0.4'
4
+ VERSION = '0.1.0'
5
5
  public_constant :VERSION
6
6
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module CSVPlusPlus
4
4
  module Writer
5
- ##
6
5
  # Some shared functionality that all Writers should build on
7
6
  class BaseWriter
8
7
  attr_accessor :options
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './file_backer_upper'
4
+
3
5
  module CSVPlusPlus
4
6
  module Writer
5
- ##
6
7
  # A class that can output a +Template+ to CSV
7
8
  class CSV < ::CSVPlusPlus::Writer::BaseWriter
9
+ include ::CSVPlusPlus::Writer::FileBackerUpper
10
+
8
11
  # write a +template+ to CSV
9
12
  def write(template)
10
13
  # TODO: also read it and merge the results
@@ -1,25 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './file_backer_upper'
3
4
  require_relative './rubyxl_builder'
4
5
 
5
6
  module CSVPlusPlus
6
7
  module Writer
7
8
  # A class that can output a +Template+ to an Excel file
8
9
  class Excel < ::CSVPlusPlus::Writer::BaseWriter
10
+ include ::CSVPlusPlus::Writer::FileBackerUpper
11
+
9
12
  # write the +template+ to an Excel file
10
13
  def write(template)
11
14
  ::CSVPlusPlus::Writer::RubyXLBuilder.new(
12
- output_filename: @options.output_filename,
15
+ input_filename: @options.output_filename,
13
16
  rows: template.rows,
14
17
  sheet_name: @options.sheet_name
15
- ).write
16
- end
17
-
18
- protected
19
-
20
- def load_requires
21
- require('rubyXL')
22
- require('rubyXL/convenience_methods')
18
+ ).build_workbook.write(@options.output_filename)
23
19
  end
24
20
  end
25
21
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'pathname'
5
+
6
+ module CSVPlusPlus
7
+ module Writer
8
+ # A module that can be mixed into any Writer that needs to back up it's @output_filename (all of them except Google
9
+ # Sheets)
10
+ module FileBackerUpper
11
+ # I don't want to include a bunch of second/millisecond stuff in the filename unless we
12
+ # really need to. so try a less specifically formatted filename then get more specific
13
+ DESIRED_BACKUP_FORMATS = [%(%Y_%m_%d-%I_%M%p), %(%Y_%m_%d-%I_%M_%S%p), %(%Y_%m_%d-%I_%M_%S_%L%p)].freeze
14
+ private_constant :DESIRED_BACKUP_FORMATS
15
+
16
+ # Assuming the underlying spreadsheet is file-based, create a backup of it
17
+ def write_backup
18
+ return unless ::File.exist?(@options.output_filename)
19
+
20
+ # TODO: also don't do anything if the current backups contents haven't changed (do a md5sum or something)
21
+
22
+ attempt_backups.tap do |backed_up_to|
23
+ warn("Backed up #{@options.output_filename} to #{backed_up_to}") if @options.verbose
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def attempt_backups
30
+ attempted =
31
+ # rubocop:disable Lint/ConstantResolution
32
+ DESIRED_BACKUP_FORMATS.map do |file_format|
33
+ # rubocop:enable Lint/ConstantResolution
34
+ filename = format_backup_filename(file_format)
35
+ backed_up_to = backup(filename)
36
+
37
+ next filename unless backed_up_to
38
+
39
+ return backed_up_to
40
+ end
41
+
42
+ raise(::CSVPlusPlus::Error, "Unable to write backup file despite trying these: #{attempted.join(', ')}")
43
+ end
44
+
45
+ def backup(filename)
46
+ return if ::File.exist?(filename)
47
+
48
+ ::FileUtils.cp(@options.output_filename, filename)
49
+ filename
50
+ end
51
+
52
+ def format_backup_filename(file_format)
53
+ pn = ::Pathname.new(@options.output_filename)
54
+ pn.sub_ext("-#{::Time.now.strftime(file_format)}" + pn.extname)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -7,7 +7,7 @@ module CSVPlusPlus
7
7
  # Given +rows+ from a +Template+, build requests compatible with Google Sheets Ruby API
8
8
  # rubocop:disable Metrics/ClassLength
9
9
  class GoogleSheetBuilder
10
- # initialize
10
+ # @param current_sheet_values
11
11
  def initialize(current_sheet_values:, sheet_id:, rows:, column_index: 0, row_index: 0)
12
12
  @current_sheet_values = current_sheet_values
13
13
  @sheet_id = sheet_id
@@ -17,6 +17,8 @@ module CSVPlusPlus
17
17
  end
18
18
 
19
19
  # Build a Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest
20
+ #
21
+ # @return [Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest]
20
22
  def batch_update_spreadsheet_request
21
23
  build_batch_request(@rows)
22
24
  end
@@ -42,20 +44,16 @@ module CSVPlusPlus
42
44
  end
43
45
  end
44
46
 
45
- # rubocop:disable Metrics/AbcSize
46
47
  def build_cell_format(mod)
47
48
  sheets_ns::CellFormat.new.tap do |cf|
48
49
  cf.text_format = mod.text_format
49
50
 
50
- cf.horizontal_alignment = mod.halign if mod.halign
51
- cf.vertical_alignment = mod.valign if mod.valign
52
-
53
- cf.background_color = mod.color if mod.color
54
-
55
- cf.number_format = mod.numberformat if mod.numberformat
51
+ cf.horizontal_alignment = mod.halign
52
+ cf.vertical_alignment = mod.valign
53
+ cf.background_color = mod.color
54
+ cf.number_format = mod.numberformat
56
55
  end
57
56
  end
58
- # rubocop:enable Metrics/AbcSize
59
57
 
60
58
  def grid_range_for_cell(cell)
61
59
  sheets_ns::GridRange.new(
@@ -68,7 +66,7 @@ module CSVPlusPlus
68
66
  end
69
67
 
70
68
  def current_value(row_index, cell_index)
71
- @current_values[row_index][cell_index]
69
+ @current_sheet_values[row_index][cell_index]
72
70
  rescue ::StandardError
73
71
  nil
74
72
  end
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../google_api_client'
3
4
  require_relative 'base_writer'
4
5
  require_relative 'google_sheet_builder'
5
6
 
6
- AUTH_SCOPES = ['https://www.googleapis.com/auth/spreadsheets'].freeze
7
- FULL_RANGE = 'A1:Z1000'
8
-
9
7
  module CSVPlusPlus
10
8
  module Writer
11
- # A class that can output a +Template+ to Google Sheets (via their API)
9
+ # A class that can write a +Template+ to Google Sheets (via their API)
12
10
  class GoogleSheets < ::CSVPlusPlus::Writer::BaseWriter
13
- # XXX it would be nice to raise this but we shouldn't expand out more than necessary for our data
11
+ # TODO: it would be nice to raise this but we shouldn't expand out more than necessary for our data
14
12
  SPREADSHEET_INFINITY = 1000
15
13
  public_constant :SPREADSHEET_INFINITY
16
14
 
17
- # initialize
15
+ # @param options [Options]
18
16
  def initialize(options)
19
17
  super(options)
20
18
 
@@ -23,8 +21,10 @@ module CSVPlusPlus
23
21
  end
24
22
 
25
23
  # write a +template+ to Google Sheets
24
+ #
25
+ # @param template [Template]
26
26
  def write(template)
27
- auth!
27
+ @sheets_client = ::CSVPlusPlus::GoogleApiClient.sheets_client
28
28
 
29
29
  fetch_spreadsheet!
30
30
  fetch_spreadsheet_values!
@@ -32,15 +32,12 @@ module CSVPlusPlus
32
32
  create_sheet! if @options.create_if_not_exists
33
33
 
34
34
  update_cells!(template)
35
- rescue ::Google::Apis::ClientError => e
36
- handle_google_error(e)
37
35
  end
38
36
 
39
- protected
40
-
41
- def load_requires
42
- require('google/apis/sheets_v4')
43
- require('googleauth')
37
+ # write a backup of the google sheet
38
+ def write_backup
39
+ drive_client = ::CSVPlusPlus::GoogleApiClient.drive_client
40
+ drive_client.copy_file(@sheet_id)
44
41
  end
45
42
 
46
43
  private
@@ -50,12 +47,7 @@ module CSVPlusPlus
50
47
  end
51
48
 
52
49
  def full_range
53
- format_range(::FULL_RANGE)
54
- end
55
-
56
- def auth!
57
- @gs ||= sheets_ns::SheetsService.new
58
- @gs.authorization = ::Google::Auth.get_application_default(::AUTH_SCOPES)
50
+ format_range('A1:Z1000')
59
51
  end
60
52
 
61
53
  def fetch_spreadsheet_values!
@@ -85,7 +77,7 @@ module CSVPlusPlus
85
77
  end
86
78
 
87
79
  def get_all_spreadsheet_values(render_option)
88
- @gs.get_spreadsheet_values(@sheet_id, full_range, value_render_option: render_option)
80
+ @sheets_client.get_spreadsheet_values(@sheet_id, full_range, value_render_option: render_option)
89
81
  end
90
82
 
91
83
  def sheet
@@ -95,7 +87,7 @@ module CSVPlusPlus
95
87
  end
96
88
 
97
89
  def fetch_spreadsheet!
98
- @spreadsheet = @gs.get_spreadsheet(@sheet_id)
90
+ @spreadsheet = @sheets_client.get_spreadsheet(@sheet_id)
99
91
 
100
92
  return unless @sheet_name.nil?
101
93
 
@@ -105,34 +97,23 @@ module CSVPlusPlus
105
97
  def create_sheet!
106
98
  return if sheet
107
99
 
108
- @gs.create_spreadsheet(@sheet_name)
109
- get_spreadsheet!
100
+ @sheets_client.create_spreadsheet(@sheet_name)
101
+ fetch_spreadsheet!
110
102
  @sheet_name = @spreadsheet.sheets.last.properties.title
111
103
  end
112
104
 
113
105
  def update_cells!(template)
114
- builder = ::CSVPlusPlus::Writer::GoogleSheetBuilder.new(
106
+ @sheets_client.batch_update_spreadsheet(@sheet_id, builder(template).batch_update_spreadsheet_request)
107
+ end
108
+
109
+ def builder(template)
110
+ ::CSVPlusPlus::Writer::GoogleSheetBuilder.new(
115
111
  rows: template.rows,
116
112
  sheet_id: sheet&.properties&.sheet_id,
117
113
  column_index: @options.offset[1],
118
114
  row_index: @options.offset[0],
119
- current_sheet_values: @current_sheet_values
115
+ current_sheet_values: @current_values
120
116
  )
121
- @gs.batch_update_spreadsheet(@sheet_id, builder.batch_update_spreadsheet_request)
122
- rescue ::Google::Apis::ClientError => e
123
- handle_google_error(e)
124
- end
125
-
126
- def sheets_ns
127
- ::Google::Apis::SheetsV4
128
- end
129
-
130
- def handle_google_error(error)
131
- if @options.verbose
132
- warn("#{error.status_code} Error making Google Sheets API request [#{error.message}]: #{error.body}")
133
- else
134
- warn("Error making Google Sheets API request: #{error.message}")
135
- end
136
117
  end
137
118
  end
138
119
  end
@@ -5,21 +5,29 @@ require_relative './rubyxl_modifier'
5
5
  module CSVPlusPlus
6
6
  module Writer
7
7
  # Build a RubyXL workbook formatted according to the given +rows+
8
+ #
9
+ # @attr_reader input_filename [String] The filename being written to
10
+ # @attr_reader rows [Array<Row>] The rows being written
8
11
  class RubyXLBuilder
9
- attr_reader :output_filename, :rows
12
+ attr_reader :input_filename, :rows
10
13
 
11
- # initialize
12
- def initialize(output_filename:, rows:, sheet_name:)
14
+ # @param input_filename [String] The file to write to
15
+ # @param rows [Array<Row>] The rows to write
16
+ # @param sheet_name [String] The name of the sheet within the workbook to write to
17
+ def initialize(input_filename:, rows:, sheet_name:)
13
18
  @rows = rows
14
- @output_filename = output_filename
15
- @workbook = open_workbook(sheet_name)
16
- @worksheet = @workbook[sheet_name]
19
+ @input_filename = input_filename
20
+ @sheet_name = sheet_name
17
21
  end
18
22
 
19
- # write the given @rows in +sheet_name+ to +@output_filename+
20
- def write
21
- build_workbook!
22
- @workbook.write(@output_filename)
23
+ # Build a +RubyXL::Workbook+ with the given +@rows+ in +sheet_name+
24
+ #
25
+ # @return [RubyXL::Workbook]
26
+ def build_workbook
27
+ open_workbook.tap do |workbook|
28
+ @worksheet = workbook[@sheet_name]
29
+ build_workbook!
30
+ end
23
31
  end
24
32
 
25
33
  private
@@ -96,14 +104,14 @@ module CSVPlusPlus
96
104
  end
97
105
  end
98
106
 
99
- def open_workbook(sheet_name)
100
- if ::File.exist?(@output_filename)
101
- ::RubyXL::Parser.parse(@output_filename).tap do |workbook|
102
- workbook.add_worksheet(sheet_name) unless workbook[sheet_name]
107
+ def open_workbook
108
+ if ::File.exist?(@input_filename)
109
+ ::RubyXL::Parser.parse(@input_filename).tap do |workbook|
110
+ workbook.add_worksheet(@sheet_name) unless workbook[@sheet_name]
103
111
  end
104
112
  else
105
113
  ::RubyXL::Workbook.new.tap do |workbook|
106
- workbook.worksheets[0].sheet_name = sheet_name
114
+ workbook.worksheets[0].sheet_name = @sheet_name
107
115
  end
108
116
  end
109
117
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- # Writer
5
4
  module Writer
6
5
  # Build a RubyXL-decorated Modifier class adds some support for Excel
7
6
  class RubyXLModifier < ::SimpleDelegator
@@ -32,21 +31,29 @@ module CSVPlusPlus
32
31
  }.freeze
33
32
  private_constant :BORDER_STYLES
34
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
+
35
45
  # The excel-specific number format code
46
+ #
47
+ # @return [String]
36
48
  def number_format_code
49
+ return unless numberformat
50
+
37
51
  ::RubyXL::NumberFormats::DEFAULT_NUMBER_FORMATS.find_by_format_id(
38
52
  # rubocop:disable Lint/ConstantResolution
39
53
  NUM_FMT_IDS[numberformat.to_sym]
40
54
  # rubocop:enable Lint/ConstantResolution
41
55
  ).format_code
42
56
  end
43
-
44
- # The excel-specific border weight
45
- def border_weight
46
- # rubocop:disable Lint/ConstantResolution
47
- BORDER_STYLES[borderstyle.to_sym]
48
- # rubocop:enable Lint/ConstantResolution
49
- end
50
57
  end
51
58
  end
52
59
  end
data/lib/csv_plus_plus.rb CHANGED
@@ -1,21 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'google/apis/drive_v3'
4
+ require 'google/apis/sheets_v4'
5
+ require 'googleauth'
6
+ require 'rubyXL'
7
+ require 'rubyXL/convenience_methods'
8
+
9
+ require_relative 'csv_plus_plus/cli'
3
10
  require_relative 'csv_plus_plus/error'
11
+ require_relative 'csv_plus_plus/language/builtins'
4
12
  require_relative 'csv_plus_plus/language/compiler'
13
+ require_relative 'csv_plus_plus/language/runtime'
5
14
  require_relative 'csv_plus_plus/options'
6
15
  require_relative 'csv_plus_plus/writer'
7
16
 
8
- # A language for writing rich CSV data
17
+ # A programming language for writing rich CSV files
9
18
  module CSVPlusPlus
10
19
  # Parse the input into a +Template+ and write it to the desired format
20
+ #
21
+ # @param input [String] The csvpp input to compile
22
+ # @param filename [String, nil] The filename the input was read from. +nil+ if it is read from stdin.
23
+ # @param options [Options] The various options to compile with
24
+ #
25
+ # rubocop:disable Metrics/MethodLength
11
26
  def self.apply_template_to_sheet!(input, filename, options)
12
27
  warn(options.verbose_summary) if options.verbose
13
28
 
14
- ::CSVPlusPlus::Language::Compiler.with_compiler(input:, filename:, options:) do |c|
15
- template = c.parse_template
29
+ ::CSVPlusPlus::Language::Compiler.with_compiler(
30
+ options:,
31
+ runtime: ::CSVPlusPlus::Language::Runtime.new(input:, filename:)
32
+ ) do |c|
33
+ template = c.compile_template
16
34
 
17
35
  output = ::CSVPlusPlus::Writer.writer(options)
18
- c.outputting! { output.write(template) }
36
+ c.outputting! do
37
+ output.write_backup if options.backup
38
+ output.write(template)
39
+ end
19
40
  end
20
41
  end
42
+ # rubocop:enable Metrics/MethodLength
21
43
  end