この記事の続きです
まずは作ろうとしているものをざっくり説明
「未経験のエンジニアが実践経験を積む機会がない」という課題が世の中にあるんじゃないかと思いました。
プログラミング言語やフレームワークを学ぶ方法はいくらでもあると思いますが、エンジニアが実際に仕事をするときって、プログラミング言語だけじゃなくていろいろとありますよね。
まずアプリケーション動くところまで環境構築しないといけないですし、コミュニケーションにはSlack使ったり、タスクは普通JiraなりRedmineなりで管理されてますし、コードを書くにしても動くコードだけじゃなくてテストコードも必要ですし、githubでPR出したらそれで終わりじゃなくて、マージされるまでレビューに対応したり必要ですし。
未経験のエンジニアがこのあたりを経験できていれば、非エンジニアからエンジニアへの転職がもっとスムーズになるんじゃないかと思いました。
あと、重要なポイントとして、彼ら(未経験エンジニア)の不安も払しょくできると思います。
どうやったらこういうリアルな体験ができるかというと、現役のエンジニアと一緒に仕事(?)をしてもらうのが一番だと考えたので、
現役のエンジニアが出した「お題」を、未経験のエンジニアに解決してもらう(PRがマージされるところまでやってもらう)という流れを生み出すきっかけになるマッチングシステムを作ってみよう、というのがざっくりした構想です。
あまり細かいところは考えずミニマムでパッと作って反応を見ます。
ER図を考える
なにはともあれDBから考えます。
手書きですが、とりあえずこんな感じになりそうです。fieldsはちょっと適当に書いてしまいました。
現役エンジニアがtask(お題)を投稿して、未経験のエンジニアが気になったお題に応募(application)するということでとりあえず必要なテーブルは三つです。
最初は現役エンジニア役は僕ひとりでやるつもりなので、tasksは管理画面から登録します。
ユーザーがお題に応募すると、status: :pending なapplicationレコードが作られて、お題提出側が、承認するとstatus: :acceptedに、却下するとstatus: :rejectedになります。
tasksのend_atを過ぎると自動的にrejectedになります。
モデルを作る
まずはdeviseを入れます。
devise、嫌われてる印象がありますが僕は好きです。あのソースコードを読むのは確かにナイトメアですが、ドキュメントが充実してる(かつ大抵のやりたいことはできるように作られている)ので大抵の場合は避けられるナイトメアだと思います。
$ bundle $ bundle exec rails generate devise:install $ vim config/environments/development.rb # default_url_optionsのやつ $ bundle exec rails g devise User RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment invoke active_record create db/migrate/20191012061040_devise_create_users.rb create app/models/user.rb invoke rspec create spec/models/user_spec.rb invoke factory_bot create spec/factories/users.rb insert app/models/user.rb route devise_for :users
migrationファイルを適当に修正してmigrate
$ bundle exec rails db:migrate RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment == 20191012061040 DeviseCreateUsers: migrating ================================ -- create_table(:users) -> 0.0346s -- add_index(:users, :email, {:unique=>true}) -> 0.0228s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0246s -- add_index(:users, :confirmation_token, {:unique=>true}) -> 0.0226s -- add_index(:users, :unlock_token, {:unique=>true}) -> 0.0242s -- add_index(:users, :display_name, {:unique=>true}) -> 0.0236s == 20191012061040 DeviseCreateUsers: migrated (0.1531s) =======================
webpackerがどうとか警告出るのでGemfileからwebpackerを消しておきます。
余談ですが、一個前のプロジェクト(個人プロジェクト)では途中からwebpackerを消してwebpackに移行しました。 そのときめちゃくちゃ苦労したのでwebpackerという文字列を見るとネガティブな感情が沸き起こってしまいます。
今回は厄介にならないうちに消しさります。
$ vim Gemfile $ bundle
shoulda-matchersも好きなのでいれておきます。
user
# $ cat spec/models/user_spec.rb require 'rails_helper' RSpec.describe User, type: :model do describe 'associations' do it { should have_many(:applications).dependent(:destroy) } end describe 'validations' do subject { FactoryBot.build(:user, display_name: 'a') } it { should validate_presence_of(:display_name) } it { should validate_presence_of(:email) } it { should validate_uniqueness_of(:display_name).case_insensitive } it { should validate_uniqueness_of(:email).case_insensitive } end end
一個だけテスト落ちるので、次はApplicationモデルを作ります
Failures: 1) User associations is expected to have many applications dependent => destroy Failure/Error: it { should have_many(:applications).dependent(:destroy) } Expected User to have a has_many association called applications (no association called applications) # ./spec/models/user_spec.rb:5:in `block (3 levels) in <top (required)>'
ところでApplicationモデルって名前なにかと衝突しそうで怖いですね、大丈夫ですかね
$ bundle exec rails g model Application user:references status:integer invoke active_record create db/migrate/20191012070112_create_applications.rb create app/models/application.rb invoke rspec create spec/models/application_spec.rb invoke factory_bot create spec/factories/applications.rb
# $ cat app/models/application.rb class Application < ApplicationRecord belongs_to :user end
「Application」はやっぱり強烈に嫌な感じがしますね、、変更します。「応募」というとapplicationだと思うんですけどね、、
Application < ApplicationRecord
ですからね
TaskApplicationにします
$ bundle exec rails g model TaskApplication user:references task:references status:integer invoke active_record create db/migrate/20191012073325_create_task_applications.rb create app/models/task_application.rb invoke rspec create spec/models/task_application_spec.rb invoke factory_bot create spec/factories/task_applications.rb
しかもここでTaskApplicationがtaskにbelongs_toなので先にTaskを作らないといけないことに気付きました。はぁ、自分は何をやっているんだ、、、
migrationが外部キーのほげほげで落ちるので一旦TaskApplicationはrails d modelします
$ bundle exec rails d model TaskApplication
というわけでTask作成
$ bundle exec rails g model Task title:string description:text end_at:datetime invoke active_record create db/migrate/20191012074205_create_tasks.rb create app/models/task.rb invoke rspec create spec/models/task_spec.rb invoke factory_bot create spec/factories/tasks.rb
テストはUserモデルと似たような感じです。例によってhas_manyのテストは落ちます
$ cat spec/models/task_spec.rb require 'rails_helper' RSpec.describe Task, type: :model do describe 'associations' do it { should have_many(:task_applications).dependent(:destroy) } end describe 'validations' do it { should validate_presence_of(:title) } it { should validate_presence_of(:description) } it { should validate_presence_of(:end_at) } end end
こんどこそTaskApplicationを作成
$ bundle exec rails g model TaskApplication user:references task:references status:integer
テストはこれも同じような感じです。
# $ cat spec/models/task_application_spec.rb require 'rails_helper' RSpec.describe TaskApplication, type: :model do describe 'associations' do it { should belong_to(:task) } it { should belong_to(:user) } end describe 'validations' do it { should validate_presence_of(:status) } end end
statusはenumです
class TaskApplication < ApplicationRecord belongs_to :user belongs_to :task enum status: { pending: 0, accepted: 1, rejected: 2, } validates :status, presence: true end
$ bundle exec rspec ............ Finished in 0.16116 seconds (files took 1.77 seconds to load) 12 examples, 0 failures
これでモデルがそろいました。
つぎはControllerをやっていきます