Class: RailsSpreadsheetReader::Base

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Model, ActiveModel::Validations::Callbacks
Defined in:
lib/rails_spreadsheet_reader/base.rb

Defined Under Namespace

Classes: InvalidTypeError, MethodNotImplementedError

Constant Summary collapse

BASE_ATTRIBUTES =
%w(row_number record_with_error copied_errors collection)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(arr_or_hash = {}) ⇒ Base

Generalizes the constructor of ActiveModel::Model to make it work with an array argument. When an array argument is passed, it calls formatted_hash method to generate a Hash and then pass it to the ActiveModel::Model constructor

Parameters:

arr_or_hash

Array or Hash of values which represents an excel column.



135
136
137
138
139
140
141
142
# File 'lib/rails_spreadsheet_reader/base.rb', line 135

def initialize(arr_or_hash = {})
  self.copied_errors = ActiveModel::Errors.new(self)
  if arr_or_hash.is_a?(Array)
    super(self.class.formatted_hash(arr_or_hash))
  else
    super(arr_or_hash)
  end
end

Instance Attribute Details

#collectionObject

Returns the value of attribute collection.



18
19
20
# File 'lib/rails_spreadsheet_reader/base.rb', line 18

def collection
  @collection
end

#copied_errorsObject

Returns the value of attribute copied_errors.



17
18
19
# File 'lib/rails_spreadsheet_reader/base.rb', line 17

def copied_errors
  @copied_errors
end

#record_with_errorObject

Returns the value of attribute record_with_error.



16
17
18
# File 'lib/rails_spreadsheet_reader/base.rb', line 16

def record_with_error
  @record_with_error
end

#row_numberObject

Returns the value of attribute row_number.



15
16
17
# File 'lib/rails_spreadsheet_reader/base.rb', line 15

def row_number
  @row_number
end

Class Method Details

.array_to_hash(arr) ⇒ Object

Transform an array [a0, a1, a2, …] to a Hash { a0 => 0, a1 => 1, etc } which is the format required by the #formatted_hash method.



209
210
211
# File 'lib/rails_spreadsheet_reader/base.rb', line 209

def self.array_to_hash(arr)
  Hash[[*arr.map { |e| e.to_sym }.map.with_index]]
end

.formatted_hash(array) ⇒ Object

Returns the Hash representation of a given array using self.format method (which internally uses self.columns). The keys of this Hash are defined in the self.columns method and the values of each key depends on the order of the columns.

For example, given the following self.columns definition

def self.headers
  [:username, :email, :gender]
end

Or

def self.headers
  { :username => 0, :email => 1, :gender => 2 }
end

Row.formatted([username [email protected] male]) will return { username: ‘username’, email: ‘[email protected]’, gender: ‘male’ }

Parameters:

array

Array of values which represents an excel column.

Returns:

Hash representation of the given array which maps columns names to the array values.



115
116
117
118
119
120
121
122
123
124
# File 'lib/rails_spreadsheet_reader/base.rb', line 115

def self.formatted_hash(array)

  if format.keys.count > 0
    parsed_row = {}
    format.each_pair { |key, col| parsed_row[key] = array[col] }
    return parsed_row
  end

  return nil
end

.headersObject

Defines the columns of the excel that the class will read. This method must return a Array of strings/symbols (representing columns names) or a Hash (which map column names to columns indexes).

Array Example

def self.headers
  [:username, :email, :gender]
end

Hash Example

def self.headers
  { :username => 0, :email => 1, :gender => 2 }
end

Returns:

An Array or a Hash defining the columns of the excel.



85
86
87
88
89
90
# File 'lib/rails_spreadsheet_reader/base.rb', line 85

def self.headers
  fail(
      MethodNotImplementedError,
      'Please implement this method in your class.'
  )
end

.last_recordObject



32
33
34
# File 'lib/rails_spreadsheet_reader/base.rb', line 32

def self.last_record
  @last_record ||= nil
end

.last_record=(record) ⇒ Object



36
37
38
# File 'lib/rails_spreadsheet_reader/base.rb', line 36

def self.last_record=(record)
  @last_record = record
end

.open(spreadsheet_file) ⇒ Object



171
172
173
# File 'lib/rails_spreadsheet_reader/base.rb', line 171

def self.open(spreadsheet_file)
  Roo::Spreadsheet.open(spreadsheet_file)
end

.persist(collection) ⇒ Object

Persist all rows of collection if they all are valid

Parameters:

row_collection

SpreadsheetReader::RowCollection instance



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rails_spreadsheet_reader/base.rb', line 149

def self.persist(collection)
  if collection.valid?
    ActiveRecord::Base.transaction do
      collection.each do |row|
        # If any of validations fail ActiveRecord::RecordInvalid gets raised.
        # If any of the before_* callbacks return false the action is cancelled and save! raises ActiveRecord::RecordNotSaved.
        begin
          row.persist
        rescue ActiveRecord::RecordInvalid => e
          row.record_with_error = e.record
          collection.invalid_row = row
          rollback
        rescue ActiveRecord::RecordNotSaved => e
          row.model_with_error = e.record
          collection.invalid_row = row
          rollback
        end
      end
    end
  end
end

.read(spreadsheet_file) ⇒ Object

Read and validates the given #spreadsheet_file. Persistence is triggered after all validation pass

Parameters:

spreadsheet_file

File instance

Returns:

row_collection

SpreadsheetReader::RowCollection instance



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/rails_spreadsheet_reader/base.rb', line 184

def self.read(spreadsheet_file)

  if headers.empty?
    raise MethodNotImplementedError
  end

  spreadsheet = open(spreadsheet_file)
  collection = RailsSpreadsheetReader::RowCollection.new

  # Populate collection
  (starting_row..spreadsheet.last_row).each do |row_number|
    parameters = formatted_hash(spreadsheet.row(row_number))
    parameters[:row_number] = row_number
    parameters[:collection] = collection
    collection << self.new(parameters)
  end

  # Validation and persist
  persist(collection)

  collection
end

.starting_rowObject

Defines the starting row of the excel where the class should start reading the data.

Returns:

A integer representing the starting row of the data.



65
66
67
# File 'lib/rails_spreadsheet_reader/base.rb', line 65

def self.starting_row
  2
end

Instance Method Details

#check_record_with_errorObject



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

def check_record_with_error
  if record_with_error.present? and record_with_error.errors.any?
    record_with_error.errors.full_messages.each do |msg|
      @errors.add(:base, msg)
    end
  end
end

#modelsObject



40
41
42
43
44
45
# File 'lib/rails_spreadsheet_reader/base.rb', line 40

def models
  fail(
      MethodNotImplementedError,
      'Please implement this method in your class.'
  )
end

#persistObject



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rails_spreadsheet_reader/base.rb', line 47

def persist
  models = self.class.models.is_a?(Array) ? self.class.models : [self.class.models]
  models.each do |model|
    method_name = model.model_name.human.downcase
    if respond_to?(method_name)
      instance = model.new(send(model.model_name.human.downcase))
    else
      instance = model.new(as_json(except: BASE_ATTRIBUTES))
    end
    instance.save!
  end
end