csv_plus_plus 0.0.5 → 0.1.1

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