Class: SheetsDB::Worksheet

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/sheets_db/worksheet.rb,
lib/sheets_db/worksheet/row.rb,
lib/sheets_db/worksheet/column.rb

Defined Under Namespace

Classes: Column, ColumnNotFoundError, Row, WorksheetContainsDataError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spreadsheet:, google_drive_resource:, type: nil) ⇒ Worksheet

Returns a new instance of Worksheet.



17
18
19
20
21
22
# File 'lib/sheets_db/worksheet.rb', line 17

def initialize(spreadsheet:, google_drive_resource:, type: nil)
  @spreadsheet = spreadsheet
  @google_drive_resource = google_drive_resource
  @type = type
  @synchronizing = true
end

Instance Attribute Details

#google_drive_resourceObject (readonly)

Returns the value of attribute google_drive_resource.



13
14
15
# File 'lib/sheets_db/worksheet.rb', line 13

def google_drive_resource
  @google_drive_resource
end

#spreadsheetObject (readonly)

Returns the value of attribute spreadsheet.



13
14
15
# File 'lib/sheets_db/worksheet.rb', line 13

def spreadsheet
  @spreadsheet
end

#synchronizingObject (readonly)

Returns the value of attribute synchronizing.



13
14
15
# File 'lib/sheets_db/worksheet.rb', line 13

def synchronizing
  @synchronizing
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



42
43
44
45
46
# File 'lib/sheets_db/worksheet.rb', line 42

def ==(other)
  other.is_a?(self.class) &&
    other.google_drive_resource == google_drive_resource &&
    other.type == type
end

#allObject



170
171
172
# File 'lib/sheets_db/worksheet.rb', line 170

def all
  to_a
end

#attribute_at_row_position(attribute_name, row_position) ⇒ Object



100
101
102
103
104
105
106
107
# File 'lib/sheets_db/worksheet.rb', line 100

def attribute_at_row_position(attribute_name, row_position)
  attribute_definition, column = get_definition_and_column(attribute_name)
  return value_if_column_missing(attribute_definition) unless column
  raw_value = read_value_from_google_drive_resource(
    dimensions: [row_position, column.column_position],
    attribute_definition: attribute_definition
  )
end

#attribute_definitionsObject



71
72
73
# File 'lib/sheets_db/worksheet.rb', line 71

def attribute_definitions
  type.attribute_definitions
end

#clear_google_drive_resource(save: false) ⇒ Object



284
285
286
287
# File 'lib/sheets_db/worksheet.rb', line 284

def clear_google_drive_resource(save: false)
  empty_matrix = Array.new(google_drive_resource.num_rows, Array.new(google_drive_resource.num_cols))
  write_matrix(empty_matrix, save: save)
end

#clear_google_drive_resource!Object



289
290
291
# File 'lib/sheets_db/worksheet.rb', line 289

def clear_google_drive_resource!
  clear_google_drive_resource(save: true)
end

#column_namesObject



67
68
69
# File 'lib/sheets_db/worksheet.rb', line 67

def column_names
  columns.keys
end

#columnsObject



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/sheets_db/worksheet.rb', line 54

def columns
  @columns ||= begin
    {}.tap { |directory|
      header_row = google_drive_resource.rows.first || []
      header_row.each_with_index do |name, i|
        unless name == ""
          directory[name] = Column.new(name: name, column_position: i + 1)
        end
      end
    }
  end
end

#convert_to_boolean(raw_value) ⇒ Object



205
206
207
208
209
210
211
# File 'lib/sheets_db/worksheet.rb', line 205

def convert_to_boolean(raw_value)
  return nil if raw_value.nil?
  trues = ["y", "yes", "true", "1"]
  falses = ["n", "no", "false", "0"]
  return true if trues.include?(raw_value.downcase)
  return false if falses.include?(raw_value.downcase)
end

#convert_value(raw_value, attribute_definition) ⇒ Object



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/sheets_db/worksheet.rb', line 213

def convert_value(raw_value, attribute_definition)
  raw_value = raw_value.strip if attribute_definition.fetch(:strip, true)
  return nil if raw_value == ""
  converted_value = case attribute_definition[:type].to_s
  when "Integer"
    raw_value.to_i
  when "Decimal"
    raw_value.to_d
  when "DateTime"
    DateTime.strptime(raw_value, "%m/%d/%Y %H:%M:%S")
  when "Boolean"
    convert_to_boolean(raw_value)
  else
    raw_value
  end
  attribute_definition[:transform] ?
    attribute_definition[:transform].call(converted_value) :
    converted_value
