csv_plus_plus 0.0.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +1 -0
  4. data/lib/csv_plus_plus/cell.rb +24 -8
  5. data/lib/csv_plus_plus/cli.rb +29 -16
  6. data/lib/csv_plus_plus/cli_flag.rb +10 -2
  7. data/lib/csv_plus_plus/code_section.rb +55 -3
  8. data/lib/csv_plus_plus/color.rb +19 -5
  9. data/lib/csv_plus_plus/google_options.rb +6 -2
  10. data/lib/csv_plus_plus/graph.rb +0 -1
  11. data/lib/csv_plus_plus/language/ast_builder.rb +68 -0
  12. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +65 -0
  13. data/lib/csv_plus_plus/language/builtins.rb +46 -0
  14. data/lib/csv_plus_plus/language/cell_value.tab.rb +106 -134
  15. data/lib/csv_plus_plus/language/code_section.tab.rb +163 -192
  16. data/lib/csv_plus_plus/language/compiler.rb +75 -92
  17. data/lib/csv_plus_plus/language/entities/boolean.rb +3 -2
  18. data/lib/csv_plus_plus/language/entities/cell_reference.rb +10 -3
  19. data/lib/csv_plus_plus/language/entities/entity.rb +20 -8
  20. data/lib/csv_plus_plus/language/entities/function.rb +6 -4
  21. data/lib/csv_plus_plus/language/entities/function_call.rb +17 -5
  22. data/lib/csv_plus_plus/language/entities/number.rb +6 -4
  23. data/lib/csv_plus_plus/language/entities/runtime_value.rb +9 -8
  24. data/lib/csv_plus_plus/language/entities/string.rb +6 -4
  25. data/lib/csv_plus_plus/language/references.rb +22 -5
  26. data/lib/csv_plus_plus/language/runtime.rb +80 -22
  27. data/lib/csv_plus_plus/language/scope.rb +34 -39
  28. data/lib/csv_plus_plus/language/syntax_error.rb +10 -5
  29. data/lib/csv_plus_plus/lexer/lexer.rb +27 -13
  30. data/lib/csv_plus_plus/lexer/tokenizer.rb +35 -11
  31. data/lib/csv_plus_plus/modifier.rb +38 -18
  32. data/lib/csv_plus_plus/modifier.tab.rb +2 -2
  33. data/lib/csv_plus_plus/options.rb +20 -2
  34. data/lib/csv_plus_plus/row.rb +15 -4
  35. data/lib/csv_plus_plus/template.rb +26 -6
  36. data/lib/csv_plus_plus/version.rb +1 -1
  37. data/lib/csv_plus_plus/writer/excel.rb +2 -9
  38. data/lib/csv_plus_plus/writer/file_backer_upper.rb +22 -20
  39. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +8 -10
  40. data/lib/csv_plus_plus/writer/google_sheets.rb +4 -10
  41. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +23 -15
  42. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +15 -8
  43. data/lib/csv_plus_plus.rb +42 -8
  44. metadata +5 -2
@@ -1,30 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
- require_relative './color'
5
- require_relative './expand'
6
- require_relative './language/syntax_error'
7
-
8
3
  module CSVPlusPlus
9
4
  # A container representing the operations that can be applied to a cell or row
10
5
  #
11
- # @attr expand [Expand]
12
- # @attr fontfamily [String]
13
- # @attr fontsize [String]
14
- # @attr halign ['left', 'center', 'right']
15
- # @attr valign ['top', 'center', 'bottom']
16
- # @attr note [String]
17
- # @attr numberformat [String]
18
- # @attr row_level [Boolean]
6
+ # @attr borders [Array<String>] The borders that will be set
7
+ # @attr expand [Expand] Whether this row expands into multiple rows
8
+ # @attr fontfamily [String] The font family
9
+ # @attr fontsize [Number] The font size
10
+ # @attr halign ['left', 'center', 'right'] Horizontal alignment
11
+ # @attr note [String] A note/comment on the cell
12
+ # @attr numberformat [String] A number format to apply to the value in the cell
13
+ # @attr row_level [Boolean] Is this a row modifier? If so it's values will apply to all cells in the row
14
+ # (unless overridden by the cell modifier)
19
15
  # @attr validation [Object]
