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