sheets_db 0.11.0 → 0.12.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdf056137eb54d33c6647240ada1498ca2527d44fe68dd8f170882ecff233725
4
- data.tar.gz: 61d76eff207e4d88bbad700ea9be592dbdbac4a2d709a1883c53dbeae055790e
3
+ metadata.gz: 8186e2c37fc147e8bcc4fed8c4bf288b05dde2466c9145e24882d7e1c41d3d35
4
+ data.tar.gz: cdaf52978086b08fed7cd9635aed44ae2f06bfb756058f1eb4d4fc0bf73e8945
5
5
  SHA512:
6
- metadata.gz: a589ac3ae9d86b63b65691d12ef212323f1ea9266bb8e2230b67972781ba0ca5ec812f3befc0341f67261e16e70fb1511bce37103583ee452869fef1df7bc24b
7
- data.tar.gz: a71d6cf51c4795e6d4185e27c37a0dadf51ea2ec8fdf55607586e510e3a437723f508a20ed7c52fb50373c98c08e8c0cfa74289b5b3da40414241a0079c520f0
6
+ metadata.gz: 89fd0245435dcfe13010330ca1220bc1d1ec1082333583021cb65f1d74857f6886eebf4325d7f1107ccdc3a406eb58877c914ef3bbdab8b9c4f1e98ffedb7e70
7
+ data.tar.gz: 2575c45b0f15e24cdbe8296d79a0429eb8120a41414cf112328106b650c70142df3312de2e1d86ffe63ae7c65e32f839bf413ee94c3ea647deae2d2eb692d77a
@@ -2,17 +2,40 @@ module SheetsDB
2
2
  class Collection < Resource
3
3
  set_resource_type GoogleDrive::Collection
4
4
 
5
- def self.has_many(resource, class_name:, resource_type: :subcollections)
6
- unless [:subcollections, :spreadsheets].include?(resource_type)
7
- raise ArgumentError, "resource_type must be :subcollections or :spreadsheets"
5
+ class << self
6
+ def has_many(resource, class_name:, resource_type: :subcollections)
7
+ unless [:subcollections, :spreadsheets].include?(resource_type)
8
+ raise ArgumentError, "resource_type must be :subcollections or :spreadsheets"
9
+ end
10
+ register_association(resource, class_name: class_name, resource_type: resource_type)
11
+ define_method(resource) do
12
+ @associated_resources ||= {}
13
+ @associated_resources[resource] ||= google_drive_resource.send(resource_type).map { |raw|
14
+ Support.constantize(class_name).new(raw)
15
+ }
16
+ end
8
17
  end
9
- register_association(resource, class_name: class_name, resource_type: resource_type)
10
- define_method(resource) do
11
- result = instance_variable_get(:"@#{resource}")
12
- result || instance_variable_set(:"@#{resource}",
13
- google_drive_resource.send(resource_type).map { |raw| Support.constantize(class_name).new(raw) }
14
- )
18
+ end
19
+
20
+ %i[
21
+ spreadsheet
22
+ subcollection
23
+ ].each do |child_resource_type|
24
+ define_method :"google_drive_#{child_resource_type}_by_title" do |title, **kwargs|
25
+ find_child_google_drive_resource_by(type: child_resource_type, title: title, **kwargs)
15
26
  end
16
27
  end
28
+
29
+ def spreadsheets
30
+ @anonymous_resources ||= {}
31
+ @anonymous_resources[:spreadsheets] ||=
32
+ google_drive_resource.spreadsheets.map { |raw| Spreadsheet.new(raw) }
33
+ end
34
+
35
+ def collections
36
+ @anonymous_resources ||= {}
37
+ @anonymous_resources[:collections] ||=
38
+ google_drive_resource.subcollections.map { |raw| Collection.new(raw) }
39
+ end
17
40
  end
18
- end
41
+ end
@@ -2,6 +2,7 @@ module SheetsDB
2
2
  class Resource
3
3
  class ResourceTypeMismatchError < StandardError; end
4
4
  class CollectionTypeAlreadyRegisteredError < StandardError; end
5
+ class ChildResourceNotFoundError < StandardError; end
5
6
 
6
7
  class << self
7
8
  attr_reader :resource_type
@@ -17,8 +18,8 @@ module SheetsDB
17
18
 
18
19
  def find_by_id(id, session: SheetsDB::Session.default)
19
20
  google_drive_resource = session.raw_file_by_id(id)
