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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +1 -0
- data/lib/csv_plus_plus/cell.rb +24 -8
- data/lib/csv_plus_plus/cli.rb +29 -16
- data/lib/csv_plus_plus/cli_flag.rb +10 -2
- data/lib/csv_plus_plus/code_section.rb +55 -3
- data/lib/csv_plus_plus/color.rb +19 -5
- data/lib/csv_plus_plus/google_options.rb +6 -2
- data/lib/csv_plus_plus/graph.rb +0 -1
- data/lib/csv_plus_plus/language/ast_builder.rb +68 -0
- data/lib/csv_plus_plus/language/benchmarked_compiler.rb +65 -0
- data/lib/csv_plus_plus/language/builtins.rb +46 -0
- data/lib/csv_plus_plus/language/cell_value.tab.rb +106 -134
- data/lib/csv_plus_plus/language/code_section.tab.rb +163 -192
- data/lib/csv_plus_plus/language/compiler.rb +75 -92
- data/lib/csv_plus_plus/language/entities/boolean.rb +3 -2
- data/lib/csv_plus_plus/language/entities/cell_reference.rb +10 -3
- data/lib/csv_plus_plus/language/entities/entity.rb +20 -8
- data/lib/csv_plus_plus/language/entities/function.rb +6 -4
- data/lib/csv_plus_plus/language/entities/function_call.rb +17 -5
- data/lib/csv_plus_plus/language/entities/number.rb +6 -4
- data/lib/csv_plus_plus/language/entities/runtime_value.rb +9 -8
- data/lib/csv_plus_plus/language/entities/string.rb +6 -4
- data/lib/csv_plus_plus/language/references.rb +22 -5
- data/lib/csv_plus_plus/language/runtime.rb +80 -22
- data/lib/csv_plus_plus/language/scope.rb +34 -39
- data/lib/csv_plus_plus/language/syntax_error.rb +10 -5
- data/lib/csv_plus_plus/lexer/lexer.rb +27 -13
- data/lib/csv_plus_plus/lexer/tokenizer.rb +35 -11
- data/lib/csv_plus_plus/modifier.rb +38 -18
- data/lib/csv_plus_plus/modifier.tab.rb +2 -2
- data/lib/csv_plus_plus/options.rb +20 -2
- data/lib/csv_plus_plus/row.rb +15 -4
- data/lib/csv_plus_plus/template.rb +26 -6
- data/lib/csv_plus_plus/version.rb +1 -1
- data/lib/csv_plus_plus/writer/excel.rb +2 -9
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +22 -20
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +8 -10
- data/lib/csv_plus_plus/writer/google_sheets.rb +4 -10
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +23 -15
- data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +15 -8
- data/lib/csv_plus_plus.rb +42 -8
- 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
|
12
|
-
# @attr
|
13
|
-
# @attr
|
14
|
-
# @attr
|
15
|
-
# @attr
|
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 [
|
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
|
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
|
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
|
-
#
|
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})"
|
data/lib/csv_plus_plus/row.rb
CHANGED
@@ -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
|
-
#
|
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
|
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
|
-
#
|
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 :
|
9
|
+
attr_reader :code_section, :rows
|
7
10
|
|
8
|
-
#
|
9
|
-
|
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
|
-
#
|
18
|
+
# @return [String]
|
15
19
|
def to_s
|
16
|
-
"Template(rows: #{@rows}
|
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)
|
@@ -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
|
-
|
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
|
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
|
-
#
|
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
|
-
|
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
|
-
|
24
|
-
|
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
|
-
#
|
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
|
51
|
-
cf.vertical_alignment = mod.valign
|
52
|
-
|
53
|
-
cf.
|
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
|
-
@
|
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
|
-
#
|
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: @
|
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 :
|
12
|
+
attr_reader :input_filename, :rows
|
10
13
|
|
11
|
-
#
|
12
|
-
|
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
|
-
@
|
15
|
-
@
|
16
|
-
@worksheet = @workbook[sheet_name]
|
19
|
+
@input_filename = input_filename
|
20
|
+
@sheet_name = sheet_name
|
17
21
|
end
|
18
22
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
100
|
-
if ::File.exist?(@
|
101
|
-
::RubyXL::Parser.parse(@
|
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
|