- 投稿日:2020-11-19T23:31:05+09:00
【Rails6】ActionCableでマルチチャット
はじめに
初投稿です!
今回はRailsのActionCableを用いて、リアルタイムチャットアプリを作ります。
マルチチャットとは、ユーザごとに複数のチャット部屋があり、それらを行き来できるイメージです。デモ
ソースコードはこちらです。
https://github.com/yamori-masato/chat-app動作環境
- Ruby 2.6.3
- Rails 6.0.3.4
下準備
$ rails new chat-app $ cd chat-appモデルの作成
今回はこのようなモデル設計にしました。
それでは一通りモデルを作成します。$ rails g model User name:string $ rails g model Room name:string $ rails g model UserRoom user:references room:references $ rails g model Message content:string user:references room:references $ rails db:migrateapp/models/user.rbclass User < ApplicationRecord has_many :user_rooms, dependent: :destroy has_many :rooms, through: :user_rooms endapp/models/room.rbclass Room < ApplicationRecord has_many :messages, dependent: :destroy has_many :user_rooms, dependent: :destroy has_many :users, through: :user_rooms endapp/models/user_room.rbclass UserRoom < ApplicationRecord belongs_to :user belongs_to :room endapp/models/message.rbclass Message < ApplicationRecord belongs_to :user belongs_to :room validates_presence_of :content endコントローラーの作成
$ rails g controller rooms index show
config/routes.rbRails.application.routes.draw do resources :users, only: [] do resources :rooms, only: [:index, :show] end end
- index... 自分が所属するRoom一覧
- show... チャットルーム閲覧
コントローラを作成して、routesファイルを編集しました。
実際にルーティングを確認してみましょう。$ rails routes ︙ user_rooms GET /users/:user_id/rooms(.:format) rooms#index user_room GET /users/:user_id/rooms/:id(.:format) rooms#show ︙2つのルーティングができています。
resourcesのonlyオプションに空の配列を渡しているので、userに対するルーティングが生成されません。それでは、コントローラーの中身を書いていきます。
app/controller/rooms_controller.rbclass RoomsController < ApplicationController before_action :set_user, only: [:index, :show] def index @rooms = @user.rooms.all end def show @room = @user.rooms.find(params[:id]) @messages = @room.messages.all end private def set_user @user = User.find(params[:user_id]) end endビューの作成
最低限ですがビューも作っていきます。
app/views/rooms/index.html.erb<h1>Chat Rooms</h1> <ul> <% @rooms.each do |room| %> <li><%= link_to room.name, user_room_path(@user,room) %></li> <% end %> </ul>app/views/rooms/show.html.erb<h1><%= @room.name %></h1> <div id="container"> <%= render @messages %> </div> <%= link_to 'Back', user_rooms_path(@user) %>app/views/messages/_message.html.erb<p> <strong><%= message.user.name %></strong> > <%= message.content %> </p>テストデータの登録
ここまでで最低限の機能が作れているはずです。
テストデータを登録して確認してみましょう!コンソールでデータを登録します。
$ rails c
> user1 = User.create!(name: "user1") > user2 = User.create!(name: "user2") > room1 = Room.create!(name: "room1") > room1.users << [user1, user2] > Message.create!(content:"hello!", room_id: room1.id, user_id: user1.id)$ rails s
先ほど登録したメッセージがroom1に表示されているはずです。
メッセージ送信
現時点では送信ボタンを押してもメッセージが送られないので、これを送れるようにしたいと思います。
Ajaxで送信できるようにします。まず、メッセージを新規追加できるようにコントローラーを修正します。
$ rails g controller message create
config/routes.rbRails.application.routes.draw do resouces :usersn, only: [] do resources :rooms, only: [:index, :show] end + resources :messages, only: [:create] end
app/controllers/messages_controller.rbclass MessagesController < ApplicationController def create @message = Message.create!(message_params) end private def message_params params.require(:message).permit(:content, :room_id, :user_id) end endフォーム送信後に、このcreateアクションが呼ばれるようにします。
app/controller/rooms_controller.rbclass RoomsController < ApplicationController ︙ def show @room = @user.rooms.find(params[:id]) @messages = @room.messages.all + @message = @room.messages.build end ︙ end
app/views/rooms/show.html.erb︙ + <%= form_with(model: @message, html:{name: "myform"}) do |f| %> + <%= f.text_field :content %> + <%= f.hidden_field :user_id, value: @user.id %> + <%= f.hidden_field :room_id, value: @room.id %> + <%= f.submit '送信' %> + <% end %> <%= link_to 'Back', user_rooms_path(@user) %>
これで送信ボタンを押すたびにajaxリクエストが送られて、messageが新規作成されるようになりました。
form_withにあるhidden_fieldは、描画はされませんが、post送信時にパラメータの1つとして送信されます。
ヘルパーと実際の出力の対応はrailsガイドをご覧ください。classやname属性も自動的に付与されます。このままでは送信後に入力した文字がそのままなので、送信したら文字が消えるようにしましょう。
app/views/messages/create.js.erbdocument.getElementById("message_content").value = ''ajaxリクエスト後はデフォルトで
views/messages/create.html.erb
が呼ばれます。
ここでは送信後にフォームの値を空にしています。ここで、idに"message_content"を指定しています。
これはform_withヘルパーによって自動生成されたinput要素に付与されるidです。
実際にChromeの検証ツールで見てみましょう。このようにform_withで自動生成されるフォームにはそれぞれ決まったクラス名やid名がついていることがわかります。今回の場合は、inputタグのidにmessage_contentが付与されているのでこれを指定しています。
これでAjaxでメッセージ送信することができるはずなので実際に確認してみましょう!
※この時点ではまだリロードしないと描画に反映されません。
WebSocketを用いた双方向通信
いちいちリロードするのは不便なので、リロードなしで描画に反映されるようにしましょう。
そこで使われるのがActionCableです。ActionCable
ActionCableとは、Rails5で追加されたWebSocketを扱えるフレームワークです。
これを用いることで、閲覧者が画面の操作を行わなくても受動的に新しい情報をリアルタイムで取得できるようになります。Channelの作成
$ rails g channel room create app/channels/room_channel.rb identical app/javascript/channels/index.js identical app/javascript/channels/consumer.js create app/javascript/channels/room_channel.js
いくつかファイルが作成されたので確認してみましょう。
app/channels/room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed # stream_from "some_channel" end def unsubscribed # Any cleanup needed when channel is unsubscribed end endapp/javascript/channels/room_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("RoomChannel", { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel } });これはRoomChannelを購読するクライアント側のコードになります。サブスクリプションを作成することでチャネルを購読することができます。
Channelの購読について
先ほど生成されたコードについて詳しく見ていきましょう。
まず、ページが読み込まれたタイミングでRoomChannelが購読されます。これは、デフォルトで
javascript/channels
配下のファイルがコンパイル対象になっているからです。先ほどのコードを少し変更してみましょう。
app/javascript/channels/room_channel.jsimport consumer from "./consumer" consumer.subscriptions.create( - "RoomChannel", + { channel: "RoomChannel", room_id: 1, user_id: 1 }, { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel } });第一引数を変更しました。
これらの引数は、サブスクリプション作成時にパラメータとしてサーバー側に渡すことができます。では、サーバー側ではどのようにパラメータを受け取るのでしょうか?
実際に確認してみましょう。Gemfile︙ + gem 'pry-rails'
app/channels/room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed + binding.pry end def unsubscribed end end
$ bundle install $ rails s[1] pry(#<RoomChannel>)> params => {"channel"=>"RoomChannel", "room_id"=>1, "user_id"=>1}クライアント側でサブスクリプションが作成されると、購読されたチャネルのsubscribedメソッドが呼ばれます。
また、第一引数に渡した値がparamsとして受け取れることがわかります。streamとbroadcast
クライアント側から受け取ったパラメータ(params)を使うことでマルチチャットを実装することができます。
先ほどのコードを次のように変更してみましょう。app/channels/room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed @user = User.find_by(id: params[:user_id]) reject if @user.nil? @room = @user.rooms.find_by(id: params[:room_id]) reject if @room.nil? stream_for(@room) end def unsubscribed end end今回は、あるチャットルームで発言した時、ルーム内のユーザーのみに送信を知らせるようにします。
例えば、room1で発言した内容はroom1に所属するメンバーにブロードキャストされるといった感じになります。
そこで、チャットルームをストリームと紐付けます。また、実在しないユーザーや、ユーザーが所属しないチャットルームであった場合はrejectしています。
rejectすると、チャネルの購読を拒否することができます。チャットルームとストリームを紐づける
今のままではどの部屋に対しても
user_id: 1
,room_id: 1
としてストリームが作成されてしまいます。これではどのチャットルームで発言しても、user1のroom1での発言と見されてしまうのでこれを修正していきます。app/views/rooms/show.html.erb<h1><%= @room.name %></h1> + <div id="data" data-room-id="<%= @room.id %>" data-user-id="<%= @user.id %>"></div> <div id="container"> <%= render @messages %> </div> <%= form_with(model: @message, html:{name: "myform"}) do |f| %> <%= f.text_field :content, rows: '1'%> <%= f.hidden_field :user_id, value: @user.id %> <%= f.hidden_field :room_id, value: @room.id %> <%= f.submit '送信' %> <% end %> <%= link_to 'Back', user_rooms_path(@user) %>
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { const data = document.getElementById("data") const room_id = data.getAttribute("data-room-id") const user_id = data.getAttribute("data-user-id") consumer.subscriptions.create( { channel: "RoomChannel", room_id: room_id, user_id: user_id }, { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel } } ) })room_idとuser_idは、view側から渡してあげます。
また、DOMの読み込み完了を待ちたいので"turbolinks:load"を記述します。これにより、
/users/1/rooms/1
にアクセスすれば、room_id: 1
,user_id: 1
としてRoomChannelがサブスクライブされます。このままではチャットルーム以外のページでも読み込まれてしまう為、document.getElementById("data")が見つからずにエラーになってしまいます。なのでif文で分岐してあげます。
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { const data = document.getElementById("data") + if (data === null) { + return + } const channel = "RoomChannel" const room_id = data.getAttribute("data-room-id") const user_id = data.getAttribute("data-user-id") ︙
また、チャットルームのページを開くたびに同じチャットルームに対するサブスクリプションが複数作成されてしまう恐れがあるのでこれも修正します。
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { const data = document.getElementById("data") if (data === null) { return } const channel = "RoomChannel" const room_id = data.getAttribute("data-room-id") const user_id = data.getAttribute("data-user-id") + if (!isSubscribed(channel, room_id, user_id)) { consumer.subscriptions.create( ︙ ) } }) // helper + const isSubscribed = (channel, room_id, user_id) => { + const identifier = `{"channel":"${channel}","room_id":"${room_id}","user_id":"${user_id}"}` + const subscription = consumer.subscriptions.findAll(identifier) + return !!subscription.length + }チャットルームごとにストリームを紐づけることができました。
ブロードキャストする
チャットルームとストリームを紐づけることができたので、ブロードキャストする処理を書いていきます。
今回のチャットアプリでは、メッセージを新規作成したタイミングで知らせたいので、そのままコントローラに記述します。app/controllers_messages_controller.rbclass MessagesController < ApplicationController def create @message = Message.create!(message_params) @room = Room.find_by(id: message_params[:room_id]) + RoomChannel.broadcast_to(@room, message: @message.template) end private def message_params params.require(:message).permit(:content, :room_id, :user_id) end end
app/models/message.rbclass Message < ApplicationRecord belongs_to :user belongs_to :room validates_presence_of :content + def template + ApplicationController.renderer.render partial: 'messages/message', locals: { message: self } + end end
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { ︙ consumer.subscriptions.create( { channel: "RoomChannel", room_id: room_id, user_id: user_id }, { connected() { }, disconnected() { }, received(data) { + const container = document.getElementById("container") + container.insertAdjacentHTML('beforeend', data['message']) } } ) }) ︙broadcastされるとreceived(data)が呼び出されます。
受け取ったデータは、data['message']のようにして受け取れます。
これをチャットログの末尾に挿入してあげれば完成です!!参考文献
- 描いて理解する Action Cable
- Rails5のActionCableでイカゲームもどきを作ってみた
- 【Rails6】(送信時のリロード無し!)Action CableでSlack風チャットアプリを作成
- 【Rails6.0】ActionCableを使用したライブチャットアプリを実装する手順を解説
- Rails 6: Subscribing to Multiple Channels in Action Cable
おわりに
次回は、このチャットアプリにログイン機能を追加したいと思います!
ご意見アドバイス等ぜひお願いいたします。
- 投稿日:2020-11-19T23:31:05+09:00
【Rails6】ActionCableでマルチルームチャット
はじめに
初投稿です!
今回はRailsのActionCableを用いて、リアルタイムチャットアプリを作ります。
マルチルームチャットとは、ユーザごとに複数のチャット部屋があり、それらを行き来できるイメージです。デモ
ソースコードはこちらです。
https://github.com/yamori-masato/chat-app動作環境
- Ruby 2.6.3
- Rails 6.0.3.4
下準備
$ rails new chat-app $ cd chat-appモデルの作成
今回はこのようなモデル設計にしました。
それでは一通りモデルを作成します。$ rails g model User name:string $ rails g model Room name:string $ rails g model UserRoom user:references room:references $ rails g model Message content:string user:references room:references $ rails db:migrateapp/models/user.rbclass User < ApplicationRecord has_many :user_rooms, dependent: :destroy has_many :rooms, through: :user_rooms endapp/models/room.rbclass Room < ApplicationRecord has_many :messages, dependent: :destroy has_many :user_rooms, dependent: :destroy has_many :users, through: :user_rooms endapp/models/user_room.rbclass UserRoom < ApplicationRecord belongs_to :user belongs_to :room endapp/models/message.rbclass Message < ApplicationRecord belongs_to :user belongs_to :room validates_presence_of :content endコントローラーの作成
$ rails g controller rooms index show
config/routes.rbRails.application.routes.draw do resources :users, only: [] do resources :rooms, only: [:index, :show] end end
- index... 自分が所属するRoom一覧
- show... チャットルーム閲覧
コントローラを作成して、routesファイルを編集しました。
実際にルーティングを確認してみましょう。$ rails routes ︙ user_rooms GET /users/:user_id/rooms(.:format) rooms#index user_room GET /users/:user_id/rooms/:id(.:format) rooms#show ︙2つのルーティングができています。
resourcesのonlyオプションに空の配列を渡しているので、userに対するルーティングが生成されません。それでは、コントローラーの中身を書いていきます。
app/controller/rooms_controller.rbclass RoomsController < ApplicationController before_action :set_user, only: [:index, :show] def index @rooms = @user.rooms.all end def show @room = @user.rooms.find(params[:id]) @messages = @room.messages.all end private def set_user @user = User.find(params[:user_id]) end endビューの作成
最低限ですがビューも作っていきます。
app/views/rooms/index.html.erb<h1>Chat Rooms</h1> <ul> <% @rooms.each do |room| %> <li><%= link_to room.name, user_room_path(@user,room) %></li> <% end %> </ul>app/views/rooms/show.html.erb<h1><%= @room.name %></h1> <div id="container"> <%= render @messages %> </div> <%= link_to 'Back', user_rooms_path(@user) %>app/views/messages/_message.html.erb<p> <strong><%= message.user.name %></strong> > <%= message.content %> </p>テストデータの登録
ここまでで最低限の機能が作れているはずです。
テストデータを登録して確認してみましょう!コンソールでデータを登録します。
$ rails c
> user1 = User.create!(name: "user1") > user2 = User.create!(name: "user2") > room1 = Room.create!(name: "room1") > room1.users << [user1, user2] > Message.create!(content:"hello!", room_id: room1.id, user_id: user1.id)$ rails s
先ほど登録したメッセージがroom1に表示されているはずです。
メッセージ送信
現時点では送信ボタンを押してもメッセージが送られないので、これを送れるようにしたいと思います。
Ajaxで送信できるようにします。まず、メッセージを新規追加できるようにコントローラーを修正します。
$ rails g controller message create
config/routes.rbRails.application.routes.draw do resouces :usersn, only: [] do resources :rooms, only: [:index, :show] end + resources :messages, only: [:create] end
app/controllers/messages_controller.rbclass MessagesController < ApplicationController def create @message = Message.create!(message_params) end private def message_params params.require(:message).permit(:content, :room_id, :user_id) end endフォーム送信後に、このcreateアクションが呼ばれるようにします。
app/controller/rooms_controller.rbclass RoomsController < ApplicationController ︙ def show @room = @user.rooms.find(params[:id]) @messages = @room.messages.all + @message = @room.messages.build end ︙ end
app/views/rooms/show.html.erb︙ + <%= form_with(model: @message, html:{name: "myform"}) do |f| %> + <%= f.text_field :content %> + <%= f.hidden_field :user_id, value: @user.id %> + <%= f.hidden_field :room_id, value: @room.id %> + <%= f.submit '送信' %> + <% end %> <%= link_to 'Back', user_rooms_path(@user) %>
これで送信ボタンを押すたびにajaxリクエストが送られて、messageが新規作成されるようになりました。
form_withにあるhidden_fieldは、描画はされませんが、post送信時にパラメータの1つとして送信されます。
ヘルパーと実際の出力の対応はrailsガイドをご覧ください。classやname属性も自動的に付与されます。このままでは送信後に入力した文字がそのままなので、送信したら文字が消えるようにしましょう。
app/views/messages/create.js.erbdocument.getElementById("message_content").value = ''ajaxリクエスト後はデフォルトで
views/messages/create.html.erb
が呼ばれます。
ここでは送信後にフォームの値を空にしています。ここで、idに"message_content"を指定しています。
これはform_withヘルパーによって自動生成されたinput要素に付与されるidです。
実際にChromeの検証ツールで見てみましょう。このようにform_withで自動生成されるフォームにはそれぞれ決まったクラス名やid名がついていることがわかります。今回の場合は、inputタグのidにmessage_contentが付与されているのでこれを指定しています。
これでAjaxでメッセージ送信することができるはずなので実際に確認してみましょう!
※この時点ではまだリロードしないと描画に反映されません。
WebSocketを用いた双方向通信
いちいちリロードするのは不便なので、リロードなしで描画に反映されるようにしましょう。
そこで使われるのがActionCableです。ActionCable
ActionCableとは、Rails5で追加されたWebSocketを扱えるフレームワークです。
これを用いることで、閲覧者が画面の操作を行わなくても受動的に新しい情報をリアルタイムで取得できるようになります。Channelの作成
$ rails g channel room create app/channels/room_channel.rb identical app/javascript/channels/index.js identical app/javascript/channels/consumer.js create app/javascript/channels/room_channel.js
いくつかファイルが作成されたので確認してみましょう。
app/channels/room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed # stream_from "some_channel" end def unsubscribed # Any cleanup needed when channel is unsubscribed end endapp/javascript/channels/room_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("RoomChannel", { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel } });これはRoomChannelを購読するクライアント側のコードになります。サブスクリプションを作成することでチャネルを購読することができます。
Channelの購読について
先ほど生成されたコードについて詳しく見ていきましょう。
まず、ページが読み込まれたタイミングでRoomChannelが購読されます。これは、デフォルトで
javascript/channels
配下のファイルがコンパイル対象になっているからです。先ほどのコードを少し変更してみましょう。
app/javascript/channels/room_channel.jsimport consumer from "./consumer" consumer.subscriptions.create( - "RoomChannel", + { channel: "RoomChannel", room_id: 1, user_id: 1 }, { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel } });第一引数を変更しました。
これらの引数は、サブスクリプション作成時にパラメータとしてサーバー側に渡すことができます。では、サーバー側ではどのようにパラメータを受け取るのでしょうか?
実際に確認してみましょう。Gemfile︙ + gem 'pry-rails'
app/channels/room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed + binding.pry end def unsubscribed end end
$ bundle install $ rails s[1] pry(#<RoomChannel>)> params => {"channel"=>"RoomChannel", "room_id"=>1, "user_id"=>1}クライアント側でサブスクリプションが作成されると、購読されたチャネルのsubscribedメソッドが呼ばれます。
また、第一引数に渡した値がparamsとして受け取れることがわかります。streamとbroadcast
クライアント側から受け取ったパラメータ(params)を使うことでマルチルームチャットを実装することができます。
先ほどのコードを次のように変更してみましょう。app/channels/room_channel.rbclass RoomChannel < ApplicationCable::Channel def subscribed @user = User.find_by(id: params[:user_id]) reject if @user.nil? @room = @user.rooms.find_by(id: params[:room_id]) reject if @room.nil? stream_for(@room) end def unsubscribed end end今回は、あるチャットルームで発言した時、ルーム内のユーザーのみに送信を知らせるようにします。
例えば、room1で発言した内容はroom1に所属するメンバーにブロードキャストされるといった感じになります。
そこで、チャットルームをストリームと紐付けます。また、実在しないユーザーや、ユーザーが所属しないチャットルームであった場合はrejectしています。
rejectすると、チャネルの購読を拒否することができます。チャットルームとストリームを紐づける
今のままではどの部屋に対しても
user_id: 1
,room_id: 1
としてストリームが作成されてしまいます。これではどのチャットルームで発言しても、user1のroom1での発言と見されてしまうのでこれを修正していきます。app/views/rooms/show.html.erb<h1><%= @room.name %></h1> + <div id="data" data-room-id="<%= @room.id %>" data-user-id="<%= @user.id %>"></div> <div id="container"> <%= render @messages %> </div> <%= form_with(model: @message, html:{name: "myform"}) do |f| %> <%= f.text_field :content, rows: '1'%> <%= f.hidden_field :user_id, value: @user.id %> <%= f.hidden_field :room_id, value: @room.id %> <%= f.submit '送信' %> <% end %> <%= link_to 'Back', user_rooms_path(@user) %>
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { const data = document.getElementById("data") const room_id = data.getAttribute("data-room-id") const user_id = data.getAttribute("data-user-id") consumer.subscriptions.create( { channel: "RoomChannel", room_id: room_id, user_id: user_id }, { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel } } ) })room_idとuser_idは、view側から渡してあげます。
また、DOMの読み込み完了を待ちたいので"turbolinks:load"を記述します。これにより、
/users/1/rooms/1
にアクセスすれば、room_id: 1
,user_id: 1
としてRoomChannelがサブスクライブされます。このままではチャットルーム以外のページでも読み込まれてしまう為、document.getElementById("data")が見つからずにエラーになってしまいます。なのでif文で分岐してあげます。
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { const data = document.getElementById("data") + if (data === null) { + return + } const channel = "RoomChannel" const room_id = data.getAttribute("data-room-id") const user_id = data.getAttribute("data-user-id") ︙
また、チャットルームのページを開くたびに同じチャットルームに対するサブスクリプションが複数作成されてしまう恐れがあるのでこれも修正します。
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { const data = document.getElementById("data") if (data === null) { return } const channel = "RoomChannel" const room_id = data.getAttribute("data-room-id") const user_id = data.getAttribute("data-user-id") + if (!isSubscribed(channel, room_id, user_id)) { consumer.subscriptions.create( ︙ ) } }) // helper + const isSubscribed = (channel, room_id, user_id) => { + const identifier = `{"channel":"${channel}","room_id":"${room_id}","user_id":"${user_id}"}` + const subscription = consumer.subscriptions.findAll(identifier) + return !!subscription.length + }チャットルームごとにストリームを紐づけることができました。
ブロードキャストする
チャットルームとストリームを紐づけることができたので、ブロードキャストする処理を書いていきます。
今回のチャットアプリでは、メッセージを新規作成したタイミングで知らせたいので、そのままコントローラに記述します。app/controllers_messages_controller.rbclass MessagesController < ApplicationController def create @message = Message.create!(message_params) @room = Room.find_by(id: message_params[:room_id]) + RoomChannel.broadcast_to(@room, message: @message.template) end private def message_params params.require(:message).permit(:content, :room_id, :user_id) end end
app/models/message.rbclass Message < ApplicationRecord belongs_to :user belongs_to :room validates_presence_of :content + def template + ApplicationController.renderer.render partial: 'messages/message', locals: { message: self } + end end
app/javascript/channels/room_channel.jsimport consumer from "./consumer" document.addEventListener("turbolinks:load", () => { ︙ consumer.subscriptions.create( { channel: "RoomChannel", room_id: room_id, user_id: user_id }, { connected() { }, disconnected() { }, received(data) { + const container = document.getElementById("container") + container.insertAdjacentHTML('beforeend', data['message']) } } ) }) ︙broadcastされるとreceived(data)が呼び出されます。
受け取ったデータは、data['message']のようにして受け取れます。
これをチャットログの末尾に挿入してあげれば完成です!!参考文献
- 描いて理解する Action Cable
- Rails5のActionCableでイカゲームもどきを作ってみた
- 【Rails6】(送信時のリロード無し!)Action CableでSlack風チャットアプリを作成
- 【Rails6.0】ActionCableを使用したライブチャットアプリを実装する手順を解説
- Rails 6: Subscribing to Multiple Channels in Action Cable
おわりに
次回は、このチャットアプリにログイン機能を追加したいと思います!
ご意見アドバイス等ぜひお願いいたします。
- 投稿日:2020-11-19T23:27:16+09:00
アラビア数字からローマ数字に変換してみた
はじめに
今回は課題としてアラビア数字からローマ数字に変換するという課題が出たので、その課題を解いてみる事にする
解答
プログラム
- 正直全く良い方法分からない、、、、、
num = ARGV[0] def divide_num(num) ans_list,count = [],0 while num > 0 tmp = num % 10 ans_list.push(tmp * (10 ** count)) num = num / 10 count = count + 1 end return ans_list.reverse end ans_list,count = divide_num(num.to_i),0 def num_to_roman_hundred(num) if num == 900 print "CM" elsif num <= 800 && num >= 500 num = num - 500 if num != 0 print "D" + "C" * (num / 100) else print "D" end elsif num == 400 print "CD" elsif num <= 300 && num >= 100 num = num - 100 if num != 0 print "C" + "C" * (num / 100) else print "C" end end end def num_to_roman_ten(num) if num == 90 print "XC" elsif num <= 80 && num >= 50 num = num - 50 if num != 0 print "L" + "X" * (num / 10) else print "L" end elsif num == 40 print "XL" elsif num <= 30 && num >= 10 num = num - 10 if num != 0 print "X" + "X" * (num / 10) else print "X" end end end def num_to_roman(num) roman_first = ['I','II','III','IV','V','VI','VII','VIII','IX','X'] if num >= 1000 tmp = num / 1000 x = "M" * tmp print x elsif num <= 900 && num >= 100 num_to_roman_hundred(num) elsif num <= 90 && num >= 10 num_to_roman_ten(num) elsif num <= 10 && num >= 1 print roman_first[num-1] end end for i in ans_list do num_to_roman(i) end print "\n"自分で書いてて思います。頭悪い実装です(笑)
(深夜にやっていて頭回ってないという言い訳をしときます(笑))
個別の関数毎の説明
- divide_num 関数の中で受けとった数字を分解
- num_to_roman_hundred, num_to_roman_ten はそれぞれの桁数に合わせてローマ数字に変換を行う
- num_to_roman で全ての数字をローマ数字に変換
答え合わせ
では作成したプログラムが正しいかどうかの答え合わせをしましょう!!
以下のようなプログラム(a.sh)を作成します
for i in $(seq 1 3999);do ruby a.rb $i # a.rb はソースコードの名前なので何でもいいです donebash a.sh > tmp.txt
エクセルから 3999 までのローマ数字を作成(1 分以内で可能)し、tmp2.txt として保存
diff tmp.txt tmp2.txt
何も出力されなければ成功
GitHub
以下の URL に全てのプログラムと txt file を置いておきます。
- source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/roman.org
- 投稿日:2020-11-19T23:27:16+09:00
マルチスケールシミュレーション特論: roman numerals
はじめに
今回は課題としてアラビア数字からローマ数字に変換するという課題が出たので、その課題を解いてみる事にする
解答
プログラム
- 正直全く良い方法分からない、、、、、
num = ARGV[0] def divide_num(num) ans_list,count = [],0 while num > 0 tmp = num % 10 ans_list.push(tmp * (10 ** count)) num = num / 10 count = count + 1 end return ans_list.reverse end ans_list,count = divide_num(num.to_i),0 def num_to_roman_hundred(num) if num == 900 print "CM" elsif num <= 800 && num >= 500 num = num - 500 if num != 0 print "D" + "C" * (num / 100) else print "D" end elsif num == 400 print "CD" elsif num <= 300 && num >= 100 num = num - 100 if num != 0 print "C" + "C" * (num / 100) else print "C" end end end def num_to_roman_ten(num) if num == 90 print "XC" elsif num <= 80 && num >= 50 num = num - 50 if num != 0 print "L" + "X" * (num / 10) else print "L" end elsif num == 40 print "XL" elsif num <= 30 && num >= 10 num = num - 10 if num != 0 print "X" + "X" * (num / 10) else print "X" end end end def num_to_roman(num) roman_first = ['I','II','III','IV','V','VI','VII','VIII','IX','X'] if num >= 1000 tmp = num / 1000 x = "M" * tmp print x elsif num <= 900 && num >= 100 num_to_roman_hundred(num) elsif num <= 90 && num >= 10 num_to_roman_ten(num) elsif num <= 10 && num >= 1 print roman_first[num-1] end end for i in ans_list do num_to_roman(i) end print "\n"自分で書いてて思います。頭悪い実装です(笑)
(深夜にやっていて頭回ってないという言い訳をしときます(笑))
個別の関数毎の説明
divide_num 関数の中で受けとった数字を分解
num_to_roman_hundred, num_to_roman_ten はそれぞれの桁数に合わせてローマ数字に変換を行う
num_to_roman で全ての数字をローマ数字に変換
答え合わせ
では作成したプログラムが正しいかどうかの答え合わせをしましょう!!
以下のようなプログラム(a.sh)を作成します
for i in $(seq 1 3999);do ruby a.rb $i # a.rb はソースコードの名前なので何でもいいです donebash a.sh > tmp.txt
エクセルから 3999 までのローマ数字を作成(1 分以内で可能)し、tmp2.txt として保存
diff tmp.txt tmp2.txt
何も出力されなければ成功!!
逆に出力されたら失敗してます。。。
GitHub
以下の URL に全てのプログラムと txt file を置いておきます。
- source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/roman.org
- 投稿日:2020-11-19T22:55:07+09:00
Rails 日付カラムのバリデーション設定
はじめに
Railsを使ってオリジナルアプリを開発しています。日付カラムに今日の日付より後(明日以降)は保存できないように、バリデーションを設定しました。忘れないように書き記します。
目次
1.日付カラムのバリデーション
2.モデルへの追加記述1.日付カラムのバリデーション
今回suggestionテーブルにlast_cleaned_dateカラム(最後に掃除した日付)を作成した。下記のように空の場合、保存できないバリデーションは設定した。しかし、他カラムと異なり、空以外のバリデーションは別で行う必要がある。
app/models/suggestionclass Suggestion < ApplicationRecord with_options presence: true do #----------------中略------------------- validates :last_cleaned_date #←対象のカラム end belongs_to :user end2.モデルへの追加記述
同ファイルにバリデーションを追記する。ここではlast_cleaned_dateカラムが空でなかった場合、day_after_todayの処理が行われる。errors.addでエラーの種類を追加する。ここでもif文で条件を定義した。last_cleaned_dateが今日の日付(Date.today)より大きい場合、明日以降を示す。そして条件を満たした場合、保存されずエラーを表示する。
app/models/suggestion.rbvalidate :day_after_today def day_after_today unless last_cleaned_date == nil errors.add(:last_cleaned_date, 'は、今日を含む過去の日付を入力して下さい') if last_cleaned_date > Date.today end end参考までに実装後のエラー表示画面を下記に示す。本日の日付(2020/11/19)で保存しようとした場合の、エラー文である。
以上
- 投稿日:2020-11-19T22:47:21+09:00
heroku run rails db:migrateできなかった時の話
herokuでデプロイしようとした際、データベースにマイグレーションの情報を入力
% heroku run rails db:migrate入力、、、なんか違和感を感じる
logを遡っていくと
Mysql2::Error: Table 'heroku_XXXXXXXXXXXXXXXX.users' doesn't exist 〜省略〜とエラーが出てる
heroku上のuserテーブルが存在していないってこと?
当然、urlを指定しても画面には
と表示される。
データベースのステータス確認
% heroku run rails db:migrate:status↓
Status Migration ID Migration Name -------------------------------------------------- (2.2ms) SELECT `schema_migrations`.`version` FROM `schema_migrations` ORDER BY `schema_migrations`.`version` ASC down 202010XXXXXXXX Create posts down 202010XXXXXXXX Create active storage tablesactive storage down 202010XXXXXXXX Devise create users down 202010XXXXXXXX Create comments down 202011XXXXXXXX Add columns to users全ファイルupされてない。。。
試したこと
ローカルリポジトリでは問題なく反映されていたものの、一度こちらをリセットしてみようと試みる。
% rails db:migrate:resetすると
Mysql2::Error: Table 'power_spot_development.users' doesn't exist 〜省略〜えー
ローカルでもエラー発生herokuでの状況と全く一緒で全ファイルがdown
マイグレイトできない
解決策
rails db:migrate:up VERSION=Migration IDで一つづつファイルupしてみた。
するとStatus Migration ID Migration Name -------------------------------------------------- up 20201021042500 Create posts up 20201023030611 Create active storage tablesactive storage up 20201023054938 Devise create users up 20201030032407 Create comments up 20201101015416 Add columns to users全部マイグレーションできた!
herokuの方でも同じ動作を行う
heroku run rails db:migrate:up VERSION=Migration ID全部upになった。
挙動も正常を確認
疑問点
なぜ、一つづつならマイグレートできたのか分からない
- 投稿日:2020-11-19T22:44:33+09:00
【Rails】ルーティングの確認(2パターン)
ターミナルでルートを確認する
ターミナルに $ rails routes とコマンドを打つとルーティングが表示される
ターミナル$ rails routes Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel「ターミナルだと見にくいなぁ」と思ったことはないでしょうか!
そんな方には別の方法で確認することができます!ブラウザでルートを確認する
URLに http://localhost:3000/rails/info/routes にアクセス!
ルートが増えるとターミナルでは確認し辛くなってくるのでコチラの方法はかなりオススメです!
ぜひお試しください!
- 投稿日:2020-11-19T22:08:27+09:00
[Rails]flashとflash.nowの違いについて![初心者]
flash[:notice]とは
主に
redirect_to
メソッドとセットで使用。
次のアクションまでエラーメッセージが表示されたままになる。なお、
redirect_to
メソッドとともに使用する場合、引数にnotice
というキーを渡すだけで、フラッシュメッセージを表示することができます。flash.now[:notice]とは
主に
render
とセットで使用。
次のアクションに移行した時点で、エラーメッセージが消えてしまう。使用上の注意点
render
とともにflash[:notice]
を使用してしまうと、画面遷移後のページ(renderの次のアクションで表示するページ)にも、エラーメッセージが残ったままになってしまう。
逆に、redirect_to
とセットでflash.now[:notice]
を使っても、アクションを経由しているので、エラーメッセージが表示されない。使用例
controllers/books.rbclass BooksController < ApplicationController def create @book = Book.new(book_params) @book.user_id = current_user.id if @book.save redirect_to books_path, notice: '投稿成功!!' #notice:で引数を渡しているため、flashの記載は省略 else flash.now[:alert] = "投稿失敗!!" render 'new' end end endまとめ
以下のように使い分けるべし!!!!
redirect_to パス名, notice: "エラーメッセージ"flash.now[:alert] = "エラーメッセージ" render :アクション名
- 投稿日:2020-11-19T22:00:47+09:00
[Rails][ransack] link_toで検索条件を作り、classをつける
link_toにclassをつけるのに苦戦したので備忘録もかねて。
ransackで検索機能を作っており、あるdivをクリックした時にあらかじめ決めておいた検索結果にリンクさせようとlink_toを使い苦戦。
最初に書いていたコード
<%= link_to controller: "shops", action: "index", q: { name_cont: '東京'}, class: "反映させたいクラス" do %> <div>このdivをクリックしたら「東京」の検索結果にとぶ</div> <% end %>こちらを参考に書きました。やってることはほぼ同じで、リンクテキストを踏ませるかdivを踏ませるかの違いです。
リンクテキストを指定する代わりにdo endで囲んでブロックにし、controller: ~ 東京 }
までがパスで、その後にclassを書いているというイメージです。エラーは出ずリンクは機能するものの、classが反映されず...
うまくいったコード
<%= link_to ({ controller: "shops", action: "index", q: { name_cont: '東京' }}, { class: "反映させたいクラス" }) do %> <div>このdivをクリックしたら「東京」の検索結果にとぶ</div> <% end %>最初に書いていたコードも文法的には間違いではないそうですが、classとパスの境目が曖昧でclassもURLの一部と見なされていたようなので、URL部分とその他の部分でハッシュを分けたらうまくいきました。
- 投稿日:2020-11-19T20:15:34+09:00
【Rails】deviseが最低限使えるようになるためのポイント
インストール
Gemfilegem 'devise'$ bundle install $ rails g devise:install
config/initializers/devise.rb
とconfig/locales/devise.en.yml
の2つのファイルが作成され、以下のメッセージが表示される原文Depending on your application's configuration some manual setup may be required: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. * Required for all applications. * 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" * Not required for API-only Applications * 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> * Not required for API-only Applications * 4. You can copy Devise views (for customization) to your app by running: rails g devise:views * Not required *ざっくり翻訳以下の設定が必要だよ 1. config/environments/development.rbにデフォルトのURLを設定する。 書き方はこんな感じ↓ config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } 上記の例は開発環境用だから、本番環境では実際のURLがセットされるようにする → 新規会員登録メール、パスワード再設定メールにサイトのURLを埋め込む時とかに使うっぽい。 2. config/route.rbにroot_urlをセットする(普通してる) 3. app/views/layouts/application.html.erbにflashメッセージの表示エリアを作る 書き方はこんな感じ↓ <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. deviseが自動生成するページのデザインをカスタマイズしたい場合、rails g devise:viewsで生成されるフ ァイルを編集するユーザーテーブルを作成
$ rails g devise User # rails g devise モデル名
app/models/user.rb
の生成- マイグレーションファイルの作成
config/routes.rb
にdevise_for :users
という記述が追加され、deviseで使うルーティングが作成されるマイグレーションファイルには、デフォルトでメールアドレスやパスワードなど、いろんな項目が設定されている。
必要に応じてカラムを追加。ここではnameというカラムを追加。db/migrate/timestamp_devise_create_users.rbclass DeviseCreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :name # 追加 t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" t.string :reset_password_token t.datetime :reset_password_sent_at t.datetime :remember_created_at t.string :name t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true end end$ rails db:migratestrong parametersを設定
deviseのコントローラーは、ライブラリ側で用意されているので、直接修正できない。
だからdeviseのコントローラーに修正が必要なときは、application_controllerに書く。今回nameというカラムを追加したので、
configure_permitted_parameters
を上書きしてsignup時にnameを扱えるようにする。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters # deviseで使われているstrong_parameterを上書き devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) end endこの時点で
http://開発環境のIP/users/signup
にアクセスすると、ユーザー登録画面が表示される
http://開発環境のIP/users/sign_in
にアクセスすると、ログイン画面が表示される
http://開発環境のIP/users/sign_out
にdeleteでアクセスすると、ログアウトするこれでひとまずユーザー登録、ログイン、ログアウト処理が完成。(※メール認証挟まないからまだ実運用はきつめ)
画面を編集する
deviseをインストールした時のメッセージ[4]にもあったようにviewファイルを生成してデザインや項目をカスタマイズする。
$ rails g devise:views
viewファイルがたくさん生成される。今回はユーザー登録時の画面にname項目を追加。
app/views/devise/registrations/new.html.erb<h2>Sign up</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <!-- #以下を追加 --> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name, autofocus: true %> </div>ログインしているかどうかで表示を分ける
<ul> <% if user_signed_in? %> <li><%= link_to "ログアウト", destroy_user_session_path, method: :delete %></li> <% else %> <li><%= link_to "新規登録", new_user_registration_path %></li> <li><%= link_to "ログイン", new_user_session_path %></li> <% end %> </ul>
form_for
をform_with
に変更deviseのformはデフォルトで
form_for
を使っているのでより新しい書き方のform_with
に修正app/views/devise/registrations/new.html.erb<h2>Sign up</h2> <%= #削除 form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= form_with model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %>app/views/devise/sessions/new.html.erb<h2>Log in</h2> <%= #削除 form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <%= form_with model: @user, url: user_session_path, local: true do |f| %>ログイン後に遷移する画面を変更
デフォルトだと
root_path
に遷移するようになっているようです。メソッドを上書きしてuser_path(user)
に設定してみます。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base #省略 def after_sign_in_path_for(resource) user_path(resource) end #省略 endユーザー詳細ページを作成
これはdevise無視して勝手にcontrollerやviewを作る。編集とか削除とかも同じ、deviseは気にしないで作る。
$ rails g controller users showapp/controllers/users_controller.rbclass UsersController < ApplicationController def show @user = User.find(params[:id]) end endconfig/routes.rbresources :users, only: %i[show]app/views/users/show.html.erb<h2>ユーザー詳細</h2> <p><%= @user.name %></p> <p><%= @user.email %></p>ヘルパーいろいろ
current_user # ログイン中のユーザーのオブジェクト user_signed_in? # ログインしてるか確認
- 投稿日:2020-11-19T20:04:45+09:00
MVCとは
- 投稿日:2020-11-19T18:43:16+09:00
RSpecのテストにおいて期待されるエラーが出力されなかった話
はじめに
画像とテキストを投稿するRailsアプリケーションで、RSpecによるテストを行っていた。
アプリケーションの設計ではテキストがない場合はツイート(投稿)が出来ないようになっている。テスト内容
テキストなしの投稿は保存できない、という内容のテストで以下のコードを実行した。
spec/models/tweet_spec.rbit 'テキストがないとツイートは保存できない' do tweet = Tweet.new(text: "",image: "https://i.imgur.com/GL7igry.png") tweet.valid? expect(@tweet.errors.full_messages).to include("Text can't be blank") endところが、tweet.valid?はtrueを返しており、"Text can't be blank"のエラーが出力されなかった。
[1] pry(#<RSpec::ExampleGroups::Tweet::Nested::Nested_2>)> @tweet.valid? => true [2] pry(#<RSpec::ExampleGroups::Tweet::Nested::Nested_2>)> @tweet.errors.full_messages => []ここでエラーが出ないということは、アプリケーションの作成段階でミスがあったと考え、Tweetのモデルを見直したところvalidationに関する記述がなかった。
解決法
以下を書き足してもう一度テストを行ったところ、無事"Text can't be blank"のエラーが出力された。
app/models/tweet.rbvalidates :text, presence: true
- 投稿日:2020-11-19T18:20:44+09:00
【Ruby】基本文法を個人的にまとめた。
Rubyについて、理解を深めるためにまとめる。
随時更新していく。参考文献
☆伊藤潤一『プロを目指す人のためのRuby入門』[コマンド]
irb
(Interactive Ruby)➡︎REPL(Read-eval-print loop、対話型評価環境)の1つ
ターミナルで動作確認ができる$ irb irb(main):001:0> 1 + 2 => 3 irb(main):002:0> a = 'Hello, World' => "Hello, World" irb(main):003:0> exit
ruby
➡︎同じくターミナルで動作確認ができる--sample.rb-- a = 'こんにちは' puts a --ターミナル-- $ ruby sample.rb こんにちは【FizzBuzz】
def fizz_buzz(n) if n % 15 == 0 "Fizz Buzz" elsif n % 3 == 0 "Fizz" elsif n % 5 == 0 "Buzz" else n.to_s end end puts fizz_buzz(1) --1 puts fizz_buzz(2) --2 puts fizz_buzz(3) --Fizz puts fizz_buzz(4) --4 puts fizz_buzz(5) --Buzz puts fizz_buzz(6) --6 puts fizz_buzz(15) --Fizz Buzz【unless文】
status = "error" message = unless status == "ok" --「if status != "ok"」と同義 "異常発生" else "正常" end puts message【case文】
country = "イタリア" say = case country when "日本" "こんにちは" when "アメリカ" "Hello" when "イタリア" "Ciao" else "???" end puts say
- 投稿日:2020-11-19T18:17:39+09:00
プログラミング初心者がRailsで日記SNS作ってみた
はじめに
日々の学びをメモして、良い感じのやつだけ共有できるようなアプリが欲しいと思ったので自作してみました.
現在、α版としてHerokuにアップしているので覗いていただけると嬉しいです。アプリ名
Output App
リンク
URL : https://outputapp.herokuapp.com/
GitHub : https://github.com/tatsuhiko-nakayama/output_app機能紹介
投稿画面
- ハッシュタグを入力することで情報源を共有できる
- デフォルトが非公開なのでメモとして使える
- closedボタンをopenに切り替えて公開する
- マークダウンが使える
詳細画面
- ハッシュタグ、参考URLへリンク
- LIKE機能
- フォロー機能
- コメント機能
ユーザーページ
- 各ステータス表示&一覧リンク
- ユーザーの投稿(open)一覧
ここまで作ってみて
所感
あまり難しい機能は実装せず、初心者でも自力でできる範囲でいろいろと実装してみました。
特にJavaScriptが勉強不足でAjaxがうまくいかなったり、改善したいところが多々あります。
次のステップ
テスト公開中なので、ユーザーさんの声を聞きながら使いたくなるサービスにどんどん改良していきます。
「アウトプット」と聞くと意識高い感じがして使いにくいのかなと思いつつも、特徴のない「日記アプリ」や「SNS」では既存サービスでええやんと思ったり。
その辺、試行錯誤しながら楽しく作り込んでいきたいなと思います。
作者スペック
- 2020年8月からプログラミングを始める
- 2ヵ月間、某プログラミングスクールで学習
- 前職は営業(8年在籍)
- 現在はニート生活を満喫しつつ、Web系自社開発企業で働きたいと思っている
- 趣味は料理とYoutube
制作期間
11/1〜現在
使用技術
- Ruby / Rails
- JavaScript / jQuery
- Amazon S3
- AWS (これから)
- Docker(これから)
- Circle CI(これから)
おわりに
テストユーザーのお願いをして使ってもらって、そこからが開発だなと思いました。
技術ももちろん大事だけど、ユーザーさんにとって使いやすかったり、感動する体験にどうやってつなげていくのか。
そこを考えるのはとても楽しいけれど、同じくらい悩ましい笑
ご覧いただけた方いらっしゃいましたら、ぜひ感想・アドバイスお願いします。
✔︎
- 投稿日:2020-11-19T18:17:05+09:00
RubyでFirebase Authenticationが発行したID Tokenを検証する
https://firebase.google.com/docs/auth/admin/verify-id-tokens
上記URLにやり方が記載されているのですが、Rubyに関してはSDKが存在しないため自前で実装する必要があります。
https://github.com/fschuindt/firebase_id_tokenこういうgemもあるのですが、Firebaseが公開している証明書をキャッシュするためだけにRedisが必要なので、Redisを使っていないプロジェクトではこのためだけにRedisを用意するのは過剰です。
https://satococoa.hatenablog.com/entry/2018/10/05/210933
JWTの検証に関してはこの記事が非常に参考になりますが、証明書のキャッシュ部分だけはRials.cacheに依存していてイマイチ。またHTTPクライアントはFaradayを利用したかったこともありFaradayを使ってなんとかいい感じにできないかと考えていたところ、いい感じのFaraday Middlewareがあったのでその紹介をします(本当はJWTの検証部分も解説しようと思ったんですが、ぐぐってみたら上記の記事を発見して書きたいことが書いてあったのでやめました)
faraday-http-cache
https://github.com/sourcelevel/faraday-http-cache
HTTPのレスポンスのCache-Controlヘッダーに含まれるmax-ageの値を利用していい感じにキャッシュしてくれるMiddlewareです。キャッシュの保存先としてキャッシュ用のクラスのインスタンスを渡すことができ、任意の場所にキャッシュできます。サンプルではRails.cacheを渡していますが、ファイルやDBやRedisなどに保存したい場合は同じインターフェースをもつクラスのインスタンスを作成して渡すこともできます(もちろんRails.cacheのストレージをを変更しても良いです)
証明書や公開鍵に関しては、Cache-Controlヘッダーに含まれるmax-ageの値を利用してキャッシュすることが推奨されているので、このgemはうってつけです。connection = Faraday.new do |builder| builder.use :http_cache, store: Rails.cache, logger: Rails.logger builder.request :url_encoded builder.response :json, parser_options: { symbolize_names: true }, content_type: 'application/json' builder.adapter Faraday.default_adapter end connection.get('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com')使い方としては上記のような形です。
特段変わったことをせず普通にFaradayを使っていればキャッシュされるようになるので非常に便利です。
またloggerを設定することで、キャッシュから取得しているのかどうかがわかるようになるので、loggerも合わせて設定することをおすすめします。ログレベルはdebugなので、本番環境でやたらログが出ることもありません(本番環境のログレベルがinfo以上である前提ですが…)FirebaseはJWKsを公開しているわけではない罠
最初公式のドキュメントにある証明書がJWKsだと勘違いをしていてハマりました。実際は独自のフォーマットで、しかも公開鍵ではなく証明書なので、証明書から公開鍵に変換する必要があるする必要があります。
GCPではJWksが公開されてるので、Firebaseも同様にJWKsだろうと思いこんでしまうと罠にはまります。
JWKsであれば、JWTのdecodeの際にjwksのHashを渡すことでJWT.decodeがいい感じに公開鍵を使って検証してくれて楽なのに残念です…。と、記事をここまで書いて、「もしかしたらJWKsも公開されてるのでは?」と思ってぐぐってみたところ、公式サイトにはなかったJWKsを発見しました。
https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.comこれを用いれば以下のようにシンプルに書くことができます。
connection = Faraday.new do |builder| builder.use :http_cache, store: Rails.cache, logger: Rails.logger builder.request :url_encoded builder.response :json, parser_options: { symbolize_names: true }, content_type: 'application/json' builder.adapter Faraday.default_adapter end res = connection.get('https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com ') payload, = JWT.decode(id_token, nil, true, { algorithms: ['RS256'], jwks: res.body })シンプルに書けるようになりますが、公式ドキュメントに書かれているURLではないため、利用する際には注意してください。
- 投稿日:2020-11-19T18:04:02+09:00
jQueryを使ったページスクロールの作成方法まとめ
フロントエンドをよりみやすくしようと思ったところで、よく企業のページに行くと、クリックでスクロールするような動きがあり、いいなと思ったので作成してみました。
環境
ruby 2.6.5
rails 6.0.3導入の流れ
Step1 jQueryのインストール
Step2 webpackの編集
Step3 jQueryの呼び出し
Step4 スクロールをできるようにする記述を書くStep1 JQueryのインストール
railsを作成した後にターミナルにてコマンドを打ち込みます。
% yarn add jqueryStep2 webpackの編集
railsアプリケーション内にwebpackがあるので、jQueryが使えるようにするため、そこのファイルを記述し直します。
config/webpack/environment.js//元からある記述 const { environment } = require('@rails/webpacker') //ここから記述していく------------------------------ const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', jquery: 'jquery', }) ) //記述ここまで----------------------------------- //元々ある記述 module.exports = environmentjQueryの呼び出し
application.jsにjQueryを呼び出します。
app/javascript/packs/application.js//コメントアウトでいろいろ書かれている require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require('jquery') //この行を追加 //コメントアウトでいろいろ書かれているStep1からStep3でrailsにjQueryを導入しました。
Step4 スクロールをできるようにする記述を書く
scroll.jsファイルを作成し、記述していきます。
scroll.jsはjavascriptのディレクトリ内に作成します。app/javascript/scroll.js$(function(){ // #で始まるリンクをクリックしたら実行されます。 $('a[href^="#"]').click(function() { // スクロールの速度 const speed = 400; // ミリ秒で記述 ここで定義している const href= $(this).attr("href"); const target = $(href == "#" || href == "" ? 'html' : href); const position = target.offset().top; $('body,html').animate({scrollTop:position}, speed, 'swing'); return false; }); });上から3行目の部分に
$('a[href^="#"]').click(function()とありますが、JavaScriptでこのように設定しているため、HTMLでリンクを指定する場合は"#hoge"といった形で、指定してやる必要があります。
scroll.jsを作成後もう一度application.jsに記述をします。
app/javascript/packs/application.js//コメントアウトでいろいろ書かれている require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require('jquery') //先ほど追加したやつ require('../scroll') //この行をさらに追加 //コメントアウトでいろいろ書かれているHTMLは下記のような流れになると思います。(かなり省略しています)
html.erb<a href="#hoge">一覧へ</a> <!--リンク--> <div id= 'hoge' class='fuga'>一覧</div> <!--飛んでいく先-->最後に
スクロールページの記述方法は他のサイトにも乗っていますので下記のURLを貼っておきます。
railsのバージョンが変わるごとにstep3までの導入方法が変わるのかなあと思っています。(自分が調べたサイトは古いバージョンで本当にこのやり方でいいのかと疑心暗鬼になっていました。)jQueryでスムーススクロールを実装する方法【初心者向け】
https://techacademy.jp/magazine/9532RailsでjQueryのページスクロール設定
https://qiita.com/taKassi/items/c1e2b54138744afe8b6d徹底解説!スムーススクロールをjQueryで実装する方法
https://changeup.tech/article/jquery-smooth-scroll/
- 投稿日:2020-11-19T17:33:52+09:00
Rails いいね機能
conslerails g model Favorite user:references post:references rails db:migrate一人のユーザーはいいねを一回までにする為
favorite.rbclass Favorite < ApplicationRecord belongs_to :user belongs_to :post validates_uniqueness_of :post_id, scope: :user_id endpost.rbhas_many :favorites, dependent: :destroyuser.rbhas_many :favorites, dependent: :destroyconsolerails g controller favoritespostsにネストする
routes.rbresources :posts do resource :favorites, only: [:create, :destroy] endrails routes すると、
post_favorites
DELETE /posts/:post_id/favorites favorites#destroy
POST /posts/:post_id/favorites favorites#create
と表示される。
:post_idの部分をcontrollerで取得favorites_controller.rbclass FavoritesController < ApplicationController def create @favorite = current_user.favorites.create(post_id: params[:post_id]) redirect_back(fallback_location: root_path) end def destroy @post = Post.find(params[:post_id]) @favorite = current_user.favorites.find_by(post_id: @post.id) @favorite.destroy redirect_back(fallback_location: root_path) end endすでにいいねしてるかどうかを判定するメソッド
User.rbhas_many :favorites, dependent: :destroy def already_favorited?(post) self.favorites.exists?(post_id: post.id) endview<% if current_user.already_favorited?(@post) %> <%= link_to post_favorites_path(@post), method: :delete do %> <i class="fas fa-heart" style="color: red;"></i> <% end %> <span style="color: red;"><%= @post.favorites.count %></span> <% else %> <%= link_to post_favorites_path(@post), method: :post do %> <i class="far fa-heart"></i> <% end %> <%= @post.favorites.count %> <% end %>引数の@postはDELETE /posts/:post_id/favoritesの:post_idに対応する。
- 投稿日:2020-11-19T17:10:04+09:00
roman numbers
課題
https://qiita.com/daddygongon/items/2d0a73a51ddab2d9da1b
問題
アラビア数字(arabic numerals)を受け取って,ローマ数字(roman numerals)を返すmethodを書きなさい.
解答
#!/usr/bin/env ruby # frozen_string_literal: true class Integer def to_roman() symbols = %w[I V X L C D M] roman = digits.each_with_index.map do |n, i| one = symbols[i * 2] five = symbols[i * 2 + 1] ten = symbols[(i + 1) * 2] case n when 1..3 then one * n when 4 then one + five when 5 then five when 6..8 then five + one * (n - 5) when 9 then one + ten end end roman.reverse.join end end [1, 4, 9, 16, 25, 36, 49, 64, 81, 100].each do |i| puts(i.to_roman) end学んだこと
Integerクラス
数値に対して
999.to_roman
を実装したいなら、Integer クラスを拡張する。class Integer def to_roman() # ここに書く end end冗長な self
self
を書かなくてもいい。roman = self.digits.each_with_index.map do |n, i|ではなく
roman = digits.each_with_index.map do |n, i|
- source ~/go/src/github.com/TeamNishitani/grad_members_20f/members/iPolyomino/roman_numbers.org
- 投稿日:2020-11-19T16:50:20+09:00
[Rails]パンくず機能
はじめに
画面遷移をわかりやすくするために、gretelというgemを使用してパンくず機能を実装しました。
目次
- 1. gretelのインストール
- 2. パンくずの設定
- 3. ビュー
1. gretelのインストール
gretelというgemを用いることで、リンクを設置したリストを画面に表示させるパンくずを実装することができます。
gemfilegem "gretel"ターミナル
bundle install2. パンくずの設定
パンくずの親子関係を設定するファイルを作成します。
ターミナル
rails g gretel:install例
config/breadcrumbs.rbcrumb "現在のページ名(表示させるビューにもページ名記述)" do link "パンくずリストでの表示名", "アクセスしたいページのパス" parent :親要素のページ名(前のページ) endconfig/breadcrumbs.rbcrumb :root do link "ホーム", root_path end crumb :posts do link "クチコミ一覧", posts_path parent :root end crumb :post_show do |post| link post.name, post_path(post) parent :posts end crumb :user do |user| link user.nickname, user_path(user) parent :root end # 親カテゴリーのパンくず crumb :parent_category do |category| category = Category.find(params[:id]).root link "#{category.name}", search_post_path(category) parent :root end # 子カテゴリーのパンくず crumb :child_category do |category| category = Category.find(params[:id]) # 表示しているページが子カテゴリーの一覧ページの場合 if category.has_children? link "#{category.name}", search_post_path(category) parent :parent_category # 表示しているページが孫カテゴリーの一覧ページの場合 else link "#{category.parent.name}", search_post_path(category.parent) parent :parent_category end end # 孫カテゴリーのパンくず crumb :grandchild_category do |category| category = Category.find(params[:id]) link "#{category.name}", search_post_path(category) parent :child_category end crumb :post_new do link "新しいクチコミ投稿", new_post_path parent :root end crumb :name_search do |search| if search == "" link "クチコミ検索結果", name_search_posts_path else link "「#{search}」のクチコミ検索結果", name_search_posts_path end parent :posts endカテゴリーのパンくずリストは、親カテゴリー > 子カテゴリー > 孫カテゴリーになるように設定しています。
子カテゴリーの処理については、孫カテゴリーのページから呼び出した場合(孫カテゴリーを選択した時)と、子カテゴリーのページから呼び出した場合(子カテゴリーを選択した時)の2つの条件で処理を変えるようにします。3. ビュー
今回はパンくずを表示させたいところだけに適応したかったので、部分テンプレートを作成しました。
app/views/shared/_breadcrumbs.html.erb<div> <%= breadcrumbs separator: " › " %> </div>separator: " › “はパンくずの区切りである「>」を示します。
投稿一覧のindexファイルだけ載せておきます。
app/views/posts/index.html.erb~略~ <% breadcrumb :posts %> <%= render "shared/breadcrumbs" %> ~略~breadcrumb :postsはbreadcrumbs.rbで設定したページ名を記述しています。
参考リンク
- 投稿日:2020-11-19T16:46:56+09:00
配列とif文を使って文字列をleet文字に変換
概要
- 結論
- 前提条件
- どのようにコーディングするか
- 開発環境
結論
文字列とそれに対応するleet文字列をそれぞれ配列にすることで、変換プログラムを作ることができる。
前提条件
Rubyで文字列「A、I、U、E、O」をleet文字列「4、1、(_)、3、0」に変換する。
どのようにコーディングするか
leet.rbinput = gets.split("") #入力された文字列を配列化する input.each {|s| string = ["A", "I", "U", "E", "O"] #変換元の文字列を配列にする leet = ["4", "1", "(_)", "3", "0"] #変換後の文字列も配列にする a = string.index(s) #配列inputの値が配列stringの何番目にあるか探す if a == nil print s #該当がなければそのまま出力 else print leet[a] #該当があれば配列leetの同じインデックス番号の値を出力 end }if文やcase文のみで変換を行っていく方法もありますが、上記のやり方で行数を少なくすることができました(処理スピードはあまり変わらないようですが)。
開発環境
Mac catalina 10.15.7
Vscode
Ruby 2.6.5
- 投稿日:2020-11-19T16:03:10+09:00
Ruby がない環境で Rails プロジェクトを一撃で作る Docker コマンド
Dockerはないとダメです。
docker run --rm -it --workdir /app/ --volume $PWD:/app/ ruby:2.6.3 bash -c "gem install rails -v 5.2.4 && rails new sample_project"以上です。
- 投稿日:2020-11-19T13:35:57+09:00
未経験からのエンジニア転職を目指します。
はじめに
こんにちは。
11月より未経験からのエンジニア転職を目指すべく、プログラミング学習をスタートし始めたものです。
学習記録を残していければと思っております。自己紹介
地方国立大学卒業後、大手学習塾に新卒で就職して約1年半経ちます。
理想と仕事内容のギャップで転職を決意しました。高校はかなり勉強には力を入れた記憶があります。
が第一志望には受からず...。プログラミング自体は、大学の講義でおそらくC言語は軽く触れましたが
難しすぎて断念しました(笑)ただ「学力の規定要因分析―都道府県別のクロスセクションデータを用いて―」というテーマで卒業論文を制作し
このときにR言語を使用して、統計分析などを行いました。
1日のほとんどを分析や分析結果の執筆に費やしていた時期で大変でしたが、楽しかったですね。ですので今Progateを中心に学習を進めておりますが、まだ勉強が嫌という感じではないです。
多分ここから大変になってくるのだと思いますが...。ここまでの学習記録
11月の初週からまずはProgateを中心に学習をスタートしました。
最初はスマホ版でやっていましたが、パソコンでやってみたいと思い、先述の卒論制作に使っていたパソコンを使ってProgateに取り組んでおります。
ひとまず19日現在でHTML&CSS,Sass,Command Line,Git,Rubyを一周終わらして、Web開発パスのruby on railsコースのrails Ⅰに取り組んでいる最中です。最後に
逃げ道の無いよう、新型Mac Book Airを購入致しました。今使っているパソコンが動作モッサリしていてイライラなのとWindowsだからですね。
学習していく中での気づきや、学習記録を定期的に更新していけたらと思いますので、よろしくお願いいたします。
- 投稿日:2020-11-19T13:24:27+09:00
Google recruit problem (exp and prime)
概要
ネイピア数 e の連続する 10 桁の数のうち、最初の素数を Ruby で求める
ただし、e は 200 桁まででよい
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190ref:
- https://qiita.com/daddygongon/items/ba94b0f2a73990fc6a07
- https://www.cnet.com/news/google-recruits-eggheads-with-mystery-billboard/
(Ruby 勉強中)
コード
1 def is_prime(num) 2 warden = true 3 for i in Range.new(2, Math::sqrt(num - 1)) 4 if num % i == 0 5 warden = false 6 break 7 end 8 end 9 return warden 10 end 11 12 exp = gets.chomp 13 exp = exp.delete('.') 14 15 for i in Range.new(0, exp.length - 10) 16 ten_digits = exp.slice(i .. i + 9).to_i 17 if is_prime(ten_digits) 18 puts ten_digits 19 break 20 end 21 endline1~10:
- 素数かどうかを判定する method を作成する
ine12~13:
- 以下のテキストファイルを読み込む
ファイル:
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190
- 小数点を取り除く
line15~21:
- ループを回して、前から順に連続する10桁の数字が素数かどうかを判定する
- 素数が見つかれば終了し、見つかった素数を出力する
結果は以下の通り
7427466391
- source ~/doc/lecture/multi-scale/grad_members_20f/members/yukiue/docs/google_recruit.org
- 投稿日:2020-11-19T13:14:24+09:00
Ruby で文字列を置換する方法 gsub
Rubyで文字列を置換:Stringクラスのgsub
Ruby初学者である自分のための、Ruby 2.7.0についての記事です。
文字列の一部を特定の文字列に置き換える方法です。
- 基本的な使い方 gsub(pattern, replace)
例えば、"def"を"!!"に置き換えたい場合
# 公式ドキュメントより 'abcdefg'.gsub(/def/, '!!') # => "abc!!g" # 変数を使う場合(自分の解釈) S = "abcdefg" # 文字列を変数Sに代入します puts S.gsub(/def/, '!!') # 出力される文字列 abc!!g一番簡単な部分だけ抜粋しています。詳細は公式ドキュメントを確認します。
https://docs.ruby-lang.org/ja/latest/method/String/i/gsub.html
- 投稿日:2020-11-19T09:40:28+09:00
Herokuに「The page you were looking for doesn't exist. 」が出たがコミットできてなかったという凡ミスだった、、、
拍子抜けするほどの凡ミスをして数十分ハマってたので、共有します
まず、PCの環境を分かる範囲で書いておきます。
- Ruby 2.6.5
- Ruby on Rails 6.0.3
試作のRailsアプリケーションをHerokuへデプロイし、URLを開こうとしたところ、
The page you were looking for doesn't exist.
のエラー文が表示されました。。とりあえず、ググる、、、
ただ、調べた情報だと
root_path
を設定していなかったために、「それを設定してもう一回HerokuにpushしたらOKでした〜」というものがほとんど。僕の場合は、ローカル環境できちんとrootに対応するviewが表示されていたので、そんなはずはないと思い引続き調査続行。。。
しかし、Terminal上でHerokuのエラーログを調査すると
ActionController::RoutingError (No route matches [GET] "/")という記述があるので、やっぱりroutingはきちんと実装できてないんだなぁ、、、と。
格闘すること数十分、ふと目に止まったGitHub Desktopで異変に気づく。
Terminal上でコミットしたつもりだった「initial commit」以降の変更がcommitされていない模様、、、なななんと、、、超初歩的なミスやないかい!!!(T_T)
ということで、再度コミット後、GitHubのリモートリポジトリにpush、そして、heroku masterにもpush
めでたくきちんと表示されました!!
- 投稿日:2020-11-19T09:06:51+09:00
スクール1週目の振り返り
スクール入校
10月5日、ついにスクールへ入校。学習スタイルは短期集中オンラインです。
新型コロナの影響で在宅オンライン学習となっています。コミュニケーションはSlackとZOOMで行われます。基礎カリキュラムの学習内容
環境構築(準備)
カリキュラム通りにターミナルでコマンドを打ち続けました。今、自分が何の作業をしているのか不安になりながらも環境構築しなくては、コーディングが出来ないと改めて痛感させられました。
そう思うと、Progateは本当に初学者に優しい教材だったんだなと感謝の気持ちで満たされました。HTML&CSS
Progateで楽しく学ぶことが出来たマークアップ言語。道中コースでは自力でのコーティングに苦労しました。
スクールのカリキュラムはイラストや動画で丁寧にまとまっていて理解度が高まりました。一つのカリキュラムを読み終えるのに1時間程度はかかるので、まずは、全体像を把握した上で要点を抑え最低限のメモ書きをしました。なぜなら、プログラミングは暗記しようとしても記憶に定着しないからです。
なので、コーティングで手を動かしスクールの特徴でもある同じグループの同期に学んだことをしっかりとアウトプットすることを心がけました。
アウトプットの効果は凄まじく、人に教えるつもりでインプットして口に出すことで90%記憶に定着するとか科学的に実証されているそうです。Ruby
マークアップ言語を終えて、ついにプログラミング言語に挑戦。基礎カリキュラムでは主にRubyを学びました。
数多くあるプログラミング言語の中で、なぜRubyなのかと言うと日本人の方が開発されたことで国内でのシェア率は高いからです。
また、ネット上で日本語での投稿が多くエラーに躓き挫折してしまいがちな初学者にとっては学びやすい言語だと言えます。Ruby on Rails
Rubyが日本国内で普及した一因でもあるフレームワークのRails。フレームワークの数も多くPHPならLaravelなどその言語に応じて使いやすいフレームワークが採用させる傾向があるそうです。Railsを活用することでファイル作成等を省略できることやコーティング量を減らし可読性を高められることも大きなメリットといえます。振り返り・感想
これまで、HTML&CSS、Ruby、RailsをProgateで2周していたので、上々の滑り出しを切ることができました。基礎カリキュラムでは、最低限のマークアップ言語の知識とRailsの理解度UP、コーディング力を身に着けそこから中間試験、本試験を実施。本試験に合格し応用カリキュラムへと進みます。まず中間試験が難しすぎて自信が打ち砕かれました。そこから試験に向けて猛勉強ですね。特にCSSの配置指定やRailsのインスタンス変数問題が出来なさすぎて課題となりました。
そして、しっかりと中間試験を復習した上で本試験に臨みました。。合格点80点に対し、自己採点は84点とひと安心。そして、メンターさんからも84点と採点していただき、一発で応用カリキュラムに進むことができました。基礎カリキュラムでは、スピート感重視で理解できておらず詰まったらカリキュラムに戻り復習して記憶に定着の繰り返しすることを学びました。
不安を抱えながらも応用カリキュラムへと進みます。プログラミングはどうしても点と点が繋がらない間はこのまま進めて良いのだろうか?と不安になります。
しかし、学習を進めていくうちにどこかで必ず点と点が繋がり理解度が高まった時プログラミングが楽しくなるそうです。その日を待ち望んで日々、愚直に2週目も1日10時間をキープしながら同期と楽しくアウトプットし続けます。
- 投稿日:2020-11-19T06:10:40+09:00
コピペでwebアプリが作れる。魔法のコード。
細かいことは抜きにこのコードを自分のターミナルで打ってみてください。
rails new testapp -d postgresql; cd testapp && rails g scaffold post title:string body:string && rails g scaffold comment content:string post:references && bundle && rails db:create && rails db:migrate && rails sこのコードだけでwebアプリを作ることができます。
用途としては、
- 使ったことないgemのテストのためのアプリとして
- Railsでどんなことができるのか初めの一歩としてとりあえず動かしてみたいときなどです。
コードの詳細としては
rails new testapp -d postgresqlcd testapprails g scaffold post title:string body:stringrails g scaffold comment content:string post:referencesbundlerails db:createrails db:migraterails sです。
それぞれの意味がわからない人は是非調べてみてください!他にも便利で楽しいコードを発見したら投稿したいと思います。
- 投稿日:2020-11-19T02:39:09+09:00
記事一つでわかるRuby
言語の種類
まず初めにプログラミング言語の種類について深ぼっていきます。
プログラミング言語は大きく分けて下記の二つに分かれます。フロントエンド言語
フロントエンド言語とはwebサイトの見た目の部分を司る言語となります。
これも、厳密に言うと大きく二つに分類されます。
マークアップ言語
見た目の部分を作り込む言語。HTMLとCSSのことを指します。
プログラミング言語
フロントエンド言語の中のプログラミング言語は主にJavascriptがあります。
Javascriptは、動的なwebサイトを実現するときによく採用される言語です。バックエンド言語(サーバーサイド言語)
フロントエンドが見た目の部分を司るのに対し、こちらは内部の構造を司るものになる。例えば、自動販売機にお金を入れオレンジジュースのボタンを押すとお金が計算され、ジュースとお釣りが出てくるような物です。
こちらは、フロントエンド言語と違い全てプログラミング言語となります。
それぞれ、言語には特化したものがありニーズに合わせて使い分けるのが良い。
例)python、ruby、java、C、C#、C++、PHP等フレームワークとは
フレームワークとは、簡単に言えばプログラミングの言語を使うための便利ツールのような物です。本来であれば、膨大な量のコードを書かなければいけないのに対し、フレームワークを使用することで、簡単にシステムを開発することができます。
Ruby on Rails
Rubyのフレームワーク。開発を効率化する仕組みが多数用意されているため生産性が高い。
Laravel
PHPのフレームワーク。手軽さや扱いやすさから人気を集め最近ではよく使用されるようになっている。
上記以外にも多数のフレームワークが存在するがここでは省略します。
今回学んだ言語
題名の通り今回学んだ言語はRubyとなります。Rubyは習得が比較的簡単かつ最近注目されてきている言語の一つである。
文字,数字の出力、計算
・文字の出力
puts "文字が出力される" 文字が出力される #出力結果・数字の出力
puts 10 10 #出力結果・計算の出力
puts 5 + 5 10 #出力結果(足し算を行う) puts 5 - 5 0 #出力結果(引き算を行う) puts 5 * 5 25 #出力結果(掛け算を行う) puts 5 / 5 1 #出力結果(割り算を行う) puts 5 % 5 0 #出力結果(割り算の余りを出す) # 組み合わせも可能 puts (5 + 5) * 5 50 #出力結果変数
変数とは簡単に言えば数学における代入と同じです。
例えば、aに数字の10を代入したいときは以下のようなコードで記します。a = 10 puts a 10 #出力結果変数には文字列であったり四則計算も代入することができます。変数における=は等しいではなく、代入を指します。
条件分岐処理(if文)
条件分岐の処理を行いたいときはif文を用いて行います。
#条件分岐がないの場合 if "条件" "処理の内容" end #条件分岐がある場合 if "条件" "処理の内容" else "処理の内容" end #条件分岐が複数の場合 if "条件" "処理の内容" elsif "条件" "処理の内容" else "処理の内容" endここで重要なのが必ずendをつけること。付けないと、どこで処理が終わって良いのかわからず、エラーが発生してしまいます。
条件には下記のような内容を記載します。#AとBが等しいかどうか(=ではなく必ず==にすること) A == B #AはB以下かどうか A <= B #AはB未満またはAはC以上かどうか(||でまたはを意味する。複数つけることも可能) A < B || A >= C #AはBより大きいかつAとCが等しいかどうか(&&でかつを意味する。複数つけることも可能) A > B && A == C&と&&、|と| |の違い
&は共通する要素を出力し、&&はtrueかfalseを出力する。
同様に|は和集合の値を出力し、||はtrueかfalseを出力する。繰り返し処理(timesメソッド、whileメソッド、eachメソッド等)
繰り返しの処理を行いたいときは、様々な方法があるが代表的なものをあげると、times文、while文、each文などがある。
timesメソッド
回数を指定する時に用いる
3.times do puts "アイウ" end アイウアイウアイウ #出力結果whileメソッド
条件式を用いて、それが真で有る限り繰り返す
num = 0 while num < 5 puts num num += 1 end #出力結果 0 1 2 3 4eachメソッド
eachメソッドは使う頻度がかなり多く、繰り返し処理の中で最も使われていると言っても過言ではないです。のちに出てくる配列を用いるため、そちらと照らし合わせて確認するのをお勧めします。
nums = [a,b,c,d,e] nums.each do |num| puts num end # 出力結果 a b c d e変数numsに代入した値を順に繰り返し変数numに代入していきながら、処理を繰り返し、全ての値が終了した時、処理が終わります。
配列とハッシュについて
配列
配列とは、複数の値をまとめて管理する時に用います。
配列は変数に代入することもできる。lists = [a,b,c,d,e] puts lists[0] a #出力結果 puts lists[4] e #出力結果配列は左から順番に0から数字が割り当てられ、出力の際は上記のように記載する。
ハッシュ
ハッシュは配列と一見似ているが、簡単に言えば辞書のようなものです。
ハッシュにはキーとバリューというものが存在していて、それらで管理するこの方式をキーバリューストアといいます。こちらも変数に代入可能です。コードの記述の仕方は以下の三つとなります。lists = { "key" => "value" } lists = { :key => "value" } lists = { key : "value" } puts lists[key]上記のように出力を行う。
※配列とハッシュは組み合わせることも可能。
メソッド、class(クラス)、インスタンスについて
メソッド
メソッドとは今までも条件分岐であったり、繰り返し処理を行う時に使用してきましたが、簡単な処理の塊と覚えると楽です。
メソッドは自分で作成することも可能でその際は、defメソッドを使用します。def "変数名" "メソッドの処理" end def "変数名"("引数1", "引数2") "メソッドの処理" enddefで指定したメソッドは変数名を記載することで呼び出すことができます。また引数というものを指定することで、スコープの外の変数を使用することができるようになります。
スコープ
スコープとは、変数を扱える範囲のことです。メソッド内で定義した変数はメソッド外で使用することができないのと同時に、メソッドを定義した後に記述した変数はメソッド内では使用できません。それらの範囲のことを専門用語としてスコープと呼びます。
引数
引数とは、スコープ外の変数を使用したい時に用います。例えば、引数を指定することで、引数の値により処理内容を変化させることができるので汎用性が上がります。class(クラス)
クラスとは簡単に説明すると沢山のメソッドをまとめた箱のようなものです。
クラスを指定してあげることで、コードの可読性が上がったり、保守がしやすくなります。よく設計図と言い表されることがあります。class "クラス名"{ def "変数名" end def "変数名" end }上記のように記載します
インスタンス
簡単に説明すると、classの中に定義したメソッドを呼び出すためのもの。変数名.newをクラス外に記載することで、使用可能になる。
class "Item"{ def "hello" puts "こんにちは" end def "変数名" end } item = Item.new item.hello こんにちは # 出力結果上記のようにインスタンスの生成を行う。
まとめ
まだまだ、rubyで行えることはたくさんありますが、量があまりにも膨大なため今回はここまでとします。ここまでの内容は基礎の範囲なので、必ず抑えておきたいポイントだと思いました。次回はRubyのフレームワークのRuby on Railについての記事を書いていきます。