20
- if @resource_type && !google_drive_resource.is_a?(@resource_type)
21
- fail(ResourceTypeMismatchError, "The file with id #{id} is not a #{@resource_type}")
21
+ if resource_type && !google_drive_resource.is_a?(resource_type)
22
+ fail(ResourceTypeMismatchError, "The file with id #{id} is not a #{resource_type}")
22
23
  end
23
24
  new(google_drive_resource)
24
25
  end
@@ -26,10 +27,10 @@ module SheetsDB
26
27
  def belongs_to_many(resource, class_name:)
27
28
  register_association(resource, class_name: class_name, resource_type: :parents)
28
29
  define_method(resource) do
29
- result = instance_variable_get(:"@#{resource}")
30
- result || instance_variable_set(:"@#{resource}",
31
- google_drive_resource.parents.map { |id| Support.constantize(class_name).find_by_id(id) }
32
- )
30
+ @associated_resources ||= {}
31
+ @associated_resources[resource] ||= google_drive_resource.parents.map { |id|
32
+ Support.constantize(class_name).find_by_id(id)
33
+ }
33
34
  end
34
35
  end
35
36
 
@@ -43,6 +44,15 @@ module SheetsDB
43
44
  class_name: class_name
44
45
  }
45
46
  end
47
+
48
+ def association_methods_for_type(type)
49
+ raise ArgumentError unless %i[spreadsheet worksheet subcollection].include?(type)
50
+
51
+ google_type = type == :spreadsheet ? :file : type
52
+ create_prefix = type == :worksheet ? :add : :create
53
+
54
+ OpenStruct.new(find: :"#{google_type}_by_title", create: :"#{create_prefix}_#{type}")
55
+ end
46
56
  end
47
57
 
48
58
  extend Forwardable
@@ -56,6 +66,16 @@ module SheetsDB
56
66
  @google_drive_resource = google_drive_resource
57
67
  end
58
68
 
69
+ def reload!
70
+ @associated_resources = nil
71
+ @anonymous_resources = nil
72
+ google_drive_resource.reload_metadata
73
+ end
74
+
75
+ def delete!
76
+ google_drive_resource.delete
77
+ end
78
+
59
79
  def ==(other)
60
80
  other.is_a?(self.class) &&
61
81
  other.google_drive_resource == google_drive_resource
@@ -75,5 +95,14 @@ module SheetsDB
75
95
  updated_at: updated_at
76
96
  }
77
97
  end
98
+
99
+ def find_child_google_drive_resource_by(type:, title:, create: false)
100
+ association_methods = self.class.association_methods_for_type(type)
101
+ child_resource = google_drive_resource.public_send(association_methods.find, title)
102
+ child_resource ||= google_drive_resource.public_send(association_methods.create, title) if create
103
+ raise ChildResourceNotFoundError, [self, type, title] if child_resource.nil?
104
+
105
+ child_resource
106
+ end
78
107
  end
79
108
  end
@@ -5,33 +5,26 @@ module SheetsDB
5
5
 
6
6
  set_resource_type GoogleDrive::Spreadsheet
7
7
 
8
- def self.has_many(resource, worksheet_name:, class_name:)
9
- register_worksheet_association(resource, worksheet_name: worksheet_name, class_name: class_name)
10
- create_worksheet_association(resource, worksheet_name: worksheet_name, class_name: class_name)
11
- end
8
+ class << self
9
+ def has_many(resource, worksheet_name:, class_name:)
10
+ register_worksheet_association(resource, worksheet_name: worksheet_name, class_name: class_name)
11
+ create_worksheet_association(resource, worksheet_name: worksheet_name, class_name: class_name)
12
+ end
12
13
 
13
- def self.register_worksheet_association(resource, worksheet_name:, class_name:)
14
- @associations ||= {}
15
- if @associations.fetch(resource, nil)
16
- raise WorksheetAssociationAlreadyRegisteredError
14
+ def register_worksheet_association(resource, worksheet_name:, class_name:)
15
+ @associations ||= {}
16
+ if @associations.fetch(resource, nil)
17
+ raise WorksheetAssociationAlreadyRegisteredError
18
+ end
19
+ @associations[resource] = {
20
+ worksheet_name: worksheet_name,
21
+ class_name: class_name
22
+ }
17
23
  end
18
- @associations[resource] = {
19
- worksheet_name: worksheet_name,
20
- class_name: class_name
21
- }
22
- end
23
24
 
