- 投稿日:2019-08-27T23:47:03+09:00
Rails6でブログのタグのacts-as-taggable-onがエラーになったので別のタグgem(gutentag tags)を使う
Rails6にアップデートした時の問題
Rails6になってから、今まで使っていた
acts-as-taggable-onがエラーになるようになった。原因は
まあ悪い意味での原因というわけではなく
https://github.com/rails/rails/pull/35933
この部分で
set_attribute_wasが消されたため発生したものまた、これの対応のブランチもあるのだが、初歩的なバグを含むため、ブランチ切り替えの対応してもあまり意味がない。
とはいえ、ライブラリへのPRも全然反映されないし、Gemfileが汚れるのも嫌なので別のやつを実験的に使ってみた。
https://github.com/pat/gutentag
というやつで、スターは315の、RubyGemダウンロード数は54,474というところなので、act-as-taggable-onのわずか6%程度のスター数となり、多少心もとない。
開発当初のブログがあるので、読んでみた
個人的には、act-as-taggable-onしか使ったことなかったので、とりあえず調べていたら、どうも2013年頃に開発されたようだ。
https://freelancing-gods.com/2013/07/11/gutentag-simple-rails-tagging.html
https://github.com/pat/gutentag/blob/master/lib/gutentag/persistence.rb
永続化周りをforwardableを使って委譲して実装してある
使ってみた
環境
- Rails 6.0.0
- ruby 2.6.3
Gemfile
記載したら、
bundle installするgem 'gutentag', '~> 2.5'インストール設定
ここは、よくあるお決まりのやつをただ打っていくだけ
migrateすることで、gutentagにより作られたマイグレーションファイルが実行され、使えるようになる
使う側のモデルにカラムを追加することなく使えるところが楽で良いbundle exec rake gutentag:install:migrations rails db:migrateSchema例
create_table "blogs", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false endBlog.rb
今回、ブログにタグ機能をもたせたいので、
ここにGutentag::ActiveRecord.call selfを置くclass Blog < ApplicationRecord Gutentag::ActiveRecord.call self include TagExtensions end FactoryBot.define do factory :blog do tags_as_string { "hoge,fuga,piyo" } end endTagExtensions.rb
これは、いわゆる
attr_accessorの役割をincludeして使うために別途作っただけ
メソッド毎モデルに直で書いても良いが、他のモデルでも使いたかったのでこのようにサクッと作っただけ。(若干気に入らないので後で修正する気がする)module TagExtensions def tags_as_string tag_names.join(', ') end def tags_as_string=(string) self.tag_names = string.split(/,\s*/) end end作成時
View
一般的なinputタグに先程TagExtensionsで作ったgetterメソッドを配置しておく
.form = f.label :tags_as_string = f.text_field :tags_as_string, value: @blog.tags_as_stringController#create
Scaffoldして、必要なところだけ抜粋しておく
フォームから送られて新規作成するところの処理と、Strong Parametersの設置class BlogsController < ApplicationController def create @blog = Blog.new(blog_params) respond_to do |format| if @blog.save format.html { redirect_to blog_url(@blog), notice: 'Blog was successfully created.' } format.json { render :show, status: :created, location: @blog } else format.html { render :new } format.json { render json: @blog.errors, status: :unprocessable_entity } end end end def blog_params params.require(:blog).permit(:tags_as_string) end end一覧表示時
View
tag_namesなどで引っ張ってこれる
- @blogs.each do |blog| - blog.tag_names.each do |tag| = tagController#index
例えば
includesなどをしないとN+1で死ぬので注意class BlogsController < ApplicationController def index @blogs = Blog.includes(:tags) end end挙動確認
実際にテストなどで挙動見てみるとこんなように使える
blog = Blog.create! # ブログ用レコード作る blog.tag_names << 'hogehoge' blog.tag_names #=> ['hoge'] blog.tag_names << 'fuga' << 'piyo' blog.tag_names #=> ['hoge', 'fuga', 'piyo'] blog.tag_names -= ['fuga'] blog.tag_names #=> ['hoge', 'piyo'] blog.save # 保存する感想
gutentag自体は、rails6にすぐ対応したcommitがあるし、issueのclose率も100%なのでけっこういいかなと思った。懸念点はpatという創造者しかほぼ保守していないところ。
acts-as-taggable-onは、使ってる人が多いので、また修正版が上がりそうなので、そのあたり様子見
- 投稿日:2019-08-27T22:58:43+09:00
HerokuのDB更新時のActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column ... does not existの対処方
PG::UndefinedColumn: ERROR
ローカル環境に置けるrails db:migrateはエラーが起きなかったが、
本番環境(Heroku)に heroku run rails db:migrateをすると出るエラー今回起きた要因
これが発生した要因として
今までは普通にできたが、不要なカラができてしまったので削除する。
そしてHerokuも同様に反映させようとしたが今回のエラーが発生。
ゆえに今回はカラム削除のDBファイルがからんでいると推測。エラー内容 &対処法
PG::UndefinedColumn: ERROR: column "string" for relation "users" does not existとでていた。
そこでusersテーブルの削除したファイルを見てみると..._remove_delete_to_usersclass RemoveDelete2ToUsers < ActiveRecord::Migration[5.1] def change remove_column :users, :job, :string remove_column :users, :string, :string remove_column :users, :basic_overtime_pay, :string remove_column :users, :integer, :string remove_column :users, :uid, :string remove_column :users, :string, :string remove_column :users, :image, :string remove_column :users, :string, :string remove_column :users, :encrypted_password, :string remove_column :users, :string, :string remove_column :users, :reset_password_token, :string remove_column :users, :string, :string remove_column :users, :note, :string remove_column :users, :string, :string end endこのようにカラムの型がたくさんある状態になっている。
そこでここを整理整頓してみる。..._remove_delete_to_usersclass RemoveDelete2ToUsers < ActiveRecord::Migration[5.1] def change remove_column :users, :job, :string remove_column :users, :basic_overtime_pay, :integer remove_column :users, :uid, :string remove_column :users, :image, :string remove_column :users, :encrypted_password, :string remove_column :users, :reset_password_token, :string remove_column :users, :note, :string end endここで改めて heroku run rails db:migrateをすると
通った!!!
ロールバックするさいもカラムと型が合わずにうまくいかない!
みたいなことがあるみたいなのです。
今回のように同じエラーがでてるかたは参考にしてみていただけると幸いです。
- 投稿日:2019-08-27T22:08:12+09:00
HTTPリクエストについて
HTTPリクエストについて
1.get
get = 通常のアクセス。ページを表示。
2.post
post = フォームを使ってデータを送信。
・何か新しいデータを追加するとき
・ログインの処理をするときにデータを送る場合3.patch / put
patch / put = データの更新を行う。
※若干ニュアンスが違う。
・patch => 現在保存されているデータがあって、そのデータを更新したい場合
・put => 現在データがあるかどうかわからない状態でput形式でのデータを送る。もしデータが入っていなかったら「新規作成」を行い、もしデータが入っていたらそのデータに「上書き保存」をする。4.delete
delete = データの削除を行う。
- 投稿日:2019-08-27T22:07:38+09:00
rails viewの繰り返しでitemの一覧を表示する方法
railsのviewでアイテムをfor文を使って表示しています。
<%for i in 0..4 do%> <li><%=link_to @ranking_articles[i].title,article_path(@ranking_articles[i].id)%><span>(<%=@previews[i]%>)</span></li> <%end%>
- 投稿日:2019-08-27T21:35:58+09:00
Railsの勉強会に行ってきた話
こんにちは。はじめての投稿になるので、よろしくお願いします。
感想
とても楽しい時間を過ごすことができました。
技術的な話としては、MVC構造は1960年代?ごろから使われていたものらしく、結構古いもののようで、それをRailsは未だに使っていて大丈夫なの?っていうのが話題にでたときに、Railsが批判されてる理由が少しわかった気がしました。自由時間に現役のエンジニアの方からアドバイスをもらうことができて、とても参考になりました。きつい時もあるけど、楽しくコードを書いていこうというのが励みになりました。勉強会の後に、数人で食事に行き、業界のことなどいろいろ話ができて楽しかったです。ちなみに奢ってもらっちゃいました笑
Ruby界隈は、親切な人が多いなと思いましたし、いろんな所でイベントがあり、活発なのでまた行ってみたいと思いました。
- 投稿日:2019-08-27T21:28:27+09:00
プログラミング学習(三日目)ProgateでRuby On Rails レッスン3
今日学んだこと
find_byメソッドで、データベースからデータを取得
モデル名.find_by(カラム名:値)で、指定したカラムの値を持つレコードを取得できる
例えば、
>a = Post.find_by(id:3)
で、変数aにpostsテーブルのidが3のレコードを代入できる。
そしてここから、
>a.カラム名
とすれば、idが3の指定したカラム名の値を出力できる。ルーティングのURLに:idで、任意のすべての値を適用
具体的に言うと、
routes.rbget "/home/:id" => "home#about"とすると、URLが「/home/a」でも、「/home/xyz」でも、「/home/1234567890」でも、
homeコントローラのaboutアクションを指定されると言う訳。
ちなみに、ルーティングはファイルの上から順に該当するURLを探すので、:idを使う場合は下の方にやらないとやばい。paramsの使い方
そして上のURLの:idに該当する値は、コントローラ内のアクション内で取得できる。
paramsにハッシュで入っているらしく、params[:id]とやると取得できる。入力フォームを作成し、データベースに保存する方法。
これが少し複雑で難しかった。
「form_tag」と「redirect_to」と「post」を使うまずhtml.erbファイルに入力フォームを作るのだが、
<%= form_tag("送信先のURL") do %>
<textarea name="test"></textarea>
<input type="submit" value="送信">
<% end %>と作る。form_tagで送信先のURLを設定しつつ、入力内容の属性をname属性とし{test:"入力した内容"}というハッシュで、指定したURLに対応するコントローラのアクションへと送られる。
ちなみに、textareaにname属性を付与しないと送信できないらしい。そしてそのアクションへ入力内容が送られるわけだが、まずそこでここではpostsテーブルを用いているので、Postインスタンスを作成し保存する。その際にparamsを使い、任意のカラムが入力された内容になるインスタンスを作成する。
こんな感じ(postsテーブルにはcontentカラムがある)
**def create**
** @inputfile = Post.new(content:params[:test])**
** @inputfile.save**
** redirect_to("送信先のURL")**
**end**ちなみに「redirect_to」とは、アクションに対応するビューファイルが無い時にエラーにならないために、処理?データ?をほかのアクションへ送るメソッドである。
データの並べ替え
orderメソッドを用いて、「order(カラム名:並び順)」と指定するとその指定した並び順になる。昇順が「:asc」、降順が「:desc」
@posts=Post.new.order(id: :asc)とすると、idが1から並ぶ。結構抽象的に書いた分、早く書けたが少しわかりにくい可能性が、、、
- 投稿日:2019-08-27T20:58:52+09:00
#Ruby #Rails で 日付同士の差分が 何週間ぐらいかを 計算しようとしてみた記録 ( ActionView::Helpers::DateHelper が使えない ))
Ruby #Rails record to calculate how many weeks the difference between dates is (ActionView :: Helpers :: DateHelper cannot be used))
ActionView::Helpers::DateHelperが週に対応してないようなのでrequire 'action_view' require 'action_view/helpers' include ActionView::Helpers::DateHelper distance = if some_date.between?(1.weeks.after, 1.month.after) distance_weeks = (some_date - Date.current).to_i / 7 "#{distance_weeks}週間" else distance_of_time_in_words(Time.zone.now, some_date) endhttps://edgeapi.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
Original by Github issue
- 投稿日:2019-08-27T20:12:32+09:00
#Rails の ActionView::Helpers を直接 include して i18n チックに xヶ月 x日 x年 みたいなのを表示する例
Rails' ActionView :: Helpers is included directly to display i18n ticks like x month x day x year
rails console で動作確認した
ja.yml
なぜかトップレベルに設定する必要がありそう?
ja: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" less_than_x_minutes: one: 1分以内 other: "%{count}分未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_seconds: one: 1秒 other: "%{count}秒" x_minutes: one: 1分 other: "%{count}分" x_days: one: 1日 other: "%{count}日" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年"rails console
require 'action_view' require 'action_view/helpers' include ActionView::Helpers distance_of_time_in_words(Time.zone.now, 3.months.after) # => "3ヶ月"Ref
https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml
ja.yml 設定例
https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
Original by Github issue
- 投稿日:2019-08-27T19:51:09+09:00
Rails & Nuxt.jsのDocker環境をalpineイメージで構築
Ruby on Rails、Nuxt.js、MySQLのDocker環境を作成します。
Rails、Nuxtのalpine環境の構築手順はそれぞれだと多くあるですが、まとまったものがあまりない見つからなかったので、復習を兼ねてポストを作成します。準備
ディレクトリ作成
作業ディレクトリは任意です。
$ NEW_APP=rails-nuxt-app #任意のアプリ名 $ mkdir ${NEW_APP} $ cd ${NEW_APP} $ mkdir ./backend ./frontend
backendはRails用、frontendはNuxt用のディレクトリです。
まずは下記のファイルを修正していきます。. ├ backend │ ├ Dockerfile │ ├ Gemfile │ └ Gemfile.lock │ ├ frontend │ └ Dockerfile │ ├ docker-compose.yml └ .envdocker-compose.yml
.envdocker-compose.ymlで参照する環境変数を記載します。
ここではMySQLのrootパスワード、RailsおよびNuxt環境のホスト、ポート番号のみ定義します。
RailsとNuxtは、共にデフォルトのポートが3000番なので、後の利便性のためにいずれかを変えておきます。
(本記事ではNuxt側を8080に変更)MYSQL_ROOT_PASSWORD=password BACKEND_HOST=0.0.0.0 BACKEND_PORT=3000 FRONTEND_HOST=0.0.0.0 FRONTEND_PORT=8080
./docker-compose.yml.envで定義した変数を参照しています。
docker-compose.ymlのenvironment、 DockerfileのENVで同じ環境変数が定義されていた場合は、前者が使用されます。
(本記事ではDockerfile単体でもイメージ作成できるように環境変数の記載を残していますが、docker-compose.ymlに定義されていれば問題ありません)下記はDockerボリュームを作成します。
- mysqlのdatadir
- rubyのgem_home
- nodeのnode_modules
docker-compose.ymlversion: '3' services: db: image: mysql:5.7.27 restart: always volumes: - db-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} backend: build: ./backend ports: - ${BACKEND_PORT}:3000 command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b ${BACKEND_HOST}" volumes: - ./backend:/app - backend-bundle:/usr/local/bundle environment: - HOST=${BACKEND_HOST} - PORT=${BACKEND_PORT} depends_on: - db tty: true stdin_open: true frontend: build: ./frontend ports: - ${FRONTEND_PORT}:8080 command: /bin/sh -c "yarn dev" volumes: - ./frontend:/app - frontend-node_modules:/app/node_modules environment: - HOST=${FRONTEND_HOST} - PORT=${FRONTEND_PORT} tty: true volumes: db-data: backend-bundle: frontend-node_modules:backend
Rails環境構築のためのファイルを準備します。
./backend/GemfileRailsのバージョンのみ指定しておきます。
Gemfilesource 'https://rubygems.org' gem 'rails', '5.2.3'
./backend/Gemfile.lock空ファイルをtouchしておけばOKです。
./backend/DockerfileAlpine Linuxのパッケージは最低限のものだけインストールします。
開発を進めるうちにgemのインストールで依存エラーが発生した場合には、不足パッケージを都度追加しましょう。DockerfileFROM ruby:2.6.3-alpine ENV RUNTIME_PACKAGES "mysql-client mysql-dev tzdata nodejs" ENV DEV_PACKAGES "build-base curl-dev" ENV APP_HOME /app ENV TZ Asia/Tokyo ENV HOST 0.0.0.0 ENV PORT 3000 WORKDIR ${APP_HOME} ADD Gemfile ${APP_HOME}/Gemfile ADD Gemfile.lock ${APP_HOME}/Gemfile.lock RUN apk update \ && apk upgrade \ && apk add --update --no-cache ${RUNTIME_PACKAGES} \ && apk add --update --no-cache --virtual=.build-dependencies ${DEV_PACKAGES} \ && bundle install -j4 \ && rm -rf /usr/local/bundle/cache/*.gem \ && find /usr/local/bundle/gems/ -name "*.c" -delete \ && find /usr/local/bundle/gems/ -name "*.o" -delete \ && apk del --purge .build-dependencies \ && rm -rf /var/cache/apk/* COPY . ${APP_HOME} EXPOSE ${PORT} CMD ["rails", "server", "-b", ${HOST}]frontend
./frontend/DockerfileDockerfileFROM node:12.9.0-alpine ENV APP_HOME /app ENV PATH ${APP_HOME}/node_modules/.bin:$PATH ENV TZ Asia/Tokyo ENV HOST 0.0.0.0 ENV PORT 8080 WORKDIR ${APP_HOME} ADD . ${APP_HOME} RUN apk update \ && apk upgrade \ && yarn install \ && rm -rf /var/cache/apk/* EXPOSE ${PORT} CMD ["yarn", "dev"]アプリケーション作成
Rails、Nuxt環境にプロジェクトを作成します。
docker-compose runを実行したタイミングで、それぞれのDockerイメージがbuildされ、さらにコンテナが立ち上がります。
--no-deps... docker-compose.ymlでdepends_onorlinks指定するサービスは起動しない。
--rm... 処理を終えたコンテナを自動的に削除。backend
アプリケーション作成
rails newでRailsアプリケーションを作成します。
--apiオプションでAPIモードにしていますが、不要な方は外してください。$ docker-compose run --no-deps --rm backend rails new . --force --api --database=mysql --skip-bundleDB接続のため、下記のファイルを修正します。
. └ backend ├ config │ └ database.yml ├ Gemfile └ .env
./backend/.envdocker-compose.ymlで参照している
MYSQL_ROOT_PASSWORDと同じもの設定します。MYSQL_ROOT_PASSWORD=password
./backend/Gemfile.envから環境変数を読み込むdotenv-railsというgemを追加します。
Gemfile~~省略~~ group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem "dotenv-rails" #追加 end ~~省略~~
./backend/config/database.ymlDBへのアクセスに使用するパスワードを、環境変数から取得します。
database.yml~~省略~~ default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: <%= ENV.fetch("MYSQL_ROOT_PASSWORD") { '' } %> #環境変数を参照するように修正 host: db #localhostからdocker-compose.ymlのサービス名に修正 ~~省略~~frontend
アプリケーション作成
npxでNuxtアプリケーションを作成します。
後から追加/変更できるので、このタイミングではEnterキー連打でOKです。$ docker-compose run --rm frontend npx create-nuxt-app . ~~省略~~ create-nuxt-app v2.10.0 ✨ Generating Nuxt.js project in . ? Project name #アプリ名 <Enter> ? Project description #任意 <Enter> ? Author name #任意 <Enter> ? Choose the package manager #Yarn <Enter> ? Choose UI framework #None <Enter> ? Choose custom server framework #None <Enter> ? Choose Nuxt.js modules #(Nothing) <Enter> ? Choose linting tools #(Nothing) <Enter> ? Choose test framework #None <Enter> ? Choose rendering mode #Universal (SSR) <Enter> ? Choose development tools #(Nothing) <Enter>Dockerイメージ作成
アプリケーション作成時にできたDockerボリュームは削除しておきます。
$ docker-compose down --volume # もしくは docker volume rm ボリューム名各ファイルを修正した状態で、Dockerイメージをビルドします。
$ docker-compose builddocker-compose.ymlで
buildを指定しているbackendとfrontendのイメージが作成されたことを確認します。$ docker images --format "{{.Repository}}\t{{.CreatedSince}}" ${NEW_APP}* rails-nuxt-app_frontend About a minutes ago rails-nuxt-app_backend About a minutes agoHello World
最後にDockerコンテナを起動し、Rails、NuxtアプリケーションのHelloWorldを確認します、
docker-compose.ymlで定義したサービスを
-dオプション(デタッチモード)でバックグラウンド起動します。$ docker-compose up -dプロセスが立ち上がっていることを確認します。
$ docker-compose ps Name Command State Ports --------------------------------------------------------------------------------------- rails-nuxt-app_backend_1 /bin/sh -c rm -f /app/tmp/ ... Up 0.0.0.0:3000->3000/tcp rails-nuxt-app_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp rails-nuxt-app_frontend_1 docker-entrypoint.sh /bin/ ... Up 0.0.0.0:8080->8080/tcp失敗している場合は
docker-compose logsなどで原因を探りましょう。backend
RailsアプリケーションのDBを作成します。
$ docker-compose exec backend rails db:create Created database 'app_development' Created database 'app_test'ブラウザで http://localhost:3000/ を開きます。
frontend
ブラウザで http://localhost:8080/ を開きます。
お疲れさまでした。
次回は GraphQL の導入について投稿したいと思います。
- 投稿日:2019-08-27T19:43:44+09:00
【Rails jquery】jbuilderを使ってインクリメンタルサーチを実装する
前置き
映画情報が保存されたproductテーブルをインクリメンタルサーチする。
映画情報は次の画像のような形式で保存されているとする。
インクリメンタルサーチ実装のステップ
- ルーティングなどAPI側の準備をする
- テキストフィールドを作成する
- テキストフィールドに入力されるたびにイベントが発火するようにする
- イベント時に非同期通信できるようにする
- 非同期通信の結果を得て、HTMLを作成する
- エラー時の処理を行う
まずjbuilderについて
入力データをJSON形式で出力するテンプレートエンジン。gemfileにデフォルトで記述されているgem。
コントローラ内でrender json:と書かなくてもよい。jbuilderはview同様に、該当するアクションと同じ命名規則を用いる。
ex. view/comment/create.html.erb とある場合。
commentのcreateアクションに対応するjbuilderのファイルになるので
→views/comments/create.json.jbuilder とする。1. ルーティングなどAPI側の準備をする
respond_toを利用してフォーマット毎に処理を分ける
app/controlers/products_controller.rb#省略 def search @products = Product.where('title LIKE(?)', "%#{params[:keyword]}%").limit(20) respond_to do |format| format.html format.json end endapp/viws/products配下に「search.json.jbuilder」というファイルを作成する。
jbuilderファイルを作成したら次の通り編集する。
search.json.jbuilder##配列の形でjsonを取得できる # # [{"id":1, "tile":"オオカミ少女と黒王子", "image":"http://image~.jpeg", "detail": "八田鮎子の同名人気コミック~"}] json.array! @products do |product| json.id product.id json.title product.title json.image product.image_url json.detail product.detail end2. テキストフィールドを作成する
todo: 検索フォームのスクショ
search.html.erb#省略 <div class="container"> <header class="header header--section"> <h2 class="text-middle"> <i class="icon-movie color-gray-light"></i>投稿する作品を検索 </h2> </header> <%= form_tag('/products/search', method: :get) do %> <input class="search__query" name="keyword" placeholder="作品名で検索" type="text" value=""><button class="btn-search" title="検索" type="submit"><i class="icon-search"></i></button></input> <% end %> <form accept-charset="utf-8" action="/" class="js-search-submit" method="get"> </form> </div>3. テキストフィールドに入力されるたびにイベントが発火するようにする
テキストフィールドに文字が入力されるたびにイベントが呼び出されるようにする。
文字打ち込み終わった場合、つまりキーを離したら処理実行させるためkeyupメソッドを使用する。app/assets/javascripts/search.js$(function() { $(".search__query").on("keyup", function() { var input = $(".search__query").val(); console.log(input); }); });検索フォームのテキストフィールドのclass名はsearch__queryとなっている。
よって、「class="search__query”の部分のテキストフィールドがkeyupしたら、テキストフィールドの文字を取得して変数inputに代入する」処理となる。
search.html.erb//省略 <%= form_tag('/products/search', method: :get) do %> <input class="search__query" name="keyword" placeholder="作品名で検索" type="text" value=""><button class="btn-search" title="検索" type="submit"><i class="icon-search"></i></button></input> <% end %>4. イベント時に非同期通信できるようにする
キーが入力される度に非同期通信で映画タイトルを検索できるようにする。
1で設定したルーティングにアクセスし、コントローラで該当する映画タイトルを検索できるようにする。Ajax通信を実現するため、$.ajaxメソッドを使う。
HTTPメソッドはGETで、/products/searchのURLに{ keyword: input }を送信する。サーバから値を返す際は、jsonになるapp/assets/javascripts/search.js(function() { $(".search__query").on("keyup", function() { var input = $(".search__query").val(); $.ajax({ type: 'GET', url: '/products/search', data: { keyword: input }, dataType: 'json' }) }); });前述のapp/controlers/products_controller.rbを確認しておく。
app/controlers/products_controller.rbdef search @products = Product.where('title LIKE(?)', "%#{params[:keyword]}%").limit(20) respond_to do |format| format.html format.json end end$.ajaxのdataTypeでjsonを指定しているので、サーバはjson形式で値を返却する。
app/views/products/search.html.erbが読まれるが、json形式の場合は、app/views/products/search.json.jbuilderが読まれることになる。
処理成功の場合は該当する映画情報がjbuilderによってJSONに変換され、javascriptのファイルに返される。
5. 非同期通信の結果を得て、HTMLを作成する
非同期通信の結果をdoneの関数の引数から受取り、ビューに追加するためのHTMLを作成する。
app/assets/javascripts/search.js//省略 .done(function(products) { //emptyメソッドで指定したDOM要素の子要素のみを削除する $(".listview.js-lazy-load-images").empty(); if (products.length !== 0) { //foreachメソッドで配列に含まれる各要素に対して一度ずつ呼びだす。 products.forEach(function(product){ appendProduct(product); }); } else { appendErrMsgToHTML("一致する映画はありません"); } }) }); });上記結果をビューに反映できればok。
- 投稿日:2019-08-27T19:32:17+09:00
Rails ターミナルコマンド
・rake db:create 新しくデータベースを作成する方法
・rails g controller コントローラの作成
「rails g controller コントローラ名」
作成したいコントローラの名前を後ろに付けて実行する・rails d controller コントローラの削除
「rails g controller コントローラ名」
コントローラ名を間違えて作成してしまった場合・rails g model モデルの作成
マイグレーションファイルはテーブルの設計図
カラム名を追加できる・rake db:migrate
未実行のマイグレーションファイルの実行をできる
マイグレーションファイルを編集した時などに使う
- 投稿日:2019-08-27T18:51:51+09:00
予約システム pickadate.jsで実装 ~ヘルスケアwebサービスを自分で作る医者の日記~
クリニックの予約サイトを作るために、勉強中
https://qiita.com/Yuta_Fujiwara/items/66af40dcc5ce206933eb
こちらの記事を参考にさせていただいた。公式のドキュメントとにらめっこしていると、
なんとか、実装はできるようなった。作りたいのは、予約システム
jsからwebサーバーに送る処理の理解が必要
上の記事はajax使っているのね、
なんとか、ググってajaxの基本を1日で習得
vagrantでphpウェブサーバーを立ち上げる方法で実装$(function() { // datepicker表示イベント $('#date').pickadate(); // timepicker表示イベント $('#time').pickatime({ format: 'HH:i', // 24時間表記 interval: 30, // 表示間隔 min: [10,00], // 予約開始時間 max: [20,00] // 予約終了時間 }); // 予約ボタン押下イベント $('#submit').click(onClickSubmit); //予約ボタン押下処理 function onClickSubmit(){ $('#submit_result').remove(); var date = $('#date').val(); var time = $('#time').val(); if(date!='' && time !=''){ $.ajax({ async: true, url: 'http://192.168.33.10:8000/yoyaku_datepicker.php', type: "POST", dataType: 'json', data: { 'hiduke': date, 'jikan': time } }).done(function(data) { console.log(data.kekka + "を取得しました。"); }).fail(function() { console.log("処理を失敗しました。"); }); //予約完了メッセージ $('#result').after('<div id="submit_result" class="section__block section__block--notification"><p><strong>'+date+time+'〜</strong><br>予約受け付けました。</p></div>'); }else{ //予約失敗メッセージ $('#result').after('<div id="submit_result" class="section__block section__block--notification-red"><p>予約日・予約時間を選択してください。</p></div>'); } } });<?php $hiduke = $_POST['hiduke']; $jikan = $_POST['jikan']; if ($hiduke && $jikan) { $datepick['kekka'] = "ご指定の日時"; } else { echo false; } echo json_encode($datepick);最終的にはrailsでやりたいのだが、
データベースへの格納を理解できなきゃならない
railsのacctive recordかていうか、vagrant のwebサーバーでデータベースの取り扱いはできるのか??
道は長い、、、
- 投稿日:2019-08-27T18:07:40+09:00
【Rails】turbolinksやAjaxの影響でjQueryが正常に動作しないとき
はじめに
Rails app内で、カルーセルスライダーやsubmitの自動送信といったjQueryで実装した機能が正常に動作しない現象に何度か遭遇しました。
本記事ではその対処法を紹介します。注意
Rails学習中の初学者が備忘録を兼ねて書いています。
内容に誤りを含む可能性・さらに良い手法がある可能性が多分にありますので、参考にする際はその点ご留意ください。RubyとRailsのバージョン
下記のバージョンにて動作確認しています。
- Ruby 2.5.1
- Rails 5.2.1現象と原因、解決策
僕が遭遇したエラーは下記の2点でした。
- 初回読み込み時にjQuery(スライダー)が動かない。リロードしたら動き出す。 →(原因)Turbolinksが発動し、ページ読み込みを起点としたjQueryが発火しない
- AjaxでHTMLの一部を差し替えた後、差し替え部に埋め込んだjQuery(submit送信)が動かない。 →(原因)Ajaxで差し替えられた部分ではページ読み込みを起点とするjQueryが発火しない
(解決策)
どちらの問題も、イベントの発火タイミングを設定し直すことで修正できました。修正前のコード
custom.jsに下記の記載をしていました。custom.js// carousel-slider(bxslider) jQuery(document).ready(function() { $('.bxslider').bxSlider({ }); }); // submit自動送信 jQuery(document).ready(function() { $('.select-box').change(function() { $(this).parent().submit(); }); });上記の
jQuery(document).readyや、$(function()の記述では、ページの読み込みを起点として発火します。
つまり、画面の一部が切り替わった場合はイベントが発生しないことになります。
そこで、Turbolinksを無効化させる、Ajax後にも発火するように設定する、ということで対処していきます。
Turbolinksについては こちらの記事 で詳しく解説されています。修正後のコード
修正後のコードがこちらです。
custom.js// carousel-slider(bxslider) jQuery(document).on('turbolinks:load', function(){ $('.bxslider').bxSlider({ }); }); // submit自動送信 jQuery(document).bind('ready ajaxComplete', function() { $('.select-drop').change(function() { $(this).parent().submit(); }); });
turbolinks: load’とすることで、初回読み込み時、リロードどちらでも発火するようになります。ajaxCompleteとすることでAjaxが完了したのちに発火するように設定できます。今回はbind(‘ready ajaxComplete’)とすることで初回読み込み時、Ajax後の両方で動作するようにしています。完成
綺麗な解決法ではないかもしれませんが、なんとか無事に狙い通りの機能を実装できました。
jQueryについてはもっと勉強が必要そうです。。。参考
- https://ryoutaku-jo.hatenablog.com/entry/2019/01/15/213420
- Rails5でjqueryを動かす方法 - Qiita
- jQueryでajax完了後に実行開始したい処理があるときの対応 - ホームページ制作 株式会社KOP
- jQuery: both .ready() and .ajaxComplete - Stack Overflow
Qiita
- 投稿日:2019-08-27T17:38:54+09:00
fields_for内のf.collection_selectとactive_hashを表示させる
こんにちは!
ビューのマークアップ中に学んだ内容を備忘録として実装方法
開発環境
Ruby 2.5.1
Rails 5.2.3参考
Railsウィザード形式フォームで新規登録~Devise+session~
https://qiita.com/ATORA1992/items/40fc543742a6df5a17c1
Railsのgem 'active_hash'で都道府県データを作成したみた
https://kossy-web-engineer.hatenablog.com/entry/2019/01/08/205702
Github active_hash
https://kossy-web-engineer.hatenablog.com/entry/2019/01/08/205702やりたい事
- active_hashで実装した都道府県データをセレクトボックスを使ってビューに表示する
- セレクトボックスのデフォルト矢印をFontAwesomeでナイスな矢印に変える
事前準備
- field_forを扱えるようにmodel/controller/routesの実装を完了させる
- 都道府県データをモデルを介さず扱えるようにする為にgem 'active_hash'を導入する
- modelを自作し都道府県の元データを作成する
↑↑チームメンバーが実装してくれたのでこの記事では割愛
セレクトボックスをビューに表示させる
完成
って感じです
コード
:prefecture.html.haml %body.new-member-registration-body %main.new-member-registration-body__main %section .new-member-registration__title 発送元・お届け先入力 = form_for @user, url: signup_index_path, method: :post, html: { class: 'registration-form' } do |f| = f.fields_for :deliver_adress do |d| .new-member-registration-form-content .new-member-registration-form-content__group = f.label :都道府県, { class: 'new-registration-label' } = f.label :必須, { class: 'form-require' } .prefecture-select-wrap = d.collection_select :prefecture, Prefecture.all, :id, :namecolleciton_selectはform_for,form_tagにて使用可能
引数については、
(プロパティ名,オブジェクトの配列,value属性の項目,テキストの項目[,オプション])
value属性にはid: 1 ~ id: 47、テキストの項目にはname: '北海道 ~ name: 沖縄'とキー: バリュー形式にてmodels/prefecture.rbには実装されているのでPrefecture.allで都道府県データを全て引っ張ってきますcssでレイアウトを整える
prefecture.scss#user_deliver_adress_attributes_prefecture { height: 32px;//箱の高さ width: 300px;//箱の横幅 padding: 10px 16px 8px;//select_box内の文字を適切な位置へ margin-top: 8px;//箱の上部に余白を与える border-radius: 4px;//箱の角を滑らかに border: 1px solid #ccc;//箱の線のスタイル background-color: #fff;//箱の背景色 font-size: 16px;//文字の大きさ }このidはcollection_selectに自動で付与されるidです
最初、クラス名を与えていたのですが、
= d.collection_select :prefecture, Prefecture.all, :id, :name, class: 'クラス名'って書いてもcssが反映されないんですよね...なぜだろう?と調べてみると、第三引数にclass名は持ってこれないみたいですね。Railsドキュメントのソースコードに書いていますメソッドの定義def select(method, choices = nil, options = {}, html_options = {}, &block)第三引数は空ハッシュでoptionをスキップしてあげる必要があります。
= d.collection_select :prefecture, Prefecture.all, :id, :name, {}, class: 'クラス名'これでクラスを与える事ができます。
が、id持っているのにわざわざクラスを付与しなくても良いと思い、id = user_deliver_adress_attributes_prefectureへcssを当てましたFontAwesomeで矢印をナイスに
このデフォルト矢印を、
FontAwesomeを使って、
へ変更しますprefecture.html.haml.prefecture-select-wrap = d.collection_select :prefecture, Prefecture.all, :id, :name %i.prefecture.fas.fa-chevron-downhamlさんは親要素
.prefecture-select-wrapに対して= d.collection_select :prefecture, Prefecture.all, :id, :nameと%i.prefecture.fas.fa-chevron-downを子要素として並列にしてあげないとうまくいかなかったですprefecture.scss#user_deliver_adress_attributes_prefecture { height: 32px; width: 300px; padding: 10px 16px 8px; margin-top: 8px; border-radius: 4px; border: 1px solid #ccc; background-color: #fff; line-height: 1.5; font-size: 16px; -webkit-appearance: none;//追加:デフォルト矢印を消す } .prefecture-select-wrap { position: relative;//親のポジション指定 } .prefecture.fas.fa-chevron-down { position: absolute;//子のポジション指定 right: 25px;//右25px空ける top: 60%;//上から60%の位置へ z-index: 2;//親の下へ潜り揉まないようにする color: #888; transform: translate(0, -50%);//translate(X方向の移動距離, Y方向の移動距離) font-size: 18px; }
transform: translate(0, 50%)はHTMLリファレンスを参考にしましたがあまり理解できていないです。transform …… 要素に2D変形、または、3D変形を適用する
http://www.htmq.com/css3/transform.shtmlこのプロパティ設定がないと矢印が意図したレイアウトになってくれなさそう
まとめ
今回はフロントのマークアップで自分が躓いた部分を記事にまとめて見ました。
本当はactive_hashやfields_forの実装部分もアウトプットできれば良いのだが、いかんせんちゃんと理解ができていないです。やはり自分の力で実装しないと理解はできないと痛感しています。個人アプリを作る時は自力で実装するのでその時にまたQittaに記事書きます。最後に
筆者について
TEXH::EXPERT渋谷校の夜間クラスで4月からRuby・Railsの学習をしています。
記載内容に不備・不足があればご指摘いただけると幸いです。
至らぬ点ばかりですので、改善点がありましたらどんどんご指摘下さい!
- 投稿日:2019-08-27T16:49:56+09:00
Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #12 ActionMailer, アクティベーション編
こんな人におすすめ
- プログラミング初心者でポートフォリオの作り方が分からない
- Rails Tutorialをやってみたが理解することが難しい
前回:#11 プロフィール編集編
次回:準備中こんなことが分かる
- ユーザ新規登録時のメールによるアクティベーションの方法
- メーラーの使い方
- URLによるトークンとダイジェストの認証方法
- 本番/テスト環境ごとによるメールの設定方法
- 文字列よりシンボルが推奨される理由
一緒に勉強していきまっせ
今回の流れ
- アクティベーションのイメージを掴む
- コントローラ、属性、トークン/ダイジェストを用意する
- メールを生成し、メール内のURLにトークンを仕込む
- トークン/ダイジェストを照らし合わせ、アクティベートする
- ログイン時にもアクティベーションを確認する
- アクティベーションに関するテストを書く
アクティベーションのイメージ
今の仕様だとどんなメールアドレスでも登録が完了してしまう。
だから登録時にメールを送ってアクティベーションしたい...
どうすればよい??というわけでどんな風に行えば実装できるのか考えてみよう。
これまでは登録に成功すると/signupから/users/:idにリダイレクトするだけだった。
この間にアクティベーション用のメールを挟みたい。
アプリはホーム画面にリダイレクトする。
先述しちゃったけど、メール用に必要なパスは1つ。
その他必要なビューなどは存在しない。
よってAcountActivationsコントローラと対応するリソースを生成しよう。bash$ rails g controller AccountActivationsconfig/routes.rbRails.application.routes.draw do # 中略 resources :account_activations, only: [:edit] endRESTに従うと、情報を更新するためにはpatchを使用する。
でもユーザにURLをクリックしてもらう以上それはgetだ。
よってupdateアクションではなくeditアクションを使用する。アクティベーションの手順
- 新規作成時にトークンとダイジェストを生成する
- メールを生成する
- メール内のURLにトークンを忍ばせる
- URLをクリックしたらトークンとダイジェストと照らし合わせる
- 正しければアクティベーション済みにする
以上の動作に必要なものを列挙する。
- アクティベーション用トークン/ダイジェスト
- アクティベーション済みかどうかを確認する属性
- アクティベーション用メール
- アクティベーションするための動作コード
ポートフォリオ#9をご覧いただきたい。
アクティベーションに必要なトークンとダイジェストは同じような手順で生成できる。
対してメールを生成するメーラーは新たに使用する。
混乱する前に頭の中で整理しておくことをおすすめする。それでは1つずつ用意していこう。
AccountActivationの属性を用意する
属性として必要なのはこの2つ。
- activation_digest → アクティベーション用ダイジェスト
- activated → アクティベーション済みかどうかの真偽値
bash$ rails g migration add_activation_to_users activation_digest:string activated:boolean真偽値はデフォルトでfalseにしておく。
db/migrate/[timestamp]_add_activation_to_users.rbclass AddActivationToUsers < ActiveRecord::Migration[5.2] def change add_column :users, :activation_digest, :string add_column :users, :activated, :boolean, default: false end endマイグレーションもお忘れなく。
bash$ rails db:migrateトークンとダイジェストを生成する
トークンとダイジェストの生成に関しては、以前作成したUser.new_tokenとUser.digestでまかなえる。
アクティベーション用にcreate_activation_digestメソッドを作り、これらをまとめておこう。加えてactivation_tokenは仮属性なのでattr_accessorに追加する。
/app/models/user.rbclass User < ApplicationRecord attr_accessor :remember_token, :activation_token # 中略 private def create_activation_digest self.activation_token = User.new_token self.activation_digest = User.digest(activation_token) end end # 中略どうやって新規作成時に生成させるか
以前emailを入力させる際before_saveを使用し、save前にアドレスをdowncaseメソッドで小文字化した。
今回はsave前ではなくcreate前に呼び出したい。
お察しのとおりRailsにはbefore_createというメソッドが用意されている。
...使おうついでにリファクタリングとして小文字化の処理もメソッド化しておく。
最終的にはこうなる。/app/models/user.rbclass User < ApplicationRecord attr_accessor :remember_token, :activation_token before_save :downcase_email before_create :create_activation_digest # 中略 private def downcase_email email.downcase! end def create_activation_digest self.activation_token = User.new_token self.activation_digest = User.digest(activation_token) end endアクティベーション用メールを生成する
メールを生成するにはメーラーというものを使う。
早速生成してみよう。
ついでに次の章で紹介するパスワードリセット用のpassword_resetメソッドも生成しておく。bash$ rails g mailer UserMailer account_activation password_reset生成されたメーラーはコントローラのアクションと似ており、メーラーの各メソッドが同名のビューと対応する形。
Tutorialを参考に改変。app/mailers/application_mailer.rbclass ApplicationMailer < ActionMailer::Base default from: 'noreply@example.com' layout 'mailer' endapp/mailers/user_mailer.rbclass UserMailer < ApplicationMailer def account_activation(user) @user = user mail to: user.email, subject: "【重要】Lantern Lanternよりアカウント有効化のためのメールを届けました" end # この部分は次章 def password_reset @greeting = "Hi" mail to: "to@example.org" end end「なんでaccount_activationに引数与えられるの?」
とちょっと疑問に思った。答えはシンプルでコントローラのアクションと違い、メーラーのメソッドはコントローラ内で自発的に使っていくから(っぽい)。
まあ要は普通に定義したメソッドだから使えるよねって話。メール内のURLにトークンを仕込む
ここからはビューでメール本文を作成しよう。
必要なのはトークンとキーにするメールアドレスを仕込んだURL。
以上を踏まえるとこんな感じ。app/views/user_mailer/account_activation.text.erb<%= @user.name %>さんへ Lantern Lanternにいらして下さりありがとうございます。下記のリンクをクリックして認証を済ませてください。 <%= edit_account_activation_url(@user.activation_token, email: @user.email) %>app/views/user_mailer/account_activation.html.erb<h1>Lantern Lantern</h1> <p><%= @user.name %>さんへ</p> <p>Lantern Lanternにいらして下さりありがとうございます。下記の『認証する』をクリックして認証を済ませてください。</p> <%= link_to "認証する", edit_account_activation_url(@user.activation_token, email: @user.email) %>さてこの部分。
edit_account_activation_url(@user.activation_token, email: @user.email)ルートを確認するとedit_account_activationのURLパターンを教えてくれる。
bash$ rails routes | grep edit_account_activation edit_account_activation GET /account_activations/:id/edit(.:format):id部分には引数が入る。でも今回は第2引数まである。
実はこの第2引数にハッシュを使用するとクエリパラメータを付与することができる。
結果的にこんな感じのURLを生み出す。http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.comこれでURLにトークンとメールアドレスを仕込むことができた。
メールのプレビュー
メールができたとはいえプレビューを確認したい。
そのためには設定を変える必要がある。まずdevelopment.rbのこの箇所を、
config/environments/development.rb# Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false config.action_mailer.perform_caching = falseこうする。
config/environments/development.rb# Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :test host = '『〜自分の環境に合わせる〜』' config.action_mailer.default_url_options = { host: host, protocol: 'https' } config.action_mailer.perform_caching = falseここに注目。
host = '〇〇'この箇所には自分の環境によって変更する必要がある。
具体的にはdevelopment環境のURLを挿入する。
本ポートフォリオではこの部分。
参考にさせていただきました↓
rails チュートリアルの11章の2・2にてクラウドIDEのホスト名がわからない
Railsのconfig/enviroments配下を読んでみるあとはプレビュー用のメソッドもいじる必要がある。
spec/mailers/previews/user_mailer_preview.rbclass UserMailerPreview < ActionMailer::Preview # https://〇〇/rails/mailers/user_mailer/account_activation def account_activation user = User.first user.activation_token = User.new_token UserMailer.account_activation(user) end def password_reset UserMailer.password_reset end endこれで下記のリンクでプレビューを確認できるようになった。
https://〇〇/rails/mailers/user_mailer/account_activation新規作成時にメールを送る
それではメールを送ろう。
手順としては、処理を書いた後、本番環境を整える。
しかし本番環境については、完成後に整える方がスムーズなので後回しにする。メールを送る処理を書く
ユーザ新規作成時にメールを送るようにしよう。
メーラーで作成したメールを送るにはdeliver_nowメソッドを使用する。UserMailer.account_activation(@user).deliver_nowここは慣習的にメソッド化しておこう。
app/models/user.rbclass User < ApplicationRecord # 中略 def send_activation_email UserMailer.account_activation(self).deliver_now endあとはcreateアクションに書き込む。
app/controllers/users_controller.rbclass UsersController < ApplicationController # 中略 def create @user = User.new(user_params) if @user.save UserMailer.account_activation(@user).deliver_now flash[:info] = "認証用メールを送信しました。登録時のメールアドレスから認証を済ませてください" redirect_to root_url else render 'new' end endその他、flashメッセージやリダイレクト先などの変更がある。
これによりいくつかのテストが通らなくなる。
記事後半にテスト関係をまとめたので、この問題はあとで修正する。知識を助けていただきました↓
【Rails入門】Action Mailerのメール送信を初心者向けに基礎から解説アクティベーションを完了させる
メール内のURLをクリックしたらアクティベーション完了にしたい。
やることは2つ。
- トークンとダイジェストと照らし合わせるauthenticated?を編集する
- editアクションに処理を書く
authenticated?を編集する
以前remember_me機能を追加する際、authenticated?メソッドでトークンとダイジェストを照らし合わせていた。
アクティベーションでも同じメソッドが使えるよう、カスタマイズする。app/models/user.rbclass User < ApplicationRecord # 中略 # 以前のauthenticated?を書き換え def authenticated?(attribute, token) digest = send("#{attribute}_digest") return false if digest.nil? BCrypt::Password.new(digest).is_password?(token) end第1引数 → rememberに使うかactivationに使うかなど
第2引数 → 指定したトークン
これで汎用性の高いメソッドに生まれ変わった。sendを使うことで文字列をメソッドとして認識してくれる。
そこに#{}を使うことで、引数によってメソッド名を変化させるという仕組み。再び以前書いたテストが失敗する。
これも記事後半に。それよりもauthenticated?を使用していたcurrent_userを編集しよう。
app/helpers/sessions_helper.rbmodule SessionsHelper # 中略 def current_user if (user_id = session[:user_id]) @current_user ||= User.find_by(id: user_id) elsif (user_id = cookies.signed[:user_id]) user = User.find_by(id: user_id) if user && user.authenticated?(:remember, cookies[:remember_token]) log_in user @current_user = user end end endauthenticated?では第1引数を指定する際にシンボルを使用している。
ん?なんで?文字列よりシンボルが推奨される理由
第1引数に指定する際はシンボルを推奨する。
理由はざっくりいうとこんな感じ。
- Rubyの内部実装は、速度面で名前を整数で管理している
- シンボルはソース上文字列、内部上整数という性質を持つ
- よって文字列ではなくシンボルを使用する
より詳しい解説↓
Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたいeditアクションに処理を書く
ようやくAccountActivationsコントローラに処理を書ける。
以下の記述でactivatedをtrueにするが、user.update_attribute(:activated, true)ここも慣習にならってメソッド化する。
app/models/user.rbclass User < ApplicationRecord # 中略 def activate update_attribute(:activated, true) end終わったらeditアクションに処理を書こう。
app/controllers/account_activations_controller.rbclass AccountActivationsController < ApplicationController def edit user = User.find_by(email: params[:email]) if user && !user.activated? && user.authenticated?(:activation, params[:id]) user.activate log_in user flash[:success] = "Lantern Lanternへようこそ!" redirect_to user else flash[:danger] = "アクティベーションに失敗しました" redirect_to root_url end end endここに注目。
!user.activated?わざわざこうしているのは、アクティベーション済みにも関わらずtrueが可能になると、リンクを盗み出すだけで攻撃でアクティベーションが成功してしまうから。
ログイン時にアクティベーションを確認する
新規登録時のアクティベーションは完了した。
でもまだログイン時にアクティベーション済みかを確認する処理が書かれていない。
Sessionsコントローラを編集しよう。app/controllers/sessions_controller.rbclass SessionsController < ApplicationController # 中略 def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) if user.activated? log_in user params[:session][:remember_me] == '1' ? remember(user) : forget(user) redirect_back_or user else flash[:danger] = "メールを確認してアクティベーションを済ませてください" redirect_to root_url end else flash.now[:danger] = 'メールアドレスかパスワードが正しくありません' render 'new' end endこれでアクティベーション関係は終了となる。
本番環境でメールが届くようにする
実際のアプリでメールを送信するにはいくつかやることがある。
- Herokuのアドオンを追加する
- production.rbを編集する
Herokuのアドオンを追加する
Herokuからメールを送信できるようにしよう。
そのためにはアドオンを追加する。bashheroku addons:create sendgrid:starterproduction.rbを編集する
production.rbにこういう記述があると思う。
config.action_mailer.raise_delivery_errors = falseこのあたりをこんな感じに編集する。
config/environments/production.rb# 中略 config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp host = '〇〇.herokuapp.com' config.action_mailer.default_url_options = { host: host } ActionMailer::Base.smtp_settings = { :address => 'smtp.sendgrid.net', :port => '587', :authentication => :plain, :user_name => ENV['SENDGRID_USERNAME'], :password => ENV['SENDGRID_PASSWORD'], :domain => 'heroku.com', :enable_starttls_auto => true }〇〇の部分はHerokuで自分のアプリを開いたときのURLを挿入しよう。
(https除く)host = '〇〇.herokuapp.com'これで無事動作する。
メーラーのテストを書く
最後にメーラーのテストだ。
ただメーラーをテストするまでにいくつかやることがある。
- FactoryBotにactivated属性を与える
- ホスト側にドメイン名を与える
これが終わったら実際にテストといこう。
FactoryBotにactivated属性を与える
activated属性が新たに追加されたので、FactotyBotに追記しよう。
acitvatedがfalseのユーザも加えてみる。spec/factories/users.rbFactoryBot.define do factory :user do name { "Michael Example" } email { "michael@example.com" } password { "password" } password_confirmation { "password" } activated { true } end factory :other_user, class: User do name { "Sterling Archer" } email { "duchess@example.gov" } password { "foobar" } password_confirmation { "foobar" } activated { true } end factory :no_activation_user, class: User do name { "No Activation" } email { "no@activation.co.jp" } password { "foobar" } password_confirmation { "foobar" } activated { false } end endホスト側にドメイン名を与える
今のままではメールの送信元のアドレスがテスト環境に存在しないことになっている。
設定を変更しよう。config/environments/test.rb# 中略 config.action_mailer.delivery_method = :test config.action_mailer.default_url_options = { host: 'example.com' } # 中略メーラーテストを書く
意外と面倒くさいメーラーテスト。
自動生成されたpassword_resetに関してはコメントアウト。spec/mailers/user_mailer_spec.rbrequire "rails_helper" RSpec.describe UserMailer, type: :mailer do let(:user) { create(:user) } describe "account_activation" do it "renders mails" do user.activation_token = User.new_token mail = UserMailer.account_activation(user) expect(mail.subject).to eq("【重要】Lantern Lanternよりアカウント有効化のためのメールを届けました") expect(mail.to).to eq(["michael@example.com"]) expect(mail.from).to eq(["noreply@example.com"]) expect(mail.body.encoded.split(/\r\n/).map{|i| Base64.decode64(i)}.join).to include("Michael Example") end end # describe "password_reset" do # let(:mail) { UserMailer.password_reset } # end endこれが気になる。
expect(mail.body.encoded.split(/\r\n/).map{|i| Base64.decode64(i)}.join).to include("Michael Example")もちろんこうしたいのは山々だけれど、
expect(mail.body.encoded).to include("Michael Example")これだと失敗する。理由はエンコードの関係。
このままだと訳がわからない文字列を参照することになる。
それをデコードする必要があるからこうなった訳だ。参考にさせていただきました↓
Rails つまづき駆動投稿(TDP)
ActionMailerのメール送信テストをRSpecで行うその他のテストを書く
テスト追加と修正を行う。
- アクティベートしていないユーザをログイン失敗にするテストを追加と修正
- 新規作成時のリダイレクト先をルートに修正
- authenticated?のモデルテストを修正
アクティベートしていないユーザをログイン失敗にするテスト
ユーザをログインさせる際、アクティベート済みとそうでないユーザを分けたい。
以前テストでユーザ情報をpostする際にはメソッドを使用した。
それの修正とリファクタリングを行い、テストを完成させる。spec/requests/users_logins_spec.rbrequire 'rails_helper' RSpec.describe "UsersLogins", type: :request do include SessionsHelper let(:user) { create(:user) } let(:no_activation_user) { create(:no_activation_user) } def post_invalid_information post login_path, params: { session: { email: "", password: "" } } end def post_valid_information(login_user, remember_me = 0) post login_path, params: { session: { email: login_user.email, password: login_user.password, remember_me: remember_me } } end describe "GET /login" do it "fails having a danger flash message" do get login_path post_invalid_information expect(flash[:danger]).to be_truthy expect(is_logged_in?).to be_falsey expect(request.fullpath).to eq '/login' end it "fails because they have not activated account" do get login_path post_valid_information(no_activation_user) expect(flash[:danger]).to be_truthy expect(is_logged_in?).to be_falsey follow_redirect! expect(request.fullpath).to eq '/' end it "succeeds having no danger flash message" do get login_path post_valid_information(user) expect(flash[:danger]).to be_falsey expect(is_logged_in?).to be_truthy follow_redirect! expect(request.fullpath).to eq '/users/1' end # 中略post_valid_informationに引数を定義したので、使用する際は引数を与えよう。
(中略している部分にも忘れずに)
新規作成時のリダイレクト先をルートに修正する
あとは修正だけ。
spec/requests/users_signups_spec.rb# 中略 it "is valid signup information" do get signup_path expect { post_valid_information }.to change(User, :count).by(1) expect(is_logged_in?).to be_falsey follow_redirect! expect(request.fullpath).to eq '/' expect(flash[:info]).to be_truthy endspec/systems/signup_spec.rb# 中略 it "is valid because it fulfils form information" do visit signup_path submit_with_valid_information expect(current_path).to eq root_path expect(page).to have_selector '.alert-info' endauthenticated?のテストを修正
spec/models/user_spec.rb# 中略 describe "User model methods" do describe "authenticated?" do it "return false for a user with nil digest" do expect(user.authenticated?(:remember, '')).to be_falsey end end end最後にテストを走らせておこう。
$ rails specこれがグリーンならアクティベーションは完了。
お疲れ様ー。前回:#11 プロフィール編集編
次回:準備中
- 投稿日:2019-08-27T12:27:30+09:00
Rails6でknockを導入してload関連のエラーが発生したときの対処法
Ruby on Rails 6.0.0 で作成したアプリケーションに、JWTトークンの発行・認証を行う目的で knock をインストールしようとしたところ、loadに関するエラーが発生したので対処法をまとめます。
発生したエラー
現象1
rails generate knock:installを実行するとwarningとerrorが出力される。
config/initializers/knock.rbの生成自体はできている。$ rails generate knock:install Running via Spring preloader in process 14337 [WARNING] Could not load generator "generators/knock/install_generator". Error: expected file /home/user/.rbenv/versions現象2
ApplicationControllerにてKnock::Authenticableをincludeさせると、railsが起動できない。$ rails c /home/user/Documents/develop/knock_test/app/controllers/application_controller.rb:2:in `<class:ApplicationController>': uninitialized constant Knock::Authenticable (NameError)原因
Rails6でのautoloadがzeitwerkモードに変更となったことで、autoloadの挙動がRails5以前と変わったことに起因しています。
Rails 6 zeitwerk autoload problem with gem
対処法
対処法1. 明示的にrequireする[推奨]
上記issueにある通り、明示的にソースをrequireすることでエラーを回避できます。
config/initializers/eager_load_knock.rbrequire 'knock/version' require 'knock/authenticable'対処法2. autoloadをclassicモードに戻す[非推奨]
Rails5以前と同じ方法でautoloadさせることでもエラーを回避できます。
基本的には新しいバージョンに追従すべきなので非推奨ですが、他のgemで同様のエラーが発生する場合などはこちらの方が手っ取り早いかもしれません。config/application.rbrequire_relative 'boot' require "rails" require "active_model/railtie" require "active_job/railtie" require "active_record/railtie" require "active_storage/engine" require "action_controller/railtie" require "action_mailer/railtie" require "action_mailbox/engine" require "action_text/engine" require "action_view/railtie" require "action_cable/engine" require "rails/test_unit/railtie" Bundler.require(*Rails.groups) module KnockTest class Application < Rails::Application config.load_defaults 6.0 config.api_only = true config.autoloader = :classic # ★追記 end endまとめ
こちらの issueで何かしら修正が入ることに期待しつつ、まずは上記のような対策でエラーを回避するしかなさそうです。
- 投稿日:2019-08-27T12:18:30+09:00
Rails6 のちょい足しな新機能を試す70(ActiveModel::Errors#slice! 編)
はじめに
(多分)Rails 6 に追加された新機能を試す第70段。 今回は、
ActiveModel::Errors#slice!編です。
Rails 6 では、ActiveModel::Errorsに インスタンスメソッドslice!が追加されました。Ruby 2.6.3, Rails 6.0.0.rc2 で確認しました。Rails 6.0.0.rc2 は
gem install rails -v 6.0.0rc2 --prereleaseでインストールできます。
(Rails 6.0.0 がリリースされましたが、動作確認当時の最新版は、Rails 6.0.0.rc2 でした。悪しからず)
$ rails --version Rails 6.0.0.rc2今回は適切な例を思いつかなかったので、
rails consoleを使って確認します。プロジェクトを作る
rails new rails_sandbox cd rails_sandboxUser モデルを作る
4つの属性
name,country,cityを持つ User モデルを作ります。bin/rails g model User name email country cityヴァリデーションを追加する
単純に必須入力のヴァリデーションを4つの項目に追加します。
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true validates :email, presence: true validates :country, presence: true validates :city, presence: true endマイグレーションを実行する
bin/rails db:create db:migraterails console で確認する
rails consoleで確認します。
User モデルのインスタンスを作って、validateメソッドを呼び出します。irb(main):001:0> user = User.new => #<User id: nil, name: nil, email: nil, country: nil, city: nil, created_at: nil, updated_at: nil> irb(main):002:0> user.validate => false
slice!メソッドで、countryとcityのメッセージだけ残します。irb(main):003:0> errors = user.errors.slice!(:country, :city) => {:name=>["can't be blank"], :email=>["can't be blank"]}
full_messagesでエラーメッセージを確認するとcountryとcityのエラーメッセージだけになっていることがわかります。irb(main):004:0> user.errors.full_messages => ["Country can't be blank", "City can't be blank"]なお、
ActiveModel::Errors#slice!の戻り値はHashオブジェクトのため、 戻り値に対してfull_messagesは使えません。irb(main):005:0> errors.full_messages Traceback (most recent call last): 1: from (irb):5 NoMethodError (undefined method `full_messages' for {:name=>["can't be blank"], :email=>["can't be blank"]}:Hash)`)試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try070_active_model_errors_slice参考情報
- 投稿日:2019-08-27T09:04:36+09:00
主キー(primary key)の選択、has_one,belongs_toについて某アニメを具体例にまとめてみた
はじめに
Railsにおけるアソシエーション(
belongs_to,has_many,has_one)について、それから主キーの設定について分かったようで分かっていない部分があったので、自分の理解のためにも簡単な具体例を作ってみた。取り上げる相関
今回使用する相関図はコチラ
大抵、商品の管理システムや従業員データなど例が取り上げられるので、イメージしやすそうな某冒険友情活劇を例にうまいこと当てはめてみた。(逆に分かりづらくなっているかも)どれを主キーにするか問題
意外と悩んだのがどれを主キーにしたらよいのかという部分。
そもそも主キーとはなんのために設定するのかというところから考えてみる主キーとは
- テーブル内でレコードを一意に識別することができるように指定される項目
- 主キーに選ばれた列はすべてのレコードが異なる値を持たなければならず、NULL値とすることもできない。
主キーの選択
主キーの定義には次のように書かれている。
候補キーが複数あるとき、組を識別するという機能においてそれらの間に差異はないから、主キーにどれを選んでも論理的には問題がない。
しかし実用性を考えると以下に注意して選択するとよい。
- 主キーは検索のキーとして利用されたり、他の関係に参照のために格納されたりする確率が高いため、できる限りデータ量の小さい方がよい。
- 他の関係で主キーを使用していた場合、主キーを更新すると他の関係の値(外部キー)も同時に更新しなければならなくなるため、更新がかからない項目がよい
主キーを用いた例
生徒名簿(生徒番号, 生徒名, クラス)という関係
生徒番号が主キーになり得る。
同姓同名を考慮すると、生徒番号は唯一の候補キーであるから、代理キーはない。町村(町村ID, 町村名, 郡名, 都道府県名)という関係
町村ID と {都道府県名, 郡名, 町村名} が候補キーであり、いずれかが主キーになり得る。
例えば、町村ID を主キーにした場合、{都道府県名, 郡名, 町村名} は代理キーとなる。以上のことを踏まえて選択
- キャラクターは中身の情報更新が多そうな気もする、データ量も多い
- インデックスとして機能する
という観点から「海賊団」を主キーに選択
「麦わらの一味」と検索をかけたら、ズラーーっと船員が表示されると便利だと思うので。
なお、所属がないキャラクターは「無所属」にする。(NULL回避)データベース
あまりこの漫画について知らないのですが、それっぽいデータベースにしました。
[海賊団]
id (主キー) team_name ship 1 無所属 なし 2 麦わらの一味 サウザンド・サニー号 3 バギー海賊団 ビックトップ号 4 アーロン一味 シャーク・サバーブ号 [キャラクター]※prize_money=懸賞金 (金額はテキトー)
id team_id(外部キー) ch_name prize_money 1 2 ルフィ 2億 2 2 ナミ 100万 3 3 バギー 3億 4 2 チョッパー 1万 [悪魔の実]
id ch_id(外部キー) skill_name genre 1 1 ゴムゴム 超人(パラミシア) 2 23 モクモク 自然(ロギア) 3 3 バラバラ 超人(パラミシア) 4 4 ヒトヒト 動物(ゾオン) キャラクターに関しては、身長、体重、性別、出身などなど色々情報を入れられそうですね。
外部キーをどこに作るか問題もあったのでそれについては後述します。
(悪魔の実のテーブルにch_idを作るか、キャラクターのテーブルにskill_idを作るかみたいな問題)アソシエーション
相関図をもう一度載せます。
海賊団とキャラクターの関係は分かり易いと思います。
海賊団は複数のメンバーが所属し、キャラクターはそこに所属している。
いわゆる「多:1」の関係です。問題はキャラクターと悪魔の実の能力の関係です。
has_oneとbelongs_toの使い分け
この関係は「1:1」です。
たしか複数の実の能力を持つことができないみたいな設定でしたよね?
(黒ひげは能力2つもってる的なことは聞いたような、、無視!笑)ところで、どちらを
has_oneにするの?と引っかかったのでまとめます。has_oneとbelongs_toの条件
まず、「主従関係」を考えてみると良い。
主となる方がhas_oneで従となる方がbelongs_toになる。今回の例では悪魔の実の能力を持たないキャラクターは存在するが、悪魔の実自体は必ず誰かの所有物であるという関係である。
例えば「ナミ」は悪魔の実の能力を持っていない。
一方で「バラバラの実」は誰かが持っている能力である。ゆえに、キャラクターが主で悪魔の実の能力が従である
また、先ほど後述するといった外部キーをどちらに書くか問題について
自身が他のテーブルをたどるキーを所持している場合は、
belongs_to
自身が他のテーブルからたどるキーで示されている場合は、has_one
というルールがある。したがって、悪魔の実の能力に
ch_idを持たせている。まとめ
正直、初め何も考えずにキャラクターを主キーにしようとしていた。(普段Userを主キーに設定されていることが多いので、、)
しかし、主キーはインデックスや更新頻度など目的を考えて設定する必要がある。また、主キーについて、それから
has_oneとbelongs_toについてを上手くまとめようとしていく際に、
そもそもまず何を基準にしてテーブルを分けていくのかがなかなか難しいと感じた。
データベースは初めに作成するので、ここで時間を割かないようしっかり理解しておく必要がある。どこを主キーにしたら良いのか、主従関係はどうなっているのかというのは慣れていないと時間がかかりそうなので、今後は主キーの設定箇所や主従関係に注目しながらデータベースを見ていこうと感じた。
- 投稿日:2019-08-27T09:04:36+09:00
主キー(primary key)の選択、has_one,belongs_toについて某アニメを例にまとめてみた
はじめに
Railsにおけるアソシエーション(
belongs_to,has_many,has_one)について、それから主キーの設定について分かったようで分かっていない部分があったので、自分の理解のためにも簡単な具体例を作ってみた。取り上げる相関
今回使用する相関図はコチラ
大抵、商品の管理システムや従業員データなど例が取り上げられるので、イメージしやすそうな某冒険友情活劇を例にうまいこと当てはめてみた。(逆に分かりづらくなっているかも)どれを主キーにするか問題
意外と悩んだのがどれを主キーにしたらよいのかという部分。
そもそも主キーとはなんのために設定するのかというところから考えてみる。主キーに適するのは
- テーブル内でレコードを一意に識別することができるように指定される項目
- 主キーに選ばれた列はすべてのレコードが異なる値を持たなければならず、NULL値とすることもできない
主キーの選択
主キーの定義には次のように書かれている。
候補キーが複数あるとき、組を識別するという機能においてそれらの間に差異はないため、主キーにどれを選んでも論理的には問題がない。
しかし実用性を考えると以下に注意して選択するとよい。
- 主キーは検索のキーとして利用されたり、他の関係に参照のために格納されたりする確率が高いため、できる限りデータ量の小さい方がよい
- 他の関係で主キーを使用していた場合、主キーを更新すると他の関係の値(外部キー)も同時に更新しなければならなくなるため、更新がかからない項目がよい
主キーを用いた例
生徒名簿(生徒番号, 生徒名, クラス)という関係
生徒番号が主キーになり得る。
同姓同名を考慮すると、生徒番号は唯一の候補キーであるから、代理キーはない。町村(町村ID, 町村名, 郡名, 都道府県名)という関係
町村ID と {都道府県名, 郡名, 町村名} が候補キーであり、いずれかが主キーになり得る。
例えば、町村ID を主キーにした場合、{都道府県名, 郡名, 町村名} は代理キーとなる。以上のことを踏まえて選択
- キャラクターは中身の情報更新が多そうな気もする、データ量も多い
- インデックスとして機能するものがよい
という観点から「海賊団」を主キーに選択。
「麦わらの一味」と検索をかけたら、ズラーーっと船員が表示されると便利だと思うので。
なお、所属がないキャラクターは「無所属」とする。(NULL回避)データベース
あまりこの漫画について知らないのですが、それっぽいのを設定した。
[海賊団]
id (主キー) team_name ship 1 無所属 なし 2 麦わらの一味 サウザンド・サニー号 3 バギー海賊団 ビックトップ号 4 アーロン一味 シャーク・サバーブ号 [キャラクター]※prize_money=懸賞金 (金額はテキトー)
id team_id(外部キー) ch_name prize_money 1 2 ルフィ 2億 2 2 ナミ 100万 3 3 バギー 3億 4 2 チョッパー 1万 [悪魔の実]
id ch_id(外部キー) skill_name genre 1 1 ゴムゴム 超人(パラミシア) 2 23 モクモク 自然(ロギア) 3 3 バラバラ 超人(パラミシア) 4 4 ヒトヒト 動物(ゾオン) キャラクターに関しては、身長、体重、性別、出身などなど色々情報を入れられそうですね。
また、外部キーをどこに作るか問題もあったのでそれについては後述します。
(悪魔の実のテーブルにch_idを作るか、キャラクターのテーブルにskill_idを作るかみたいな問題)アソシエーション
相関図をもう一度載せます。
海賊団とキャラクターの関係は分かり易いと思います。
海賊団は複数のメンバーが所属し、キャラクターはそこに所属している。
いわゆる「多:1」の関係です。問題はキャラクターと悪魔の実の能力の関係です。
has_oneとbelongs_toの使い分け
この関係は「1:1」です。
たしか複数の実の能力を持つことができないみたいな設定でしたよね?
(黒ひげは能力2つもってる的なことを聞いたような、、ややこしいので無視!笑)ところで、どちらを
has_oneにするの?と引っかかったのでまとめます。has_oneとbelongs_toの条件
まず、「主従関係」を考えてみると良い。
主となる方がhas_oneで従となる方がbelongs_toになる。今回の例では悪魔の実の能力を持たないキャラクターは存在するが、悪魔の実自体は必ず誰かの所有物であるという関係である。
例えば「ナミ」は悪魔の実の能力を持っていない。
一方で「バラバラの実」は誰かが持っている能力である。ゆえに、キャラクターが主で悪魔の実の能力が従となる。
また、先ほど後述するといった外部キーをどちらに書くか問題については
自身が他のテーブルをたどるキーを所持している場合は、
belongs_to
自身が他のテーブルからたどるキーで示されている場合は、has_one
というルールがある。したがって、悪魔の実の能力に
ch_idを持たせている。まとめ
正直、初め何も考えずにキャラクターを主キーにしようとしていた。(普段Userを主キーに設定されていることが多いので、、)
しかし、主キーはインデックスや更新頻度など目的を考えて設定する必要がある。また、主キーについて、それから
has_oneとbelongs_toについてを上手くまとめようとしていく際に、
そもそもまず何を基準にしてテーブルを分けていくのかがなかなか難しいと感じた。
データベースは初めに作成するので、ここで時間を割かないようしっかり理解しておく必要がある。どこを主キーにしたら良いのか、主従関係はどうなっているのかというのは慣れていないと時間がかかりそうなので、今後は主キーの設定箇所や主従関係に注目しながらデータベースを見ていこうと感じた。
- 投稿日:2019-08-27T08:46:33+09:00
個人開発「positiveな改善案の口コミサイト」
はじめに
未経験からエンジニアを目指しており、勉強中に開発をした個人アプリについて作成理由や機能の解説になります。現在も開発を続けておりますので、途中経過を含めて内容については開発次第、随時更新をしていきます。
※何故やりたいのか、というのは個人的に結構重要視をしているので、若干サービス開発にかける想いの部分が若干長めかと思います。
作成アプリケーションの内容
開発環境
サーバーサイド言語:Ruby2.5.1
フレームワーク:Rails:5.2.2.1
フロントエンド:HTML
CSS (scss)
JavaScript (jQuery)
CSSフレームワーク:bootstrap
データベース:MySQL
サーバー:AWS(EC2 + S3)開発のきっかけ(興味が薄ければ飛ばしていただければと思います)
食べログやオープンワーク等に代表をされる、口コミサイトは今までサービスに対しての情報が全くなかったのを見える化したことで、すごく生活を便利にしてくれたと思います。
個人的には食べログは社会人になってからずっと有料会員で、一定以上に評価をされているかを基準にすることで失敗をしない店の選び方もできるようになりました。
すごく存在価値のあることだと思っています。ただ、段々と既存の口コミサイトを利用するにつれて、問題意識を感じるようになったため、今回開発をしてみました。
特に課題に感じたのが2点あります。①影響力の大きさを要因とした、評価の不確実性
私は前職が人材紹介の法人営業だったのですごく感じるのですが、クチコミサイトは1つの投稿が持つ、影響力がすごく大きいです。選考途中に辞退になるケースの中で、口コミサイトの評判をみて不安になってしまった、という辞退理由は実は結構多いです。
本来はこういった評価は真摯に受け止めて、しっかりと改善をしていくことが重要だと思っています。しかし、現実にはそうではない企業もあり、自社で評価を操作をしてしまっている企業が多いことも事実です。
※正規分布という確率分布があるので、一定の投稿数があれば通常は評価の点数はなだらかな評価の割合になります。
例えば5点満点で、十分なサンプルがありながら1と5の評価が2〜4と比べて飛び抜けて多い口コミの企業とかは要注意です。また、不自然にならないように自然を装って、釣り上げる業者も存在をしています。
こういった企業が多くなると必然的に本質的な改善を目指している企業がますます評価をされない、という状況になってしまいます。本来、口コミサイトが目指す世界観としては「ユーザー側からの評価をして、透明度を増すことで誰にとってもいいサービスを作っていくこと」だと思っていますが、完全にずれてしまっていると思います。②いつまでも残り続ける低評価が、改善をしても企業につきまとってしまう
悪い口コミがいつまでも残って影響を与え続けてしまうのは個人的には企業がすごく可愛そうだと思っています。これもまた、前職での個人的な経験からすごく感じていることです。
こんな経験がありました。
ある上場企業様から、すごく高い評価での内定が出ていた求職者の方がいたのですが、急に辞退をしたい、との連絡をいただいてしまいました。理由は案の定、クチコミサイトを見て評価が低かったのと(3年前の投稿)、過去に不正行為があったからとおっしゃっていました。
企業側の評価も元々かなり高く、何もせずに辞退になってしまうことも企業側にすごく申し訳なかったので、求職者の方に交渉をして、カウンセラーも何とか次の日の20時開始の時間で企業との面談の時間を確保しました。
企業担当の私も正直に相談をしたところ、企業側の回答は「正直に話してもらいありがとうございました、何とかします!」とのことでした。翌日、面談後に求職者の方から連絡があったのですが、なんと辞退は取り消します、とのことでした。
面談の際に何があったのかを聞くと翌日の調整依頼だったのにも関わらず、人事担当の方が急いで社長の面談をセッティングしていただき、面談では「何故不正が当時起きてしまったのか」、「経営陣を一掃した今の経営体制の強固さ」を真摯に話してくれたそうです。それに心打たれて、是非とも入社をしたい、という考えに変わったとのことでした。こちらの企業様のこの姿勢には私自身すごく好感を持ち、それから何名も入社をしていただくことができました。この時にすごく感じたのが、「改善をしていくことの大切さ」や「真摯に対応をしていくことが人との結びつきを強くする」ということです。
この企業様にはすごくよくしていただいていたのですが、反対に応募段階で過去のクチコミサイトの情報だけで判断をされるケースもすごく多く、個人的にはかなりもったいないなと感じました。
この例では上場企業でしたが、特に創業間もない企業は課題は山積みです。そんな中でその時の評価だけを元にして判断をされていては育つ企業も育たないと思います。本アプリの目指す世界観
上記の個人的に感じた課題感を解決して、いいサービスを提供をする意志のある企業が報われる、そんな口コミサイトを作っていければと考えております。
①改善をしていくことがいいサービス
ユーザーが現時点での企業側の評価をされるのではなく、改善をしていくことが評価をされるサービスにします。
ITの進歩で変化の早い世界になっており、現状維持はもはや退化と言われるような中では、いかにして変わっていくことができるかが重要です。
飲食店のアンケートでは、改善案の欄がよくあると思います。ただ、積極的に書いている人をほとんど見たことがありません。
中々面と向かって渡すアンケートではやりづらさもあると思いますが、企業側はすごく意見がほしいと思います。
アプリであれば心理的なハードルが下がると思うので、改善案を提言をしていくスタイルにしていきます。
ユーザー側からしても自分が提言をした内容が、実施をされればすごく嬉しいと思いますし、そうやって作っていったストーリーは唯一無二のものになってくると思います。②前向きな表現を使う
改善案を言うということは悪く考えれば、現時点でのそのサービスにおける弱みの指摘です。ただ、本アプリではユーザーが一方的に評価をするのではなく、応援をしていけるようにしていきたいと思っています。
そのため、攻撃的な投稿が出ないようにすべてポジティブな言い回しにしています。
サービスを利用していてよかった点をGoodPointにしているのはそれほど違和感はない、と思いますが改善点をChance Pointにしているのはそのためです。
また、本アプリではGCP:cloud language natural APIを使うことによって、投稿をした内容がpositiveなものかnagativeなものか判定ができるようになっています。③投稿された改善案の数によってマネタイズをする
本質的な改善を目指すためには双方向でのやり取りが必要不可欠になってきます。
ユーザーが勝手に投稿をしていくのは簡単ですが、それだと改善はまずしないと思うので、企業側のサービスの登録を必須にして、基本的に企業から金銭をもらうモデルを想定しています。
その際に、例えば投稿数3件もらうまでは企業側は無料、といった形にしていけば新規でやり取りをしやすいはずです。
無料でサービスを良くする改善案が入ってくるのなら企業側はまずやると思います。
そして、いくつか入ってきた口コミが良質であれば、続けたくなる(はず)ので、そのまま4件以上の投稿をしてもらうためには有料プラン、みたいな形式にしたいです。
個人的に感じることなのですが、改善をするのはすごく気持ちいいことだと思っています。「あのプロジェクト実は俺がやった」とか「あれは私が変えたんだ」とかのワードの言葉は誰しも聞いたことがあるかと思います。
いつまでも残っている、というのは特に企業側の導入を決めた人にとってはすごく誇らしいものでもあるので、離脱率はそこまで高くないのではないかと勝手に考えています。実装内容
①実装済機能
・一覧表示機能
・詳細表示機能
・投稿機能
・削除機能
・編集機能
・管理ユーザ登録機能(deviseで2つのモデルを使用)
・画像ファイルアップロード機能
・DBテーブルリレーション管理
・ページネーション機能
・コメント機能
・ユーザー登録、削除機能ポジティブ・ネガティブ判定(GCP:cloud language natural API)
・検索機能(ransack)②未実装機能(実装予定)
課金機能(payjp)
フォロー機能
単体テスト
統合テストまとめ
完成途中ではあるものの、スクールの課題と違ってゴールが決まっていないものを作るのはすごく楽しいなと感じました。まだまだ技術力的にできることに限りがあるので、もやもやを感じることは多いですが、色々思いついたことをすぐに実現をできるのはすごく魅力的です。
おまけ(似ている部分があると思ったアプリ)
サービスの質から言えば恐れ多いにもほどがありますが、個人的に似ている部分があるなと思ったサービスです。私のサービスに対してのイメージもしやすくなるかと思うので、いくつか挙げてみます。
①Insight Tech
不満を集めて「お客さまの声」や「レビューデータ」等をAIで分析をして改善につなげるサービスです。この解析をもとに実際にコンサルとかもしているそうです。大量のデータがある分、効果的な提案ができそうです。
最も概念が似ているサービスだと思うのですが、差別化のためにはこちらのサービスが大量のデータを元に分析をしているのに対して、一人ひとりとのストーリーをとにかく着目をしていく必要があると思いました。
②Unipos
前職の時に使っていたサービスです。社内で相手に感謝を伝えるサービスです。何かいい動きをした同僚に対してポイント付きのメッセージを送ることができます。同僚対同僚でのやり取りに使うサービスなので、ユーザー対企業であれば差別化にはつながるかな、とは勝手に思っています。
- 投稿日:2019-08-27T00:41:12+09:00
Ruby on Railsを使って個人開発ver1
Ruby on Rails で個人開発
【目標】
Ruby on Railsを使って簡単なWebサイトを作成する今回は、RailsにPostgreSQLを使用するために少しつまずいた部分を書きます。
【前提】
・Vagrant上にRails、PostgreSQLをインストール済み
・rails new #{アプリケーション名}を実行し、railsサーバーを立ち上げる
・ブラウザにアクセスすると、role "vagrant" does not exist の文字解決方法
1.sudo -u postgres createuser --createdb vagrant を実行
postgres というユーザ名でPostgreSQLにログインする。
そしてcreatedb vagrant でDBを作成する。
これはcreatedbのrole権限の登録を行っているはず。2.rails db:create を実行
データベースを作成する。3.ブラウザへアクセス
無事Railsサーバーが立ち上がり、エラーが出ずにアクセスすることが出来た。
- 投稿日:2019-08-27T00:41:12+09:00
Ruby on Railsを使って個人開発SQL編
Ruby on Rails で個人開発
【目標】
Ruby on Railsを使って簡単なWebサイトを作成する今回は、RailsにPostgreSQLを使用するために少しつまずいた部分を書きます。
【前提】
・Vagrant上にRails、PostgreSQLをインストール済み
・rails new #{アプリケーション名}を実行し、railsサーバーを立ち上げる
・ブラウザにアクセスすると、role "vagrant" does not exist の文字解決方法
1.sudo -u postgres createuser --createdb vagrant を実行
postgres というユーザ名でPostgreSQLにログインする。
そしてcreatedb vagrant でDBを作成する。
これはcreatedbのrole権限の登録を行っているはず。2.rails db:create を実行
データベースを作成する。3.ブラウザへアクセス
無事Railsサーバーが立ち上がり、エラーが出ずにアクセスすることが出来た。
- 投稿日:2019-08-27T00:31:30+09:00
カラムのデータ型の変更(Rails)
環境
windows(64bit)
ruby 2.6.3p62
Rails 5.2.2
Cloud9上で開発はじめに
$ rails g model ... などのコマンドでカラムを作成して、後からカラムのデータ型を間違えてしまったことに気づくことがあると思います。
そんな場合のために、この記事ではカラムのデータ型の変更についてご紹介します。1. 空のマイグレーションファイルの作成
以下のようにして空のマイグレーションファイルを作成します。
$ rails g migration change_data_<カラム名>_to_<テーブル名>僕の場合はEventのテーブルdateカラムのデータ型を変更したかったので次のようにしました。
$ rails g migration change_data_date_to_eventsすると、以下のようなマイグレーションファイルが作成されます。
db/migrate/〇〇_chenge_data_date_to_events.rbclass ChangeDataDateToEvents < ActiveRecord::Migration[5.2] def change end end2. データ型を変更する
僕の場合、dateカラムのデータ型をdatetime型からstring型へ変更したかったので、上記のマイグレーションファイルを次のように変更しました。
db/migrate/〇〇_chenge_data_date_to_events.rbdef change change_column :events, :date, :string end「これで変更完了!」といいたいところですが、最後に一番大事なことを忘れずに!
3. データベースに変更内容を反映
$ rails db:migrateおしまいです。
コンソールなどで変更内容が反映されているか確認してみてください。
- 投稿日:2019-08-27T00:03:36+09:00
DBの型 〜生年月日はdate型〜
生年月日がDBに登録されない
![]()
ユーザーの新規登録の部分の生年月日の部分だけDBに登録されなかったので困ってました。
ということで備忘録メモ困ってたこと
registration_controller.rbdef create @user = User.new(user_params) if @user.save sign_in(@user) session[:user_id] = @user.id redirect_to phonemumber_users_path else render 'new' end end private def user_params params.require(:user).permit(:nickname, :email, :password, :password_confirmation, :last_name, :first_name, :first_name_kana, :last_name_kana, :birthday) endニックネームとかemailはDBに入るのにbirthdayだけ登録されない
解決した方法
birthdayのカラムがstring型だったのでdate型に直した
一番最初のマイグレーションファイルに書いていたので、書き直してrake db:migrate:resetで書き換えました
t.string :nickname, null: false t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" t.string :last_name, null: false t.string :first_name, null: false t.string :last_name_kana, null: false t.string :first_name_kana, null: false t.date :birthday, null: false t.integer :tel, null: false, unique: true t.string :avatarこれでできました
電話番号はintegerだとダメで、stringはOKだそうです。
勉強になりました











