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

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

TAKAHASHI Kazunari
August 20, 2014
990

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

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