Resources

Summary

# config/application.rb
require 'csv'

# config/routes.rb
  resources :products do
    collection { post :import }
  end

# products_controller.rb
  def index
    @products = Product.all
    respond_to do |format|
      format.html
      format.csv { send_data @products.to_csv(['name', 'category', 'price']) }
    end
  end

  def import
    Product.import(params[:file])
    redirect_to root_url, notice: "Products imported."
  end

# product.rb
class Product < ApplicationRecord
  def self.to_csv(fields = column_names, options = {})
    CSV.generate(options) do |csv|
      csv << fields
      all.each do |product|
        csv << product.attributes.values_at(*fields)
      end
    end
  end

  def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|
      product_hash = row.to_hash
      product = find_or_create_by!(name: product_hash['name'], category: product_hash['category'])
      product.update_attributes(product_hash)
    end
  end
end

# index.html.erb
<%= form_tag import_products_path, multipart: true, class: 'form-inline' do %>
  <div class="form-group">
    <%= link_to "Export CSV", products_path(format: "csv"), class: 'btn btn-primary' %>
  </div>
  <div class="form-group">
    <%= file_field_tag :file, class: '' %>
  </div>
  
  <div class="form-group">
    <%= submit_tag "Import CSV", class: 'btn btn-info' %>
  </div>
<% end %>

If you need to background process the file import, check out the ActiveJob branch of this episode's source

https://github.com/driftingruby/035-importing-and-exporting-csv-data/compare/ActiveJobs?expand=1