end

#create!(**attributes) ⇒ Object



151
152
153
# File 'lib/sheets_db/worksheet.rb', line 151

def create!(**attributes)
  new(**attributes).save!
end

#delete_google_drive_resource!(force: false) ⇒ Object



249
250
251
252
253
254
255
# File 'lib/sheets_db/worksheet.rb', line 249

def delete_google_drive_resource!(force: false)
  raise WorksheetContainsDataError if google_drive_resource.num_rows.nonzero? && !force

  google_drive_resource.delete
  @google_drive_resource = nil
  spreadsheet.reload!
end

#disable_synchronization!Object



237
238
239
# File 'lib/sheets_db/worksheet.rb', line 237

def disable_synchronization!
  @synchronizing = false
end

#eachObject



163
164
165
166
167
168
# File 'lib/sheets_db/worksheet.rb', line 163

def each
  return to_enum(:each) unless block_given?
  (google_drive_resource.num_rows - 1).times do |i|
    yield type.new(worksheet: self, row_position: i + 2)
  end
end

#enable_synchronization!Object



233
234
235
# File 'lib/sheets_db/worksheet.rb', line 233

def enable_synchronization!
  @synchronizing = true
end

#existing_raw_dataObject



299
300
301
302
303
304
# File 'lib/sheets_db/worksheet.rb', line 299

def existing_raw_data
  @existing_raw_data ||= begin
    rows = google_drive_resource.rows
    rows.drop(1).map { |row| Hash[rows.first.zip(row)] }
  end
end

#find_by_attribute(attribute_name, value) ⇒ Object



189
190
191
192
193
194
195
# File 'lib/sheets_db/worksheet.rb', line 189

def find_by_attribute(attribute_name, value)
  definition = attribute_definitions[attribute_name]
  select { |item|
    attribute = item.send(attribute_name)
    definition[:multiple] ? attribute.include?(value) : attribute == value
  }
end

#find_by_id(id) ⇒ Object



174
175
176
# File 'lib/sheets_db/worksheet.rb', line 174

def find_by_id(id)
  find_by_ids([id]).first
end

#find_by_ids(ids) ⇒ Object



178
179
180
181
182
183
184
185
186
187
# File 'lib/sheets_db/worksheet.rb', line 178

def find_by_ids(ids)
  result = []
  each do |model|
    break if result.count == ids.count
    if ids.include?(model.id)
      result << model
    end
  end
  result
end

#first_existing_column_name(attribute_name) ⇒ Object



83
84
85
86
87
88
89
90
91
# File 'lib/sheets_db/worksheet.rb', line 83

def first_existing_column_name(attribute_name)
  attribute_definition = attribute_definitions.fetch(attribute_name)
  [
    attribute_definition.fetch(:column_name, attribute_name),
    attribute_definition.fetch(:aliases, [])
  ].flatten.detect { |name|
    columns.key?(name.to_s)
  }
end

#get_definition_and_column(attribute_name) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/sheets_db/worksheet.rb', line 75

def get_definition_and_column(attribute_name)
  column_name = first_existing_column_name(attribute_name)
  [
    attribute_definitions.fetch(attribute_name),
    column_name && columns[column_name.to_s]
  ]
end

#hashObject



50
51
52
# File 'lib/sheets_db/worksheet.rb', line 50

def hash
  [self.class, google_drive_resource, type].hash
end

#import!(attribute_sets) ⇒ Object



155
156
157
158
159
160
161
# File 'lib/sheets_db/worksheet.rb', line 155

def import!(attribute_sets)
  transaction do
    attribute_sets.each do |attributes|
      create!(**attributes)
    end
  end
end

#new(**attributes) ⇒ Object



145
146
147
148
149
# File 'lib/sheets_db/worksheet.rb', line 145

def new(**attributes)
  type.new(worksheet: self, row_position: nil).tap { |row|
    row.stage_attributes(attributes)
  }
end

#next_available_row_positionObject



141
142
143
# File 'lib/sheets_db/worksheet.rb', line 141

def next_available_row_position
  google_drive_resource.num_rows + 1
end

#read_value_from_google_drive_resource(dimensions:, attribute_definition:) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/sheets_db/worksheet.rb', line 109

