csv_plus_plus 0.0.4 → 0.1.0

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