Upgrade to Pro — share decks privately, control downloads, hide ads and more …

普通のCSVアップロードフォームを作りたい(改)

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for TAKAHASHI Kazunari TAKAHASHI Kazunari
August 20, 2014
1.1k

 普通のCSVアップロードフォームを作りたい(改)

Avatar for TAKAHASHI Kazunari

TAKAHASHI Kazunari

August 20, 2014
Tweet

Transcript

  1. ✓ Kazunari Takahashi ✓ Yokohama.rb ✓ 2012 Ruby kaja award

    winner ✓ MinatoRubyKaigi01 organizer ✓ TDDBC Yokohama TA @1syo
  2. class CreateUsers < ActiveRecord::Migration! def change! create_table :users do |t|!

    t.string :first_name, null:false! t.string :last_name, null:false! t.string :email, null:false! t.timestamps! end! ! add_index :users, :email, unique: true! end! end Users
  3. class Admin::UsersController < ApplicationController! def index! end! ! def upload!

    f = params[:data_file]! flash[:notice] = 'Upload successful'! CSV.open(f.path, 'rb', encoding: 'UTF-8').each do |row|! User.create(! ! ! ! email: row[0], ! ! ! ! first_name: row[1], ! ! ! ! last_name: row[2]! ! ! )! end! redirect_to action: :index! end! end Admin:UsersController
  4. <div class="container">! <h1>Users</h1>! <% if flash[:notice].present? %>! <div class="bg-success"><%= flash[:notice]

    %></div>! <% end %>! <%= form_tag(upload_admin_users_path, ! method: :post, multipart: true, role: "form", ! ! ! ! class: "form-inline") do %>! <div class="form-group">! <%= file_field_tag :data_file, class: "form-control" %>! </div>! <div class="form-group">! <%= button_tag "Upload", class: ["btn","btn-default"] %>! </div>! </div>! <% end %>! </div> index.html.erb
  5. def upload! f = params[:data_file]! if f.blank?! flash[:notice] = 'File

    not found'! redirect_to action: :index! return! end! ! flash[:notice] = 'Upload successful'! CSV.open(f.path, 'rb', encoding: 'UTF-8').each do |row|! unless row[0] =~ /\A[a-z.-_]+@[a-z.-_]+\z/! next! end! ! User.create(! email: row[0], first_name: row[1], last_name: row[2])! end! redirect_to action: :index! end Admin:UsersController Added!
  6. f = params[:data_file]! if f.blank?! ! flash[:notice] = 'File not

    found'! ! redirect_to action: :index! return! end Admin:UsersController
  7. class Admin::UsersController < ApplicationController! def index! @user_import = UserImport.new! end!

    ! def import! @user_import = UserImport.new(params[:user_import])! if @user_import.save! redirect_to url_for(action: :index), ! ! ! ! ! notice: 'Upload successful'! else! render :index! end! end! end admin/users_controller.rb
  8. <%= form_for(@user_import, url: import_admin_users_path,! html: {method: :post, multipart: true}) do

    |f| %>! <% if @user_import.errors.any? %>! <div class="alert alert-danger">! <ul>! <% @user_import.errors.full_messages.each do |msg| %>! <li><%= msg %></li>! <% end %>! </ul>! </div>! <% end %>! <% end %>! … index.html.erb
  9. class UserImport! CSV_OPTIONS = {encoding: 'UTF-8'}.freeze! include ActiveModel::Model! ! attr_accessor

    :file! ! validates :file, presence: true! validate :csv_format, ! ! ! ! if: Proc.new { |m| m.file.present? }! ! def save; end! ! private! def csv_format;end! def path; end! end user_import.rb
  10. class User < ActiveRecord::Base! validates :first_name, ! ! ! length:

    { maximum: 100 }, presence: true! validates :last_name, ! ! ! length: { maximum: 100 }, presence: true! validates :email, ! ! ! length: { maximum: 100 }, presence: true, uniqueness: true! end user.rb
  11. def save! return false unless valid?! ! User.transaction do! CSV.read(path,

    "r", CSV_OPTIONS).each_with_index do |row, index|! user = User.new(! ! ! ! email: row[0], ! ! ! ! first_name: row[1], ! ! ! ! last_name: row[2]! ! ! )! unless user.save! errors.add(! ! ! ! ! "line #{index+1} : ", ! ! ! ! ! user.errors.full_messages.join(" , “)! ! ! ! )! end! end! ! if errors.any?! raise ActiveRecord::Rollback! end! end! ! !errors.any?! end user_import.rb
  12. private! def csv_format! CSV.open(path, "r", CSV_OPTIONS) { |csv| }! rescue!

    errors.add(:file, "is invalid csv format")! end! ! def path! if file.respond_to? :path! file.path! else! file! end! end user_import.rb
  13. describe "#save" do! subject { UserImport.new(file: file) }! context "༗ޮͳϑΥʔϚοτͷ࣌"

    do! let(:file) do! ! ! Rails.root.join(“spec/fixtures/valid.csv")! ! ! end! let(:error_count) { CSV.read(file).count }! it { expect(subject.save).to be_truthy }! it do! ! ! expect { subject.save }.to! ! ! ! ! change(User, :count).by(error_count)! ! ! end! end! end user_import_spec.rb
  14. RSpec.describe UserImport do! describe "#save" do! subject { UserImport.new(file: file)

    }! context "UserϞσϧͰΤϥʔͰ͕͋Δ࣌" do! let(:file) do! ! ! Rails.root.join(“spec/fixtures/valid.csv")! ! ! end! let(:errors) { double(add: nil, clear: nil, empty?: nil) }! before do! allow_any_instance_of(User).to receive(:save) { false }! expect(subject).to receive(:errors) { errors }.twice! end! it { expect(subject.save).to be_falsey }! it do! ! ! expect { subject.save }.not_to ! ! ! ! change(User, :count)! ! ! end! end! end! end user_import_spec.rb
  15. RSpec.describe Admin::UsersController do! describe "POST import" do! let(:attributes) { {

    file: Rack::Test::UploadedFile.new(file, "text/plan") } }! ! describe "with valid params" do! let(:file) { Rails.root.join("spec/fixtures/valid.csv") }! let(:record_count) { CSV.read(file).count }! ! it "creates new Users" do! expect {! post :import, {user_import: attributes}! }.to change(User, :count).by(record_count)! end! ! it "redirects to the created user" do! post :import, {user_import: attributes}! expect(response).to redirect_to(action: :index)! end! end! end! end admin/users_controller_spec.rb