Skip to content

Commit 7e26e79

Browse files
committed
feature: Add monkey-patch to allow input of serial dates
PR open at: gimite/google-drive-ruby#379 Also - Update the spreadsheet mock with a way to input serial dates. - Remove core_extensions from coverage report. - Remove CoreExtensions::Worksheet rubocop checking
1 parent 5bd7de4 commit 7e26e79

File tree

8 files changed

+154
-29
lines changed

8 files changed

+154
-29
lines changed

lib/core_extensions.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
module AspireBudget
66
module CoreExtensions
7+
# rubocop:disable all
78
module Worksheet
89
def rows(skip = 0)
910
nc = num_cols
@@ -24,9 +25,98 @@ def rows(skip = 0)
2425
def rows_with_numerics(skip = 0)
2526
rows(skip) { |row, col| numeric_value(row, col) || self[row, col] }
2627
end
28+
29+
def []=(*args) # rubocop:disable all
30+
(row, col) = parse_cell_args(args[0...-1])
31+
value = args[-1]
32+
33+
reload_cells unless @cells
34+
@numeric_values[[row, col]] = value.is_a?(Numeric) ? value : nil
35+
value = value.to_s
36+
validate_cell_value(value)
37+
38+
@cells[[row, col]] = value
39+
@input_values[[row, col]] = value
40+
@modified.add([row, col])
41+
self.max_rows = row if row > @max_rows
42+
self.max_cols = col if col > @max_cols
43+
if value.empty?
44+
@num_rows = nil
45+
@num_cols = nil
46+
else
47+
@num_rows = row if @num_rows && row > @num_rows
48+
@num_cols = col if @num_cols && col > @num_cols
49+
end
50+
end
51+
52+
def save # rubocop:disable all
53+
sent = false
54+
55+
if @meta_modified
56+
add_request({
57+
update_sheet_properties: {
58+
properties: {
59+
sheet_id: sheet_id,
60+
title: title,
61+
index: index,
62+
grid_properties: {row_count: max_rows, column_count: max_cols},
63+
},
64+
fields: '*',
65+
},
66+
})
67+
end
68+
69+
if !@v4_requests.empty?
70+
self.spreadsheet.batch_update(@v4_requests)
71+
@v4_requests = []
72+
sent = true
73+
end
74+
75+
@remote_title = @title
76+
77+
unless @modified.empty?
78+
min_modified_row = 1.0 / 0.0
79+
max_modified_row = 0
80+
min_modified_col = 1.0 / 0.0
81+
max_modified_col = 0
82+
@modified.each do |r, c|
83+
min_modified_row = r if r < min_modified_row
84+
max_modified_row = r if r > max_modified_row
85+
min_modified_col = c if c < min_modified_col
86+
max_modified_col = c if c > max_modified_col
87+
end
88+
89+
# Uses update_spreadsheet_value instead batch_update_spreadsheet with
90+
# update_cells. batch_update_spreadsheet has benefit that the request
91+
# can be batched with other requests. But it has drawback that the
92+
# type of the value (string_value, number_value, etc.) must be
93+
# explicitly specified in user_entered_value. Since I don't know exact
94+
# logic to determine the type from text, I chose to use
95+
# update_spreadsheet_value here.
96+
range = "'%s'!R%dC%d:R%dC%d" %
97+
[@title, min_modified_row, min_modified_col, max_modified_row, max_modified_col]
98+
values = (min_modified_row..max_modified_row).map do |r|
99+
(min_modified_col..max_modified_col).map do |c|
100+
next unless @modified.include?([r, c])
101+
102+
@numeric_values[[r, c]] || @cells[[r, c]] || ''
103+
end
104+
end
105+
value_range = Google::Apis::SheetsV4::ValueRange.new(values: values)
106+
@session.sheets_service.update_spreadsheet_value(
107+
spreadsheet.id, range, value_range, value_input_option: 'USER_ENTERED')
108+
109+
@modified.clear
110+
sent = true
111+
end
112+
113+
sent
114+
end
27115
end
116+
# rubocop:enable all
28117
end
29118
end
30119

31120
# https://github.com/gimite/google-drive-ruby/issues/378 (PR #377)
121+
# https://github.com/gimite/google-drive-ruby/issues/380 (PR #379)
32122
GoogleDrive::Worksheet.prepend AspireBudget::CoreExtensions::Worksheet

lib/utils.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
# frozen_string_literal: true
22

33
require 'date'
4-
require 'configuration'
54

65
module AspireBudget
76
module Utils
87
class << self
9-
DATE_FORMAT = '%d/%m/%y'
10-
118
def parse_date(value)
129
return parse_serial_date(value) if value.is_a?(Numeric)
1310
return value.to_date if value.respond_to?(:to_date)

spec/spec_helper.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
if ENV['CI']
66
require 'simplecov'
77
SimpleCov.start do
8+
add_filter 'lib/core_extensions.rb'
89
add_filter %r{^/spec/}
910
end
1011
end
1112

1213
require 'pry'
1314
require 'support/google_drive_mock'
14-
require 'support/spreadsheet_version_helper'
15+
require 'support/spreadsheet_mock_helpers'
1516
require 'aspire_budget'
1617

1718
RSpec.configure do |config|