16
+ # @attr valign ['top', 'center', 'bottom'] Vertical alignment
20
17
  #
21
- # @attr_writer borderstyle [String]
18
+ # @attr_writer borderstyle ['dashed', 'dotted', 'double', 'solid', 'solid_medium', 'solid_thick']
19
+ # The style of border on the cell
22
20
  #
23
21
  # @attr_reader bordercolor [String]
24
22
  # @attr_reader borders [Array<String>]
25
- # @attr_reader color [Color]
26
- # @attr_reader fontcolor [Color]
27
- # @attr_reader formats [Array<String>]
23
+ # @attr_reader color [Color] The background color of the cell
24
+ # @attr_reader fontcolor [Color] The font color of the cell
25
+ # @attr_reader formats [Array<String>] Bold/italics/underline/strikethrough formatting
28
26
  class Modifier
29
27
  attr_reader :bordercolor, :borders, :color, :fontcolor, :formats
30
28
  attr_writer :borderstyle
@@ -39,25 +37,32 @@ module CSVPlusPlus
39
37
  end
40
38
 
41
39
  # Set the color
40
+ #
42
41
  # @param hex_value [String]
42
+ #
43
+ # @return [Color]
43
44
  def color=(hex_value)
44
45
  @color = ::CSVPlusPlus::Color.new(hex_value)
45
46
  end
46
47
 
47
48
  # Assign a border
49
+ #
48
50
  # @param side ['top', 'left', 'bottom', 'right', 'all']
49
51
  def border=(side)
50
52
  @borders << side
51
53
  end
52
54
 
53
55
  # Does this have a border along +side+?
56
+ #
54
57
  # @param side ['top', 'left', 'bottom', 'right', 'all']
58
+ #
55
59
  # @return [Boolean]
56
60
  def border_along?(side)
57
61
  @borders.include?('all') || @borders.include?(side)
58
62
  end
59
63
 
60
64
  # Does this have a border along all sides?
65
+ #
61
66
  # @return [Boolean]
62
67
  def border_all?
63
68
  @borders.include?('all') \
@@ -65,65 +70,79 @@ module CSVPlusPlus
65
70
  end
66
71
 
67
72
  # Set the bordercolor
73
+ #
68
74
  # @param hex_value [String] formatted as '#000000', '#000' or '000000'
69
75
  def bordercolor=(hex_value)
70
76
  @bordercolor = ::CSVPlusPlus::Color.new(hex_value)
71
77
  end
72
78
 
73
79
  # Are there any borders set?
80
+ #
74
81
  # @return [Boolean]
75
82
  def any_border?
76
83
  !@borders.empty?
77
84
  end
78
85
 
79
86
  # Set the fontcolor
87
+ #
80
88
  # @param hex_value [String] formatted as '#000000', '#000' or '000000'
81
89
  def fontcolor=(hex_value)
82
90
  @fontcolor = ::CSVPlusPlus::Color.new(hex_value)
83
91
  end
84
92
 
85
93
  # Set a text format (bolid, italic, underline or strikethrough)
94
+ #
86
95
  # @param value ['bold', 'italic', 'underline', 'strikethrough']
87
96
  def format=(value)
88
97
  @formats << value
89
98
  end
90
99
 
91
100
  # Is the given format set?
101
+ #
92
102
  # @param type ['bold', 'italic', 'underline', 'strikethrough']
103
+ #
93
104
  # @return [Boolean]
94
105
  def formatted?(type)
95
106
  @formats.include?(type)
96
107
  end
97
108
 
98
109
  # Freeze the row from edits
110
+ #
111
+ # @return [true]
99
112
  def freeze!
100
113
  @frozen = true
101
114
  end
102
115
 
103
- # Is the row forzen?
116
+ # Is the row frozen?
117
+ #
104
118
  # @return [Boolean]
105
119
  def frozen?
106
120
  @frozen
107
121
  end
108
122
 
109
123
  # Mark this modifer as row-level
124
+ #
125
+ # @return [true]
110
126
  def row_level!
111
127
  @row_level = true
112
128
  end
113
129
 
114
130
  # Is this a row-level modifier?
131
+ #
115
132
  # @return [Boolean]
116
133
  def row_level?
117
134
  @row_level
118
135
  end
119
136
 
