csv_plus_plus 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea5b029c524b401348cc42399514c9fb689b13bfcf7298664429f69c85ae6607
4
- data.tar.gz: 5bb79395742dcd89bf3b4ca7ede92a9a41a2c3ddefc4dff1b4232c285117373d
3
+ metadata.gz: c4f5c9b5a342102fa8e1c31b319fb05f19ad55278bc633435129254b5b2511a8
4
+ data.tar.gz: 0c2656d4d7b22d0b3b3311745ed5a70b789277c622f0f2d09d2b8a144de71a8d
5
5
  SHA512:
6
- metadata.gz: 4a1bf4ccf98486b64ed69f34e701e9c4c69aea2b971c569aab7f6345ca721ccbde01570449033be23eeea37ec59d244dbb2692f27bfbfaae6069c84e23ea988a
7
- data.tar.gz: 8cef1aa204255588787b3e3138ec282a9b45c174b83e647a91d0476aca199ee4ee8640f11c40418cded6026016a4383e1e1df7600cdfa5c5524d25a6c953fb76
6
+ metadata.gz: 90965e2275cc7988f4054b59e30523cd645b21c1adb89797828e86a767217b09ac174bbc17094604dd608b93defd79f4c9a74ccf7e2f20dd17995f8c1b5dadac
7
+ data.tar.gz: 291011896232e88a7bd0e252887459986812d2877c1d0383458250e1c30cf735d8b2bdaf5dd759f44fa5b8b33b08f74d780df7960622ab774e9f6d856628508f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## v0.0.5
2
+
3
+ - Support the --backup/-b option
4
+ - bin/csvpp (which does the same thing as bin/csv++ but will work better on other filesystems)
5
+ - Fix links in gemspec (which end up on rubygems.org)
6
+ - docs & tests
7
+
1
8
  ## v0.0.4
2
9
 
3
10
  - Excel support
data/README.md CHANGED
@@ -21,12 +21,17 @@ def profit() (price * quantity) - fees
21
21
  ![[expand]],[[format=bold]],,,"=PROFIT()",$$fees