spec/support/google_drive_mock.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,11 @@ def [](*args)
8686
else
8787
args
8888
end
89-
rows[row - 1][col - 1].to_s
89+
90+
rows[row - 1][col - 1]
9091
end
9192

92-
def []=(row, col, value) # rubocop:disable Metrics/MethodLength
93+
def []=(row, col, value) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
9394
@dirty = true
9495

9596
if row > num_rows
@@ -104,15 +105,21 @@ def []=(row, col, value) # rubocop:disable Metrics/MethodLength
104105
end
105106
end
106107

107-
@rows[row - 1][col - 1] = value
108+
value = treat_value(row, col, value) || value
109+
110+
@rows[row - 1][col - 1] = value.to_s
108111
end
109112

113+
# treat the values (e.g. convert to date or currency)
114+
# used by #treat_cells_as test helper
115+
def treat_value(row, col, value); end
116+
110117
# assumes that "," is not a decimal separator
111118
# assumes date format as defined above
112119
def numeric_value(*args)
113-
value = self[*args]
120+
value = self[*args].to_s
114121
if value.match?(CURRENCY_REGEX)
115-
value.gsub(/[€$,]/, '').to_f
122+
Float(value.gsub(/[€$,]/, ''))
116123
elsif value.match?(%r{\d+/\d+/\d+})
117124
Float(Date.strptime(value, DATE_FORMAT) - LOTUS_DAY_ONE)
118125
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
require 'utils'
4+
5+
module SpreadsheetMockHelpers
6+
def use_spreadsheet_version(version)
7+
AspireBudget.configure do |config|
8+
config.session = GoogleDrive.from_config('foo')
9+
config.spreadsheet_key = version
10+
end
11+
end
12+
13+
# When inputing into a numeric cell, mock it first so we'll try to coerce its
14+
# contents.
15+
#
16+
# @param currency [Array] currency cells ([row, col] format)
17+
# @param date [Array] date cels ([row, col] format)
18+
def treat_cells_as(currency: [], date: [])
19+
allow_any_instance_of(GoogleDriveMock::Worksheet)
20+
.to receive(:treat_value) do |_obj, row, col, value|
21+
if date.include?([row, col])
22+
AspireBudget::Utils.parse_date(Float(value)).strftime('%-d/%-m/%y')
23+
elsif currency.include?([row, col])
24+
value = format('%<value>.2f', value: value)
25+
"$#{format('%<value>.2f', value: value)}"
26+
end
27+
end
28+
end
29+
end
30+
31+
RSpec.configure do |config|
32+
config.include SpreadsheetMockHelpers
33+
end

spec/support/spreadsheet_version_helper.rb

Lines changed: 0 additions & 14 deletions
This file was deleted.

spec/worksheets/category_transfers_spec.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@
3232
end
3333

3434
describe '#insert' do
35+
before do
36+
treat_cells_as(date: [[11, 2]], currency: [[11, 3]])
37+
end
38+
3539
let(:params) do
36-
{ date: '03/06/2020', amount: 120.43, from: 'Available to budget', to: 'Groceries', memo: 'ruby' }
40+
{ date: Date.parse('2020-06-03'), amount: 120.43, from: 'Available to budget', to: 'Groceries', memo: 'ruby' }
3741
end
3842
let(:new_record_attributes) do
3943
{
@@ -52,7 +56,7 @@
5256
.to change { subject.all.size }
5357
.by(1)
5458

55-
expect(new_record).to have_attributes(new_record_attributes)
59+
expect(subject.all.last).to have_attributes(new_record_attributes)
5660

5761
expect(subject).not_to be_dirty
5862
end
@@ -67,7 +71,7 @@
6771
allow(category_transfer)
6872
.to receive(:to_row)
6973
.with(%i[date amount from to memo])
70-
.and_return(['03/06/20', '120.43', 'Available to budget', 'Groceries', 'ruby'])
74+
.and_return([43_985.0, 120.43, 'Available to budget', 'Groceries', 'ruby'])
7175
end
7276

7377
it 'inserts new data' do
@@ -76,7 +80,7 @@
7680
.to change { subject.all.size }
7781
.by(1)
7882

79-
expect(new_record).to have_attributes(new_record_attributes)
83+
expect(subject.all.last).to have_attributes(new_record_attributes)
8084

8185
expect(subject).not_to be_dirty
8286
end

spec/worksheets/transactions_spec.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,16 @@
5656
end
5757

5858
describe '#insert' do
59+
before do
60+
treat_cells_as(
61+
date: [[13, 2]],
62+
currency: [[13, 3], [13, 4]]
63+
)
64+
end
65+
5966
let(:params) do
6067
{
61-
date: '03/06/2020',
68+
date: Date.parse('2020-06-03'),
6269
outflow: 900,
6370
inflow: 800,
6471
category: 'Test',
@@ -101,7 +108,7 @@
101108
allow(transaction)
102109
.to receive(:to_row)
103110
.with(%i[date outflow inflow category account memo status])
104-
.and_return(['03/06/20', '900.00', '800.00', 'Test', '💰 Checking', 'ruby', '✅'])
111+
.and_return([43_985.0, 900.00, 800.00, 'Test', '💰 Checking', 'ruby', '✅'])
105112
end
106113

107114
it 'inserts new data' do

0 commit comments

Comments
 (0)