120
137
  # Is this a cell-level modifier?
138
+ #
121
139
  # @return [Boolean]
122
140
  def cell_level?
123
141
  !@row_level
124
142
  end
125
143
 
126
144
  # Style of border
145
+ #
127
146
  # @return [String]
128
147
  def borderstyle
129
148
  @borderstyle || 'solid'
@@ -137,6 +156,7 @@ module CSVPlusPlus
137
156
  end
138
157
 
139
158
  # Create a new modifier instance, with all values defaulted from +other+
159
+ #
140
160
  # @param other [Modifier]
141
161
  def take_defaults_from!(other)
142
162
  other.instance_variables.each do |property|
@@ -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
@@ -19,11 +28,18 @@ module CSVPlusPlus
19
28
  end
20
29
 
21
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
+ #
35
+ # @return [String]
22
36
  def google_sheet_id=(sheet_id)
23
37
  @google = ::CSVPlusPlus::GoogleOptions.new(sheet_id)
24
38
  end
25
39
 
26
40
  # Returns an error string or nil if there are no validation problems
41
+ #
42
+ # @return [String, nil]
27
43
  def validate
28
44
  return if @google || @output_filename
29
45
 
@@ -31,6 +47,8 @@ module CSVPlusPlus
31
47
  end
32
48
 
33
49
  # Return a string with a verbose description of what we're doing with the options
50
+ #
51
+ # @return [String]
34
52
  def verbose_summary
35
53
  <<~SUMMARY
36
54
  #{summary_divider}
@@ -55,7 +73,7 @@ module CSVPlusPlus
55
73
  SUMMARY
56
74
  end
57
75
 
58
- # to_s
76
+ # @return [String]
59
77
  def to_s
60
78
  "Options(create_if_not_exists: #{@create_if_not_exists}, google: #{@google}, key_values: #{@key_values}, " \
61
79
  "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,27 @@
2
2
 
3
3
  module CSVPlusPlus
4
4
  # Contains the flow and data from a code section and CSV section
5
+ #
6
+ # @attr_reader code_section [CodeSection] The +CodeSection+ containing the functions and variables defined herein
7
+ # @attr_reader rows [Array<Row>] The +Row+s that comprise this +Template+
5
8
  class Template
6
- attr_reader :rows, :scope
9
+ attr_reader :code_section, :rows
7
10
 
8
- # initialize
9
- def initialize(rows:, scope:)
11
+ # @param code_section [CodeSection] The +CodeSection+ containing the functions and variables
12
+ # @param rows [Array<Row>] The +Row+s that comprise this +Template+
13
+ def initialize(code_section:, rows:)
14
+ @code_section = code_section
10
15
  @rows = rows
11
- @scope = scope
12
16
  end
13
17
 
14
- # to_s
18
+ # @return [String]
15
19
  def to_s
16
- "Template(rows: #{@rows}, scope: #{@scope})"
20
+ "Template(rows: #{@rows})"
17
21
  end
18
22
 
19
23
  # Apply any expand= modifiers to the parsed template
24
+ #
25
+ # @return [Array<Row>]
20
26
  def expand_rows!
21
27
  expanded_rows = []
22
28
  row_index = 0
@@ -32,6 +38,8 @@ module CSVPlusPlus
32
38
  end
33
39
 
34
40
  # Make sure that the template has a valid amount of infinite expand modifiers
41
+ #
42
+ # @param runtime [Runtime] The compiler's current runtime
35
43
  def validate_infinite_expands(runtime)
36
44
  infinite_expand_rows = @rows.filter { |r| r.modifier.expand&.infinite? }
37
45
  return unless infinite_expand_rows.length > 1
@@ -42,6 +50,18 @@ module CSVPlusPlus
42
50
  )
43
51
  end
44
52
 
53
+ # Provide a summary of the state of the template (and it's +@code_section+)
54
+ #
55
+ # @return [String]
56
+ def verbose_summary
57
+ # TODO: we can probably include way more stats in here
58
+ <<~SUMMARY
59
+ #{@code_section.verbose_summary}
60
+
61
+ > #{@rows.length} rows to be written
62
+ SUMMARY
63
+ end
64
+
45
65
  private
46
66
 
47
67
  def expand_rows(push_row_fn)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- VERSION = '0.0.5'
4
+ VERSION = '0.1.1'
5
5
  public_constant :VERSION
6
6
  end
@@ -12,17 +12,10 @@ module CSVPlusPlus
12
12
  # write the +template+ to an Excel file
13
13
  def write(template)
14
14
  ::CSVPlusPlus::Writer::RubyXLBuilder.new(
15
- output_filename: @options.output_filename,
15
+ input_filename: @options.output_filename,
16
16
  rows: template.rows,
17
17
  sheet_name: @options.sheet_name
18
- ).write
19
- end
20
-
21
- protected
22
-
23
- def load_requires
24
- require('rubyXL')
25
- require('rubyXL/convenience_methods')
18
+ ).build_workbook.write(@options.output_filename)
26
19
  end
27
20
  end
28
21
  end
@@ -5,41 +5,43 @@ require 'pathname'
5
5
 
6
6
  module CSVPlusPlus
7
7
  module Writer
8
- # A mixin that can
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)
9
10
  module FileBackerUpper
10
- # we don't want to include a bunch of second/millisecond stuff in the filename unless we
11
+ # I don't want to include a bunch of second/millisecond stuff in the filename unless we
11
12
  # really need to. so try a less specifically formatted filename then get more specific
12
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
13
14
  private_constant :DESIRED_BACKUP_FORMATS
14
15
 
15
16
  # Assuming the underlying spreadsheet is file-based, create a backup of it
16
- # rubocop:disable Metrics/MethodLength
17
17
  def write_backup
18
18
  return unless ::File.exist?(@options.output_filename)
19
19
 
20
- attempted = []
21
- backed_up_to = nil
20
+ # TODO: also don't do anything if the current backups contents haven't changed (do a md5sum or something)
22
21
 
23
- # rubocop:disable Lint/ConstantResolution
24
- DESIRED_BACKUP_FORMATS.find do |file_format|
25
- # rubocop:enable Lint/ConstantResolution
26
- filename = format_backup_filename(file_format)
27
- attempted << filename
28
- backed_up_to = backup(filename)
29
-
30
- break if backed_up_to
31
- end
32
-
33
- unless backed_up_to
34
- raise(::CSVPlusPlus::Error, "Unable to write backup file despite trying these: #{attempted.join(', ')}")
22
+ attempt_backups.tap do |backed_up_to|
23
+ warn("Backed up #{@options.output_filename} to #{backed_up_to}") if @options.verbose
35
24
  end
36
-
37
- warn("Backed up #{@options.output_filename} to #{backed_up_to}") if @options.verbose
38
25
  end
39
- # rubocop:enable Metrics/MethodLength
40
26
 
41
27
  private
42
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
+
43
45
  def backup(filename)
44
46
  return if ::File.exist?(filename)
45
47
 
@@ -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
@@ -12,7 +12,7 @@ module CSVPlusPlus
12
12
  SPREADSHEET_INFINITY = 1000
13
13
  public_constant :SPREADSHEET_INFINITY
14
14
 
15
- # initialize
15
+ # @param options [Options]
16
16
  def initialize(options)
17
17
  super(options)
18
18
 
@@ -21,6 +21,8 @@ module CSVPlusPlus
21
21
  end
22
22
 
23
23
  # write a +template+ to Google Sheets
24
+ #
25
+ # @param template [Template]
24
26
  def write(template)
25
27
  @sheets_client = ::CSVPlusPlus::GoogleApiClient.sheets_client
26
28
 
@@ -38,14 +40,6 @@ module CSVPlusPlus
38
40
  drive_client.copy_file(@sheet_id)
39
41
  end
40
42
 
41
- protected
42
-
43
- def load_requires
44
- require('google/apis/drive_v3')
45
- require('google/apis/sheets_v4')
46
- require('googleauth')
47
- end
48
-
49
43
  private
50
44
 
51
45
  def format_range(range)
@@ -118,7 +112,7 @@ module CSVPlusPlus
118
112
  sheet_id: sheet&.properties&.sheet_id,
119
113
  column_index: @options.offset[1],
120
114
  row_index: @options.offset[0],
121
- current_sheet_values: @current_sheet_values
115
+ current_sheet_values: @current_values
122
116
  )
123
117
  end
124
118
  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