def read_value_from_google_drive_resource(dimensions:, attribute_definition:)
  raw_value = case attribute_definition[:type].to_s
    when "DateTime"
      google_drive_resource.input_value(*dimensions)
    else
      google_drive_resource[*dimensions]
  end
  if attribute_definition[:multiple]
    raw_value.split(/,\s*/).map { |value| convert_value(value, attribute_definition) }
  else
    convert_value(raw_value, attribute_definition)
  end
end

#reload!Object



197
198
199
200
201
202
203
# File 'lib/sheets_db/worksheet.rb', line 197

def reload!
  @columns = nil
  @existing_raw_data = nil
  @generated_type = nil
  google_drive_resource.reload
  self
end

#set_up!Object



24
25
26
27
28
29
30
# File 'lib/sheets_db/worksheet.rb', line 24

def set_up!
  if google_drive_resource.num_rows == 0
    registered_column_names = attribute_definitions.map { |name, definition| definition[:column_name] }
    write_matrix!([registered_column_names])
  end
  self
end

#synchronize!Object



293
294
295
296
297
# File 'lib/sheets_db/worksheet.rb', line 293

def synchronize!
  google_drive_resource.synchronize
  @existing_raw_data = nil
  self
end

#transactionObject



241
242
243
244
245
246
247
# File 'lib/sheets_db/worksheet.rb', line 241

def transaction
  disable_synchronization!
  yield
  synchronize!
ensure
  enable_synchronization!
end

#truncate!Object



257
258
259
260
261
# File 'lib/sheets_db/worksheet.rb', line 257

def truncate!
  clear_google_drive_resource!
  set_up!
  reload!
end

#typeObject



32
33
34
35
36
37
38
39
40
# File 'lib/sheets_db/worksheet.rb', line 32

def type
  @type || @generated_type ||= begin
    Class.new(Row).tap { |generated_type_class|
      column_names.each do |name|
        generated_type_class.attribute name.to_sym
      end
    }
  end
end

#update_attribute_at_row_position(attribute_name:, value:, row_position:) ⇒ Object



134
135
136
137
138
139
# File 'lib/sheets_db/worksheet.rb', line 134

def update_attribute_at_row_position(attribute_name:, value:, row_position:)
  attribute_definition, column = get_definition_and_column(attribute_name)
  raise ColumnNotFoundError, column unless column
  assignment_value = attribute_definition[:multiple] ? value.join(",") : value
  google_drive_resource[row_position, column.column_position] = assignment_value
end

#update_attributes_at_row_position(staged_attributes, row_position:) ⇒ Object



123
124
125
126
127
128
129
130
131
132
# File 'lib/sheets_db/worksheet.rb', line 123

def update_attributes_at_row_position(staged_attributes, row_position:)
  staged_attributes.each do |attribute_name, value|
    update_attribute_at_row_position(
      attribute_name: attribute_name,
      value: value,
      row_position: row_position
    )
  end
  synchronize! if synchronizing
end

#value_if_column_missing(attribute_definition) ⇒ Object



93
94
95
96
97
98
# File 'lib/sheets_db/worksheet.rb', line 93

def value_if_column_missing(attribute_definition)
  unless attribute_definition[:if_column_missing]
    raise ColumnNotFoundError, attribute_definition[:column_name]
  end
  instance_exec(&attribute_definition[:if_column_missing])
end

#write_matrix(matrix, rewrite: false, save: false) ⇒ Object



263
264
265
266
267
268
# File 'lib/sheets_db/worksheet.rb', line 263

def write_matrix(matrix, rewrite: false, save: false)
  clear_google_drive_resource if rewrite
  google_drive_resource.update_cells(1, 1, matrix)
  synchronize! if save
  self
end

#write_matrix!(matrix, **options) ⇒ Object



270
271
272
# File 'lib/sheets_db/worksheet.rb', line 270

def write_matrix!(matrix, **options)
  write_matrix(matrix, **options.merge(save: true))
end

#write_raw_data(data, **options) ⇒ Object

Raises:

  • (ArgumentError)


274
275
276
277
278
# File 'lib/sheets_db/worksheet.rb', line 274

def write_raw_data(data, **options)
  raise ArgumentError, "mismatched keys" if data.map(&:keys).uniq.count > 1

  write_matrix(data.map(&:values).prepend(data.first.keys), **options)
end

#write_raw_data!(data, **options) ⇒ Object



280
281
282
# File 'lib/sheets_db/worksheet.rb', line 280

def write_raw_data!(data, **options)
  write_raw_data(data, **options.merge(save: true))
end