24
- def self.create_worksheet_association(resource, worksheet_name:, class_name:)
25
- define_method(resource) do
26
- @worksheets ||= {}
27
- @worksheets[resource] ||= begin
28
- google_drive_worksheet = google_drive_resource.worksheet_by_title(worksheet_name)
29
- raise WorksheetNotFoundError, worksheet_name if google_drive_worksheet.nil?
30
- Worksheet.new(
31
- spreadsheet: self,
32
- google_drive_resource: google_drive_worksheet,
33
- type: Support.constantize(class_name)
34
- )
25
+ def create_worksheet_association(resource, **kwargs)
26
+ define_method(resource) do
27
+ worksheet_association(resource, **kwargs)
35
28
  end
36
29
  end
37
30
  end
@@ -51,5 +44,35 @@ module SheetsDB
51
44
  def select_from_association(association_name, &block)
52
45
  send(association_name).select(&block)
53
46
  end
47
+
48
+ def worksheet_association(association_name, worksheet_name:, class_name:)
49
+ @associated_resources ||= {}
50
+ @associated_resources[association_name] ||=
51
+ find_or_create_worksheet!(title: worksheet_name, type: Support.constantize(class_name))
52
+ end
53
+
54
+ def worksheets
55
+ @anonymous_resources ||= {}
56
+ @anonymous_resources[:worksheets] ||= google_drive_resource.worksheets.map { |raw|
57
+ set_up_worksheet!(google_drive_resource: raw)
58
+ }
59
+ end
60
+
61
+ def set_up_worksheet!(google_drive_resource:, type: nil)
62
+ Worksheet.new(
63
+ google_drive_resource: google_drive_resource,
64
+ spreadsheet: self,
65
+ type: type
66
+ ).set_up!
67
+ end
68
+
69
+ def find_or_create_worksheet!(title:, type: nil)
70
+ resource = google_drive_worksheet_by_title(title, create: true)
71
+ set_up_worksheet!(google_drive_resource: resource, type: type)
72
+ end
73
+
74
+ def google_drive_worksheet_by_title(title, **kwargs)
75
+ find_child_google_drive_resource_by(type: :worksheet, title: title, **kwargs)
76
+ end
54
77
  end
55
78
  end
@@ -1,3 +1,3 @@
1
1
  module SheetsDB
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
@@ -172,6 +172,7 @@ module SheetsDB
172
172
  def reload!
173
173
  worksheet.reload!
174
174
  reset_attributes_and_associations_cache
175
+ self
175
176
  end
176
177
 
177
178
  def save!
@@ -179,6 +180,7 @@ module SheetsDB
179
180
  worksheet.update_attributes_at_row_position(staged_attributes, row_position: row_position)
180
181
  save_changed_foreign_items!
181
182
  reset_attributes_and_associations_cache
183
+ self
182
184
  end
183
185
 
184
186
  def assign_next_row_position_if_not_set
@@ -192,6 +194,7 @@ module SheetsDB
192
194
  def reset_attributes_and_associations_cache
193
195
  @loaded_attributes = {}
194
196
  @loaded_associations = {}
197
+ self
195
198
  end
196
199
 
197
200
  def staged_attributes
@@ -7,15 +7,34 @@ module SheetsDB
7
7
 
8
8
  include Enumerable
9
9
 
10
- attr_reader :spreadsheet, :google_drive_resource, :type, :synchronizing
10
+ attr_reader :spreadsheet, :worksheet_title, :google_drive_resource, :synchronizing
11
11
 
12
- def initialize(spreadsheet:, google_drive_resource:, type:)
12
+ def initialize(spreadsheet:, google_drive_resource:, type: nil)
13
13
  @spreadsheet = spreadsheet
14
14
  @google_drive_resource = google_drive_resource
15
+ @worksheet_title = google_drive_resource.title
15
16
  @type = type
16
17
  @synchronizing = true
17
18
  end
18
19
 
20
+ def set_up!
21
+ if google_drive_resource.num_rows == 0
22
+ registered_column_names = attribute_definitions.map { |name, definition| definition[:column_name] }
23
+ write_matrix!([registered_column_names])
24
+ end
25
+ self
26
+ end
27
+
28
+ def type
29
+ @type || @generated_type ||= begin
30
+ Class.new(Row).tap { |generated_type_class|
31
+ column_names.each do |name|
32
+ generated_type_class.attribute name.to_sym
33
+ end
34
+ }
35
+ end
36
+ end
37
+
19
38
  def ==(other)
20
39
  other.is_a?(self.class) &&
21
40
  other.google_drive_resource == google_drive_resource &&
@@ -31,7 +50,8 @@ module SheetsDB
31
50
  def columns
32
51
  @columns ||= begin
33
52
  {}.tap { |directory|
34
- google_drive_resource.rows.first.each_with_index do |name, i|
53
+ header_row = google_drive_resource.rows.first || []
54
+ header_row.each_with_index do |name, i|
35
55
  unless name == ""
36
56
  directory[name] = Column.new(name: name, column_position: i + 1)
37
57
  end
@@ -98,12 +118,20 @@ module SheetsDB
98
118
 
99
119
  def update_attributes_at_row_position(staged_attributes, row_position:)
100
120
  staged_attributes.each do |attribute_name, value|
101
- attribute_definition, column = get_definition_and_column(attribute_name)
102
- raise ColumnNotFoundError, column unless column
103
- assignment_value = attribute_definition[:multiple] ? value.join(",") : value
104
- google_drive_resource[row_position, column.column_position] = assignment_value
121
+ update_attribute_at_row_position(
122
+ attribute_name: attribute_name,
123
+ value: value,
124
+ row_position: row_position
125
+ )
105
126
  end
106
- google_drive_resource.synchronize if synchronizing
127
+ synchronize! if synchronizing
128
+ end
129
+
130
+ def update_attribute_at_row_position(attribute_name:, value:, row_position:)
131
+ attribute_definition, column = get_definition_and_column(attribute_name)
132
+ raise ColumnNotFoundError, column unless column
133
+ assignment_value = attribute_definition[:multiple] ? value.join(",") : value
134
+ google_drive_resource[row_position, column.column_position] = assignment_value
107
135
  end
108
136
 
109
137
  def next_available_row_position
@@ -163,7 +191,11 @@ module SheetsDB
163
191
  end
164
192
 
165
193
  def reload!
194
+ @columns = nil
195
+ @existing_raw_data = nil
196
+ @generated_type = nil
166
197
  google_drive_resource.reload
198
+ self
167
199
  end
168
200
 
169
201
  def convert_to_boolean(raw_value)
@@ -205,9 +237,64 @@ module SheetsDB
205
237
  def transaction
206
238
  disable_synchronization!
207
239
  yield
208
- google_drive_resource.synchronize
240
+ synchronize!
209
241
  ensure
210
242
  enable_synchronization!
211
243
  end
244
+
245
+ def delete_google_drive_resource!
246
+ google_drive_resource.delete
247
+ @google_drive_resource = nil
248
+ spreadsheet.reload!
249
+ end
250
+
251
+ def truncate!
252
+ clear_google_drive_resource!
253
+ set_up!
254
+ reload!
255
+ end
256
+
257
+ def write_matrix(matrix, rewrite: false, save: false)
258
+ clear_google_drive_resource if rewrite
259
+ google_drive_resource.update_cells(1, 1, matrix)
260
+ synchronize! if save
261
+ self
262
+ end
263
+
264
+ def write_matrix!(matrix, **options)
265
+ write_matrix(matrix, **options.merge(save: true))
266
+ end
267
+
268
+ def write_raw_data(data, **options)
269
+ raise ArgumentError, "mismatched keys" if data.map(&:keys).uniq.count > 1
270
+
271
+ write_matrix(data.map(&:values).prepend(data.first.keys), **options)
272
+ end
273
+
274
+ def write_raw_data!(data, **options)
275
+ write_raw_data(data, **options.merge(save: true))
276
+ end
277
+
278
+ def clear_google_drive_resource(save: false)
279
+ empty_matrix = Array.new(google_drive_resource.num_rows, Array.new(google_drive_resource.num_cols))
280
+ write_matrix(empty_matrix, save: save)
281
+ end
282
+
283
+ def clear_google_drive_resource!
284
+ clear_google_drive_resource(save: true)
285
+ end
286
+
287
+ def synchronize!
288
+ google_drive_resource.synchronize
289
+ @existing_raw_data = nil
290
+ self
291
+ end
292
+
293
+ def existing_raw_data
294
+ @existing_raw_data ||= begin
295
+ rows = google_drive_resource.rows
296
+ rows.drop(1).map { |row| Hash[rows.first.zip(row)] }
297
+ end
298
+ end
212
299
  end
213
300
  end
data/sheets_db.gemspec CHANGED
@@ -22,6 +22,6 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency "bundler", "~> 2.1"
24
24
  spec.add_development_dependency "rake", "~> 13"
25
- spec.add_development_dependency "rspec", "~> 3.10"
25
+ spec.add_development_dependency "rspec", "~> 3.11"
26
26
  spec.add_development_dependency "simplecov"
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sheets_db
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ravi Gadad
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-17 00:00:00.000000000 Z
11
+ date: 2023-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google_drive
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.10'
61
+ version: '3.11'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.10'
68
+ version: '3.11'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: simplecov
71
71
  requirement: !ruby/object:Gem::Requirement