Reputation: 2118
I created a controller an model to import an Excel sheet, and insert or update records in the database.
It works fine, and I now want to add a counter of inserts / updates to notify the user. I was expecting to do it using instance variables, but this does not work: the variables are not known inside the model.
Can you help me understand why and find a solution?
Here is the controller:
class ClassificationValuesImportsController < ApplicationController
# Check for active session
before_action :authenticate_user!
def new
@classification = Classification.find(params[:classification_id])
@classification_values_import = ClassificationValuesImport.new
end
def create
@update_counter = 0
@insert_counter = 0
@classification = Classification.find(params[:classification_id])
@classification_values_import = ClassificationValuesImport.new(params[:classification_values_import])
if @classification_values_import.file.nil?
render :new, notice: t('FileNameCannotBeEmpty')
end
if @classification_values_import.save
redirect_to @classification, notice: "#{t('ImportedObjects')}: #{@insert_counter} inserted, #{@update_counter} updated"
else
@classification_values_import.errors.full_messages.each do |msg|
puts msg
@classification.errors[:base] << msg
end
render :new
end
end
end
Here is the model:
class ClassificationValuesImport
include ActiveModel::Model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :file, :parent_id
def initialize(attributes = {})
attributes.each { |name, value| send("#{name}=", value) }
end
def persisted?
false
end
def save
if imported_values_lists.map(&:valid?).all?
imported_values_lists.each(&:save!)
puts "///////////////////// Count of linked values_lists /////////////////////"
print "Updates : "
puts @update_counter
print "Insert : "
puts @insert_counter
true
else
imported_values_lists.each_with_index do |column, index|
column.errors.full_messages.each do |message|
errors.add :base, "Row #{index+2}: #{message}"
end
end
false
end
end
def imported_values_lists
@imported_values_lists ||= load_imported_values_lists
end
def load_imported_values_lists
# Read input file
spreadsheet = self.open_spreadsheet(file)
puts spreadsheet.sheets
puts spreadsheet.sheets[1]
spreadsheet.default_sheet = spreadsheet.sheets.last
header = spreadsheet.row(1)
# Get the list of values lists to upload
@classification = Classification.find(parent_id)
playground_id = @classification.playground_id
values_lists = Array.new
@classification.values_lists_classifications.order(:sort_order).each do |link|
values_lists[link.sort_order] = link.values_list_id
end
header = spreadsheet.row(1)
# Upload or update values for each values list
(2..spreadsheet.last_row).map do |i|
# Read column indexes
code = header.index("Code") +1
parent = header.index("Parent") +1
level = header.index("Level") +1
valid_from = header.index("Valid from") +1
valid_to = header.index("Valid to") +1
name = header.index("Name_en") +1
if record = Value.find_by(values_list_id: values_lists[spreadsheet.cell(i,level).to_i], code: spreadsheet.cell(i,code))
@update_counter += 1
else record = Value.new(playground_id: playground_id, values_list_id: values_lists[spreadsheet.cell(i,level).to_i], code: spreadsheet.cell(i,code))
@insert_counter += 1
end
#record.active_from = spreadsheet.cell(i,'Valid from')
#record.active_to = spreadsheet.cell(i,'Valid to')
record.name = spreadsheet.cell(i,name)
record.description = 'HCL upload test'
record.created_at = record.created_at || Time.now
record.updated_at = record.updated_at || Time.now
#record.anything = { 'Comment' => 'This is a test', 'Author' => 'Fred'}.to_json
### create first translation for current_user if data is available
#record.name_translations.build(field_name: 'name', language: 'en', translation: record.name) unless record.name.blank?
#record.description_translations.build(field_name: 'description', language: language, translation: record.description) unless record.description.blank?
puts "test"
puts record.attributes
record
end
end
def open_spreadsheet(file)
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path, csv_options: {col_sep: ";"})
# when ".xls" then Roo::Excel.new(file.path, nil, :ignore)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
end
Upvotes: 0
Views: 49
Reputation: 1415
You can define transient attributes in your model:
attr_accessor :update_counter, :insert_counter
... and them set and access the values both from the controller and the model, as a regular attribute:
@classification.update_counter = 0
@classification.insert_counter = 0
You should be careful, however, as this approach can make it harder to debug where the values are coming from.
I believe the best thing to do would be to move the entire logic to the model, handling update_counter
and insert_counter
as instance variables, and then expose a public getter for them. You could use attr_reader :update_counter, :insert_counter
instead of attr_accessor
in this case. Then, you can use @classification.update_counter
to obtain the value in the controller.
If the model needs any information from the controller, you could pass them as arguments to the save
method, and pass them forward as needed.
Upvotes: 1