22
22
  ```
23
23
 
24
- ## Predefined Variables
24
+ ## Variables
25
25
 
26
- * `$$rownum` - The current row number. The first row of the spreadsheet starts at 1
26
+ Variables can be defined in the code section by giving a name (a combination of letters, numbers and underscores ) the expression `:=` and followed with a value.
27
27
 
28
- ## Predefined Functions
28
+ ### Built-in Variables
29
29
 
30
+ * `$$rownum` - The current row number. The first row of the spreadsheet starts at 1. Can be used anywhere and it's value will evaluate to the current row being processed.
31
+
32
+ ## Functions
33
+
34
+ ### Built-in Functions
30
35
  * `cellref(CELL)` - Returns a reference to the `CELL` relative to the current row. If the current `$$rownum` is `2`, then `CELLREF("C")` returns a reference to cell `C2`.
31
36
 
32
37
  ## Modifiers
data/bin/csv++ CHANGED
@@ -1,42 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'optparse'
5
4
  require_relative '../lib/csv_plus_plus'
6
5
 
7
- options = ::CSVPlusPlus::Options.new
8
-
9
- option_parser =
10
- ::OptionParser.new do |parser|
11
- parser.on('-h', '--help', 'Show help information') do
12
- puts(parser)
13
- exit
14
- end
15
-
16
- ::SUPPORTED_CSVPP_FLAGS.each do |flag|
17
- parser.on(flag.short_flag, flag.long_flag, flag.description) do |v|
18
- flag.handler.call(options, v)
19
- end
20
- end
21
- end
22
-
23
- option_parser.parse!
24
-
25
- error_message = options.validate
26
- unless error_message.nil?
27
- warn(error_message)
28
- puts(option_parser)
29
- exit(1)
30
- end
31
-
32
- begin
33
- ::CSVPlusPlus.apply_template_to_sheet!(::ARGF.read, ::ARGF.filename, options)
34
- rescue ::CSVPlusPlus::Error => e
35
- if e.is_a?(::CSVPlusPlus::Language::SyntaxError)
36
- warn(options.verbose ? e.to_verbose_trace : e.to_trace)
37
- else
38
- warn(e.message)
39
- end
40
-
41
- exit(1)
42
- end
6
+ ::CSVPlusPlus::CLI.launch_compiler!
data/bin/csvpp ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/csv_plus_plus'
5
+
6
+ ::CSVPlusPlus::CLI.launch_compiler!
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ module CSVPlusPlus
6
+ # Handle running the application with the given CLI flags
7
+ class CLI
8
+ # handle any CLI flags and launch the compiler
9
+ def self.launch_compiler!
10
+ cli = new
11
+ cli.compile!
12
+ rescue ::StandardError => e
13
+ cli.handle_error(e)
14
+ exit(1)
15
+ end
16
+
17
+ # initialize
18
+ def initialize
19
+ parse_options!
20
+ end
21
+
22
+ # compile the given template, using the given CLI flags
23
+ def compile!
24
+ ::CSVPlusPlus.apply_template_to_sheet!(::ARGF.read, ::ARGF.filename, @options)
25
+ end
26
+
27
+ # (nicely) handle a given error. how it's handled depends on if it's our error and if @options.verbose
28
+ def handle_error(error)
29
+ case error
30
+ when ::CSVPlusPlus::Error
31
+ handle_internal_error(error)
32
+ when ::Google::Apis::ClientError
33
+ handle_google_error(error)
34
+ else
35
+ # TODO: more if verbose?
36
+ warn(error.message)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def handle_internal_error(error)
43
+ if error.is_a?(::CSVPlusPlus::Language::SyntaxError)
44
+ warn(@options.verbose ? error.to_verbose_trace : error.to_trace)
45
+ else
46
+ warn(error.message)
47
+ end
48
+ end
49
+
50
+ def handle_google_error(error)
51
+ warn("Error making Google Sheets API request: #{error.message}")
52
+ return unless @options.verbose
53
+
54
+ warn("#{error.status_code} Error making Google API request [#{error.message}]: #{error.body}")
55
+ end
56
+
57
+ def parse_options!
58
+ @options = ::CSVPlusPlus::Options.new
59
+ option_parser.parse!
60
+ validate_options
61
+ end
62
+
63
+ def validate_options
64
+ error_message = @options.validate
65
+ return if error_message.nil?
66
+
67
+ puts(option_parser)
68
+ raise(::CSVPlusPlus::Error, error_message)
69
+ end
70
+
71
+ def option_parser
72
+ ::OptionParser.new do |parser|
73
+ parser.on('-h', '--help', 'Show help information') do
74
+ puts(parser)
75
+ exit
76
+ end
77
+
78
+ ::SUPPORTED_CSVPP_FLAGS.each do |f|
79
+ parser.on(f.short_flag, f.long_flag, f.description) { |v| f.handler.call(@options, v) }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CSVPlusPlus
4
+ # A convenience wrapper around Google's REST API client
5
+ module GoogleApiClient
6
+ # Get a +::Google::Apis::SheetsV4::SheetsService+ instance connected to the sheets API
7
+ def self.sheets_client
8
+ ::Google::Apis::SheetsV4::SheetsService.new.tap do |s|
9
+ s.authorization = ::Google::Auth.get_application_default(['https://www.googleapis.com/auth/spreadsheets'].freeze)
10
+ end
11
+ end
12
+
13
+ # Get a +::Google::Apis::DriveV3::DriveService+ instance connected to the drive API
14
+ def self.drive_client
15
+ ::Google::Apis::DriveV3::DriveService.new.tap do |d|
16
+ d.authorization = ::Google::Auth.get_application_default(['https://www.googleapis.com/auth/drive.file'].freeze)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -5,24 +5,24 @@ require_relative './entity'
5
5
  module CSVPlusPlus
6
6
  module Language
7
7
  module Entities
8
- ##
9
8
  # A boolean value
10
9
  class Boolean < Entity
11
10
  attr_reader :value
12
11
 
13
12
  # initialize
13
+ # @param value [String, Boolean]
14
14
  def initialize(value)
15
15
  super(:boolean)
16
16
  # TODO: probably can do a lot better in general on type validation
17
17
  @value = value.is_a?(::String) ? (value.downcase == 'true') : value
18
18
  end
19
19
 
20
- # to_s
20
+ # @return [String]
21
21
  def to_s
22
22
  @value.to_s.upcase
23
23
  end
24
24
 
25
- # ==
25
+ # @return [Boolean]
26
26
  def ==(other)
27
27
  super && value == other.value
28
28
  end
@@ -9,13 +9,14 @@ module CSVPlusPlus
9
9
  class Entity
10
10
  attr_reader :id, :type
11
11
 
12
- # initialize
12
+ # @param type [String, Symbol]
13
+ # @param id [String]
13
14
  def initialize(type, id: nil)
14
15
  @type = type.to_sym
15
16
  @id = id.downcase.to_sym if id
16
17
  end
17
18
 
18
- # ==
19
+ # @return [Boolean]
19
20
  def ==(other)
20
21
  self.class == other.class && @type == other.type && @id == other.id
21
22
  end
@@ -30,7 +31,8 @@ module CSVPlusPlus
30
31
  end
31
32
  end
32
33
 
33
- # support predicates by type
34
+ # Respond to predicates by type (entity.boolean?, entity.string?, etc)
35
+ # @return [Boolean]
34
36
  def respond_to_missing?(method_name, *_arguments)
35
37
  (method_name =~ /^(\w+)\?$/ && a_type?(::Regexp.last_match(1))) || super
36
38
  end
@@ -42,17 +44,19 @@ module CSVPlusPlus
42
44
  end
43
45
  end
44
46
 
45
- # An entity that can take arguments
47
+ # An entity that can take other entities as arguments
46
48
  class EntityWithArguments < Entity
47
49
  attr_reader :arguments
48
50
 
49
- # initialize
51
+ # @param type [String, Symbol]
52
+ # @param id [String]
53
+ # @param arguments [Array<Entity>]
50
54
  def initialize(type, id: nil, arguments: [])
51
55
  super(type, id:)
52
56
  @arguments = arguments
53
57
  end
54
58
 
55
- # ==
59
+ # @return [Boolean]
56
60
  def ==(other)
57
61
  super && @arguments == other.arguments
58
62
  end
@@ -7,12 +7,30 @@ require_relative './language/syntax_error'
7
7
 
8
8
  module CSVPlusPlus
9
9
  # A container representing the operations that can be applied to a cell or row
10
+ #
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]
19
+ # @attr validation [Object]
20
+ #
21
+ # @attr_writer borderstyle [String]
22
+ #
23
+ # @attr_reader bordercolor [String]
24
+ # @attr_reader borders [Array<String>]
25
+ # @attr_reader color [Color]
26
+ # @attr_reader fontcolor [Color]
27
+ # @attr_reader formats [Array<String>]
10
28
  class Modifier
11
29
  attr_reader :bordercolor, :borders, :color, :fontcolor, :formats
12
30
  attr_writer :borderstyle
13
31
  attr_accessor :expand, :fontfamily, :fontsize, :halign, :valign, :note, :numberformat, :row_level, :validation
14
32
 
15
- # initialize
33
+ # @param row_level [Boolean] Whether or not this modifier applies to the entire row
16
34
  def initialize(row_level: false)
17
35
  @row_level = row_level
18
36
  @freeze = false
@@ -20,47 +38,59 @@ module CSVPlusPlus
20
38
  @formats = ::Set.new
21
39
  end
22
40
 
23
- # Set the color. hex_value is a String
41
+ # Set the color
42
+ # @param hex_value [String]
24
43
  def color=(hex_value)
25
44
  @color = ::CSVPlusPlus::Color.new(hex_value)
26
45
  end
27
46
 
28
- # Assign a border. +side+ must be 'top', 'left', 'bottom', 'right' or 'all'
47
+ # Assign a border
48
+ # @param side ['top', 'left', 'bottom', 'right', 'all']
29
49
  def border=(side)
30
50
  @borders << side
31
51
  end
32
52
 
33
53
  # Does this have a border along +side+?
54
+ # @param side ['top', 'left', 'bottom', 'right', 'all']
55
+ # @return [Boolean]
34
56
  def border_along?(side)
35
- border_all? || @borders.include?(side)
57
+ @borders.include?('all') || @borders.include?(side)
36
58
  end
37
59
 
38
60
  # Does this have a border along all sides?
61
+ # @return [Boolean]
39
62
  def border_all?
40
- @borders.include?('all')
63
+ @borders.include?('all') \
64
+ || (border_along?('top') && border_along?('bottom') && border_along?('left') && border_along?('right'))
41
65
  end
42
66
 
43
67
  # Set the bordercolor
68
+ # @param hex_value [String] formatted as '#000000', '#000' or '000000'
44
69
  def bordercolor=(hex_value)
45
70
  @bordercolor = ::CSVPlusPlus::Color.new(hex_value)
46
71
  end
47
72
 
48
73
  # Are there any borders set?
74
+ # @return [Boolean]
49
75
  def any_border?
50
76
  !@borders.empty?
51
77
  end
52
78
 
53
79
  # Set the fontcolor
80
+ # @param hex_value [String] formatted as '#000000', '#000' or '000000'
54
81
  def fontcolor=(hex_value)
55
82
  @fontcolor = ::CSVPlusPlus::Color.new(hex_value)
56
83
  end
57
84
 
58
- # Set a format. +type+ must be 'bold', 'italic', 'underline' or 'strikethrough'
85
+ # Set a text format (bolid, italic, underline or strikethrough)
86
+ # @param value ['bold', 'italic', 'underline', 'strikethrough']
59
87
  def format=(value)
60
88
  @formats << value
61
89
  end
62
90
 
63
91
  # Is the given format set?
92
+ # @param type ['bold', 'italic', 'underline', 'strikethrough']
93
+ # @return [Boolean]
64
94
  def formatted?(type)
65
95
  @formats.include?(type)
66
96
  end
@@ -71,6 +101,7 @@ module CSVPlusPlus
71
101
  end
72
102
 
73
103
  # Is the row forzen?
104
+ # @return [Boolean]
74
105
  def frozen?
75
106
  @frozen
76
107
  end
@@ -81,21 +112,24 @@ module CSVPlusPlus
81
112
  end
82
113
 
83
114
  # Is this a row-level modifier?
115
+ # @return [Boolean]
84
116
  def row_level?
85
117
  @row_level
86
118
  end
87
119
 
88
120
  # Is this a cell-level modifier?
121
+ # @return [Boolean]
89
122
  def cell_level?
90
123
  !@row_level
91
124
  end
92
125
 
93
126
  # Style of border
127
+ # @return [String]
94
128
  def borderstyle
95
129
  @borderstyle || 'solid'
96
130
  end
97
131
 
98
- # to_s
132
+ # @return [String]
99
133
  def to_s
100
134
  # TODO... I dunno, not sure how to manage this
101
135
  "Modifier(row_level: #{@row_level} halign: #{@halign} valign: #{@valign} format: #{@formats} " \
@@ -103,8 +137,12 @@ module CSVPlusPlus
103
137
  end
104
138
 
105
139
  # Create a new modifier instance, with all values defaulted from +other+
140
+ # @param other [Modifier]
106
141
  def take_defaults_from!(other)
107
142
  other.instance_variables.each do |property|
143
+ # don't propagate row-specific values
144
+ next if property == :@row_level
145
+
108
146
  value = other.instance_variable_get(property)
109
147
  instance_variable_set(property, value.clone)
110
148
  end
@@ -15,7 +15,6 @@ module CSVPlusPlus
15
15
  @create_if_not_exists = false
16
16
  @key_values = {}
17
17
  @verbose = false
18
- # TODO: switch to true? probably a safer choice
19
18
  @backup = false
20
19
  end
21
20
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  public_constant :VERSION
6
6
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module CSVPlusPlus
4
4
  module Writer
5
- ##
6
5
  # Some shared functionality that all Writers should build on
7
6
  class BaseWriter
8
7
  attr_accessor :options
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './file_backer_upper'
4
+
3
5
  module CSVPlusPlus
4
6
  module Writer
5
- ##
6
7
  # A class that can output a +Template+ to CSV
7
8
  class CSV < ::CSVPlusPlus::Writer::BaseWriter
9
+ include ::CSVPlusPlus::Writer::FileBackerUpper
10
+
8
11
  # write a +template+ to CSV
9
12
  def write(template)
10
13
  # TODO: also read it and merge the results
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './file_backer_upper'
3
4
  require_relative './rubyxl_builder'
4
5
 
5
6
  module CSVPlusPlus
6
7
  module Writer
7
8
  # A class that can output a +Template+ to an Excel file
8
9
  class Excel < ::CSVPlusPlus::Writer::BaseWriter
10
+ include ::CSVPlusPlus::Writer::FileBackerUpper
11
+
9
12
  # write the +template+ to an Excel file
10
13
  def write(template)
11
14
  ::CSVPlusPlus::Writer::RubyXLBuilder.new(
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'pathname'
5
+
6
+ module CSVPlusPlus
7
+ module Writer
8
+ # A mixin that can
9
+ module FileBackerUpper
10
+ # we don't want to include a bunch of second/millisecond stuff in the filename unless we
11
+ # really need to. so try a less specifically formatted filename then get more specific
12
+ 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
+ private_constant :DESIRED_BACKUP_FORMATS
14
+
15
+ # Assuming the underlying spreadsheet is file-based, create a backup of it
16
+ # rubocop:disable Metrics/MethodLength
17
+ def write_backup
18
+ return unless ::File.exist?(@options.output_filename)
19
+
20
+ attempted = []
21
+ backed_up_to = nil
22
+
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(', ')}")
35
+ end
36
+
37
+ warn("Backed up #{@options.output_filename} to #{backed_up_to}") if @options.verbose
38
+ end
39
+ # rubocop:enable Metrics/MethodLength
40
+
41
+ private
42
+
43
+ def backup(filename)
44
+ return if ::File.exist?(filename)
45
+
46
+ ::FileUtils.cp(@options.output_filename, filename)
47
+ filename
48
+ end
49
+
50
+ def format_backup_filename(file_format)
51
+ pn = ::Pathname.new(@options.output_filename)
52
+ pn.sub_ext("-#{::Time.now.strftime(file_format)}" + pn.extname)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,16 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../google_api_client'
3
4
  require_relative 'base_writer'
4
5
  require_relative 'google_sheet_builder'
5
6
 
6
- AUTH_SCOPES = ['https://www.googleapis.com/auth/spreadsheets'].freeze
7
- FULL_RANGE = 'A1:Z1000'
8
-
9
7
  module CSVPlusPlus
10
8
  module Writer
11
- # A class that can output a +Template+ to Google Sheets (via their API)
9
+ # A class that can write a +Template+ to Google Sheets (via their API)
12
10
  class GoogleSheets < ::CSVPlusPlus::Writer::BaseWriter
13
- # XXX it would be nice to raise this but we shouldn't expand out more than necessary for our data
11
+ # TODO: it would be nice to raise this but we shouldn't expand out more than necessary for our data
14
12
  SPREADSHEET_INFINITY = 1000
15
13
  public_constant :SPREADSHEET_INFINITY
16
14
 
@@ -24,7 +22,7 @@ module CSVPlusPlus
24
22
 
25
23
  # write a +template+ to Google Sheets
26
24
  def write(template)
27
- auth!
25
+ @sheets_client = ::CSVPlusPlus::GoogleApiClient.sheets_client
28
26
 
29
27
  fetch_spreadsheet!
30
28
  fetch_spreadsheet_values!
@@ -32,13 +30,18 @@ module CSVPlusPlus
32
30
  create_sheet! if @options.create_if_not_exists
33
31
 
34
32
  update_cells!(template)
35
- rescue ::Google::Apis::ClientError => e
36
- handle_google_error(e)
33
+ end
34
+
35
+ # write a backup of the google sheet
36
+ def write_backup
37
+ drive_client = ::CSVPlusPlus::GoogleApiClient.drive_client
38
+ drive_client.copy_file(@sheet_id)
37
39
  end
38
40
 
39
41
  protected
40
42
 
41
43
  def load_requires
44
+ require('google/apis/drive_v3')
42
45
  require('google/apis/sheets_v4')
43
46
  require('googleauth')
44
47
  end
@@ -50,12 +53,7 @@ module CSVPlusPlus
50
53
  end
51
54
 
52
55
  def full_range
53
- format_range(::FULL_RANGE)
54
- end
55
-
56
- def auth!
57
- @gs ||= sheets_ns::SheetsService.new
58
- @gs.authorization = ::Google::Auth.get_application_default(::AUTH_SCOPES)
56
+ format_range('A1:Z1000')
59
57
  end
60
58
 
61
59
  def fetch_spreadsheet_values!
@@ -85,7 +83,7 @@ module CSVPlusPlus
85
83
  end
86
84
 
87
85
  def get_all_spreadsheet_values(render_option)
88
- @gs.get_spreadsheet_values(@sheet_id, full_range, value_render_option: render_option)
86
+ @sheets_client.get_spreadsheet_values(@sheet_id, full_range, value_render_option: render_option)
89
87
  end
90
88
 
91
89
  def sheet
@@ -95,7 +93,7 @@ module CSVPlusPlus
95
93
  end
96
94
 
97
95
  def fetch_spreadsheet!
98
- @spreadsheet = @gs.get_spreadsheet(@sheet_id)
96
+ @spreadsheet = @sheets_client.get_spreadsheet(@sheet_id)
99
97
 
100
98
  return unless @sheet_name.nil?
101
99
 
@@ -105,34 +103,23 @@ module CSVPlusPlus
105
103
  def create_sheet!
106
104
  return if sheet
107
105
 
108
- @gs.create_spreadsheet(@sheet_name)
109
- get_spreadsheet!
106
+ @sheets_client.create_spreadsheet(@sheet_name)
107
+ fetch_spreadsheet!
110
108
  @sheet_name = @spreadsheet.sheets.last.properties.title
111
109
  end
112
110
 
113
111
  def update_cells!(template)
114
- builder = ::CSVPlusPlus::Writer::GoogleSheetBuilder.new(
112
+ @sheets_client.batch_update_spreadsheet(@sheet_id, builder(template).batch_update_spreadsheet_request)
113
+ end
114
+
115
+ def builder(template)
116
+ ::CSVPlusPlus::Writer::GoogleSheetBuilder.new(
115
117
  rows: template.rows,
116
118
  sheet_id: sheet&.properties&.sheet_id,
117
119
  column_index: @options.offset[1],
118
120
  row_index: @options.offset[0],
119
121
  current_sheet_values: @current_sheet_values
120
122
  )
121
- @gs.batch_update_spreadsheet(@sheet_id, builder.batch_update_spreadsheet_request)
122
- rescue ::Google::Apis::ClientError => e
123
- handle_google_error(e)
124
- end
125
-
126
- def sheets_ns
127
- ::Google::Apis::SheetsV4
128
- end
129
-
130
- def handle_google_error(error)
131
- if @options.verbose
132
- warn("#{error.status_code} Error making Google Sheets API request [#{error.message}]: #{error.body}")
133
- else
134
- warn("Error making Google Sheets API request: #{error.message}")
135
- end
136
123
  end
137
124
  end
138
125
  end
data/lib/csv_plus_plus.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'csv_plus_plus/cli'
3
4
  require_relative 'csv_plus_plus/error'
4
5
  require_relative 'csv_plus_plus/language/compiler'
5
6
  require_relative 'csv_plus_plus/options'
@@ -15,7 +16,10 @@ module CSVPlusPlus
15
16
  template = c.parse_template
16
17
 
17
18
  output = ::CSVPlusPlus::Writer.writer(options)
18
- c.outputting! { output.write(template) }
19
+ c.outputting! do
20
+ output.write_backup if options.backup
21
+ output.write(template)
22
+ end
19
23
  end
20
24
  end
21
25
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_plus_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Carroll
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-16 00:00:00.000000000 Z
11
+ date: 2023-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: google-apis-drive_v3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: google-apis-sheets_v4
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -100,19 +114,23 @@ description: "A programming language built on top of CSV. You can define functi
100
114
  email: patrick@patrickomatic.com
101
115
  executables:
102
116
  - csv++
117
+ - csvpp
103
118
  extensions: []
104
119
  extra_rdoc_files: []
105
120
  files:
106
121
  - CHANGELOG.md
107
122
  - README.md
108
123
  - bin/csv++
124
+ - bin/csvpp
109
125
  - lib/csv_plus_plus.rb
110
126
  - lib/csv_plus_plus/cell.rb
127
+ - lib/csv_plus_plus/cli.rb
111
128
  - lib/csv_plus_plus/cli_flag.rb
112
129
  - lib/csv_plus_plus/code_section.rb
113
130
  - lib/csv_plus_plus/color.rb
114
131
  - lib/csv_plus_plus/error.rb
115
132
  - lib/csv_plus_plus/expand.rb
133
+ - lib/csv_plus_plus/google_api_client.rb
116
134
  - lib/csv_plus_plus/google_options.rb
117
135
  - lib/csv_plus_plus/graph.rb
118
136
  - lib/csv_plus_plus/language/cell_value.tab.rb
@@ -145,6 +163,7 @@ files:
145
163
  - lib/csv_plus_plus/writer/base_writer.rb
146
164
  - lib/csv_plus_plus/writer/csv.rb
147
165
  - lib/csv_plus_plus/writer/excel.rb
166
+ - lib/csv_plus_plus/writer/file_backer_upper.rb
148
167
  - lib/csv_plus_plus/writer/google_sheet_builder.rb
149
168
  - lib/csv_plus_plus/writer/google_sheet_modifier.rb
150
169
  - lib/csv_plus_plus/writer/google_sheets.rb
@@ -155,13 +174,13 @@ homepage: https://github.com/patrickomatic/csv-plus-plus
155
174
  licenses:
156
175
  - MIT
157
176
  metadata:
158
- rubygems_mfa_required: 'true'
159
177
  bug_tracker_uri: https://github.com/patrickomatic/csv-plus-plus/issues
160
178
  documentation_uri: https://www.rubydoc.info/gems/csv_plus_plus/
161
- github_repo: git://github.com/patrickomatic/csv_plus_plus
162
- homepage_uri: https://github.com/patrickomatic/csv_plus_plus
163
- source_code_uri: https://github.com/patrickomatic/csv_plus_plus
164
- changelog_uri: https://github.com/patrickomatic/csv_plus_plus/blob/main/CHANGELOG.md
179
+ github_repo: git://github.com/patrickomatic/csv-plus-plus
180
+ homepage_uri: https://github.com/patrickomatic/csv-plus-plus
181
+ source_code_uri: https://github.com/patrickomatic/csv-plus-plus
182
+ changelog_uri: https://github.com/patrickomatic/csv-plus-plus/blob/main/CHANGELOG.md
183
+ rubygems_mfa_required: 'true'
165
184
  post_install_message:
166
185
  rdoc_options: []
167
186
  require_paths: