Quantcast
Channel: DMM WEBCAMP Advent Calendarの記事 - Qiita

超初心者のための「Railsがなにをしているか例えてみよう」講座

$
0
0

超初心者のための「Railsがなにをしているか例えてみよう」講座 

おはようございます、こんにちは、こんばんわ。
私はインフラトップという会社で、
プログラミング学習の「WEBCAMP」でメンターとして勤めております。
その中で、良くされる質問が、
「Railsでそもそも何を書いたらいいのかわからない」
「Railsがなんで動いているのかわからない」
などです。その気持ち…。
よ~~~~~~~~~~~~っくわかります。

.saveとついているから何となく保存されているんだろうな、
とゲームなどでそれとなく覚えている単語から推測、
理解して使っているような時期が、私にもありました。

WEBCAMPメンターとして働く最中にしたRailsの仕組みの説明を、
フィードバックとして記事にしようと思い立ちました。

プログラミングは、
どう説明しても人によって理解の速度が違うので、
これが「完璧な説明」とは言えないと思います。
しかし生徒の皆様にご理解をいただけたことも多く、
有効な説明と自負しております。

あくまでもわかりやすくするために多少の独自解釈を含みつつ、
ざっくりかいつまんでのお話をさせていただきます。

もくじ

  • そもそもRailsって何をしているの?
  • Railsの動きは、「店舗と商品と運送と保存管理」で成り立っている
  • Railsで最低限抑えるべきこと

そもそもRailsって何?何をしているの?

まずはここからご説明をさせていただきます。
Rubyを使って作られたフレームワークということが一般的に説明されていますが、
一言で言えば、

  • Railsの規約に従って作っていけば、比較的簡単にアプリを作れる仕組み
  • Rubyを使って作られており、アプリの作成にもRubyが使える。
  • Web上にアプリ・サービスを作る事に使える。

というのが、Railsフレームワークです。
ひとまず、この3点を覚えておけばRailsの理解の下準備としては十分です。
Web上にアプリケーションを作る、というのがとても重要です。

続いて、Railsが何をしているのかをご説明します。
MVCという構造を採用しています。

  • Model…データベースの管理
  • View…ホームページとして表示する機能
  • Controller…機能を定義して、モデルと提携してデータを引き出し、Viewの管理も行う

というようになっています。

つまり、
Web上にアプリを作るというのは、
フォームに文字などを入れると、データベースに保存され、
自動で更新されるホームページが出来上がる、ということです。

それだけ?と思うかもしれませんが、極論を言えばそれだけです。
その中に数式を入れて計算させたり、
通常のホームページのように装飾したりできますが、
根本の部分はこの点に集約されます。
もっと便利にするためにgemなども使えますが、
こちらは基礎を押さえてからご利用頂いた方がいいと思います。

基本的にはこの点を起点としてRailsアプリは作られている、と考えておきましょう。

Railsの動きは「店舗と商品と運送と保存管理」で成り立っている

それでは、Railsアプリの基本を押さえる為に、
Railsがしている仕事を現実の仕事に例えていきます。
MVCの説明をしましたが、一つずつ分解して考えていきましょう。

  • Model→データベース=「運送業の倉庫」データを預けたり、預かり方を設定できる。
  • View→ホームページ=「コンビニ店舗」預かったデータをホームページに展開、閲覧できるようにする。新しいデータの預かりもできる。
  • Controller→「倉庫の従業員&運転手」仕事のやり方を作る、データの出し入れの管理=倉庫の従業員兼、運送トラック。

MVCに加えて、今度はRoutesというのも出て来ます。

  • Routes→「倉庫とコンビニの住所」ブラウザでどの住所=URLに行けば、仕事ができるかを設定する。

まずはこれくらいを意識して、解説を読んでみましょう。
今回はデータベースへの保存と、Viewからのブラウザ表示のみに焦点を当てます。
実際に手を動かしてチェックしてみて下さい。

それでは、スタート前に、以下のコマンドを入れて下準備をします。
コントローラを準備しましょう。
rails g controller posts new index create show

続いて、routesを変更します。
routesは、

  • URLを入力したら
  • viewで<%= link_to 'リンク名', ~path %>で作られるリンクをクリックしたら
  • フォームなどのデータベースに更新する処理をしたら

どのような機能につながるのかを、
設定します。

get '/URL' => 'コントローラ名#アクション名'

この場合は、URLにつないだら、コントローラに設定されたデータを持ってきて、
同じ名前のアクション名のついたViewをホームページとして設定します。

post '/モデル名s' => 'モデル名s(コントローラ名)#create'

この場合は、Viewファイルにあるフォームから送信した場合、
モデル名に対応したデータベースに保存させる、という記述です。
createのアクションで詳しく解説します。

それでは今回説明するアクションのroutesを書いていきましょう。

config/routes.rb
Rails.application.routes.draw do
  # アプリを開いた瞬間につながるページをrootで記述します。
  root 'posts#index'

  # 今回使用するrouteはこちらです。
  # resourcesで自動出力されるものと同一にしてあります。
  get  '/posts/new'  => 'posts#new',as: 'new_post'
  post '/posts'      => 'posts#create'
  get  '/posts'      => 'posts#index'
  get  '/posts/:id'  => 'posts#show',as: 'post'
end

ここまでで、下準備はOKです。
あとは、実際に起こっている出来事を追いかけながらチェックします。

まずは、データの格納先である、データベースに保存するためのモデルを作ります。
モデルは「データベース」そのものというよりは、
データベースに対応した箱を作り、その取扱い方法を記述するものです。
今回は、データベースに対応した箱を作るコマンドからスタートします。
rails new アプリ名
でアプリを作成後、アプリに移動、その後に実行していきます。
以下のコマンドをターミナルに打ち込んでみましょう。

rails g model post title:string body:string
rails db:migrate

このコマンドを実行すると、
こんな感じの仕切りが付いた箱が作れるようになります。
今後、posts_controllerでPost.newと入れると、
保存用に準備できる箱ができあがるようになります。
new.png

そして仕切り箱には名前がついています。

title,body,id,created_at,updated_at

この仕切り箱一つ一つのデータの格納場所を、
「カラム」といいます。
このうち、
titleとbodyは、コマンドで自分で準備したもので、
id,created_at,updated_atの三つは、自動で準備されるものです。
この三つはrailsが自動で記入するので、基本的にはいじれないものです。
idは保存されたデータに順番に自動で振られる番号です。(重要)
created_atはデータの作成日、
updated_atはデータの更新日です。
先ほど入力した
rails g model post title:string body:string
というコマンドで、Postモデルが出来あがり、
それ以降は「Post.new」とコントローラで記述することで、
一つ箱が作れるようになります。
これがないと、データをデータベースに保存できません。

では続いて、入力フォームのあるページと動作を記述してみます。
これはnewアクションと言います。
下記の絵と、コードを参照して動きを確認しましょう。

このnewアクションは、
localhost:3000/posts/new
にアクセスするか、

もしくはViewfileの中で
<%= link_to 'new',new_post_path %>
で作られるリンクをクリックすると、
ブラウザにフォームが書かれたnewのページが表示されます。

new_create.png

app/controllers/posts_controller.rb
def new
    @post = Post.new
end
app/views/posts/new.html.erb
<%= form_for(@post) do |f| %>
  <div class="field">
        <%= f.label :件名 %>
        <%= f.text_field :title %>
    </div>
    <br>
  <div class="field">
        <%= f.label :本文 %>
        <%= f.text_area :body %>
    </div>
    <br>
    <%= f.submit :投稿 %>
<% end %>

@post = Post.newで@postという作業員さんに、Post.newという箱を預けます。
これが届くことで入力フォームに入力可能になり、
データベースに保存ができるようになります。
そして入力フォームを画面に表示するために、
fomr_forの記述がしてある場所に@postさんに来てもらい、
Post.newの内容の記述ができるようにブラウザで表示、
ページにアクセスしている人に記述をお願いし、
最後に送信ボタンを押してもらうと、createアクションに送信します。

ここが重要で、
Post.newでPostモデル用の箱を作ったので、
app/controllers/posts_controller.rb
の中にあるcreateに、その荷物を送るという手順になっています。
Post.newで新しく箱を準備するので「新規の投稿」、
新しくデータとデータをしまう場所をcreateしてもらう、ということです。

ちなみにposts_controller.rbは、
「Postモデルの」という意味のあるコントローラ名です。
複数形にするのがポイントです。(Railsの命名規則です)
ここではPostモデルと契約していることになっている、
と考えるとイメージしやすいと思います。
こうすることで、Postモデルのデータを取り扱う役割が持てます。

createアクションでは、以下のような記述と、作業内容になります。
今回は、データベースにしまう動きですので、ブラウザには表示しません。
コントローラしか記述をしないで大丈夫です。
こちらも画像を確認しながら、動きをチェックしてください。

このcreateアクションはnewに書かれたフォームで、
データを入力、送信ボタンを押すと動きます。

create.png

app/controllers/posts_controller.rb
def create
  @post = Post.new(post_params)
  @post.save
  redirect_to posts_path
end
# private以下は、一番下のほうに記入します。ここから
private

def post_params
  params.require(:post).permit(:title, :body)
end
# ここまで。この下にendがひとつだけあるのを確認して下さい。
end

まず、
@postさんという作業員さんに、新たにPostモデルの箱を渡します。
しかし、渡す前に、post_paramsさんという別の作業員さんにチェックを依頼します。
newのフォームからもらったほうのPost.newの箱のデータを、
「Postモデルかどうか」→params.require(:post)
「カラムはtitleとbodyだけ受け付ける」→.permit(:title, :body)
という順番で検査します。
newのフォームからもらったPost.newのデータが正しければ、
createで作られたPost.newモデルにしまい直すという作業をします。
そしてしまい直したら、
@post.saveで@postさんは、倉庫にデータを収めに行ってくれます。
これらの作業は、Rails内部で行われているので、見えません。
しかし、このままですと、このページにアクセスしている人は、
データが登録できたかどうかが確認できません。
ですので、redirect_to posts_pathで、
ブラウザの表示をindexアクションのページに移動させるようにする必要があります。

ここまでで、createの仕事が終わりになります。

ではせっかくなので、続いてindexアクションについての記述を行いましょう。
indexアクションは、すべての投稿をまとめて表示する、という内容です。
今回は投稿後は必ずこのページに移動するように設定しています。

indexアクションは、
このnewアクションは、
localhost:3000/posts/
にアクセスするか、

もしくはViewfileの中で
<%= link_to 'new',posts_path %>
で作られるリンクをクリックすると、
Postモデルに保存されたデータの一覧ページにつながります。

index.png

index2.png

app/controllers/posts_controller.rb
  def index
    @posts = Post.all
  end

これだけです。
これはどういうことをしているのかというと、
PostはPostモデルにアクセスします、という意味で、
.allはPostモデルに保存されたものをすべて持ってきてください、という意味です。

今回は@postsというトラックに、
Postモデルに保存されたものをすべてしまいました。
そして、しまい込んだあと、
Viewファイルに記述された@postsにデータが届くようになっています。
このデータをすべて同じレイアウトで並べられるように、
each文で、レイアウトを決め、すべてのデータを同じ展開の仕方で表示します。

Viewファイルに以下の表記をして下さい。

app/views/posts/index.html.erb
<h2>投稿一覧</h2>
<h3>件名/本文</h3>
<% @posts.each do |post| %>
    <%= post.title %>/<%= post.body %>
    <br>
<% end %>

そうすると、すべてのPostモデルのデータを、
<%= post.title %>/<%= post.body %><br>

というレイアウトで繰り返して表示します。
棚にきれいに整列させて並べるような感じです。

では最後にshowアクションの記述を行ってみましょう。
showアクションは、
Postモデルに保存されたデータの中から、
保存されるごとに自動で番号が振られるidを基準に、
ひとつのデータを持ってくるように指定するアクションです。

このnewアクションは、
localhost:3000/posts/id(=保存された番号)
にアクセスするか、

もしくはViewfileの中で、
※この例はindex.htmlの@posts.eachの中で使われる想定です。
<%= link_to 'new',post_path(post) %>
で作られるリンクをクリックすると、
ブラウザにshowのページが表示されます。

今回は、
localhost:3000/posts/2
だった場合の動きです。
ちなみに、この場合二件以上データが投稿されていない場合は、
表示ができません。無いものは表示できないからです。

show.png

app/controllers/posts_controller.rb
    def show
        @post = Post.find(params[:id])
    end

@が先頭についた変数をインスタンス変数、と言います。

Post.findは、Postのように先頭の文字を大文字に変更すると、
コントローラはPostモデルにアクセスする、という意味になります。
そして.findで「idを頼りに一件だけデータを選択します」という意味になります。
(params[:id])で、そのデータがidです、と確定します。

app/views/posts/show.html.erb
<%= @post.title %>
<%= @post.body %>

などと書けば、データベースに保存されたPostモデルのカラムがそれぞれ表示されます。
ここまででデータベースへの保存と、
ページの表示に関する機能の実装方法を説明しました。

Railsで最低限抑えるべきこと

最後に復習とまとめとして、
Railsで最低限抑えるべきことを記載します。

  • Railsアプリを作るということは、
    フォームに文字などを入れると、データベースに保存され、
    自動で更新されるホームページを作るということ

  • MVCはあくまでデータの出し入れと、
    ホームページとしてデータをどう表示するかを制御しているというのが基本

  • routesで各機能へのURL、pathの制御を行っている

  • コントローラ内でPostとつけた場合、データベースから
    Postモデル関係のデータを引き出す操作を行うこと。
    @を付けた変数をコントローラで準備しないと、
    Viewにはデータが届けられないこと
    (回避する方法自体はある)

  • Post.newという記述がデータベースへの保存には必要なこと。
    保存されたPostモデルデータは主にposts_controllerで取り扱うこと。
    (回避する方法自体はある)

この点をひとまずしっかり抑えられれば、
初心者がRailsアプリケーションを作る上では十分です。
データの流れ、Railsの動きがしっかり分かれば、
悩みも半減すると思います。

少し長い記事になってしまいましたが、
ご覧いただきありがとうございました!

画像素材:かわいいフリー素材集いらすとや 様 (https://www.irasutoya.com/)


5分でできる!? AMIをつかってEC2にredmineを作成しよう!

$
0
0

はじめに

InfratopAdventCalendarの2日目を担当します。@tatsukichiです。

それではやっていきます

皆さんはタスク管理にどのようなツールを使っていますか?
今回は、自分で立てたEC2インスタンスに
Railsで作成されているredmineを使ってタスク管理をしていきましょうというエントリーです。

個人で使うのもよし。チームで使うのもよし。
数多くのプラグインがあるredmineを使って、自分のタスクスタイルにあった管理ツールをAWSのサービスEC2を使って5分で作成していきましょう!
※今回は、redmineを作成し管理ユーザでログインするまでをゴールとしています。

手順

行うことは大きく3つだけ。
- AMIを使ってインスタンスを作成
- アクセスしてみる
- 管理ユーザでログインしてみる

AMIを使ってインスタンスを作成

AMIを使用し、インスタンスを作成するにあたり簡単に解説を挟んでいますが、必要なければ読み飛ばしてください。

AMIとは

そもそもAMIとは、Amazon Machine Imageのそれぞれの頭文字をとった名前です。
- OS
- アプリケーションサーバ
- アプリケーション
- etc.
これらの情報をひとまとめにしたEC2のテンプレートのようなものです。

AMIは、redmineのように既に存在しているものもありますが、オリジナルで作成することも可能です。
例えば、インスタンスを複製したいときなどにAMIを使用することで容易にインスタンスのコピーを作成することができます。

AMIの中には有料のものも存在しますが、redmineの使用量は無料となってます。

インスタンスの作成

それでは早速、redmineのAMIを使用してEC2インスタンスを作成していきましょう。

まずは、インスタンスを作成する画面でAMIを選択していきます。
1. AWS Marketplaceを選択します
AWS MarketplaceはAWSが用意したAMIが表示されます。
2. 「redmine」と検索します。
3. Bitnamiのredmineを選択します。
AMI.png

次に、インスタンスタイプを選択します。
redmineの使用人数等にもよりますが、今回は無料枠でも使用可能なt2.microを使用していきます。

以下の部分を選択します。
t2_micro.png

ここまでできたら、確認と作成をクリックしインスタンスを作成します。

鍵はもちろん、既存のものでも新しく作成しても問題ありません。

アクセスしてみる

それではインスタンスが作成されましたら、パブリックIPにアクセスしてみましょう。

スクリーンショット 2018-12-01 22.46.23.png

あれ、もうできた。

そうです。
たったこれだけであなただけのredmineが作成できました。
さて、何分かかりましたか?

管理ユーザでログインしてみる

ここまでは誰でもできてしまいそうな内容ですが、ここからは知らないと苦労しますので
一緒にやってみましょう。

redmineは動いているみたいだけど。。。
そうです。ログインできないんです。

ログインするには以下の手順でパスワードを見つける必要があります。
1. EC2インスタンス起動時のシステムログをみる
sys_log.png
2. 管理ユーザのパスワードをみつける
pass.png

以下の情報を入力してログインしてみましょう。

ログインID : user
パスワード : [白塗りの部分]
login.png

最後に

これで今回作成したredmineはあなたのものになりました。
便利なプラグインやイケてるテーマなどもありますので、好みにあったものを使用して楽しんでみてください。

ありがとうございました。

超入門!slackのスラッシュコマンド をカスタマイズ。

$
0
0

※この記事は<ストーリー編>と<論理編>に分かれております。諸事情により詳しい技術のお話は別記事にまとめたいと思います。(じ、時間がなかったわけじゃないんだからね!)

<ストーリー編>

どうも、おはこんばんにちわ!
みなさん社内のチャットツールは何を使っていますか?

僕が所属する会社では、"slack"です。
slack.png
slackを使っているIT企業は多いのではないでしょうか。

そんなslackを使う中で、常日頃から不便に感じていた問題がありました。

それは、「チャンネルに複数のメンバーを招待するのがめんどくさい」問題です。

数人ならまだ良いのですが、僕たちは業務上、新しいチャンネルを作り、そこに40人くらいのメンバーを招待するという作業を、頻繁に行います。

この40人の招待が、めちゃくちゃ面倒くさい。
一般的な招待の仕方は、下記のような画面で、一人一人検索して招待するのですが、
スクリーンショット 2018-12-02 22.28.35.png

なぜか検索に引っかからない人もいるんですね🤔
なぜなんだ。。。

この作業にかかる時間とストレスを、削減するための結論が、
「そうや!スラッシュコマンドや。あれをカスタマイズして、コマンド1つで任意のメンバーを全員招待できるようにしよう。」
です。

<論理編>

ここからは、実際に僕が、スラッシュコマンドをにカスタマイズした、手順と考え方についてです。
僕は本当に何もわからなかったので、とても苦戦しました。

簡単にまとめると、
①slack api を使いslack側にアプリを作成。どのワークスペースに結びつけるかもこの時。
②今度は、slackの外に、実際にスラッシュコマンドを打った時に、動く肝心のアプリを作る。
③slack apiに戻り、何というスラッシュコマンドを叩いた時に、どのアプリにアクセスするかを設定。
→完成!

という流れです。

①については、下記の記事を参考に作りました。
https://qiita.com/t-mimura/items/d6541ec596bdebea5a7b 

slackにはデフォルトで、スラッシュコマンド が複数用意されていますが、
さらに、カスタムしやすいように、「slack api」 というgemがあり、これをインストールすると、便利なメソッドがたくさん使えるようになります。
それらは②の工程で使うことになるでしょう。

今回僕が使ったのは、.users_list や .groups_invite です。
まず、 .users_listで全ユーザー情報を集め、その中で該当するユーザーだけ取得します。
プライベートチャンネルに複数人招待するメソッドが .groups_invite になります。
.groups_inviteを使い、任意のユーザーをプライベートチャンネルに一度に招待可能というわけです。

どうですか?簡単に聞こえますよね。
ちなみに僕はわからなすぎて、形になるまでに1ヶ月くらいはかかりました。死

次回、「具体的なコードはこう書いた!」お楽しみに!!!!!

【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸

$
0
0

突然の自分語り

あれはそこまで遡らないこと去年の8月。

RubyやRailsを学び始めてひと月とちょっとが経ち、色々見よう見まねで作ってはみたものの、未だにいいね機能フォロー、フォロワー機能などの仕組みがわからず。
というか、よくよく考えてみるとどういう仕組みでSNSを使っているユーザーが投稿していて、その投稿とユーザーが結びついているのがわかっていない、という状況にありました。いや、今から考えるとどんだけわかってなかったんや。

こういったモデル同士、つまりユーザーやその投稿、そして投稿につけられる"いいね"やユーザーに対するフォローなどの関連付けの事をアソシエーションと言うのですが、そのような概念がプログラミング初心者のみなさんにはとても難しく感じるのではないか??いや、きっとそうに違いない!というスーパーお節介な動機からこのような記事を執筆するに至りました。そしたらバカみたいに長くなってしまいました。これちゃんと最後までやってくれる人いるかな・・・・・。

正直、アソシエーションやリレーションの記事などは、SQLから学ぶのなどもう既にたくさん出ています。
ですが、意外とテキストベースの解説が多く個人的には初心者がとっつきにくいような気がしており、なるべく図などを使ってわかりやすく説明できたらなと思っております。噛み砕きすぎている点についてはツッコミください。

では、よろしくお願いします!!!

目次

目次
アソシエーションとは
データベース設計とは
UserとTweetの実装(1対多)
Favoriteの実装(多対多)
Follow機能の実装(自己結合多対多)
完成版のソースコード

普段やってること

普段は機械学習でこういうの予測してます。(HMMで予測しましたが最終的にLSTMになりました)
AIによる宅建士試験出題予測『未来問』無料プレゼント

バンドもやってます。HPはプログラミング初めて3ヶ月ぐらいの時に自分で作った物です(Railsで作ってCMSっぽくしてあります。)
2018年のサマソニ出演しました!!!!!CDも全法人で全国流通してます!!!!!!
ravenknee official HP

この記事を読むにあたっての前提

・CRUDを理解している
・MVCをざっくり理解している
・特に、Model, DataBase, Table周りの意味も簡単に知っている。

もちろん、それ以外の方も是非是非挑戦してみてください。理解なんて後から帳尻合わせていきましょう。

できるようになること

丁寧すぎるherokuへのデプロイの方法はこちら - 【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】

  • 簡単な設計ができるようになる。
  • ユーザー、投稿、イイネ、コメント、フォロー機能のアソシエーションが理解でき、実装できるようになる。

注意してほしいこと

  • validationは説明しません(ごめんなさい)
  • dependent系のオプションも説明しません(この記事読んだあと調べればすぐわかります。)
  • 例外処理もしません
  • indexとかも貼りません

本当にアソシエーション周りだけの理解をするための記事です。ご了承ください。
validation, dependent, viewのパーシャル化だけ実装したコードは最後に上げておきます。

アソシエーションとは

目次
アソシエーションとは ←今ココ
データベース設計とは
UserとTweetの実装(1対多)
Favoriteの実装(多対多)
Follow機能の実装(自己結合多対多)
完成版のソースコード

https://railsguides.jp/association_basics.html
モデル間の関連付けに使われる概念です。
モデル間の関連付けと言われてもピンとこない人が多いと思うので(自分がそうでした)、図を駆使しながら具体例で考えてみましょう。

プログラミング初心者のAさんがあるブログアプリを作ったとします。そのブログは最初はAさん一人で使うものだったため、記事投稿機能のみがつけられていて、モデルもArticleモデル一つのみ実装されていました。

qiita1.png

ですが、そのサイトをたまたま見つけてしまったBさんが、勝手に自分の投稿をし始めてしまいました。

qiita2.png

これではどれが誰の投稿かわからなくなってしまう、とAさんはブログにユーザーログイン機能を付け足しました。これでアプリにはArticleモデルUserモデルの二つモデルが実装されていることになります。

qiita3.png

そして、この時にまさしくどれが誰の投稿なのかを関連付けるものがアソシエーションなのです。

qiita4.png

そして、アソシエーションを行う際は、そのモデル同士の親子関係がどのようになっているかがとても大事になってきます。

 モデル同士の親子関係とは

図で考えてみましょう。
Aさん(User)は、自分が作ったブログサイトでたくさんの記事(Article)を投稿します。

qiita5.png

Bさんも少しは投稿します。

qiita6.png

つまり、User一人一人は沢山のArticleを持っている(User has many articles.)、と考えることができます。(この突然出てきた英文は伏線ですよ!!!!)

qiita6.5.png

逆の立場(Article)も考えてみましょう。
ある日投稿された記事(Article)は、Aさん(User)によって書かれました。

qiita7.png

次の日投稿された記事はBさんによって書かれました。
他の日に投稿された記事も、それぞれAさん、Bさんどちらかによって書かれた記事です。Aさん、Bさんが共作で書いた記事というのはありえません

つまり、Articleは誰か一人のUserに所属している(Article belongs to a user.)と考えることができます。

qiita7.5.png

UserがいなくてはArticleは生まれないし、Articleは必ず誰か一人のUserから生まれます。そう、Userが親でArticleが子となっているわけなのです。これが親子関係です。

このような関係をUserとArticleは一対多の関係または1:Nの関係といいます。もちろんUserが1でArticleが多です。(これがめちゃめちゃ大事です!!!!!!!!!!)

他にも一対一(1:1)の関係や多対多(M:N)の関係などもあります。
一対一は簡単なので割愛します。多対多は少し難しいですが、これからやっていくチュートリアルで登場&解説するので、ご安心ください。

さあ、それではいざアプリ制作に取り掛かっていきたいのですが、その前にデータベース設計を行わなくてはなりません。
そう、アソシエーションはデータベース設計と密接に関わっております

データベース設計とは?(少し難しい話です)

目次
アソシエーションとは
データベース設計とは ←今ココ
UserとTweetの実装(1対多)
Favoriteの実装(多対多)
Follow機能の実装(自己結合多対多)
完成版のソースコード

情報システムにおいて、どのような情報をデータベースに格納すべきかを検討し、その「格納すべき情報」を「どのような形で保持するか?」を設計することです。

一般化した定義のためわかりづらいかもしれませんが、

  • 情報システムTwitterアプリそのもの
  • どのような情報
    • ユーザー(のemailやpassword)
    • tweet(本文やどのユーザーが投稿したのか?など)
    • お気に入りなど
  • 「格納すべき情報」を「どのような形で保持するか?」というのは
    • 情報システムをより細かく考えたデータ
      • つまりユーザーでいうと「emailは文字列型、その識別idは整数型」など

といったアプリの動的な部分の設計のことを言います。データのことをちゃんと詳しく決めよう!!ということです。

そして、データベース設計において大事な概念が4つあります。

  • エンティティ
  • 属性
  • 関連(リレーション)
  • 関連の多重度

順番に説明して行きますが、完全に理解しなくて大丈夫ですので軽〜く読んでみてください。

エンティティ

エンティティとは、モデリングの対象となるアプリの中で管理する必要がある情報です。ツイッターでいうとUser, Tweet, Favorite, Followなどがこれにあたります。RailsのModelに近いですね。(実際は違います!)
そして項目を見るとわかると思いますが、エンティティはアプリの要件定義と表裏一体なのです。ここら辺は調べるといくらでも深い記事が出てくるので割愛します。

qiitaER1.png

関連(リレーション)

関連(リレーション)とは、結び付きのあるエンティティ同士をリンクさせるものです。先ほどの例だとUserとArticle(ツイッターでいうとUserとTweet)が簡単な関連となります。
ここで、「あれ、関連付けって、アソシエーションっていうじゃないの?」と思う方もいらっしゃると思いますが、アソシエーションとリレーションは同じものと思って大丈夫です。

qiitaER2.png

属性(プロパティ)

属性とは、あるエンティティ(Model)に従属する項目のことで、エンティティを1つに定めたときに、一緒に分かる情報だと思ってください。例えば、あるユーザーを指定したら、そのユーザーのemailやアカウント名などがこれにあたります。テーブルのカラムと同じですね。

qiitaER3.png

関連の多重度

関連のあるエンティティAとBについて、片方から他方を見たときに相手が1つなのか、複数なのかということを明らかにすることです。
先ほどの例にすると、Userから見るとArticleは複数Articleから見るとUserは1つということになります。
すなわち、1つのAからBを見たときおよび1つのBからAを見たときの相手の数を明らかにすることが、多重度を決定するということになります。

qiitaER4.png

・・・・はい、ここまでがデータベースの設計についての基本の概念であり、その中の関連(リレーション)関連の多重度がまさにアソシエーションにあたります。
アソシエーションとデータベース設計が密接に関わっていること、お分りいただけたでしょうか。

早速難しい話からスタートしてしまいましたが、最初はわからないのは当たり前です。
実際の開発では、設計を完璧に仕上げることがほとんどの場合においてマストですが、とりあえず今回は小さく設計して実装=>また小さく設計&実装という形で一つ一つ確認していきましょう。
では、チュートリアルに入ります!

(ありがちだけど)twitterクローンを作ってみよう

Rails tutorialやその他シラバスなどでおなじみの、twitterクローンでアソシエーションを学んでいきます。
今回学びたいのはアソシエーションの部分のみなので、見栄えやその他細かい機能は必要最低限で行こうと思います。ご了承ください。

それではまずは設計です。設計の時によく使われるのが、ER図というものです。

ER図とは

データベース設計(データモデリング)で使う設計手法です。お絵かきの方法です。
ER図は、データベースが必要なWEBサイトやシステムの設計では必ずと言ってよいほど作成されます。逆に言うと、ER図なしにはデータベースを構築できません。データベース設計の基本中の基本と言える設計手法です。
様々な記法があったりするのですがここではIE記法というものを採用します。(とても簡単です)

IE記法

データベース設計のうち、関連(リレーション)、関連の多重度の二つを決めるものです。要するにアソシエーションを決める記号となります。
上記のデータベース設計で出て来た画像も、ER図であり、IE記法でかかれています。
以下三つの記号から成り立っています。

qiitaIE.png

今回はこれのうち鳥の足(多)を使ってツイッタークローンをシンプルに設計します。

ログイン機能とツイート機能をER図を使って設計しよう

目次
アソシエーションとは
データベース設計とは
UserとTweetの実装(1対多) ←今ココ
Favoriteの実装(多対多)
Follow機能の実装(自己結合多対多)
完成版のソースコード

(設計図の作成には Cacooというのを使用していますが、皆さんは手書きで問題ありません。)

最初に言った通り、まずは小さく設計しましょう。UserとTweetについて決めて行きます。

上述の通りエンティティ(Model)はUserとTweetの二つになります。
次にプロパティです。
Userはdeviseというgemを使う予定なので、デフォルトのカラムであるemailとpassword(deviseではencrypted_passwordという名前になっています。)を使って行きましょう。
Tweetは本文(body)のみで大丈夫でしょう。

とりあえずここまでを図に落とし込んでみましょう。

qiita8.png

次にアソシエーション部分である、関連関連の多重度についてです。

UserとTweetは最初に出した例であるUserとArticleの関係と同じであり、一つのUserはTweetを複数持っています。

つまり、UserとTweetは一対多(1:N)の関係というわけです。

なので、ER図はこのようになります。

qiita9.png

ただ、ここで注意するべきことがあります。このままだとTweetがどのUserに所属しているのかという情報がありません。

それを設定するために、foreign_keyを設定する必要があります。

foreign_keyとは

まず、データには一つ一つ識別するためのidがあります。これをPrimary Key(主キー)と言い、Railsではidというカラム名でテーブル作成時に標準搭載されています。

そして、foreign keyというのはその親のid(Primary key)を保存するカラムというわけです。
これによりどの親に所属しているのか?というのを識別することができます。

qiita10.png

(赤丸のuser_idforeign_key)

なのでプロパティにforeign_keyを追加しましょう。親のモデル名_idという名前とするとRailsの命名規則により色々便利になるので、user_idとしましょう。
ER図はこのようになります。

qiita11.png

これでUser,Tweet部分の設計は完了しました!!では実装して行きましょう。

ようやくプログラミング!

設計の通りに下準備

それではアプリを作成し、ER図の通りにUserとTweetについて準備してきましょう。

※1 $マークはターミナルのコマンドという意味です。実際には打たないでください。
※2 #~~はコメントアウトです。打たなくて大丈夫です。

ターミナル
$rails new association_tutorial
$cd association_tutorial/

Userはログイン機能をつけるため、deviseというgemを使用します。(deviseの詳しい説明は割愛します。こちらの記事がおすすめです。)

Gemfileに以下の記述をしましょう。(色々書いてあると思いますが消さずに追加してください。)

Gemfile
gem 'devise'  # 一番下に追加

ターミナルに戻り、以下のコマンドを打ってください。deviseの機能をインストールし、その機能を取り込んだUserモデルを作成します。

ターミナル
$bundle install  # gemのダウンロード
$rails g devise:install  # deviseの機能のインストール
$rails g devise User  # deviseの機能を取り込んだUserモデルの作成
$rails g controller users index show #(deviseの機能ではない)usersコントローラの作成。今回はindexとshowアクションを用意

カラム(プロパティ)は、deviseのデフォルトのカラムがemailpasswordとなっており、設計と同じなのでこのままで問題ありません。

次にTweetについてです。(foreign_keyであるuser_idもこのタイミングで追加しています。)

ターミナル
$rails g model Tweet body:text user_id:integer # 要件通りにカラムとその型も一緒に定義。
$rails g controller tweets new index show create

忘れずにテーブルの確定をしましょう。

ターミナル
$rails db:migrate

以下のように表示されればOKです。
image.png

そしてルーティングの設定です。以下のように記述してください。

config/routes.rb
Rails.application.routes.draw do

  root 'tweets#index'  # 追加

# ===========ここはいらないので削除orコメントアウト==========
  #get 'tweets/new'
  #get 'tweets/index'
  #get 'tweets/show'
  #get 'tweets/create'
  #get 'users/index'
  #get 'users/show'
#==================================================

  devise_for :users
  resources :tweets  # 追加
  resources :users  # 追加

  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

tweets, usersコントローラもサクッと表示部分を書いておきます。

app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  before_action :authenticate_user!, except: [:index]  # deviseのメソッドで「ログインしていないユーザーをログイン画面に送る」メソッド
  def new
    @tweet = Tweet.new # 新規投稿用の空のインスタンス
  end

  def create
    # 後で書きます。
  end

  def index
    @tweets = Tweet.all
  end

  def show
    @tweet = Tweet.find(params[:id])
  end
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end
end

最後に、レイアウトなどの各ページも作っておきましょう。(フロントがぐちゃぐちゃなのは本当にごめんなさい!パーシャル化はあえてしておりません。)

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>AssociationTutorial</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
  <!-- 追加 -->
    <% if user_signed_in? %>  <!-- deviseのメソッドで、「ログインしているかしていないかでtrue or falseを返す」メソッド -->
      <%= link_to "新規投稿", new_tweet_path %>
      <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
      <%= link_to "マイページ", user_path(current_user.id) %>
    <% else %>
      <%= link_to "新規登録", new_user_registration_path %>
      <%= link_to "ログイン", new_user_session_path %>
    <% end %>
      <%= link_to "ツイート一覧", tweets_path %>
      <%= link_to "ユーザー一覧", users_path %>
  <!-- ここまで -->
    <%= yield %>
  </body>
</html>
app/views/tweets/new.html.erb
<h1>Tweets#new</h1>
<p>Find me in app/views/tweets/new.html.erb</p>

<%= form_for @tweet do |f| %>
  <p>
    <%= f.label :body,"ツイート" %>
    <%= f.text_field :body %>
  </p>
  <%= f.submit %>
<% end %>
app/views/tweets/index.html.erb
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>

<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
app/views/tweets/show.html.erb
<h1>Tweets#show</h1>
<p>Find me in app/views/tweets/show.html.erb</p>

<p><span>ツイート内容: </span><%= @tweet.body %></p>
app/views/users/index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%=link_to user.email, user_path(user.id) %></p>
<% end %>
app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

いよいよアソシエーションを記述しよう

さあ、いよいよアソシエーションの記述になります。ER図で言うと棒線の部分がこれにあたります。

・・・と、いってもRailsは命名規則によってものすごくよしなにやってくれるので、記述することはとても少ないです。

has_many

UserはたくさんのTweetを持っています(User has many tweets.)。なので、Userモデルに以下の記述をしてください。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets  # これを追加
end

このように子の方のモデル名の複数形を書きます。has_manyの方が複数形なことはRailsの命名規則でマストですのでお気をつけください。(英文と同じですね!)

belongs_to

Tweetは一つのUserに所属しています(Tweet belongs to a User.)
もうおわかりですね。tweetモデルに以下のように記述します。

app/models/tweet.rb
class Tweet < ApplicationRecord
    belongs_to :user  # これを追加
end

はい、こちらは一つなので単数形です。

そして、これだけの記述だけでアソシエーションをすることができています。tweetsテーブルに追加してあるforeign_keyuser_idも、モデル名_idとしたおかげで勝手に認識してくれています。railsはこういったところがとてもすごいですね。

コントローラの編集

さて、userとtweetをアソシエーションさせた状態で保存させたいです。なので、tweetを保存するタイミング、要するにtweets#createとストロングパラメータ(tweets_params)を以下のように記述しましょう。

app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  before_action :authenticate_user!, except: [:index]  # deviseのメソッドで「ログインしていないユーザーをログイン画面に送る」メソッド

  def new
    @tweet = Tweet.new  # 新規投稿用の空のインスタンス
  end

  def create
# ============追加================
    @tweet = Tweet.new(tweet_params)  # フォームから送られてきたデータ(body)をストロングパラメータを経由して@tweetに代入
    @tweet.user_id = current_user.id # user_idの情報はフォームからはきていないので、deviseのメソッドを使って「ログインしている自分のid」を代入
    @tweet.save
    redirect_to tweets_path
# =================================
  end

  def index
    @tweets = Tweet.all
  end

  def show
    @tweet = Tweet.find(params[:id])
  end
# ===============追加=============
  private
    def tweet_params
      params.require(:tweet).permit(:body) # tweetモデルのカラムのみを許可
    end
# =================================
end

ここで注意したいポイントは、user_idの情報はフォームから送られていないので、追加で代入してあげないといけないと言うことです。

この場合はdeviseのメソッドであるcurrent_userログイン中のユーザーの情報が取得できるため(つまりツイートした本人)、current_user.id@tweet.user_idに代入しています。

これで、アソシエーションをした状態でデータの保存をすることができました!
とりあえず何人か新規登録して、投稿してみましょう。
deviseの新規登録画面 - http://localhost:3000/users/sign_up
新規投稿画面 - http://localhost:3000/tweets/new

ログをみても、きちんとuser_idに代入されているのがわかると思います。
image.png

アソシエーションしているデータの受け取り方

ここまででアソシエーションした状態でデータ保存をすることができました。
次は「ユーザーがツイートしたデータ」や、「このツイートをしたユーザー」などといった表示の方法です。

Userがtweetしたデータ

結論から言うと、

@user.tweets

(@userは一つのユーザーのデータです。)
これで「ユーザーに関連したツイート」を取得することができます。
tweetsと複数形になっていることに注意してください。この記述はUserモデルに記述したhas_many :tweetsによって決まっています。
そして、複数形になっていることからもわかりますが、@user.tweetsは複数のツイートが入った配列となっております。

これらを用いて、Usersのコントローラとビューを以下のように変更しましょう。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
# ===============追加==============
    @tweets = @user.tweets
# ================================
  end
end
app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

<!-- 追加 -->
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

http://localhost:3000/users/1
このようになっていればうまくいっています。

image.png

このtweetをしたUser

こちらも同じように

@tweet.user

このようにすれば「このツイートをしたユーザー」を取得することができます。
こちらもTweetモデルに記述したbelongs_to :userより、単数にすることで取得します。

そして、こちらは単数なので、取得したデータも一つのみです。

なので、Tweetsコントローラとビューは以下のようになります。

app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  before_action :authenticate_user!, except: [:index]  # deviseのメソッドで「ログインしていないユーザーをログイン画面に送る」メソッド

  def new
    @tweet = Tweet.new  # 新規投稿用の空のインスタンス
  end

  def create
    @tweet = Tweet.new(tweet_params)  # フォームから送られてきたデータ(body)をストロングパラメータを経由して@tweetに代入
    @tweet.user_id = current_user.id # user_idの情報はフォームからはきていないので、deviseのメソッドを使って「ログインしている自分のid」を代入
    @tweet.save
    redirect_to tweets_path
  end

  def index
    @tweets = Tweet.all
  end

  def show
    @tweet = Tweet.find(params[:id])
# ===============追加==============
    @user = @tweet.user
# ================================
  end

  private
    def tweet_params
      params.require(:tweet).permit(:body) # tweetモデルのカラムのみを許可
    end
end
app/views/tweets/show.html.erb
<h1>Tweets#show</h1>
<p>Find me in app/views/tweets/show.html.erb</p>

<p><span>email: </span><%= @user.email %></p> <!-- 追加 -->
<p><span>ツイート内容: </span><%= @tweet.body %></p>

indexもemailを表示させちゃいましょう。

app/views/tweets/index.html.erb
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>email: </span><%=link_to tweet.user.email, user_path(tweet.user.id) %></p> <!-- 追加 -->
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

ちょっと見慣れないかもしれないですが、このようにtweet.user.emailなど、メソッドチェーンで関連先を一気に取得することも可能です。(あまり長くなりすぎるとプログラムの可読性が悪くなるので注意!)

これで一対多の実装ができました!!!ひとまずお疲れ様です。

一対多まとめ

  • 親子関係を考え、子供に親のid(foreign_key)が入るカラムを追加
  • 親側モデルにhas_many :子供のモデル名の複数形、子供側モデルにbelongs_to :親のモデル名(単数形)と記述
  • 関連したモデルの情報は、関連元のモデルのインスタンス.関連先のモデル名で取得可能。関連先のモデル名は、関連元のモデルに記述した関連先の名前が入る。(has_manyなら複数、belongs_toなら単数。)

お気に入り機能をER図を使って設計しよう

目次
アソシエーションとは
データベース設計とは
UserとTweetの実装(1対多)
Favoriteの実装(多対多) ←今ココ
Follow機能の実装(自己結合多対多)
完成版のソースコード

さて、次はお気に入り機能の実装です。ここまででめちゃめちゃ長くなってしまいましたが、気合いで頑張っていきましょう!

お気に入り機能とはどういう機能なのか?

お気に入りとは、ユーザーがツイートに対してつける印だとみなすことができます。

また片方ずつ考えていきましょう。
ユーザーは「たくさんのツイートをお気に入り」することができます。

qiita12.png

逆も考えてみます。
「ツイートはたくさんのユーザーにお気に入り」されます。

qiita13.png

このように「ユーザーもツイートもたくさん持っている」関係を多対多(M:N)の関係といいます。

多対多(M:N)を設計しよう

それではER図に落とし込んでいきたいのですが、Railsでは多対多の関係をUserモデル、Tweetモデルのみでは実装することができません。

実装するためには、中間テーブルというものが必要です。

中間テーブルとは

多対多をプログラムで実装するためには、お互いがお互いのforeign_keyを知らなくてはなりません。

qiita15.png

ですが「複数のuserによってどんどんファボが増えていった」場合、画像のように配列にどんどん追加していくか、カラムをtweetsテーブルにどんどん追加しなくてはならず、非常に面倒です。

そのような状態を避けるために、お互いのidだけを保存するテーブルが必要となります。

それを、中間テーブルといいます。

これで「どうやってお気に入り数や、誰のお気に入りなのかを判断するの?」と思うかもしれませんが、それは中間テーブルに保存されるtweet_iduser_idで判断しています。

qiita16.png

例えば、「あるツイートについたお気に入り数」をみたいときは、そのtweet_idと同じレコードに保存されているuser_idの数を見ればよく、

「あるユーザーのお気に入りしたツイート」がみたいときは、そのuser_idと同じレコードに保存されているtweet_idから、ツイートを検索すれば良いことになります。

中間テーブルを使ってER図の作成

それでは中間テーブル込みのER図を作っていきましょう。

通常、中間テーブルはテーブル1_テーブル2の複数系(user_tweets)などとつけることが多いですが、今回中間テーブルの意味が「お気に入り登録」とはっきりわかっているため、favoritesテーブルとしてしまいましょう。

qiita18.png

中間テーブルはたくさん持たれる側(どちらにもbelongs_to)なのに注意してください。
それでは、これを実装していきましょう!

設計の通りに下準備

設計の通り、Favorite関連のコントローラ、モデル、テーブルを作成します。

ターミナル
$rails g model Favorite user_id:integer tweet_id:integer
$rails g controller favorites create destroy
$rails db:migrate

image.png

あとはルーティングですが、後々のことを考えて、tweetsにネストしたルーティングを作成しましょう。
下記のようにdo ~ endの中に入れる形で書くことを、ネスト(入れ子)すると言います。

config/routes.rb
Rails.application.routes.draw do
  #==================削除orコメントアウト================
  # get 'favorites/create'
  # get 'favorites/destroy' 
  #=====================================
  root 'tweets#index'
  devise_for :users

  # ================ここをネスト(入れ子)した形に変更
  resources :tweets do
    resource :favorites, only: [:create, :destroy]
  end
  #======================================

  resources :users 
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

理由としては、お気に入り時に、foreign_keyの一つであるtweet_idを取得するのが楽になるからです。(一対多の時もそうでしたが、foreign_keyを取得しないとリレーションができません。)

詳しくはこちら - Railsのルーティングを極める(後編)

また、favoriteの詳細ページは作らない、つまりfavoriteのidは要らず省略したいため、resourceと、単数形のメソッドを利用しています。

rails routesでルーティングを確認すると、以下のようにfavoritesのcreateとdestroyに:tweet_idが含まれ、かつ:id(:favorite_id)が省略された形で生成されているのがわかると思います。

スクリーンショット_2018-12-02_20_17_13.png

これで下準備は完了です。

アソシエーションの記述

中間テーブルfavoritesたくさん持たれる側なので、このようになります。

app/models/favorite.rb
class Favorite < ApplicationRecord
    belongs_to :user
    belongs_to :tweet
end

したがって、User, Tweetモデルも以下のようになります。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :favorites  # 追加
end
app/models/tweet.rb
class Tweet < ApplicationRecord
    belongs_to :user
    has_many :favorites  # 追加
end

これでアソシエーションが組めたので、
@user.favorites@tweet.favoritesなどでそれぞれに関連したお気に入り(実際にはuser,tweetのidの2つ組の情報)が取得できるようになりました。
・・・が、viewを作る前に、一つ注意しなくてはならないことがあります。

お気に入り機能は登録だけでなく解除もできなくてはならないことです。
そのため、Tweetモデルに「このツイートをユーザーがファボしているかどうか」を判定するメソッドを用意しましょう。

ユーザーがツイートをお気に入りしたかどうかの判定メソッド

「ツイートがファボしてあるかどうか」を判定したいので、Tweetモデルに以下のように記述しましょう。

app/models/tweet.rb
class Tweet < ApplicationRecord
    belongs_to :user
    has_many :favorites

# 追加
    def favorited_by?(user)
        favorites.where(user_id: user.id).exists?
    end
end

これは、Tweetのインスタンスメソッドなので、ビューなどで

if @tweet.favorited_by?(current_user)

という風に使うことができます。これはつまり

if @tweet.favorites.where(user_id: current_user.id).exists?

ということと同じなので、アソシエーションの記述である@tweet.favoritesの中に、引数で送られたuserのidがあるかどうか?ということを判定していることになります。

インスタンスメソッドについてはこちら - Rubyのクラスメソッドとインスタンスメソッドの例

それではお気に入りボタンを作っていきましょう。

お気に入りボタンの実装

tweetsのindexに実装します。先ほど定義したfavorited_by?を早速使います。
countメソッドで、配列の中の要素数を取得しており、そしてそれがそのままお気に入りの数となります。

app/views/tweets/index.html
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>email: </span><%=link_to tweet.user.email, user_path(tweet.user.id) %></p>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
  <!-- 追加 -->
  <% if user_signed_in? %>
    <% if tweet.favorited_by?(current_user) %> <!-- ログインしているユーザーがファボしたかどうかで分岐 -->
        <p><span>お気に入り解除: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :delete %></p>
    <% else %>
        <p><span>お気に入り登録: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :post %></p>
    <% end %>
  <% else %>
    <p><span>お気に入り数: </span><%= tweet.favorites.count %></p>
  <% end %>
  <!-- ここまで -->
<% end %>

お気に入り登録の時はmethod: :post
お気に入り解除の時はmethod: :deleteにすることに注意してください。rails routesVerbのところをきちんと確認しましょう。

スクリーンショット_2018-12-02_20_17_13.png

あとはコントローラです。

FavoritesControllerの実装

繰り返しますがURIに:tweet_idが含まれており、tweet_favorites_path(tweet.id)と引数でtweetのidを送ってあるので、params[:tweet_id]とすればお気に入り登録しようとしているツイートのidが取得できます

app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController
  def create
    # こう記述することで、「current_userに関連したFavoriteクラスの新しいインスタンス」が作成可能。
    # つまり、favorite.user_id = current_user.idが済んだ状態で生成されている。
    # buildはnewと同じ意味で、アソシエーションしながらインスタンスをnewする時に形式的に使われる。
    favorite = current_user.favorites.build(tweet_id: params[:tweet_id])
    favorite.save
    redirect_to tweets_path
  end

  def destroy
    favorite = Favorite.find_by(tweet_id: params[:tweet_id], user_id: current_user.id)
    favorite.destroy
    redirect_to tweets_path
  end
end

ポイントは

current_user.favorites.build(tweet_id: params[:tweet_id])

です。これは、current_userに関連したFavoriteインスタンスを生成しています。なので、Favoriteのプロパティはすでにuser_id: current_user.idが代入されております。
あとは残りのtweet_idにviewから送られてきたparams[:tweet_id]を代入するだけです。(params[:id]ではないことに注意!ルーティングで生成されるURIを見ればparams[:tweet_id]であることを確認できます。)

これにより、お気に入り登録&解除を行うことができるようになりました!
http://localhost:3000/tweets

has many through

中間テーブルに保存されている情報はそれぞれの親idでしかないので、直接取得しviewに表示しようと思うと、少しコツがいります。

# コントローラ
  def show
    @user = User.find(params[:id])
    @tweets = @user.tweets
    # mapメソッドを使いfavoriteをtweetの情報に変換
    @favorite_tweets = @user.favorites.map{|favorite| favorite.tweet}
  end
end
<!-- ビュー -->
<% @favorite_tweets.each do |tweet| %>
  <hr>
  <p><span>ファボツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

このように、mapメソッドでfavoritesの情報をtweetの集まりに変換してあげなくてはいけません。

ですが、has many throughを使えば、ユーザーがファボしたツイートを直接アソシエーションで取得することができます。

このように実装します。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :favorites
  has_many :favorite_tweets, through: :favorites, source: :tweet  # 追加
end

source

source「参照元のモデル」をさすオプションです。
これを指定することでアソシエーションでメソッドチェーンする時の名称を変更することができます。

本当はhas_many :tweets, through: :favorites
と記述したいのですが、上のhas_many :tweetsと重複してしまうため、favorite_tweetsと名称を変更しています。

has many throughを使った「ユーザーがファボしたツイートの表示」

これで、@user.favorite_tweetsとやることで、「ユーザーがファボしたツイート」を取得することができるようになりました。

それでは、Usersコントローラ、ビューも以下のように変更してください。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @tweets = @user.tweets
    @favorite_tweets = @user.favorite_tweets # 追加
  end
end
app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
<!-- 追加 -->
<% @favorite_tweets.each do |tweet| %>
  <hr>
  <p><span>ファボツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

これでファボ機能が実装できました、確認してみましょう!!
http://localhost:3000

コメント機能も作ってみよう

コメント機能も多対多となります。

qiita19.png

ここまでの知識があればきっと実装することができると思うので、是非ともやってみてください!
最後にソースコードを置いておきます。

ヒント
・中間テーブルに文章を入れれるカラムを追加
・「一回コメントした人も何度もコメントできるようにするか」はあなたの自由(favorited_by?の話です)
・form_for の引数に渡すインスタンスに注意

ネストしてあるコントローラへのルーティングは、form_forの場合

<%= form_for([@tweet, @comment]) do |f| %>

のように、配列で二つ([関連元のインスタンス, 関連先のインスタンス])渡す必要があります。

多対多まとめ

  • Railsでは多対多を実装するために中間テーブルが必要。
  • 中間テーブル側が子供(たくさん持たれる方)となるので、belongs_to :親1 belongs_to :親2と書こう.
  • 親側はもちろんhas_many :中間テーブルの名前の複数形
  • has_many_throughを使うと記述が省略できるので積極的に使おう
  • has_many_throughを使うときに指定するsource参照元のモデル名なのに注意
  • 「ファボしてるかしていないか」、という判定のメソッド(今回はfavorited_by?)を用意することに注意(コメントなど、用意しなくてもいい時もある)

#フォロー、フォロワー機能をER図を使って設計しよう

目次
アソシエーションとは
データベース設計とは
UserとTweetの実装(1対多)
Favoriteの実装(多対多)
Follow機能の実装(自己結合多対多) ←今ココ
完成版のソースコード

いよいよラストです!(本当に長かった)最後に、ユーザーフォロー機能を追加していきます。

フォロー、フォロワー機能とはどういう機能なのか?

結論から言うとフォローする側のユーザー、フォローされる側のユーザーというユーザー同士の多対多となります。

qiita20.png

とりあえず多対多なので中間テーブルは必要そうです。
しかし、ご存知の通りUserモデルは一つしかなく、今のままだとフォローする側のユーザー、フォローされる側のユーザーがどれだか全くわかりません。

qiita21.png

なので、ユーザーモデルをフォローする側、される側にうまく切り分けることがこの実装の鍵となります。

そしてこのような自分自身との結合のことを自己結合と言います。自己結合は多対多だけでなく、一対多でもあり得ます。(雇用者モデルにおいて、管理者:従業員の1対多など)

UserとUserの多対多(M:N)を設計しよう(自己結合)

ユーザーモデルは一旦忘れて考えましょう。
FollowingモデルFollowerモデルがあったとします。(フォローする方とされる方)

qiita 21.5.png

これらはもちろん多対多で関連しているため、中間テーブルが存在します。関連を表現しているため、Relationshipモデルと名付けます。
中間テーブルなので各親テーブルのprimary_keyforeign_keyとして保存したいです。
なので、

following_idにはFollowingモデルのidを
follower_idにはFollowerモデルのidを
Relationshipforeign_keyに設定すれば良いと言うことがわかります。

qiita22-3.png

こうすればやることは普通の多対多と同じです。

  • Followingから見て、Followerを(Relationshipを介して)集める
  • Followerから見て、Followingを(Relationshipを介して)集める

と言うことになります。

qiita23.png
ここでふた通りのRelationshipを考えなくてはならないのは、プログラムで記述するときにわかります。

これをUserモデルに直して考えると、

  • フォローする側のUserから見て、フォローされる側のUserを(中間テーブルを介して)集める
  • フォローされる側のUserから見て、フォローしてくる側のUserを(中間テーブルを介して)集める

と言うことになります。

qiita24.png

ヒジョーーにややこしくなってきましたが、ここまで落とし込めば十分にRailsで実装可能です。やっていきましょう!

設計の通りに下準備

ここは今までと同じく、設計通りにコマンドを打ち込むだけです。(中間テーブルの名前はRelationshipsとしました。)

ターミナル
$rails g model relationship following_id:integer follower_id:integer
$rails g controller relationships create destroy
$rails db:migrate

favoriteの時と同じく、親であるuserのidが欲しいため、usersの中にネストしましょう。
また、フォロー一覧、フォロワー一覧の画面も用意したいため、follows, followersと言うアクションへのルーティングを作ります。on memberメソッドを使います。
詳細 - Railsのルーティングを極める(後編)

config/routes.rb
Rails.application.routes.draw do

  #==================削除orコメントアウト================
  # get 'relationships/create'
  # get 'relationships/destroy'
  #=====================================
  root 'tweets#index' 

  devise_for :users

  resources :tweets do
    resource :favorites, only: [:create, :destroy]
  end

  # ================ここをネスト(入れ子)した形に変更
  resources :users do
    resource :relationships, only: [:create, :destroy]
    get :follows, on: :member # 追加
    get :followers, on: :member # 追加
  end
  #======================================
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

まだviewやコントローラも触れていませんが、先にアソシエーションの記述を完成させます。

アソシエーションの記述

いよいよアソシエーションの記述です。

中間テーブルRelationshipモデルの設定

とりあえず、Userモデルと言う呼び方はやめてFollowing, Followerと言うモデル名に変更したいです。
そしてそれはclass_nameと言うオプションを使えば実現できます。

class_name

:class_name - 関連するモデルクラス名を指定。関連名と参照先のクラス名を分けたい場合に使う
ものとなります。

これを使うことにより

belongs_to 変更したい親モデル名, class_name: "元々の親モデル名"

とすることができます("元々の親モデル名"文字列なのに注意!)。これでFollowモデルFollowerモデルを擬似的に作り出すことができそうです。

実際、Relationshipsテーブルのforeign_keyとしてfollowing_idfollower_idと名前をつけており、モデル名もFollowing, Followerとすれば帳尻が合います。
Relationshipモデルにそれぞれ所属先を定義しちゃいましょう。

relationship.rb
class Relationship < ApplicationRecord
    belongs_to :following, class_name: "User"
    belongs_to :follower, class_name: "User"
end

Userモデルの設定

Relationshipモデル側にFollowing, Followerモデルに所属している、と言うことを定義してありますので、それをうまく使えば良さそうです。

ここで、設計の時に決めた自己結合の文句を確認してみましょう。

  • フォローする側のUserから見て、フォローされる側のUserを(中間テーブルを介して)集める。
  • フォローされる側のUserから見て、フォローしてくる側のUserを(中間テーブルを介して)集める。

です。
ですが、とりあえずこの通りに記述しようとしても、has_many :relationshipsをふた通り書かなくてはならないため、名前被りが起きてしまいます。なので、フォローする側、される側ふた通りの中間テーブルの名前を再定義しなくては行けなさそうです。

今回は、

  • フォローする側のUserからみたRelationshipをactive_relationship
  • フォローされる側のUserからみたRelationshipをpassive_relationship

としてみます。

参照元のモデルは先ほどもやったclass_name: "Relationship"と指定すれば良いだけなので、

has_many :active_relationships, class_name: "Relationship"
has_many :passive_relationships, class_name: "Relationship"

となります。

ですがこれだとまだ、親モデルの外部キーがなんなのかという情報が足りません。
active_relationshipで言うと「フォローする側のUserからみた」と言う情報が足りていない、と言うことになります。

そこで親モデルの外部キー指定するオプションとして、foreign_keyと言うのがあります。

foreign_key

:foreign_key - 参照先のテーブルの外部キーのカラム名を指定できる
これにそれぞれfollowing_id, follower_id、つまり親のprimary_keyを指定してあげれば、「フォローする側のUserからみた」と言う情報も取得することができます。

よって、最終的なUserモデルの最終的なアソシエーションの記述は以下のようになります。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :favorites
  has_many :favorite_tweets, through: :favorites, source: :tweet

  # ====================自分がフォローしているユーザーとの関連 ===================================
  #フォローする側のUserから見て、フォローされる側のUserを(中間テーブルを介して)集める。なので親はfollowing_id(フォローする側)
  has_many :active_relationships, class_name: "Relationship", foreign_key: :following_id
  # 中間テーブルを介して「follower」モデルのUser(フォローされた側)を集めることを「followings」と定義
  has_many :followings, through: :active_relationships, source: :follower
  # ========================================================================================

  # ====================自分がフォローされるユーザーとの関連 ===================================
  #フォローされる側のUserから見て、フォローしてくる側のUserを(中間テーブルを介して)集める。なので親はfollower_id(フォローされる側)
  has_many :passive_relationships, class_name: "Relationship", foreign_key: :follower_id
  # 中間テーブルを介して「following」モデルのUser(フォローする側)を集めることを「followers」と定義
  has_many :followers, through: :passive_relationships, source: :following
  # =======================================================================================

  def followed_by?(user)
    # 今自分(引数のuser)がフォローしようとしているユーザー(レシーバー)がフォローされているユーザー(つまりpassive)の中から、引数に渡されたユーザー(自分)がいるかどうかを調べる
    passive_relationships.find_by(following_id: user.id).present?
  end
end

先ほどまでの話にプラスして、has many throughで(中間テーブルを介して)followingsの時はfollowerを、followersの時はfollowingを集める記述をしています。
もちろんsourceでモデルの参照元を指定しています。
followingsで集めるUserはフォローされる側(follower)。逆も然りです。

以下の画像は、これまでの自己結合アソシエーションの記述をまとめたものです。

qiita26.png

favoriteと同じく、Userがfollow済みかどうか判定したいため、followed_by?と言うメソッドも追加しています。

これでUserモデルのアソシエーションの記述も完了です。

残るはcontroller、viewを実装です!

コントローラ、ビューの実装

これで本当にチュートリアルは最後です。頑張っていきましょう!!!

ビューページの実装

(最後にある完成版のソースコードに、もう少し綺麗にしたviewファイルの奴も置いてあるので、もしよろしければそちらも参考ください。)

フォローボタンの実装

app/views/tweets/index.html.erb
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>email: </span><%=link_to tweet.user.email, user_path(tweet.user.id) %></p>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
  <% if user_signed_in? %>
    <% if tweet.favorited_by?(current_user) %> <!-- ログインしているユーザーがファボしたかどうかで分岐 -->
        <p><span>お気に入り解除: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :delete %></p>
    <% else %>
        <p><span>お気に入り登録: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :post %></p>
    <% end %>

    <!-- ここを追加 -->
    <% if current_user != tweet.user %>
        <% if tweet.user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(tweet.user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(tweet.user.id), method: :post %></p>
        <% end %>
    <% end %>
    <!-- ここまで -->

  <% else %>
    <p><span>お気に入り数: </span><%= tweet.favorites.count %></p>
  <% end %>
<% end %>
app/views/users/index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p>
    <!-- ここを追加 -->
    <% if current_user != user %>
        <% if user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(user.id), method: :post %></p>
        <% end %>
    <% end %>
    <!-- ここまで -->
<% end %>

自分をフォローする自体は避けたいので、if current_user != userで自分をのぞいた人たちのみボタンを表示するような実装にしています。あとはほとんどfavoriteボタンと同じですね。

tweetsのshowページは省略します。理屈がわかっていれば絶対にできるはずです、やってみましょう!
次にコントローラです。

relationshipsコントローラの実装

app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  def create
    follow = current_user.active_relationships.build(follower_id: params[:user_id])
    follow.save
    redirect_to users_path
  end

  def destroy
    follow = current_user.active_relationships.find_by(follower_id: params[:user_id])
    follow.destroy
    redirect_to root_path
  end
end

favoriteの時と同じくcurrent_user.active_relationships.buildとすることで「following_id: current_user.id」を代入しながらインスタンスを作成することができます。

最後に、フォロー、フォロワー一覧を実装しましょう。

usersコントローラの実装

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @tweets = @user.tweets
    @favorite_tweets = @user.favorite_tweets
  end

# ==============追加================
  def follows
    user = User.find(params[:id])
    @users = user.followings
  end

  def followers
    user = User.find(params[:id])
    @users = user.followers
  end
# ==============追加================
end

ビューの実装

follows, followersのビューは作っていなかったので、このタイミングで新規作成してください。
image.png

app/views/users/follows.html.erb
<h1>Users#follows</h1>
<p>Find me in app/views/users/follows.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p>
    <% if current_user != user %>
        <% if user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(user.id), method: :post %></p>
        <% end %>
    <% end %>
<% end %>
app/views/users/followers.html.erb
<h1>Users#followers</h1>
<p>Find me in app/views/users/followers.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p>
    <% if current_user != user %>
        <% if user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(user.id), method: :post %></p>
        <% end %>
    <% end %>
<% end %>
app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

<!-- 追加 -->
<p><%=link_to "フォロー", follows_user_path(@user.id) %></p>
<p><%=link_to "フォロワー", followers_user_path(@user.id) %></p>
<% if current_user != @user %>
    <% if @user.followed_by?(current_user) %>
        <p><%=link_to "フォロー済み", user_relationships_path(@user.id), method: :delete %></p>
    <% else %>
        <p><%=link_to "フォローする", user_relationships_path(@user.id), method: :post %></p>
    <% end %>
<% end %>
<!--ここまで -->

<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

<% @favorite_tweets.each do |tweet| %>
  <hr>
  <p><span>ファボツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

以上でフォロー機能の実装は完了です!!本当におつかれさまでした!!!!!!

http://localhost:3000

自己結合多対多まとめ

  • User同士の関連付けなので、名前の重複が色々起きてしまう。そこはclass_nameオプションを使って名称変更しよう。その時のforeign_keyの指定も忘れずに。
  • followする側が持つのはfollower達。つまりforeign_keyはfollowing_idなのに注意(逆も然り)
  • あとは普通の多対多とだいたい同じ

完成版のソースコード

目次
アソシエーションとは
データベース設計とは
UserとTweetの実装(1対多)
Favoriteの実装(多対多)
Follow機能の実装(自己結合多対多)
完成版のソースコード ←今ココ

そのまま実装 - https://github.com/mormorbump/association_tutorial
リファクタリング済み(ヴァリデーション、パーシャル化済み) - https://github.com/mormorbump/association_tutorial_refactor
heroku - https://rails-association-tutorial.herokuapp.com
丁寧すぎるherokuへのデプロイの方法はこちら - 【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】

まとめ

・・・・・・・・・・・・・・・・・・・・・・・はい、本当に長くなってしまいましたが(本当に長くなった)、これでアソシエーションの根幹の部分はほぼ全て抑えられたと思います。
アソシエーションだけに絞ったため細かい実装部分は色々足りていません(validationとか)。そこはご了承ください。
dependentオプションやscopeなども本当はやりたかったのですが、ここまで説明すれば独学でいけるだろうと思い割愛しました。興味があれば調べてみるのをオススメします。
ではみなさん、素敵なRailsライフをお過ごしくださいませ。ありがとうございました!

(最後に僕のバンドのMVを置いておきます)
https://www.youtube.com/watch?v=6bj2idcHdqk
https://www.youtube.com/watch?v=StMZoXUMPng
https://youtu.be/q_UVKz1P2CI
https://www.youtube.com/watch?v=kPnydGG5cps

ポリモーフィック関連(おまけ)

※コメント機能を実装してある前提で進めます。

いつか書きます。

10分クッキング!誰でもインフルエンサーになれるinstabotの作り方🍳

$
0
0

はじめに

いきなりですが、普段インスタグラムを使っているなら
せっかくだしたくさんの人にフォローされてみたいし、いいねされたくないですか?
プログラミングの経験を身近なSNSで活かせたら楽しそうですよね!

自分も、せっかく投稿したんだし、出来ればたくさんの人に見てもらいたい!
たまたま見てくれた方々のフォローを待つのはなかなかもどかしい〜!!
ズボラだから簡単にフォロワー・いいねを増やしたいなんて思ったわけです。

そこで、instabotを使ってみたよってお話です✌🏻

対象者

  • インスタグラムをやっている方
  • インスタグラムのフォロワーを増やしたい方
  • プログラミング経験者

instabotって??

簡単にいうとインスタグラムの自動いいねツールです。
(いいね以外の機能もありますが今回は割愛👻)

いいね・フォロワーが増える流れ

インスタグラムの投稿にはハッシュタグがつけられます。
instabotではハッシュタグを指定して、紐づいている投稿に「いいね」をしにいきます。
いいねされたユーザーは自分のアカウントに気づいて閲覧してくれて、
その中でも興味を持ったユーザーはいいね・フォローをしてくれます。

一見「たかがいいね」「それで増えるの?」なんて疑いの目を向けたくなりますが、
自分が作成したテストアカウントでは一週間に
140フォロワー増えました👏🏻👏🏻

botは気分で動かしたり、しなかったりですが日々こんな感じ↓
知らない喫茶店知ってる人を自然と知れたりと個人的に大満足。
image.png

10件前後だった投稿のいいねも400件つくことも!
プロフィールのアクセス数も1800件を超えたり!

インサイトを見るのがたのしい〜〜

合わせて使うとたのしい分析ツール「インサイト」について

個人のアカウントでも自分のアカウントへのアクセス数や
フォロワーの男女比・国籍などのデータが見られるようになります。
https://gaiax-socialmedialab.jp/post-51356/

ここから実装☝🏻

自分の環境

  • Python 3.7.0
  • anaconda 1.7.2
  • OS Mojave(10.14)/high sierra/sierra(10.12.5)で動作確認済み

導入方法

anacondaでbotの実行環境をつくる

今回使用するinstabotはPythonで書かれているため、Pythonが実行できる環境を
構築する必要があります。
Pythonの環境構築するためにanacondaをインストールする必要があります。

  1. サイトからanacondaのインストーラをダウンロード https://www.anaconda.com/download/#macos

 Python 3.7 version *を選択
Downloads_-_Anaconda.png
その際にブラウザ上に表示されるモーダルウインドウは❎ボタンで消して大丈夫です!

2. インストーラの実行。インストール先はルート以下の~/anaconda3です(VSCodeは使わなくても大丈夫なので任意)
3. プロジェクトでもpythonコマンドを実行できるようにPathを通す
 以下のコマンドはフルパスなのでどこのディレクトリで実行していただいても構いません。

terminal
$ vi ~/.bash_profile

.bash_profileに以下のpathを記述

.bash_profile
# added by Anaconda3 4.3.0 installer                                                
export PATH="/path/to/anaconda/bin:$PATH"

4.忘れずに変更を適用

terminal
$ source ~/.bash_profile

これでプロジェクトディレクトリ以下でもpython3コマンドを実行できるようになります。
念の為、ここでバージョンを確認します。

terminal
$ python3 --version

バージョンは3.0以上になっていれば大丈夫です。

ここまでで環境設定は終わりです。

botの実行

  1. まず、gitHubからソースコードをDesktop内にclone。

[ソース] https://github.com/instabot-py/instabot.py

terminal
$ git clone https://github.com/instabot-py/instabot.py.git

2.完了したらcd instabot.pyプロジェクト内に移動。
プロジェクト名が.pyなの、ちょっと気持ち悪いですね〜🤧気になる方は変更を!

  1. プロジェクトに必要なモジュールをインストールする。anacondaによりpipコマンドを使えるようになっているので以下を実行。

pip --versionで18.1ならここは飛ばしてください。

terminal
$ pip install --upgrade pip

4.以下のコマンドで必要なモジュールをインストールする

terminal
$ pip install -r requirements.txt

以下のようなlogになれば完了です。
スクリーンショット 2018-12-04 16.18.30.png

余談: pipコマンドがpip3じゃなくてもいい理由。。 https://teratail.com/questions/46066

5.ファイルの編集
example.pyを適当なエディタで開いてください。
変更箇所で囲まれている行を適宜自分のアカウントに合わせて書き換えてください。
また、今回使用する機能はいいね機能のみなので
以下のコードで、コメントやフォローの機能は初期値を0などに変更しています。

example.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import time

from src import InstaBot
from src.check_status import check_status
from src.feed_scanner import feed_scanner
from src.follow_protocol import follow_protocol
from src.unfollow_protocol import unfollow_protocol

#login:user:自分のアカウント名
#passward:自分のパスワード
#like_per_day:1日にいいねする数
#comments_per_day:1日にコメントする数
#tag_list:いいねするハッシュタグリスト
#tag_blacklist:入っていたら、いいねしないハッシュタグリスト
#max_like_for_one_tag:1つのタグに対して最大どのくらいいいねするか
#follow_per_day:1日にフォローする数
#unfollow_per_day:1日にアンフォローする数
#unfollow_break_min:最小でどのくらいフォロワーを減らすか
#unfollow_break_max:最大でどのくらいフォロワーを減らすか
#log_mod:0=コンソールにログを表示 1=ファイルにログを残す 2=ログ残さない

# ----変更箇所----
bot = InstaBot(
    login="userName", 
    password="password", 
    like_per_day=5000, 
    comments_per_day=0,
    tag_list=['純喫茶', '喫茶部', '喫茶店', '純喫茶コレクション', '珈琲'], 
    tag_blacklist=[],
    user_blacklist={},
    max_like_for_one_tag=5,
    tag_blacklist=[],               
    user_blacklist={},              
    follow_per_day=0,               
    follow_time=1 * 60,             
    unfollow_per_day=0,             
    unfollow_break_min=0,           
    unfollow_break_max=0,           

# ----変更箇所----
# 以下略

変更後の保存をお忘れなく!

6.ファイルを保存したら実行(instabot.pyディレクトリ内で)

terminal
$ python3 example.py

を実行
スクリーンショット 2018-12-02 14.38.21.png

こんな感じでログインしていいねが実行されていきます。
インスタグラムの
止めるときはControl + Cで止まります。

おわり✌🏻

注意事項

無理にいいね数をあげると、いいね機能が停止されます😢😢目安として。。。。
一日平均3000いいねが限界のような気がしました🌴
ただしbotの一定時間当たり(1分とか1時間単位)のいいね数が
安定しないので1日あたりのいいね数は多めに5000とかの設定で様子見ると良いと思います。
上手な運用方法は研究中です〜〜〜むずかしい😭

instabot.pyがプログラムの大半を動かしているのでtime.sleepを挟んでみたり色々いじっ
てみてください!

参考サイト

参考サイト: https://garmentshack.jp/instabot/
ソース: https://github.com/instabot-py/instabot.py

ファイル解説: https://review-of-my-life.blogspot.com/2017/12/python-instagram-instabotpy.html

[おまけ]エラーについて

実行時にこうなってしまったら

スクリーンショット 2018-12-02 18.56.05.png
ログイン情報を誤っている可能性が高いので見直してみてください。

スクリーンショット 2018-12-04 17.13.52.png

これはいいね機能に規制がかかってしまっています。Control + Cで停止して様子を見てください。

Rubyのクラスの概念について

$
0
0

Rubyの学習をはじめたばかりのときにつまずくポイントとして「クラス」というものがあります。
この記事ではRubyにおける「クラス」とは何か「インスタンス」とは何かを初心者の方にもわかりやすいように解説していきたいと思います。

この記事の概要

・Rubyにおける「クラス、インスタンス」の概念の説明
・「インスタンス変数、インスタンスメソッド」の説明
・「クラス変数、クラスメソッド」の説明

前提知識 「Rubyの全てのデータはオブジェクトとして扱う」

Rubyは全てのデータをオブジェクトとして扱います。そのデータ自体がメソッドを持っています。

「クラス」の概念

Rubyにおけるクラスとは「オブジェクトの種類を表すもの」、「一種のデータ型」であるとされます。

オブジェクトの種類を表すもの??と疑問に思った方もいらっしゃると思うので解説してきます。前提知識で述べたようにRubyでは全てのデータをオブジェクトとして扱います。

そのオブジェクトごとに対象となるデータとメソッドが変わってきます。
例えば文字列オブジェクトなら文字列を扱い、数値オブジェクトなら数値を扱います。
そしてオブジェクトはなんらかのクラスに必ず属しています。
そのオブジェクトが属しているクラスごとに使えるメソッドが変わります。

クラスが定義されていることによって共通点を持ったデータを保存しやすくなったり、扱えるメソッドをまとめておくことができます。

Rubyではクラスはもとから定義されているものもあります。どのようなものが定義されているかはこちらのドキュメントをご覧ください https://docs.ruby-lang.org/ja/latest/library/_builtin.html

またこれらの他にクラスは自分で定義することもできます。クラスの定義は以下のようになります。

User.rb
 class Human

 end

この中に保存するデータとメソッドを定義していきます。

インスタンスとは??

先ほど、クラスは「オブジェクトの設計図」と述べましたが、この設計図をもとにつくられるオブジェクトをインスタンスと言います。同じクラスからつくられたインスタンスは同じデータ項目をもち、同じメソッドを使えます。しかし、インスタンス一個一個のそれぞれの中に保存されているデータの中身はインスタンスごとに変わります。

ここでクラスとインスタンスについて現実世界を例に考えてみましょう

会社をクラス、社員をインスタンスとして例えます。
それぞれの会社(クラス)にはそれぞれのルールがあります。そのルールにしたがって社員(インスタンス)は行動(メソッド)を起こします。その会社(クラス)ごとにルールは異なるので属する会社(クラス)によって社員(インスタンス)の行動(メソッド)も限定されてくるのです。
そして、社員(インスタンス)は一人一人名前や性別が違います。これが先ほど説明したインスタンスの持っているデータはインスタンスごとに異なるというものです。

インスタンスの作成は以下のようにおこないます

class Company

end

member1 = Company.new #Companyクラスからインスタンスを作成し、変数member1に代入



クラス名に対してnewメソッドを使うことによってインスタンスが作成されます。

次にこのmember1に名前や性別、歳などの情報を持たせましょう。情報を持たせるために「インスタンス変数」を使います。「インスタンス変数」とはインスタンスごとに独立して持つ変数です。インスタンス変数は先頭に@マークをつけて使います。
インスタンス変数はクラス内でしたら全てのメソッドで使用することができます。同じ名前のインスタンス変数でも対象となっているオブジェクトごとに中に入っているデータ(名前、性別、歳)は異なります。

情報を持たせるための記述は以下のようになります。

class Company

 def initialize(name,sex,age)
  #インスタンス作成時に引数で渡された名前と性別、歳をインスタンス変数に代入
   @name = name
   @sex = sex
   @age = age
  end

  def foge
   puts "私は#{@name}です。性別は#{@sex}です。歳は#{@age}です"
  end

end

member1 = Company.new("daichi","男",20) #Companyクラスからインスタンスを作成し、変数member1に代入。
#引数にインスタンスの名前、性別、歳などの情報(インスタンス個別の情報)を渡します。

member1.foge
#=>私はdaichiです。性別は男です。歳は20です。



newメソッドを使ってインスタンスが作成されるとinitializeメソッドが呼ばれます。initializeメソッドでは、インスタンスの初期設定を行います。今回の場合は引数として渡したインスタンスの名前と性別と歳をインスタンス変数に代入するという処理をおこなっています。
次にmember1のインスタンス対してmember1.fogeとすることでfogeというメソッドを呼び出しています。
このfogeとはインススタンスメソッドです。インスタンスメソッドとは、その名の通りインスタンスに対して呼び出されるメソッドです。今回のfogeというインスタンスメソッドは、member1というインスタンスに対して、インスタンス変数作成時に代入された@name,@sex,@ageを使い、簡単な自己紹介の文章を出力しています。(putsは文字列などを出力するメソッド)fogeメソッドのなかで@nameなどのインスタンス変数を使っています。これが先ほど述べたインスタンス変数はクラス内でしたら全てのメソッドで使用することができるということです。

インスタンスについてもう少し詳しく

次にインスタンス変数の値を変更する方法について解説します。インスタンス変数の値を読み書きするメソッドを「アクセスメソッド」といいます。本来インスタンス変数をクラスの外部から参照したり、外部から変更したりする場合は専用のメソッドを定義しなくてはいけません。しかしアクセスメソッドを使うことでインスタンス変数の値の読み書きが容易になります。
アクセスメソッドにはインスタンス変数の内容を読みこむ専用の「attr_reader」を使います。インスタンス変数の内容を書き込む専用にしたい場合は「attr_writer」を使います。読み書き両方をできるようにするためには「attr_accessor」を使います。attr_accessorの定義はクラスの直下におこないます。

class Company
 attr_accessor :name,:sex,:age

 def initialize(name,sex,age)
  #インスタンス作成時に引数で渡された名前と性別、歳をインスタンス変数に代入
   @name = name
   @sex = sex
   @age = age
  end

  def foge
   puts "私は#{@name}です。性別は#{@sex}です。歳は#{@age}です"
  end

end

member1 = Company.new("daichi","男",20) #Companyクラスからインスタンスを作成し、変数member1に代入。
#引数にインスタンスの名前、性別、歳などの情報(インスタンス個別の情報)を渡します。

member1.foge
#=>私はdaichiです。性別は男です。歳は20です。

member1.name = "nana" #member1の名前をnanaに変更
member1.sex = "女" #member1の性別を女に変更
member1.age = 39 #member1の歳を39に変更

member1.foge #もう一度変更したmember1インスタンスに対してfogeというインスタンスメソッドを呼び出す
#=>私はnanaです。性別は女です。歳は39です

ご覧の通りmember1のもつ情報をクラスの外から変更することができましたね!!

クラス変数について

インスタンス変数の他にクラス変数というものがあります。インスタンス毎に格納されるデータが異なるインスタンス変数とは違い、クラス変数はそのクラスの中で共通のデータを持ちます。具体例をみながら覚えていきましょう。さきほどまで使っていたCampanyクラスの中に会社の社員数を表すnumberというクラス変数を定義します。クラス変数には変数の前に@@をつけます。

class Company

  @@number = 0 #クラス変数numberの定義

  #以下省略

クラスメソッドについて

次にクラスメソッドについて解説します。さきほど個々のインスタンスに対して呼び出すメソッドをインスタンスメソッドと呼びました。クラスメソッドとはそのクラスに対して呼び出すメソッドのことを言います。
クラスに関連は深いが、個々のインスタンスに含まれるデータを使わない時にクラスメソッドを定義します。
今回はクラス変数を出力するクラスメソッドを定義します。メソッド名はnumber_showにします。

クラスメソッドの定義の仕方は2種類あります。クラスメソッドをいくつか定義したい場合は後者の定義の仕方の方が便利です。

  class company
    def self.number_show #クラスメソッドの前にはselfをつける

    end
  end

 Company.number_show #クラスメソッドの呼び出しは「クラス名.メソッド名」


 class company
   class << self
     def number_show

     end
  end

 Company.number_show

さきほどのコードを使ってクラスメソッドの定義と呼び出しをみてみましょう

class Company

  @@number = 0 #クラス変数の定義

  attr_accessor :name, :sex, :age

 def initialize(name,sex,age)
   @name = name
   @sex = sex
   @age = age
   @@number = @@number + 1 #インスタンスが作成されるごとにクラス変数numberの数が増える
  end

  def self.number_show #クラスメソッドの定義
    puts @@number
  end

  def foge
   puts "私は#{@name}です。性別は#{@sex}です。歳は#{@age}です"
  end

end

member1 = Company.new("daichi","男",20) #Companyクラスからインスタンスを作成し、変数member1に代入

member2 = Company.new("jyon","男",22)

Company.number_show #クラスメソッドの呼び出し
#=> 2 (@@numberの数)

クラスメソッドを呼び出せましたね!!

まとめ

今回の記事ではRubyにおける「クラス」の概念のさわりの部分を扱いました。この記事が皆さんの理解を深めることにつながれば嬉しいです。
最後まで読んでくださりありがとうございました。

5分でわかる!クラスメソッドとインスタンスメソッドの違い

$
0
0

目的

Rubyを勉強し始めた時につまずきそうなところ
その中でも
・クラスメソッドとインスタンスメソッドの違いについて
・attr_accessorとは
この2つをまとめました。

目次

・クラスメソッドとインスタンスメソッド
・attr_accessor

クラスメソッドとインスタンスメソッド

Rubyでは、以下のようにすることでメソッドを定義することができます。

sample.rb
def number(a,b,c)
    puts a + b + c
end
number(1,2,3)
# 出力結果 => 6

通常、メソッドは以下のようにクラスの中に定義しますが、このままだとエラーになります。
クラスにメソッドを作成すると、それがインスタンスメソッドとなります。
そのため、numberはクラスに含まれたインスタンスメソッドになり、外から使用できなくなります。

sample.rb
class Add
    def number(a,b,c)
        puts a + b + c
    end
end
number(1,2,3)
# 出力結果 => エラー

回避方法1
selfをつけてクラスメソッドにすることで外から使用できるようにする

sample.rb
class Num
    def self.number(a,b,c)
        puts a + b + c
    end
end
Num.number(1,2,3)
# 出力結果 => 6

回避方法2
numberはインスタンスメソッドのため、インスタンスを作成しメソッドを呼び出す

sample.rb
class Num
    def number(a,b,c)
        puts a + b + c
    end
end
num = Num.new
num.number(1,2,3)
# またはNum.new.number(1,2,3)でも同じ
# 出力結果 => 6

その他の方法
initializeを使ってみる
以下のようにinitializeメソッドを定義することで、
classがnewされた際に呼び出され、classのデータの初期化処理を行ってくれます。

sample.rb
class Num
    def initialize(a,b,c)
        @result = a + b + c
    end

    def number
        puts @result
    end
end
num = Num.new(1,2,3)
num.number
# 出力結果 => 6

attr_accessorについて

sample.rb
class Person

  def initialize(name)
    @name = name
  end

  def name
    @name
  end

end

person = Person.new("taro")
puts person.name
# 出力結果 => taro

上記のコードでクラスの外からインスタンス変数(@name)を変更,更新したい!
と思って...

sample.rb
class Person

  def initialize(name)
    @name = name
  end

  def name
    @name
  end

end

person = Person.new("taro")
person.name = "jiro" #インスタンス変数の値を更新
puts person.name
# 出力結果 => エラー

インスタンスから直接nameを変えようとしましたが、nameというメソッドはここではゲッターの役割をしているため、値の更新はできないです。
person.@name = "jiro"としても、インスタンス変数には直接アクセスできないのでエラーになります。

値の更新をできるようにするためにはセッターが必要です。
そこで以下のようにセッターを定義すると無事に値が変更できます。
def name=(val) とは、person.name = "jiro"と「name」への代入式を書いた時に呼び出されるメソッドです。

sample.rb
class Person

  def initialize(name)
    @name = name
  end
#セッター↓
  def name=(val)
    @name = val
  end
#ゲッター↓
  def name
    @name
  end

end

person = Person.new("taro")
person.name = "jiro" 
puts person.name
# 実行結果 jiro

そして、ゲッターとセッターの両方の役割を持ったものが「attr_accessor」で、以下のようにすることで先ほどのコードよりも短く書くことができます。

sample.rb
class Person
  attr_accessor :name #追加

  def initialize(name)
    @name = name
  end

end

person = Person.new("taro")
person.name = "jiro" #インスタンス変数の値を上書き
puts person.name
# 実行結果 jiro

これでクラス外からインスタンス変数であるnameを操作できるようになりました。
attr_accessor :(インスタンス変数) とすれば、指定されたインスタンス変数が外からでも変更できるようになります。

疑問点

Q.インスタンス変数の書き方attr_accessor :nameと@nameの違い
A.attr_accessorでシンボルを渡して宣言すると、そのシンボル名のインスタンス変数(@name)を中で生成している。

Q.クラスメソッド・インスタンスメソッド使い分け
A.基本的には、インスタンスメソッドで処理を実現できるか検討する。
外部からデータを取得する処理(外部ファイルからデータを取得し、インスタンスを作成する時など)は、クラスメソッドを使用する。

まとめ

・クラスにメソッドを作成すると、それがインスタンスメソッドとなる
・インスタンスメソッドはインスタンスを作成して使用できる
・セッターとゲッターを使うことで値の参照・更新をすることができる。
・セッターとゲッターの両方の役割を持ったものが「attr_accessor」

Web APIとは? (LINE bot API・グルナビAPI)

$
0
0

こんにちは!プログラミングスクールでメンターをしている野原です。

いきなりですが、自分がLINE bot APIとグルナビAPIを使って作成した以下のLINEbotをLINEの友達に追加してみてください。Web APIをどんどん学びたくなるはずです!!!
スクリーンショット 2018-12-05 17.39.01.png

スマホの方はこちら
友だち追加

それでは、はじめにWeb APIに関して学び、その後に実際にWeb APIを使ったアプリの概要について見ていきます。

1. Web API を使うとこんなものが作れる!

上のQRコードを読み取って、使っていただいたようにWeb APIを使うと以下のようなアプリが作れます。

ここでは、LINEで自分の位置情報を送ることにより、自分がいる場所の近くにある飲食店を一つランダムに返しています。また地名やカテゴリーを送ることもできるようになっています。
このアプリは以下のURLでソースコードを公開しています。
https://github.com/NoharaMasato/tabetai

2. Web APIとは

APIとは「Application Programming Interface」の略です。これを聞いても難しいと思うのでみなさんが良く耳にするであろうUI (User Interface)との類似点を交えながら説明したいと思います!この2つの類似点は両方ともインターフェースであるという点です。
スクリーンショット 2018-12-05 17.29.13.png

インターフェースとは日本語に直訳すると境界面や接点という意味があります。つまり、両方とも、何かと何かの境界面のことだと分かればざっくりとしたイメージがつかめると思います。

そして、Web APIとはHTTPという通信方式を用いてネットワーク越しに呼び出すAPIです。

3. アプリの構造

自分が作成したアプリの仕組みを簡単に図に表したのが以下の図です。
スクリーンショット 2018-12-01 19.14.38.png

①.ユーザーが位置情報もしくは地名をLINEbotに送信する
②.LINEサーバが①のイベントを検知して、botで指定されているRailsアプリのURLにリクエストを送る
③.RailsアプリがLINEから送られてきた情報を元に、グルナビAPIにリクエストを送る。
④.グルナビAPIからのレスポンス(店舗の情報)をRailsアプリが受け取る。
⑤.受け取った情報をもとに、処理した内容をLINEサーバに送る
⑥.LINEサーバがスマホのLINEに通知を送信する

これでこのアプリの概要が分かっていただけましたか?

4. 作成のために必要な知識

ここでは自分が作成したものと同じものを作るために必要な知識をあげてみます。

1.APIに関して
2.Rubyに関して
3.Railsに関して
4.Herokuを使ったデプロイの仕方に関して
5.gitに関して

これらについての簡単な知識があれば作成ができます。

5. 作成の仕方の概要

手順

Step1 LINE bot APIの部分を設定する
Step2 グルナビAPIの部分を設定する
Step3 Railsの部分を実装する
Step4 アプリケーションをデプロイする

注意点としては、lien_botはhttpsのアドレスが必要なためローカル環境では特別な設定をしない限り、実行することができません。ここではHerokuにデプロイすることにします。自分は修正するたびにherokuにデプロイをして、実行結果を確認しながら作業を進めました。

6. 実装の仕方

ここでは簡単に説明していきます。
実際に作ってみたい方は、他の詳細な説明が書いてあるサイトも参考にしながらやってみてください。

Step1 LINE bot APIの部分を設定する

ここではLINE bot APIの設定を行います。まず、LINE Developersに登録し、新規チャンネルを作成します。ここで特に注意すべき点は以下の2点です。以下のサイトから行なってください。

1、Messaging APIを選択し、Channel Secretとアクセストークンが得られていることを確認する。
2、webhookを有効にする(webhook URLはherokuにデプロイした際にhttpsのURL
が与えられるのでそれを登録します。)

Step2 グルナビAPIの部分を設定する

以下のサイトからレストラン検索APIを使います。
登録するとアクセスキーが取得できます。
https://api.gnavi.co.jp/api/

ここまでで自分のアプリを作成する準備は完璧です。

Step3 Railsの部分を実装する

まず次のgemを入れます

Gemfile
gem 'line-bot-api'

ルーティングやコントローラーは以下の通りです。

linebot_controller
class LinebotController < ApplicationController
   require 'line/bot'  # gem 'line-bot-api'
   # callbackアクションのCSRFトークン認証を無効
   protect_from_forgery :except => [:callback] 
   def client
     @client ||= Line::Bot::Client.new { |config| 
       config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
       config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
     } 
   end

   def callback
     body = request.body.read

     signature = request.env['HTTP_X_LINE_SIGNATURE']
     unless client.validate_signature(body, signature)
       error 400 do 'Bad Request' end
     end

     events = client.parse_events_from(body)

     #ここでlineに送られたイベントを検出している
     # messageのtext: に指定すると、返信する文字を決定することができる
     #event.message['text']で送られたメッセージを取得することができる
     events.each { |event|
       if event.message['text'] != nil
         place = event.message['text'] #ここでLINEで送った文章を取得
         result = `curl -X GET http://api.gnavi.co.jp/RestSearchAPI/20150630/?keyid='&'format=json'&'address=#{place}`#ここでぐるなびAPIを叩く
       else
         latitude = event.message['latitude']
         longitude = event.message['longitude']

         result = `curl -X GET http://api.gnavi.co.jp/RestSearchAPI/20150630/?keyid='&'format=json'&'latitude=#{latitude}'&'longitude=#{longitude}`#ここでぐるなびAPIを叩く
       end

       hash_result = JSON.parse result #レスポンスが文字列なのでhashにパースする
       shops = hash_result["rest"] #ここでお店情報が入った配列となる
       shop = shops.sample #任意のものを一個選ぶ

       #店の情報
       url = shop["url_mobile"] #サイトのURLを送る
       shop_name = shop["name"] #店の名前
       category = shop["category"] #カテゴリー
       open_time = shop["opentime"] #空いている時間
       holiday = shop["holiday"] #定休日

       if open_time.class != String #空いている時間と定休日の二つは空白の時にHashで返ってくるので、文字列に直そうとするとエラーになる。そのため、クラスによる場合分け。
         open_time = ""
      end
      if holiday.class != String
         holiday = ""
       end

       response = "【店名】" + shop_name + "\n" + "【カテゴリー】" + category + "\n" + "【営業時間と定休日】" + open_time + "\n" + holiday + "\n" + url
        case event #case文 caseの値がwhenと一致する時にwhenの中の文章が実行される(switch文みたいなもの)
       when Line::Bot::Event::Message
         case event.type
         when Line::Bot::Event::MessageType::Text,Line::Bot::Event::MessageType::Location
           message = {
             type: 'text',
             text: response
           }
           client.reply_message(event['replyToken'], message)
         end

       end
     } 

     head :ok
   end
 end
routes.rb
post '/callback' => 'linebot#callback'

Step4 アプリケーションをデプロイする

railsアプリケーションをherokuにデプロイします。この時にhttpsのURLが与えられるのでLINE bot APIのWebhook URLに追加します。
ポイントとしては、heroku logs -tでlogを見ながらデプロイやデバッグをすると何が起こっているか分かりやすいです。

終わりに

どうだったでしょうか?APIが何かわかりましたか?
以下のようなサイトにWeb APIが様々な種類載っています。面白そうなのがあれば実際に使ってみてください!
http://smsurf.app-rox.com/api/

参考

LINE bot API
https://qiita.com/y428_b/items/d2b1a376f5900aea30dc

heroku
https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39


超初心者向け。ディープラーニングと機械学習の違い

$
0
0

狙い

 本記事はプログラミングを始めたばかりの初心者や、機械学習とか興味あるけどイメージすら湧かない、くらいの方を対象しています。
 以上の層の方達にディープラーニングと機械学習の大まかなイメージだけでも掴んでいただこうと思い短い文でおおまかに書いた記事です。詳細というよりは、二つが似た者同士の親戚であるというイメージのみを伝えようといった内容です。

両者の共通点

大きく分けると「あらゆるパターン学習させる」と「学習済みのものを使う」二つのフェーズがあります。
その二つについて話します!(分類器を例に)

 学習

まず学習させることで猫の特徴などを覚えさせ、猫の画像を見たら「これは猫だ!」と判別できるようにします。
他にも犬や猿などあらゆる動物の画像をサンプルとして与え、正しく分類できるようにします。

3250d6969e40c64be0bf4e71dfdfde4b.png

 使用

こうして行われた学習後に適当な動物画像を与えるとその動物がなんなのかを90%とかで正解できるようになる!っていうのが機械学習、ディープラーニングです。

ディープラーニングと機械学習の違い

 両者の概要については上の画像のイメージです。
 では違いは一体なんなのか。
60433bff0729b2fe1e035334d25427b4.png
 この部分が違います!ほとんど全部じゃんw
 できることは同じなのに、どんな具合に違うのだろう といったことを雑くチェックしていきます。

 人間がその生き物を猫と判定するまでには、輪郭や鼻、耳などから判断すると思います。

 じゃあ、機械はどうやって判断するの?

ここに機械学習とディープラーニングの違いがあります。

11da552bbfd601434ffb474536de81a1.png

624752b44ab8249ab7b01eb3378b8060.png

 このブラックボックスの大きさの違いが両者の違いなのです。

 つまり?

 知っている人もいるかもしれないですが、機械学習は英語でマシーンラーニングといい、ディープラーニングはその仲間なのです。
 ディープラーニングをやりたいと思ったら機械学習から入っても遠回りではありません。

 他に違いは?

 学習方法、適応範囲など様々な違いはあります。でもこの流行りの二つの言葉は親戚であることに変わりはありません。一つを学んで見たら、もう一つも理解できてきたりします。

あとがき

 ディープラーニングを学ぶために必要な知識が多すぎたり、「ディープラーニングとは?」とか調べるとかなり難しい記事しか出てこなくて、初学者を遠ざけてる感があったので多少雑にでも解説するサイトも必要なのでは?と思いこの記事を投稿いたしました。
 入門、初心者向けでも結局文量が膨大になってしまうのがこの分野なので、噛み砕いて極力文量を減らせるように意識しました。
 もちろんしっかりと理解するためにはそれなりの時間、努力が必要であって最後まで理解させようと思って記事を書こうものなら既存のサイトと同じ長い文になるでしょう。そこに踏み込む前の軽いストレッチ的な役を担えればいいなと思います。→これが十分な説明を果たしているわけではないので興味を持っていただけた方はもっと調べて見てください!
今回の説明は分類器というものを例にしていて、他にも色々あります!

SQLで覚えるジャニヲタ用語

$
0
0

どうも!ジャニヲタです。
今回は、SQLを使って、ジャニヲタ用語を解説していきたいと思います。
なんでそんなことをしようと思ったのかというと、大好きなSnow Manというユニットを宣伝したかったから何事も新しいことを勉強するには身近なものに例えると覚えやすいよな〜と思ったからです。
「SQLで覚えるジャニヲタ用語」というタイトルにしましたが、SQLって何?という方もご安心ください。SQLとジャニヲタ用語が一気に身につけられる記事になっています。多分...

想定読者

  • SQLって何?な方
  • Rails初心者(SQLを直接触る機会が少ない方、コンソールに表示されるSQLの意味が分からない方)
  • 仕事で使うデータを毎回エンジニアに取得依頼している方
  • 彼女や奥さんがジャニヲタなエンジニア
  • 非ヲタ・非エンジニア

スクリーンショット 2018-12-10 9.18.42.png

SQLって何?

データベースの定義や操作を行うことができる言語です。
世の中のアプリケーションはデータベースに様々な情報を保存しています。
例えば何かのサイトに登録する時、名前やメールアドレス、住所などを登録しますよね。
それらの情報はデータベースに保存され、管理されています。
「この情報くれよ〜」「この情報保存しておいてよ〜」とデータベースに話しかけるための言語がSQLです。

スクリーンショット 2018-12-06 19.12.31.png

Rails学習中の方は、アプリケーションを動かしているときにコンソールを見てみて下さい!
上記の画像では、「SELECT」で始まっている文がSQL文です!
実はSQL文を知らぬ間に実行してくれているんですね〜便利ですね〜◎

まずは準備をしよう

テーブルを用意する

まずは、 テーブル を作成します。
テーブルとは、 データを入れておく箱 のようなものと思って下さい。
データベースは、この箱の集まりです。

今回は、ジャニヲタ用語を説明するにあたって、下記のようなテーブルを用意します。

スクリーンショット 2018-12-09 18.53.24.png

wotakusテーブル
ヲタク情報を管理するテーブルです。
ヲタクの名前や、好きなタレント名などを管理します。

unitsテーブル
グループ(ジャニーズJr.のユニット)を管理するテーブルです。
デビュー組はグループ、Jr.のグループはユニットと呼ぶことが多い気がするのでユニットとしました。
ユニット名などを管理します。

talentsテーブル
ジャニタレ情報を管理するテーブルです。
ジャニタレの氏名や、どのユニットに所属しているかなどを管理します。

テーブルの中に、「id」や「name」などの項目がありますね。これは、 カラム といって、箱の中の仕切りを意味します。
idには番号が、nameには名前が...というようにデータを整理します。
EXCELと同じようなイメージです!

スクリーンショット 2018-12-07 10.18.40.png

下記が実際のSQL文です。
データベースの中にテーブルを作成するためには、「create文」という構文でSQLを書きます。

create.sql
CREATE TABLE wotakus(id integer, name varchar(100), favorite_talent_id integer, 
 created_at datetime, updated_at datetime);
CREATE TABLE units(id integer, name varchar(100), 
 created_at datetime, updated_at datetime);
CREATE TABLE talents(id integer, name varchar(100), unit_id integer, 
 created_at datetime, updated_at datetime);

これでデータを入れておく箱と仕切りができました!
Railsの場合、migrationファイルに「create_table」と書きますが、実はこのようなSQLが生成されています。
Railsさん便利!!

テーブルにデータを入れる

テーブルを作っただけでは、中身は空っぽです。箱の中にデータを入れてみましょう!
テーブルにデータを投入する為には、「INSERT文」を実行します。

unitsテーブル
まずはユニットテーブルにデータを入れます。
今回は「兄組」と呼ばれている3組を入れておきます!

INSERT INTO units (id, name) VALUES (1, 'Snow Man');
INSERT INTO units (id, name) VALUES (2, 'SixTONES');
INSERT INTO units (id, name) VALUES (3, 'Travis Japan');

上記のSQL文を実行すると、下記のようにデータがデータベースに登録されます。
それぞれの行を レコード といいます。

id name
1 Snow Man
2 SixTONES
3 Travis Japan

3レコード投入されましたね!

talentsテーブル
次に、メンバー名を投入していきます。
ジャニーズのタレント全員書くとそれだけでスクロール半端なくなるので、
とりあえず、かつて「BAD BOYS J」というドラマに出演していたSnow Manのメンバー3名をサンプルで入れておきますね。

INSERT INTO talents (id, name, unit_id) VALUES (1, '岩本照', 1);
INSERT INTO talents (id, name, unit_id) VALUES (2, '深澤辰哉', 1);
INSERT INTO talents (id, name, unit_id) VALUES (3, '渡辺翔太', 1);
id name unit_id
1 岩本照
2 深澤辰哉
3 渡辺翔太

この3人はSnow Manのメンバーなので、unitテーブルの1番(Snow Man)をunit_idに入れておきます。
このように、他のテーブル(今回はunitテーブル)のレコードを参照して下さいね、という意味でもつカラムの値(今回はunit_id)を、 外部キー と呼びます。

wotakusテーブル

最後に、ヲタク情報を投入しましょう。

INSERT INTO wotakus (id, name, favorite_talent_id) VALUES (1, 'おがっぴ', 2);
INSERT INTO wotakus (id, name, favorite_talent_id) VALUES (2, 'ヲタ子', 3);
INSERT INTO wotakus (id, name, favorite_talent_id) VALUES (3, 'ジャニ美', 1);

おがっぴは私です。僭越ながら、id=1でINSERTさせていただきます。

id name favorite_talent_id
1 おがっぴ 2
2 ヲタ子 3
3 ジャニ美 10

さぁ、準備ができました!
早速SQL文とジャニヲタ用語を一緒に学んでいきましょう!

ジャニヲタ用語に触れよう

さぁ、SQLを使ってジャニヲタ用語を感じてみよう!

担当、自担

意味:一番好きなジャニーズタレントのこと。
用例:「私は深澤くん担当です!」「私の自担はSnow Manの深澤辰哉くんです!」

今回は私の大好きなユニットSnow Manのダイマを兼ねているので、ガンガンSnow Manメンバーの名前を使っていきたいと思います。
はい、私は「ふっか」こと深澤辰哉くんのことを応援しております。

先ほど実行したCREATE文で、wotakusテーブルに「favorite_talent_id」というカラムを作りました。
「好きなタレントid」...つまり、「自担id」です!
おがっぴの自担idは、wotakusテーブルのおがっぴレコードに入っているので、まずはおがっぴのレコード(id=1)を取得してみましょう!

SELECT * FROM wotakus WHERE id = 1;

これは、

「FROM wotakus」=wotakusテーブルから
「WHERE id = 1」=idが1番のレコードの
「SELECT * 」=全てのカラム(*)を取得する

というSQL文です。このSQL文を実行すると、

id name favorite_talent_id created_at updated_at
1 おがっぴ 2 2018-12-10 00:00:00 2018-12-10 00:00:00

このようにデータが取得できます。
おがっぴの自担idは「2」のようですね!
INSERT文では、自担idとtalentsテーブルのidに、対応するデータを入れています。
つまり、下記のようにおがっぴの自担名を辿ることができます。

スクリーンショット_2018-12-09_18_59_28.jpg

「SELECT」の後に「*」と指定すると全カラム取得できますが、カラム名を指定して、ピンポイントで取得することもできます。

SELECT favorite_talent_id FROM wotakus WHERE id = 1;

上記のSQLを実行すると、

2

が返ってきます。

自担idではなく、「深澤辰哉」という名前で取得したい場合は、

SELECT name FROM talents
WHERE id = 2; -- 一つ前のSQLでゲットした深澤くんのtalent_id

こう書いてあげれば、

深澤辰哉

という文字列が返ってきます。

しかし、
① おがっぴのfavorite_talent_idを取得する
② ①で取得したidをキーにタレント名を取得する
と2段階で自担名を取得するのはちょっとまどろっこしいですね。

一発で「深澤辰哉」という文字列で取得したい場合は、下記のようなSQLでゲットできます。

SELECT name FROM talents
WHERE id = (
 SELECT favorite_talent_id FROM wotakus
 WHERE id = 1
);

急にややこしくなった気がしますね!ちょっと分解していきましょう。
まずは3,4行目だけ見てみましょう。

-- SELECT name FROM talents
-- WHERE id = (
  SELECT favorite_talent_id FROM wotakus
  WHERE id = 1
-- );

先ほど見た、「おがっぴの自担idをゲットする」というSQLです。
深澤くんの2番が返ってきますよね。

次は1,2行目です。
タレントidが?番の人の名前をタレントテーブルから取得するSQLです。

SELECT name FROM talents
WHERE id = (
--   SELECT favorite_talent_id FROM wotakus
--   WHERE id = 1
);

つまり、カッコ内でおがっぴの自担idをゲットして、それをキーにタレント名を取得していた、というだけでした!
これで一気に「深澤辰哉」という文字列がゲットできます!

自ユニ

意味:自分の応援しているグループ、自担が所属しているグループのこと
用例:「私の自ユニはSnow Manです!」

今回は、
「おがっぴが応援しているのは深澤くん。そんな深澤くんが所属しているのはどこのユニット?」
という問いかけへの答えをゲットしていきたいと思います。

SELECT name FROM units
WHERE id = (
 SELECT unit_id FROM talents 
 LEFT OUTER JOIN wotakus ON wotakus.favorite_talent_id = talents.id 
 WHERE wotakus.id = 1
);

は〜〜〜またややこしいSQL...
でも大丈夫。一つ一つ分解していきましょう。

今回欲しいのは、「Snow Man」というユニット名。

id name
1 Snow Man

この「Snow Man」をゲットするためには、
① おがっぴの自担idをゲットする
② ①で取得したタレントが所属しているユニットのidをゲットする
③ ②で取得したidをキーに、ユニット名をゲットする
という3Stepが必要になって来ます。

まずは、3〜5行目を見てみます。

-- SELECT name FROM units
-- WHERE id = (
  SELECT unit_id FROM wotakus 
  LEFT OUTER JOIN talents ON wotakus.favorite_talent_id = talents.id 
  WHERE wotakus.id = 1
-- );

おやおや...見慣れない「LEFT OUTER JOIN」「ON」という単語が出て来ました...
これは、「複数のテーブルをくっつけて、ひとつのテーブルっぽく見せる」という技です。
上記の3行を実行すると、

1

が取得できます。
ちょっとややこしいので、上記3行のSQL

SELECT unit_id FROM wotakus 
LEFT OUTER JOIN talents ON wotakus.favorite_talent_id = talents.id 
WHERE wotakus.id = 1

の「unit_id」を「*」に変えて

SELECT * FROM wotakus 
LEFT OUTER JOIN talents ON wotakus.favorite_talent_id = talents.id 
WHERE wotakus.id = 1

としてみます。
「*」は、全カラムのデータを取得するよ!という呪文です。
結果は、こうなります。

wotakus.id wotakus.name wotakus.favorite_talent_id talents.id talents.name talents.unit_id
1 おがっぴ 2 2 深澤辰哉 1

※「wotakus.id」は、「wotakusテーブルのidカラム」を表現しています
※「created_at」「updated_at」は省略しています

つまり、
「ヲタクテーブルの自担カラムと、タレントテーブルのidが同じレコードを合体させるよ!」
wotakus.favorite_talent_id = talents.id
ということでした。

スクリーンショット_2018-12-09_19_28_42.jpg

ですので、カッコ内の3行では、unit_id「1」が取得できた、というわけです。

スクリーンショット_2018-12-09_19_28_42.jpg

あとはお馴染みのSELECT文です!

SELECT name FROM units
WHERE id = (
--   SELECT unit_id FROM wotakus 
--   LEFT OUTER JOIN talents ON wotakus.favorite_talent_id = talents.id 
--   WHERE wotakus.id = 1
-- ※↑上記3行で1が取れました
);

unitのidが1番のname(ユニット名)を取得です。
最終的に「Snow Man」がゲットできました!

担降り

意味:ファンをやめること。
用例:「ちょっと私xxの担当降りるわ」

ジャニタレの熱愛報道が出たら、「はてなブログ 担降り」で検索すると、
悲痛なブログが読めたりします。

id name favorite_talent_id
3 ジャニ美 10

タレントid=10が誰かは分かりませんが、ジャニ美ちゃんは担当を降りることを決めたそうな。
この10という値、悲しいけど消しましょう...

UPDATE wotakus SET favorite_talent_id = NULL WHERE id = 3

「UPDATE」は、レコードを更新できる構文です。
今回はジャニ美ちゃんのfavorite_talent_idをNULL(空っぽ)にしました。

必ずしも担当がいるジャニヲタだけではないので、favorite_talent_idはNULL可にしときましょう。

さて、ここがジャニヲタ用語の難しいところなんですが、もう1つ意味があるんですよね。
「私、ずっとxxくんのファンやってたけど、歌声聴いて惚れて 渡辺翔太くんに担降り した!」
のように、別のジャニタレに乗り換えたときも「担降り」を使うんですね!
つまりこういうことです。

UPDATE wotakus SET favorite_talent_id = '3' WHERE id = 3

ジャニ美ちゃんのfavorite_talent_idを「3」(渡辺翔太くんのタレントid)にUPDATEしました。

ヲタ卒

意味:ジャニヲタをやめること
用例:「歳も歳だしそろそろヲタ卒して婚活でもすっか〜」

まず、声を大にして言いたいのは、
いくつになってもジャニヲタでいていいだろ!一生ヲタク上等!
ということです。
この度ヲタ子がヲタ卒をしてしまうそうです。悲しいけどテーブルから削除しましょう。

DELETE FROM wotakus WHERE id = 2

上記を実行したあと、ヲタクテーブルを見てみましょう。

SELECT * FROM wotakus
id name favorite_talent_id
1 おがっぴ 2
3 ジャニ美 10

ヲタ子はヲタクテーブルから消滅してしまいました...
私がこのSQL文を流すことは一生なさそうです。

同担

意味:応援対象が自分と同じ人
用例:「自担がドラマに出たら一気に同担増えたわ〜」

自分と同じfavorite_talent_idを設定しているヲタクを取得します。
まずは深澤くんのタレントidをゲットして、そのidをfavorite_talent_idに設定しているヲタクをゲットしましょう。

SELECT id, name, favorite_talent_id FROM wotakus
WHERE favorite_talent_id = (
 SELECT id FROM talents WHERE name = '深澤辰哉'
);
id name favorite_talent_id
1 おがっぴ 2
10 おまるちゃん 2
55 のりぞう 2

同担が2人いました!
下記の書き方でも取れますね。

SELECT name FROM wotakus
WHERE favorite_talent_id = (
 SELECT favorite_talent_id FROM wotakus WHERE id = 1
);

同担が何人いるのか?を取得することもできます。

SELECT COUNT(*) FROM wotakus
WHERE favorite_talent_id = (
 SELECT favorite_talent_id FROM wotakus WHERE id = 1
);

「COUNT(数えたいカラム名)」で実行すると、何行HITするか数値でゲットできます。

同担拒否

意味:応援しているタレントが被る人とは絡みたくない
用例:(Twitter自己紹介等で)「xx君担です。同担拒否なので悪しからず」

乙女心って複雑ですよね〜。
今までid=2のように、「idが2番のデータをとる!」というSQLを書いてきましたが、「idが2番じゃないデータをとる!」という時はどうしたら良いのでしょう?

SELECT name FROM wotakus
WHERE favorite_talent_id <> (
 SELECT favorite_talent_id FROM wotakus WHERE id = 1
);

「<>」という書き方が出現しました。これは、「〜じゃない」を意味します。
上記の場合、favorite_talent_idが2じゃない人の名前を取得できます。
他にも、「<=」「>=」で〜以上・〜以下が表現できたりします。

ちなみに私は同担大歓迎です!!みんなでふっかを応援しよう!!

カケモ

意味:好きなタレントが複数いること(掛け持ちの略)
用例:(Twitter自己紹介等で)「阿部ちゃんとのえるのカケモです、よろしく!」

データベースの世界では、一つのカラム(仕切り)に一つのデータしか持てません。
現状のテーブル定義だと、1人のヲタクに対して1人の担当しか入れられません。
それでは、どうやってカケモを表現すればいいんだ〜!?

スクリーンショット 2018-12-09 19.49.17.png

はい。 tantosテーブル (担当テーブル)を作成しました!
これで複数の担当を管理することができるようになります!

ヲタクテーブル

id name
1 おがっぴ
3 ジャニ美
4 ミーハー花子

タレントテーブル

id name unit_id
1 岩本照 1
2 深澤辰哉 1
: : :
6 阿部亮平 1
7 川島如恵留 3
: : :

担当テーブル

id wotaku_id talent_id
1 1 2
2 3 3
3 4 6
4 4 7
: : :

ミーハー花子(wotaku_id=4)のレコードが、担当テーブルに2行ありますね。

SELECT * FROM tantos WHERE wotaku_id = 4
id wotaku_id talent_id
3 4 6
4 4 7

数字だけだと分かりづらいので、JOINしてみましょうか。

SELECT tanto.wotaku_id ,talents.id ,talents.name FROM tantos
LEFT OUTER JOIN talents ON talents.id = tantos.talent_id
WHERE tantos.wotaku_id = 4 
tantos.wotaku_id talents.id talents.name
4 6 阿部亮平
4 7 川島如恵留

ミーハー花子は多分勉強できる人が好き。

古株、新規

意味:ファン歴が長い、ファン歴が短い
用例:「あの人は古株だから詳しいよね〜」「ド新規ですがよろしくお願いします」

「ごく出は永遠の新規」という言葉があります。
「ごくせん」からKAT-TUNファンになった人は、いつまで経っても新規、というパワーワードです。
もう、放送から13年経ってますが...!

担当テーブルには「created_at(レコード作成日)」を持っています。
このカラムを使えば、古株・新規が洗い出せそうです(怖い...!)

SELECT * FROM wotakus 
WHERE talent_id = 2
ORDER BY created_at ASC

「ORDER BY」はソートに使うカラムを指定、「ASC」は昇順指定です。
これで深澤担が古株順に並びます...こわひ...!

ちなみに、

SELECT * FROM wotakus 
WHERE talent_id = 2
ORDER BY created_at ASC
LIMIT 1

のように「LIMIT 数値」をつけてあげると、先頭から何行取得するか指定できます。
上記の場合、一番古株の人が取得できます...!

まとめ

はい、以上で基礎的なジャニヲタ用語のレクチャーを終わります。
出オチ感の強い記事だったかと思いますが、一応CRUDには触れてみました。
ぜひ皆さん、何か新しいことを勉強するときは、自分の好きなものや身の回りの事柄に例えて覚えてみて下さい!
最後まで読んでくれた方、サンキューサンキューで〜す!

Ubuntuにある既存RailsアプリにVue.jsつっこんでみた

$
0
0

はじめまして

はじめまして。最近はひたすら作ったシステムにRspec(system spec)+FactoryBotを書いてるエンジニアです。
この辺絶賛苦労中なんで落ち着いたら記事書きたいですね。

世の中の流行りってなんなんだろう

jsってすごい人気ですよね。(人気なんですよ)jsフレームワークがまだまだ流行っている今、何が流行っているのかってエンジニアとして気になります。こういうサイトをご存知でしょうか🤔
npmtrends
スクリーンショット 2018-12-04 20.42.38.png
(※スクショは2018/12月のものです)
jsフレームワークたちのトレンドがグラフになっているサイトです。
ダウンロード数で見ると圧倒的React人気ですね。ReactNativeやReactNativeWebなど出て色んなプラットフォームに柔軟に対応できるからなのでしょうか?

Vueに注目してみましょう。比べてみるとダウンロード数少ないですね、、ですがGitHub Starsを見てください!なんと一位です!素晴らしいですねー。スターが多いということは優秀なエンジニアが多い証みたいなものなのでとても活発なフレームワークであるというのがわかりますね。
ReactもVueも人気あるんだなーっていうのが分かって是非とも触ってみたくなりますよね。

そこで

jsフレームワーク単体でも勿論色々と触ってはみたいですが、railsのアプリにもjsフレームワークを組み込むことができるので、既存RailsアプリにVue.jsつっこんでみようと思います。

なんでVue.js?

Reactの方が人気ですし、Vue.jsはLaravelのイメージ(あくまでイメージ)なんですが、学習コスト的にVue.jsの方が楽かなって思ったからです。特に深い意味はありません。今回は導入までですが今後Vueへの書き換えなども余力あれば記事にします。

さて

まずは自分の環境を簡単に見てみることにしました。
vagrant 2.2.2
ubuntu 18.0.4
Rails 5.2.1
Ruby 2.5.1
Rails5.1からReactやVueなども簡単に導入できると話題になってましたのでこのバージョンであればいけそうですね。rubyは最低限2.2以上にしておきましょう。

Ubuntuに最新安定版のNodeいれよう

なぜNodeがいるの?

VueをRailsで使うにはgemのwebpacker(あとで簡単に説明します)をインストールする必要があります。それにはYarnと呼ばれるnpmと互換性のあるJavaScriptのパッケージマネージャー(バージョンの依存関係など解決してくれるもの。installやupdateの管理を行ってくれるもの)が無いとエラーが起きてしまいます。
Yarnをインストールするにはnpmと呼ばれるNodeのパッケージマネージャー(Yarn ≒ npm)が不可欠です。
そしてnpmを入れるにはNodeが不可欠。ということでやっていきましょう。Nodeのインストール

本来、Macの環境であればHomebrewと呼ばれるパッケージ管理システムのコマンドでポチポチやればあっという間にNode入れられるのですが、Ubuntuの場合若干厄介です。
「Ubuntu Node インストール」と検索すると下の記事が目に止まります。
Ubuntuに最新のNode.jsを難なくインストールする
↑めちゃくちゃ参考になりました🙏が、ちょっとうまくいかなかったです。正確にはもう少し自分の環境では手を加える必要がありました。

記事通りになりますが、コマンド打っていきましょう。
まずNodeを入れるためにnodejsというものとnpmをaptコマンドでインストールします。

$ sudo apt-get install -y nodejs npm

こんな感じでセットアップのログが流れてきたら大丈夫です。

...
Setting up node-isexe (2.0.0-3) ...
Setting up node-spdx-correct (1.0.2-1) ...
Setting up node-async (0.8.0-3) ...
Setting up node-cookie-jar (0.3.1-1) ...
Setting up node-mute-stream (0.0.7-1) ...
Setting up node-form-data (0.1.0-1) ...
Setting up node-request (2.26.1-1) ...
Setting up node-concat-map (0.0.1-1) ...
Setting up node-ini (1.3.4-1) ...
Setting up node-mkdirp (0.5.1-1) ...
Setting up node-once (1.4.0-2ubuntu1) ...
Setting up nodejs-dev (8.10.0~dfsg-2ubuntu0.4) ...
Setting up node-sha (1.2.3-1) ...
Setting up node-fs.realpath (1.0.0-1) ...
Setting up node-brace-expansion (1.1.8-1) ...
Setting up node-ansi-color-table (1.0.0-1) ...
Setting up node-npmlog (0.0.4-1) ...
Setting up node-is-builtin-module (1.0.0-1) ...
Setting up node-nopt (3.0.6-3) ...
Setting up node-which (1.3.0-1) ...
Setting up node-lru-cache (4.1.1-1) ...
Setting up node-block-stream (0.0.9-1ubuntu1) ...
Setting up node-validate-npm-package-license (3.0.1-1) ...
Setting up node-inflight (1.0.6-1) ...
Setting up node-read (1.0.7-1) ...
Setting up node-minimatch (3.0.4-3) ...
Setting up node-normalize-package-data (2.3.5-2) ...
Setting up node-glob (7.1.2-4) ...
Setting up node-rimraf (2.6.2-1) ...
Setting up node-read-package-json (1.2.4-1) ...
Setting up node-fstream (1.0.10-1) ...
Setting up node-fstream-ignore (0.0.6-2) ...
Setting up node-tar (2.2.1-1) ...
Setting up node-gyp (3.6.2-1ubuntu1) ...
Setting up npm (3.5.2-0ubuntu4) ...
...

ディスクの容量確保のようなコマンドで必須ではないかもしれませんが、念の為キャッシュをクリアするコマンドを打ちます。

$ sudo npm cache clean

Ubuntu環境にnpmをインストールをします。

$ sudo npm install n -g

「n」というNodeのバージョンを簡単に変更できるプラグインをインストールします。stableとすると現在の安定版も一緒にインストールしくれます。
(現在は10系が安定です。Nodeは偶数が安定版で奇数が開発版だということをなんとなく覚えておくといいかと思います。)

$ sudo n stable

シンボリックリンクを貼ります。

$ sudo ln -sf /usr/local/bin/node /usr/bin/node

ここで安定版を入れたNodeのバージョンを確認しましょう。v10.xx.xなどであればとりあえず大丈夫です。(v8.xx.xでも大丈夫かと)

$ node -v
v10.14.0

バージョンを変更したい場合ですが、その時に「n」が役に立ちます。以下のコマンドを打ちましょう。

$ n v10.14.0

これでNodeのv10.14.0をインストール&設定してくれるので、改めてnode -vでバージョンを確認してみてください。
あわせてnpmのバージョンも確認してみましょう

$ npm -v
3.5.2

んーー、2018/12現在の最新安定版は6.4なのでaptでインストールしたnpmはかなり古いような気がしますね。。
aptでインストールしたnodejsというものはもうNodeを入れたので不要になります。npmも古いバージョンのものは不要なのでアンインストールしましょう。これでNode(v10.14.0)に内包されているnpmのバージョンになるはずです。

$ sudo apt-get purge -y nodejs npm

ここで再度Nodeのバージョン確認すると自分の環境ではエラーが起きてしまいました。

$ node -v
-bash: /usr/bin/node: No such file or directory

シンボリックリンクが外れてしまっていただけなので、もう一度貼り直せば動くはずです。(ここで少し悩みました🤔)

$ sudo ln -sf /usr/local/bin/node /usr/bin/node

最後にyarnをnpmコマンドでインストールしましょう

$ sudo npm install yarn -g

改めてバージョン確認しておきます。これで大丈夫です。

$ node -v
v10.14.0
$ npm -v
6.4.1
$ yarn -v
1.12.3

RailsにVueを入れよう

これで一通り準備は整いました。
これから既存RailsアプリにVueを入れていきます。

Gemfile
gem 'webpacker', git: 'https://github.com/rails/webpacker.git' # とりあえずファイルの一番下に書いておきましょう

webpackerはwebpackと呼ばれるjsの管理をしてくれる(分割したファイルの結合や圧縮など)便利ツールのgemになります。これを用いることで簡単にreactやvue、angularなどをrailsに取り込むことができます。

$ bundle install

bundle installが完了したら以下のコマンドを打ちます。下のコマンドは先ほど入れたYarnが無いとエラーが起きてしまいます。

$ bundle exec rails webpacker:install

しばらく色々なログが流れてきますが、こんな感じに絵文字が出てくればOKです

Done in 94.26s.
You need to allow webpack-dev-server host as allowed origin for connect-src.
This can be done in Rails 5.2+ for development environment in the CSP initializer
config/initializers/content_security_policy.rb with a snippet like this:
policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
Webpacker successfully installed 🎉 🍰

ここでvueの登場です。vueではなくreactをインストールしたい場合は$ rails webpacker:install:reactにすれば大丈夫です

$ rails webpacker:install:vue

これで導入完了です😎🎉

├─ vue-template-es2015-compiler@1.6.0
└─ vue@2.5.19
Done in 91.83s.
You need to enable unsafe-eval rule.
This can be done in Rails 5.2+ for development environment in the CSP initializer
config/initializers/content_security_policy.rb with a snippet like this:
if Rails.env.development?
  policy.script_src :self, :https, :unsafe_eval
else
  policy.script_src :self, :https
end
Webpacker now supports Vue.js 🎉

ここでrailsが動くことを確認しておきましょう。

$ rails s

今回はここまで

Rails+Vue(or React)の導入の記事は世の中にたくさんありますが、自分の仮想環境内でやる上で少し手こずったので記事にしてみました。私と似た開発環境で是非導入してみたいという方の参考に少しでもなればと。
じゃあ実際vueをrailsで書き直すのはどうしたらいいの?といった話は冒頭でも申したようにいつか書きますので読んでほしいです。

流行についていくことが絶対的な正義ではないかと思いますが、それを使わずに称賛や非難するのはよくないと思うので是非食わず嫌いせず「まずは触ってみる」ことが大事だと思います。触ってみることで得られるものはあるはずなので。(仮にそれがすぐに廃れる技術であろうとです!)

長くなりましたが以上になります!ありがとうございました。

最後にVueの日本コミュニティのワークスペースがあるのでリンク貼っておきます。(meetupというチャンネルが勉強会の情報くれるのでオススメです)
https://vuejs-jp.slack.com

【Rails経験者向け】Laravelでhello worldして、簡単にフレームワークの違いを見てみよう!

$
0
0

Railsを学んだけど...

「Rubyだけでなく他の言語も学んだ方がいいですか」みたいな話はちょくちょく聞いたことがあるのですが、個人的には初心者は複数の言語に手を出すよりかは、最初は一つの言語の勉強に集中して方がいいと思っています。これはフレームワークも同じで、例えば、Ruby on Railsの基礎的な流れを把握することができれば、メジャーな他のフレームワークはなんとなく理解できるようになると思います。

他のフレームワークをみてみよう!

とはいえ、他のフレームワークがどんなものか、知りたい人は一定数いる気がするので、今回はPHP言語のメジャーなフレームワークであるLaravelで"Hello World"を出力する過程を、Railsのそれと比較していきたいと思います!ちなみにLaravelは近年すごく使われ始めている言語で、githubスター数もrailsを抜いています。
スクリーンショット 2018-12-10 10.00.19.png

また、Stack Overflow TrendsでもLaravelがRailsを約0.5%ほど上回っていますね。Laravelの人気の高さが伺えます。
スクリーンショット 2018-12-12 8.52.26.png

本記事の対象者

  • Railsを使って簡単なCRUDアプリを製作したことがある。
  • MVCの基本的な流れについて理解している。
  • 「Railsだけ勉強してるけど私大丈夫なの???」って思っている。

LaravelでHello Worldを作る過程で、Railsとの違いを解説していきます。
ちなみに僕はLaravelについてそんなに詳しいわけではないので、技術的な違いというよりは、Rails経験者がLaravelを初めて使用したときに「あ、なんか似てる!」って思うであろう部分について取り上げていきます。

※Railsの基礎について知りたい人は以下の記事をみてみてね!わかりやすいよ!
https://qiita.com/kouichi_s/items/6480586ab7f6356a91c3
https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1

Composerのインストール

Laravelを使用する為には、Composerをインストールしなくてはいけないので、以下のPHPコマンドを打ってインストールしましょう。

$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"

参考ページ: https://getcomposer.org/download/

ちなみにmacでhomebrewを入れている人は以下のコマンドでcomposerをインストールできます。

$ brew install composer

参考ページ: https://brew.sh/index_ja

Laravelのダウンロード

Composerを使ってLaravelインストーラをダウンロードしましょう。

$ composer global require "laravel/installer"

hello worldしてみよう!

まずはあの画面を表示

まずは"Yay! You're on Rails!"でおなじみ、Railsのあの画面を、Laravelでも表示してみます。
スクリーンショット 2018-12-11 23.09.46.png

プロジェクト作成

任意のディレクトリで以下のコマンドを打ってLaravelのプロジェクトを作成します。

$ composer create-project --prefer-dist laravel/laravel project_name

Railsでは新規にプロジェクトを作成するときにはいつも

$ rails new project_name

とコマンドを打っていましたね。ちなみにパスを指定すれば"laravel new project_name"という感じにプロジェクトを作成することもできますが、今回はcomposerコマンドを使っていきます。

プロジェクト作成コマンド

Rails Laravel
rails new project_name composer create-project --prefer-dist laravel/laravel project_name
laravel new project_name

サーバー起動

cdコマンドでプロジェクトのディレクトリまで移動して、サーバーを起動します。以下のコマンドを打ち込んで、ローカルホストにアクセスして見ましょう。

$ php artisan serve

ちなみにartisanは職人の意味で、Laravelのホームページには"The PHP Framework For Web Artisans(ウェブ職人のためのPHPフレームワーク)"との記述もあります!職人ってなんだかかっこいいですね(小並感)!

サーバー起動コマンド

Rails Laravel
rails server php artisan serve

あの画面へ

さて、Laravel版のあの画面はというと。。。

スクリーンショット 2018-12-11 23.45.36.png

Railsのわーい感満載の画面とは異なって、シンプルでおしゃれな画面が登場しました。すごく洗練されている感があります。

hello world!

さて、それでは"Hello world"と出力して見ましょう。一旦さらっといきます!最初にroutes/web.phpを編集して、

routes/web.php
Route::get('/', 'WelcomeController@index');

次にターミナルで以下のコマンドを打ち込み、

$ php artisan make:controller WelcomeController

新規作成されたファイルを以下のように編集し、

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class WelcomeController extends Controller
{
    public function index() {
        return view('welcome.index');
    }
}

そしてresources/viewsの配下にwelcomeディレクトリを、welcomeディレクトリの中にindex.blade.phpを作成して、

resources/views/welcome/index.blade.php
<h1>hello world</h1>

と記述します。はい完成!この状態で"localhost8000/"にアクセスしてみるとhello worldが出力されているはずです。

Railsとの比較を見ていく

MVCは同じ

お気づきになったでしょうか。「routes.rbを記述→コントローラーの作成&コントローラーにアクションを定義→Viewファイル作成」という上記の流れ、Railsと全く同じです!
MVCは他のフレームワークでも採用されていることが多く、非常に重要な概念の一つです。この考え方は実はLaravelでも採用されているんです。
他にもCakePHPでもMVCが採用されていますし、DjangoではMTVという概念が使われていますが、考え方はMVCと同じです。
すなわち、Railsをある程度マスターしてしまって、データの流れさえ覚えてしまえば、他のフレームワークを使ったときにもその知識は生きることになります。一から勉強するよりも圧倒的に楽に学習することができます。

Routing

それではRailsと比較しながらコードを見ていきましょう。まずはルーティングから。上がRailsのコード、下がLaravelのコードです。

Railsの場合

config/routes.rb
Rails.application.routes.draw do
  get '/', to: 'welcome#index'
end

Laravelの場合

<?php
Route::get('/', 'WelcomeController@index');

書き方こそ違いますが、基本的にやっていることは一緒で、「"/"にアクセスしたときは、welcomeコントローラのindexアクションを実行する」という意味です。

Controller

次にコントローラーを見ていきましょう。まずはコントローラーの作成から。Laravelでは基本的にphp artisanコマンドを使って諸々の作成をします。

$ php artisan make:controller WelcomeController

コントローラー作成コマンド

Rails Laravel
rails g controller Welcome php artisan make:controller WelcomeController

それでは実際にコントローラーの中身の記述を比較していきましょう。

Railsの場合

app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
  def index
  end

Laravelの場合

app/Http/Controllers/WelcomeController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class WelcomeController extends Controller
{
    //
    public function index() {
        return view('welcome.index');
    }
}

railsでは任意にコントローラー内にアクションを定義しただけで、views/コントローラー名/アクション名.html.erbのファイルを返してくれるのですが、laravelではreturnを指定してあげないとファイルが返りません。ですがアクションを定義して、そのアクションの中に任意の処理を書いていくという点ではRailsでもLaravelでも大して変わりません。

Viewについて

今回はhello worldしか出力していないですが、参考までに。例えば、コントローラーから変数testを渡してviewに表示したい場合は、以下のような記述になります。

Railsの場合

Controller
def hoge
  @test = 'hoge'
end
View
<%= @test %>

Laravelの場合

Controller
 public function hoge() {
        $test = 'hoge';
        return view('hogehoge.hoge', compact('test'));
    }
View
{{ $test }}

コントローラーで変数を定義して、viewファイルでそれを展開するという形は同じです。

最後に

Railsを学んでマスターしてくと、他のフレームワークへの適応力も上がります。フレームワーク感の違いは、記述形式以外にはそんなにないということを感覚ベースでわかっていただければ幸いです。

基本情報技術者試験を受ける意義って?

$
0
0

緊張のqiita初投稿です。
よろしくお願いします。

テーマは基本情報技術者試験(以下FE)です。
まったくDMM WEBCAMPらしからぬテーマで申し訳ありません笑
Railsに関する投稿が続くなか、ちょっとしたおやつ程度に考えてください。

10月に受験してきたのですが、文系駆け出しプログラマの私にとっては受ける意義(why)があったと感じられたので、そのことについてアウトプットしておこうと思います。
どのように勉強したか(how)の部分については、参考になるサイトや記事が他にたくさんあるので、今回はwhyの部分に焦点を当ててみようと思います。

自己紹介

大学4年生で、商学部に所属しています。学問としてコンピュータに触れたことは(もちろん)一度もなく、プログラマとして働き始めたのもつい最近です。

この仕事を始めてから、「情報工学を大学で専攻しておきたかったな」という思いが少しだけ募ってきています笑

なぜFEを受けるか

※主観的な内容を非常に多く含みます。

今回、私にとってFEを受けた意義は、【コンピュータの仕組みを調べて学習するきっかけになった】ということでした。以下で詳しく書いていきます。

文系出身のプログラマである私にとって、web系の仕事を始める上での不安点は、「情報工学などを専攻した人に対してハンディキャップを背負っているのではないか?」ということでした。というか、今でもこの不安は残っています。
同じような気持ちを抱いている文系出身の方もいらっしゃるのではないでしょうか。

確かに、コンピュータを大学で学んでいないぶん、学んでいる人に対しては出遅れているのは事実です。加えて、新しい技術を開発するなどということになれば、高等教育機関や企業等で研究することがほぼ必須になるでしょう。

ただし、だからと言ってまったく勉強しないのは勿体ないことだと私は思います!
コードを書いて動かしている裏側を知らなければ、本当にそのプログラムを理解できているとは言い難いですし、プログラムが動く根幹を理解していれば、予期せぬエラーへの対応をしやすくなるとも言われています。

ということで、コンピュータの勉強をしようとなるわけですが、まったくの未学習者であれば、何を学べばよいのかがわからない状態だと思います。(少なくとも私はそうでした)
そこで、学習の取っ掛かりの知識をつけるのに適しているのがFEであるというわけです。(※個人差があります)

FEの範囲の中には「論理回路」「コンピュータアーキテクチャ」「ネットワーク」「アルゴリズム」などなど…があり、これらを理解することでコンピュータが理解できるようになってきます。これらの分野に関して、かなり基礎の部分についてはFEが概ねカバーしている印象を受けました。

ただ、かなり基礎的な内容であることには変わりなく、自ら知識を補強しないといけないのは確かです。その意味で、この節の冒頭にも書きましたが、【コンピュータの仕組みを調べて学習する“きっかけ”になる】というのがFEの意義なのだと思います。
資格に向けて勉強を進めていくと、コンピュータに関して何を理解すれば良いのかが明確になってきます。そこから先、自分にとって必要な知識を書籍やサイトを使って身につけてこそ、FEの意味があるのだと思います。

まとめ

あくまで資格の一つとしてFE取得を目指すのもよいですが、コンピュータを理解する第一歩として捉えてみるのも面白いのではないでしょうか。
コードを書けるだけではなく、その構造まで理解できる人材になりたいものです。

Railsでgemを使わずに画像投稿してみた

ウルトラソウルアプリを作ってみた

$
0
0

ウルトラソウルアプリってなに?

皆さん、国民的アーティスト「B'z」をご存知でしょうか?
知ってますよね。知らない人はいないはずです。
そんな彼らの代表曲のひとつでもある「ultra soul」をご存知でしょうか?
知ってますよね。知らない人はいないはずです。
なぜかわからないけど高ぶるあの瞬間!!

そして〜かがや〜く ウルトラソウル!!
ハイ!!!!

この「ハイ!!!」の瞬間がたまらん!

ってことで作りました。

「そしてかがや〜くウルトラソウル!ハイ!!!」の 「ハイ!!!」のタイミングでタッチ!!! あなたのウルトラソウル力が試される!!

その名も「ウルトラソウルアプリ」!!

はい。

とりあえずやってみる

とりあえずやってみてください。
https://ultrasoul-app.herokuapp.com/

結果はいかがでしたでしょうか?
僕は「無」以外出せたことないです。

そうです。脳内再生がポイントです。
著作権の関係上音源を使用することが出来なかったとかじゃないです。
もともと脳内再生のつもりでした。

是非、パーフェクト目指して何度も遊んで欲しいです。

なぜ作った?

今年の2月からプログラミングを始めたのですが、未だアウトプットとして何かアプリケーションを作成したりすることが出来ていませんでした。
インプットすることも大事ですが、何らかの形でアウトプットしたい、、、と歯がゆい思いをしている中で、adventcalendarの話が舞い込んできたので、これは良いチャンスだ!!!と、アプリケーションを作成することにしました。
ちなみになぜウルトラソウルなのかについてですが、

本題

この記事の目標は、
「JavaScriptやjQueryをprogateとかで学習してみたけど、実際どう使えば良いのかわからない、、、」もしくは「Railsを学習し始めたけど、jQueryとかも触ってみたいなあ」というJavaScriptやjQueryをある程度知っている初学者が、何らかの開発で実際に使う時に参考になることです。

環境

Rails 5.2.1
Ruby 2.5.1

アプリケーションの流れ

  1. トップ画面
    スクリーンショット 2018-12-14 14.46.35.png

  2. 準備画面
    スクリーンショット 2018-12-14 14.47.03.png

  3. 脳内再生画面
    スクリーンショット 2018-12-14 14.47.37.png

  4. 結果画面
    スクリーンショット 2018-12-14 14.47.53.png

トップ画面以外は、非同期通信でコンテンツを変更しているのがポイントです。

解説入ります

Rails編

コントローラの作成とアクションを定義

まずは、アプリケーションを作成

$ rails new ultrasoul

コントローラを作成。今回はモデルは使用しません。

$ rails g controller ultrasoul

アクションは2つだけです。

/controllers/ultrasoul_controller.rb
class UltrasoulController < ApplicationController
    # トップ画面に遷移するためのアクション
    def top
    end

    # 準備画面に遷移するためのアクション
    def ultrasoul
    end
end

ルーティングを定義

あわせてルーティングも定義してしまいましょう。
Jsonの部分は後で詳しく解説します。

/config/routes.rb
Rails.application.routes.draw do
  # UltrasoulControllerのtopアクションを呼び出す
  root 'ultrasoul#top', as: 'top'

  # UltrasoulControllerのultrasoulアクションを呼び出す
  get '/ultrasoul' => 'ultrasoul#ultrasoul', as: 'ultrasoul'

  # Jsonを利用して非同期通信を行う
  get "/result" => 'ultrasoul#result', as: 'result'
end

トップ画面

続けてトップ画面のHTMLを作成していきます。

/views/ultrasoul/top.html.erb
<div class="box">
    <h1 class="title">ウルトラソウル</h1>
    <p class="about-message">「そしてかがや〜くウルトラソウル!ハイ!!!」の
    「ハイ!!!」のタイミングでタッチ!!!
    あなたのウルトラソウル力が試される!!</p>
    <%= link_to "あそぶ", ultrasoul_path %>
</div>

スクリーンショット 2018-12-14 15.39.44.png

これでは味気がないのでCSSで装飾していきましょう。

/styleseets/application.scss
/*
 :
 :
 *= require_tree .
 *= require_self
 */

/*共通*/
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
    padding: 0 5%;
}

/*トップ画面*/
.box {
    padding-top: 50px;
    width: 500px;
    margin: 0 auto;
}

.title {
    font-size: 45px;
    text-align: center;
    padding-bottom: 10px;
}

.about-message {
    font-size: 25px;
    text-align: center;
    padding: 30px 0;
}

スクリーンショット 2018-12-14 15.50.00.png

リンクの部分をボタンにしたいですね。

bootstrapの導入(Rails内でjQueryを使えるようにする)

そこで手軽に実装できるbootstrapを使用します。

まずは、Gemfileの下部に以下の記述を追加します。

gem 'jquery-rails'

gem 'bootstrap-sass'

gem 'jquery-rails'はjQueryを使用する際にもインストールしなければならないので一石二鳥です。

記述したら

$ bundle install

でインストールを実行しましょう。

インストールが完了したら、
以下のファイルに、記述を追加してください。

/styleseets/aplication.scss
/*
 :
 :
 *= require_tree .
 *= require_self
 */
@import "bootstrap-sprockets"; /*追加*/
@import "bootstrap"; /*追加*/

/javascripts/application.js
:
:
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery
//= require bootstrap-sprockets
//= require_tree .
:
:

これでbootstrapが使えるようになりました。

HTMLとCSSを編集します。

/views/ultrasoul/top.html.erb
<div class="box">
      :
    :
        <!-- class: "btn btn-default"を追加 -->
    <%= link_to "あそぶ", ultrasoul_path, class: "btn btn-default" %>
</div>

ボタンになりました!
スクリーンショット 2018-12-14 16.28.39.png

あとはCSSでフォントや余白を調整

/styleseets/application.scss
/*共通*/
:
:

.btn {
    width: 200px;
    display: block;
    border-radius: 15px;
    font-size: 20px;
    padding: 10px 30px;
    margin: 0 auto;
}
/*トップ画面*/

これでトップ画面が完成しました!
スクリーンショット 2018-12-14 14.46.35.png

準備画面・脳内再生画面・結果画面

続けて、準備画面・脳内再生画面・結果画面を作成していきます。
脳内再生画面、結果画面に関しては非同期通信によりHTMLの内容が切り替わって表示されてるので、
最初に必要な記述は「準備画面」のみです。

まずはHTMLを記述していきましょう。

/views/ultrasoul/ultrasoul.html.erb
<div class="box">
    <!-- スタートボタンを押した時、「スタート!」にテキスト変更
    ハイ!!ボタンを押したら非表示 -->
    <p id="introduction">
        音声は流れません。脳内でイントロからウルトラソウルを再生してください。
    </p>

    <img id="illust" src="/assets/ready.png">

    <!-- 初めは<div class="start-message">のみ表示
    ハイ!!ボタンを押したら<div class="start-message">を非表示にして
    <div class="end-message">を表示する -->
    <div class="message-group">
        <div class="start-message">
            <p>準備が整ったら</p>
        </div>
        <div class="end-message">
            <p>あなたの<br>ウルトラソウル力は</p>
            <p class="result-message"></p>
        </div>
    </div>

    <!-- 初めは<div id="ready">のみ表示
    ハイ!!ボタンを押したら<div id="ready">を非表示にして
    <ul id="again">を表示する -->
    <div id="ready">
        <input type="button" value="スタート" id="start" class="btn btn-default"></input>
    </div>
    <ul id="again">
        <li class="retry"><%= link_to 'やり直したい', ultrasoul_path, class: "btn btn-default" %></li>
        <li><%= link_to 'トップに戻る', top_path, class: "btn btn-default" %></li>
    </ul>
</div>

ややこしくなってきました笑
続けてCSSを記述してください。

/styleseets/application.scss
:
:
/*準備画面 脳内再生画面 結果画面*/

.start-message > p {
    font-size: 25px;
    text-align: center;
    padding-bottom: 15px;
}

.end-message > p {
    font-size: 25px;
    text-align: center;
    padding-bottom: 15px;
}

.result-message {
    font-weight: bold;
}

#again {
    list-style: none;
}

.retry {
    padding: 8px 0;
}

こちらの画面が表示されるはずです。

スクリーンショット 2018-12-14 17.32.28.png

お気づきでしょうか?
結果画面で表示されるはずの「あなたのウルトラソウル力は」や「やり直したい」ボタンなどが表示されてしまっています。
こちらを非表示にしましょう。

/styleseets/application.scss
:
:
/*準備画面 脳内再生画面 結果画面*/
:
:
/* 追加 */
.end-message {
    display: none;
}

#again {
    list-style: none;
    display: none; /* 追加 */
}

:
:

これで準備画面は完成です!

スクリーンショット 2018-12-14 14.47.03.png

JavaScript編

ようやく、JavaScriptの実装に入れます!

内部での流れの確認

その前に一度内部での流れを確認したいと思います。
このアプリは、「スタートボタンを押した時点でのミリ秒」と「ハイ!ボタンを押した時点でのミリ秒」の差分が、実際の「ハイ!!!」のタイミングに近ければ近いほど良い結果が、ウルトラソウル力として表示される仕組みです。

なので
・スタートボタンを押した時点でのミリ秒の取得
・ハイ!ボタンを押した時点でのミリ秒の取得
・差分の計算(秒数に直す)
・実際の「ハイ!!!」のタイミング(1分06秒)から差分を引く
・差分を引いた結果の秒数によってメッセージや画像の切り替え
以上の機能を実装しなければなりません。

各機能を画面構成に当てはめると
【脳内再生画面】
・スタートボタンを押した時点でのミリ秒の取得

【結果画面】
・ハイ!ボタンを押した時点でのミリ秒の取得
・差分の計算(秒数に直す)
・実際の「ハイ!!!」のタイミングから差分を引く
・差分を引いた結果の秒数によってメッセージや画像の切り替え

結果画面に遷移する際の機能が多いですね。

それでは、JavaScriptを実装していきましょう。

変数、連想配列の準備

/javascripts/application.js
:
:
$(function(){
    /**
     * スタートボタンを押したタイミングのミリ秒のための変数
     * @type {Number}
     */
    let startTime = 0;

    /**
     * 「ハイ!」を押したタイミングのミリ秒のための変数
     * @type {Number}
     */
    let endTime = 0;

    /**
     * endTimeからstartTimeを引いたミリ秒のための変数
     * @type {Number}
     */
    let totalTime = 0;

    /**
     * 結果画面でのメッセージを変更するための連想配列
     * @type {Object}
     */
    let messages = {
        'perfect': 'ウルトラソウルパーフェクト',
        'great': 'ウルトラソウルグッド',
        'good': '平凡ソウル',
        'bad': ''
    };

    /**
     * 結果画面での画像を変更するための連想配列
     * @type {Object}
     */
    let src = {
        'perfect': '/assets/perfect.png',
        'great': '/assets/great.png',
        'good': '/assets/good.png',
        'bad': '/assets/bad.png'
    };
});

「スタート」ボタンを押した時のイベント

まずは脳内再生画面へ切り替わるための実装です。
ポイントは以下のjQueryのイベントの部分です。

/javascripts/application.js
$(function(){
:
:

    // 「スタート」ボタンを押した時のイベント
    $(document).on('click', '#start', function(){
        // スタートボタンを押した時点でのミリ秒
        startTime = Date.now();
        // イラストを変更するメソッド
        $('#illust').attr('src', '/assets/start.png');
        // テキストを変更するメソッド
        $('#introduction').text('スタート!!!');
        // テキストを変更するメソッド
        $('.start-message').find('p').text('ハイ!!!のタイミングでタッチ!!')
        // ボタンのテキストとidを変更するメソッド
        $(this).attr({
            'value': 'ハイ!',
            'id': 'end'
        });
    });
});
/views/ultrasoul/ultrasoul.html.erb
:
<input type="button" value="スタート" id="start" class="btn btn-default"></input>
:

スクリーンショット 2018-12-14 14.47.03.png

このボタンを押した時に
id="start"のjQueryオブジェクトを対象にしたイベント内の処理が実行されます。

【脳内再生画面】
・スタートボタンを押した時点でのミリ秒の取得

まさにこのミリ秒の取得を行なっています。
その後の処理は、見出しのテキストを変更したり、ボタンのテキストやidを変更するメソッドを実行しています。

「ハイ!」ボタンを押した時のイベント

/javascripts/application.js
$(function(){
:
:

    // 「ハイ!」ボタンを押した時のイベント
    $(document).on('click', '#end', function(){
        // ハイ!ボタンを押した時点でのミリ秒
        endTime = Date.now();
        // 残りミリ秒を計算
        totalTime = endTime - startTime;
        // 秒に直す
        let resultTime = totalTime / 1000;
        // 「ハイ!」が66秒目なので、そこからさっき計算した秒数を引く
        resultTime = 66 - resultTime;

        // 非同期通信を行う
        $.ajax({
            url: '/result', // URLは”/result”を指定
            type: 'GET', // リクエストのタイプはGET
            dataType: 'json' // データの型はjson
        })
        // 通信に成功した場合の処理
        .done(function(data) {
            // illsutChange関数からの戻り値を受け取り画像を変更
            $('#illust').attr('src', illustChange(resultTime));
            // 要素を隠すメソッド
            $('#introduction, .start-message, #ready').hide();
            // 要素を表示するメソッド
            $('.end-message, #again').show();
            // ultraSoul関数からの戻り値をもとにテキストを変更するメソッド
            $('.result-message').text('' + ultraSoul(resultTime) + '');

            // パーフェクトであれば「やり直し」ボタンを表示しない
            if (resultTime <= 2 && resultTime >= 0) {
                $('.retry').hide();
            }
        })
        // 通信に失敗した場合の処理
        .fail(function(data) {
            // アラートで失敗した旨をダイアログで表示
            alert('エラーです。もう一度「スタート」を押してください。');
            // ボタンのテキストとidを変更するメソッド
            $('#end').attr({
                'value': 'スタート',
                'id': 'start'
            });
        })
    });
});

id="start"からid="end"に変更されたボタンを押すことにより、
id="end"のjQueryオブジェクトを対象にしたイベント内の処理が実行されます。

【結果画面】
・ハイ!ボタンを押した時点でのミリ秒の取得
・差分の計算(秒数に直す)
・実際の「ハイ!!!」のタイミング(1分06秒)から差分を引く

以上の機能を全て実行しています。

Ajax(非同期通信)

そして、

・差分を引いた結果の秒数によってメッセージや画像の切り替え

については、Ajax(非同期通信)を用いています。
以下の記事を参考にしました。
https://qiita.com/__tambo__/items/409ccf256e84017ea307

関数

そして、差分を引いた結果の秒数に基づくメッセージと画像が表示できるように以下の関数を追加しましょう。

/javascripts/application.js
$(function(){
:
:
    /**
     * 秒数によって結果画面で表示するメッセージを戻り値として返すための関数
     * @param {Number} time 秒数を受け取る
     * @return {Object}     結果メッセージを返す
     */
    function ultraSoul (time) {
        if (time <= 2 && time >= 0) {
            return messages['perfect'];
        } else if (time <= 5 && time >= 0) {
            return messages['great'];
        } else if (time <= 10 && time >= 0) {
            return messages['good'];
        } else {
            return messages['bad'];
        }
    }

    /**
     * 秒数によって結果画面で表示する画像を戻り値として返すための関数
     * @param {Number} time 秒数を受け取る
     * @return {Object}     結果メッセージを返す
     */
    function illustChange (time) {
        if (time <= 2 && time >= 0) {
            return src['perfect'];
        } else if (time <= 5 && time >= 0) {
            return src['great'];
        } else if (time <= 10 && time >= 0) {
            return src['good'];
        } else {
            return src['bad'];
        }
    }
});

これでメッセージと画像の切り替えが出来るようになりました!

あとがき

もともと画像はいらすとやではなくて北島康介さんを使用して個人的にめちゃくちゃ面白かったのですが、著作権の関係上使用することを諦めました。悔しいです。
北島康介さんの写真が著作権フリーになるのを僕は待ち続けます。

とりあえず言えることは、アウトプットめちゃくちゃ大事。


UnityでVRお絵かき ~LineRendererと遊ぶ~

$
0
0

はじめに

皆さん、仮想世界に浸ってますか?
仮想世界の住人からたくさんの歓声が上がっているような気がします。

。。。

さて、私はInfratopという会社でプログラミングスクールのメンターをしています。
そこではRailsをメインに扱っていますが、最近の私のブームがVR開発です。

2016年VR元年以降、VRを体験する機会が急速に増えています。
VR ZONEやVR PARKといったVRを体験する施設ができたり、
VTuberなんていうコンテンツが登場したりしています。
また、Oculus Goといった低価格なVR機器も登場し、手軽にVRの体験・開発ができるようになりました。

本題

本記事では、VR開発の足がけとしてUnity超初心者向けにVRお絵かきをUnityで実装する方法を解説します。

  • Unityを使う
  • Oculus Riftでの開発(Oculus Integration)
  • VRお絵かき

環境

  • Unity 2018.2.14f1
  • Oculus Rift

Unityの使い方とLineRenderer

プロジェクトを作成

Unityをインストールしていない方はこちらからダウンロードしてインストールをお願いします。
Unity ダウンロードページ

Unityを起動できたら、適当な名前でプロジェクトを作成します。このときTemplateは3Dにしてください。
スクリーンショット 2018-12-10 19.16.30.png

プロジェクトが生成できたら次にような画面になります。スクリーンショット 2018-12-10 19.20.21.png

Unity

Unityで登場する概念を簡単に説明します。
Unityで大事なものは大きく二つ、ゲームオブジェクトコンポーネントです。

  • ゲームオブジェクトは、カメラや光源、直方体や球などのモデルといったオブジェクトです。
  • コンポーネントは、物理エンジンやネットワーク管理といった機能です。

Unityではゲームオブジェクトにコンポーネントで機能を付け加えてアプリケーションを作り上げていきます。コンポーネントだけで存在することは原則ありません。

補足

例えば、ネットワークのサーバ機能をアプリケーションに追加するときには空のゲームオブジェクトを作成し、それにネットワークの機能を持ったコンポーネントを追加するということをします。

ゲームオブジェクトを追加する

試しに、球のゲームオブジェクトを追加してみましょう。
ステータスバーからGameObject > 3D Object > Sphereを選択してみてください。次にようになります。スクリーンショット 2018-12-10 19.38.46.png

ここで画面の説明をします。

  • 左のHierarchyウィンドウでは、シーンごとのオブジェクトを管理します。図の場合、SampleSceneというものがあり、その中にカメラと光源と球のゲームオブジェクトが存在しています。
  • 真ん中のSceneウィンドウでは、編集中のシーンの画面が見えています。球が見えていますよね。隣のタブのGameウィンドウを開くと、アプリケーションを実際に起動したときの見え方に変わります。
  • 右のInspectorウィンドウでは、選択しているゲームオブジェクトに付加してあるコンポーネント情報が表示されます。TransformコンポーネントのPositionの値を変化させると、Sceneでの球の位置が変わります。

7720acc5d502031eb6145f0303ca0b58.gif

LineRenderer

それではお絵かきソフトの根幹となるLineRendererを使っていきましょう。
LineRendererはコンポーネントです。空のゲームオブジェクトを作成し、それにLineRendererコンポーネントを追加します。

Sphereを追加したときと同様に、ステータスバーからGameObject > Create Emptyで空のゲームオブジェクトを作成します。HierarchyにGameObjectという名のゲームオブジェクトが追加されたでしょうか。

GameObjectを選択した状態でInspectorウィンドウのAdd Componentをクリックします。
スクリーンショット 2018-12-10 19.56.43.png
この検索窓でLine Rendererと検索し、候補に出たLineRendererを選択します。
追加すると次のようにInspectorにLineRendererコンポーネントが追加されます。
スクリーンショット 2018-12-11 18.17.57.png

LineRendererはPositionsに設定した点を通るような線を描きます。
それではLineRendererを使ってみましょう。Inspectorで設定を次の図のように変更します。
スクリーンショット 2018-12-10 20.06.53.png

画像通りに設定するとこのようになります。
(x,y,z)=[(0,0,0), (0,4,0), (4,4,0)]を通る線になっています。
スクリーンショット 2018-12-10 20.13.04.png

ここまででUnityの簡単な使い方の話を終わります。
大事なことは、Unityではゲームオブジェクトにコンポーネントを付けてアプリケーションを作ることです。
それではVR開発に内容をシフトしていきましょう。

Oculus Riftでの開発

本記事ではOculus Riftを使って開発します。
Oculusは、UnityでOculus Riftを使うためのツールを提供してくれているのでそれを利用します。

Asset Storeウィンドウを開いてOculus Integrationを探しましょう。
そしてimportをします。Oculus integrationの全てをimportして大丈夫です。

※Assetというのはライブラリやマテリアルなどの総称です。
Asset Storeでは便利なアセットが無償/有償で配布されているので色々覗いてみると面白いです。

スクリーンショット 2018-12-10 20.30.00.png

importが成功するとProjectウィンドウにOculusというフォルダが追加されます。
ProjectウィンドウからHierarchyウィンドウや、Inspectorウィンドウにドラッグアンドドロップすることで、ゲームオブジェクトやコンポーネントを追加できます。

操作アバターを追加

ProjectウィンドウにおいてAssets > Oculus > VR > Prefabsの中にあるOVRPlayerControllerをHierarchyウィンドウにドラッグアンドドロップします。

次に、Assets > Oculus > Avatar > Content > PrefabsLocalAvatarOVRPlayerController > OVRCameraRig > TrackingSpaceにドラッグアンドドロップします。
これでLocalAvatarがOVRPlayerControllerの子要素になります。
子要素にすることでプレイヤーに関するゲームオブジェクトをまとめて管理することができます。

※この段階で球とLineRenderer用のGameObjectは消しておきましょう。

適切に追加するとHierarchyウィンドウが次のようになります。
スクリーンショット 2018-12-11 15.57.45.png

アバターテスト

アバターが動作するかテストします。まずはこのプロジェクトをVRに対応させましょう。
ステータスバーのFile > Build Settingsを開き、
ポップアップウィンドウのPlayer Settingsを押します。
Inspectorウィンドウの場所にPlayerSettingが開かれるはずです。
XR Settingsを開きVirtual Reality SDKsにOculusを追加します。
スクリーンショット 2018-12-11 16.04.54.png

設定が完了したら、Oculus Riftを接続したことを確認し、Unity上部のPlayボタンを押します。
これだけでHMDやOculus Touchのトラッキング情報を反映したアバターが登場します。ね、簡単でしょ。

avatar-test.gif

VRお絵かき

ここからメインのVRお絵かきの話です。
これまではプログラムの記述をする必要はありませんでしたが、ここからはスクリプトがメインです。

スクリプト

UnityではC#Unity Scriptを使ってプログラムの記述をします。本記事ではC#で記述していきます。

ただVRお絵かきをするに必要なスクリプトは多くありません。簡単に流れを示します。

Oculus Touchのボタンが押されたことを検知
↓
LineRendererを付加したPrefabからゲームオブジェクトを生成
↓
ボタンを押してる間、コントローラーの位置情報をLineRendererのPositions配列に追加
↓
ボタンを離したらDrawを終了

Prefab(プレハブ)

ここでPrefabという概念が登場します。
Prefabはゲームオブジェクトとコンポーネントのセットを格納できるアセットです。
同じ構造のゲームオブジェクトを繰り返し生成するときに使います。例えば、ゲームの敵キャラやプレイヤーなどですね。

ここでは、LineRendererをもつゲームオブジェクトのPrefabを用意します。
Hierarchyウィンドウで空のゲームオブジェクトを作成し、LineRendererコンポーネントをそれに追加します。
ゲームオブジェクトの名前はわかりやすいようにLineObjectにしています。
LineRendererのプロパティを次のように設定します。

必ずSizeを0にしてください
スクリーンショット 2018-12-11 16.48.28.png

次にこれをPrefab化します。ProjectウィンドウでAssetsフォルダの下にPrefabsフォルダを作成します。
Projectウィンドウでのフォルダやファイルの作成は、Projectタブの下のcreateか右クリックでできます。
HierarchyウィンドウのLineObjectを、ProjectウィンドウのPrefabsフォルダにドラッグアンドドロップします。

dac12d416e600b606f6e457325ab2e44.gif

これでLineObjectのPrefab化ができました。現在HierarchyウィンドウにあるLineObjectはもういらないので削除しましょう。

お絵かきのスクリプトをかく

ではスクリプトをかいていきましょう。

ProjectウィンドウのAssetsフォルダにScriptsフォルダを作成し、DrawManagerというC#スクリプトファイルを作成します。

作成したらDrawManagerをダブルクリックしましょう。
Unityのinstall時にVisualStudioもinstallしていれば、VisualStudioでDrawManagerファイルが開かれます。

DrawManagerの中身を次にようにします。
コメントアウトに細かいことを書いたので詳しくはそちらで確認してください。

DrawManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DrawManager : MonoBehaviour {

    //変数を用意
    //SerializeFieldをつけるとInspectorウィンドウからゲームオブジェクトやPrefabを指定できます。
    [SerializeField] GameObject LineObjectPrefab;
    [SerializeField] Transform HandAnchor;//positionを取得するコントローラーの位置情報

    //現在描画中のLineObject;
    private GameObject CurrentLineObject = null;

    private Transform Pointer
    {
        get
        {
            return HandAnchor;
        }
    }

    // Use this for initialization
    void Start () {

    }


    // Update is called once per frame
    void Update () {
        // ここから追加コード

        var pointer = Pointer;
        if(pointer == null)
        {
            Debug.Log("pointer not defiend");
            return;
        }

        //Oculus Touchの人差し指のトリガーが引かれている間
        if(OVRInput.Get(OVRInput.RawButton.RIndexTrigger))
        {
            if(CurrentLineObject == null)
            {
                //PrefabからLineObjectを生成
                CurrentLineObject = Instantiate(LineObjectPrefab, new Vector3(0, 0, 0), Quaternion.identity);
            }
            //ゲームオブジェクトからLineRendererコンポーネントを取得
            LineRenderer render = CurrentLineObject.GetComponent<LineRenderer>();

            //LineRendererからPositionsのサイズを取得
            int NextPositionIndex = render.positionCount;

            //LineRendererのPositionsのサイズを増やす
            render.positionCount = NextPositionIndex + 1;

            //LineRendererのPositionsに現在のコントローラーの位置情報を追加
            render.SetPosition(NextPositionIndex, pointer.position);
        } 
        else if (OVRInput.GetUp(OVRInput.RawButton.RIndexTrigger))//人差し指のトリガーを離したとき
        {
            if(CurrentLineObject != null)
            {
                //現在描画中の線があったらnullにして次の線を描けるようにする。
                CurrentLineObject = null;
            }
        }
    }
}

Scriptの変数に割りあて

InspectorウィンドウでOVRPlayerControllerにDrawManagerコンポーネントを追加します。

scriptの[SerializeField]によって、ドラッグアンドドロップでスクリプト内の変数に直接ゲームオブジェクトやPrefabを指定できます。

LineObjectのPrefabとOVRPlayerControllerの子要素にあるRightHandAnchorを、Inspectorウィンドウの指定の場所にドラッグアンドドロップします。

スクリーンショット 2018-12-11 18.32.47.png

e1959a6bba5f92311c2da86d417c88a3.gif

最後の調整

  • OVRPlayerControllerにカメラがあるのでMainCameraは不要です。Hierarchyから消しましょう。
  • 地面が今無いので、このままだとアバターが下に消えていきます。ステータスバーからGameObject > 3D Object > Planeで地面を追加します。
  • OVRPlayerControllerのTransformのy値を1にします。

お絵かきタイム!

これでPlayするとこうなります。

draw.gif

おわりに

いかがだったでしょう。
Unityを使えばVRの開発が簡単にできます。Unityの公式ドキュメントは日本語にも対応していますし、かなりわかりやすいです。環境さえ整えてしまえば簡単に進められます。

Oculus Riftを使うには高スペックのPCが必要ですが、2019年春発売予定のOculus Questは、スタンドアローンでありながら6DoFです(位置のトラッキングができる。スタンドアローンの代表格Oculus Goは3DoFで向きのトラッキングしかできない)。HMDの開発が進むと、VR開発を始める敷居はドンドン下がっていくでしょう。

VR開発やっていきましょう!!

【知らない人のための】検証ツールってこんなに便利!【Google Chrome】

$
0
0

はじめに

はじめまして。初投稿です。WEBCAMPのメンターをやらせていただいてます。

Webサイトを作成する際に必ずと言っていいほど(いいよね?)「検証ツール(デベロッパツール)」というものを使います。

この記事ではその便利な検証ツールの基本的な使い方と機能の紹介をしていきます!

実際にWEBCAMPの教材を進めていく際に重宝するツールとなっていますので、みなさんも手を動かして使ってみましょう!

※参考としてWEBCAMPのホームページを使わせていただきました。


動作環境
OS: macOS 10.14 Mojave
ブラウザ: Google Chrome 70.0.3538.110


対象者

  • HTML・CSS(JS)習い始めの人
  • 検証ツールを使ったことがない人

まずは基本的な動作から

起動方法

  • Webサイト上で右クリック→検証
  • Webサイト上で【F12】 or 【Command + Option + I 】 (Windowsの方はCtrl + Shift + I )

場所を移動したい場合はスクリーンショット 2018-12-03 13.58.22.pngをクリックして移動先を選択。

対応する要素とCSSの確認方法

Elementsパネル内のスクリーンショット 2018-12-03 13.59.32.pngをクリックし要素にカーソルを合わせると左下の枠(DOMエレメントツリーという)に対応するHTMLの要素が自動的に表示されます。

右枠(サイドバー)はその要素に対応したCSSが一覧で確認できます。

スクリーンショット 2018-12-03 14.58.57.png

HTML・CSSのテスト的編集

多く使うであろう機能。これがなかなかに便利。

HTML

DOMエレメントツリー内の変更したい部分で
右クリック → 【Edit as HTML】 → 編集後、枠外を左クリックで反映。

circleanimationmuvie

CSS

サイドバーに直接編集・挿入できます。
ラジオボタンのチェックを外せば、その指定だけを外せます。

circleanimationmuvie

編集した内容はページ更新(Command + R または F5)をすれば元に戻ります。

これを使うとCSSがうまく反映されない時にすぐに対応が可能です。

テスト用に簡単なサイトを作りましょう。

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <title>hogehoge</title>
</head>
<body>
    <h1>hogehoge</h1>
    <div class="test">テスト</div>
</body>
</html>
style.css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
.test{
    font-size: 50ox;
    color: blue;
}

(この時点で間違いに気づく人もいると思いますが知らんぷりしてください(笑))

うまく表示されてないとサイドバーにエラーが表示されます。下記の場合だと.testfont-sizeに「Invalid property value」(プロパティ値が無効です)と書かれているので値をよーく確認してみますと…

スクリーンショット 2018-12-03 10.05.37.png

値が「50 o x」となっていました!…というようにコードとにらめっこしなくてもすぐに原因をみつけることができます。

ボックスモデルの確認

サイドバーの下にある四角になっている部分でmargin,border,paddingの値の確認ができます。

試しにテスト用サイトを作成してみましょう。

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <title>hogehoge</title>
</head>

<body>
    <div class="box clearfix">
        <div class="box-item"></div>
        <div class="box-item"></div>
        <div class="box-item"></div>
    </div>
</body>
</html>
style.css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
.clearfix::after {
    content: "";
    display: block;
    clear: both;
}
.box{
    width: 720px;
    background-color: #ffd700;
    margin: 0 auto;
}
.box-item{
    background-color: red;
    float: left;
    margin: 10px 20px;
    width: 200px;
    height: 200px;
}

検証ツールで確認すると、赤色のBOXがしっかり上下に10px、左右に20pxが反映されていることがわかります。
スクリーンショット 2018-12-03 14.51.06.png

デバイスモード

スクリーンショット 2018-12-03 13.59.46.pngをクリックするとスマホサイズでの表示が確認できます。

レスポンシブデザインをしているサイトを確認したい時に重宝しますね。
サイドバーにもレスポンシブ対応されたCSSが反映されています。

スクリーンショット 2018-12-03 13.53.31.png

様々な機能

他にも検証ツールにはたくさんの機能がありますが、その中の一つをご紹介しましょう。

JavaScriptのデバック

10000回ボタンを押したら文字が表示されるテスト用サイトを作りましょう。

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <script type="text/javascript" src="script.js"></script>
    <title>hogehoge</title>
</head>

<body>
    <h1>10000回クリックしたら...?</h1>
    <p>
        <input type="button" name="cnt" value="クリック" onClick="count()">
    </p>

    <p id="p">
        <img src="https://media.giphy.com/media/5hub03Op2aRplYCBOK/giphy.gif">
        おめでとう!
        <img src="https://media.giphy.com/media/5hub03Op2aRplYCBOK/giphy.gif">
    </p>

</body>
</html>
style.css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
#p{
    display: none;
}
sprict.js
let cnt = 0;

function count(){
  cnt++;

  if (cnt >= 10000){
    const p1 = document.getElementById("p");
    p1.style.display ="block";
  }
}

10000回クリックしたら隠れている文字がちゃんと表示されるか確認をしたいのでデバック作業を行なっていきます。

ファイル表示

ページが表示できたら検証ツールを開き、Sourcesパネルをクリックします。その後、左のタブから3つのファイルを開いた状態にします。

スクリーンショット 2018-12-03 19.04.55.png

変数の値確認

今回使っている変数はクリックした回数を格納するcntです。
Consoleパネル(対話型のJavaScript実行環境)でconsole.log(cnt)と実行して確認するのも良いですが、もっと見やすい方法があります。

右枠の欄にWatchというのがあるのでそこのスクリーンショット 2018-12-04 11.08.37.pngボタンをクリックします。
入力欄が出てきますので、cntと入力すると下記の画像のように変数名と値が表示されます。

スクリーンショット 2018-12-04 11.09.21.png

実際に値が変わっているかどうか確認しましょう。
今回はcnt++;でcntに1つ数字を足しているプログラムとなっているので数回クリックして、スクリーンショット 2018-12-04 13.14.18.pngを押して値が更新されていればOKです。

ブレークポイントを設定して値の変更

ブレークポイントとは、実行中のプログラムを一時停止させる箇所のことです。これを指定して値の変更をしましょう。

ブレークポイントの付け方は簡単で、一時停止させたい行番号のところをクリックするだけです。
今回はcntの値を10000に変更したいので4番目の行をクリックします。

スクリーンショット 2018-12-04 14.05.44.png
上記のように青いタグがついてあり、右枠のBreakpointsにクリックした行番号が表示されてあればOKです。

それでは一度ボタンをクリック実行していきましょう。
スクリーンショット 2018-12-04 14.11.07 1.png

このような画面になって一度スクリプトが止まっている状態になります。

再度動かすには、Paused in debuggerの隣にあるスクリーンショット 2018-12-04 16.10.45.pngボタンを押すとブレークポイント後のスクリプトが実行されます。

また、右枠のスクリーンショット 2018-12-04 16.18.57.pngをクリックすると一行ずつスクリプトが実行されます。

これでブレークポイントをつけることができたので、あとは一時停止した際に変数の値を変更していきましょう。

変数の値の変更の仕方は、ブレークポイントをつけた青色のところを右クリックし、【Edit breakpoint】→枠内をcnt = 10000と入力します。

スクリーンショット 2018-12-04 17.49.52.png

変更をしたら下記のようにオレンジ色になるのでこれで変更ができました。
スクリーンショット 2018-12-04 17.51.02.png

あとは同じように実行していただくと、10000回押されたことになって(watchの中身を確認してみてください)隠れていた文字が表示されるようになります!
どんなものが表示されるかは、みなさんの目でご確認ください!!

エラー文表示も

デバック機能なので当然エラーがあれば警告文も表示されます。

仮に変数の綴りを間違えてしまったら(cntcutと書いてしまった)、ボタンをクリックした際に該当箇所に波線が引かれて、Console上にエラー文が表示されます。スクリーンショット 2018-12-04 18.05.04.png

(この場合ですと、Uncaucht ReferenceError: cut is not defined(cntという関数は定義されてませんよ)となっています。)

このようにエラーした部分とその原因が一目でわかる優れものとなっています!

終わりに

私はこの検証ツールを使えるようになったおかげで、Webサイト作成が楽しくなって日々の生活が充実し、好きな人ともうまくいき、結婚することができました!:sunglasses::sunglasses:

いままであげてきたのは使い方のほんの一例です!他にもネットワーク関連の事やセキュリティ面の事も確認できるのでもし気になった方は調べてみてください!!

【rails】gemを使わずにログイン機能を実装!

$
0
0

目次

・はじめに
・そもそもなぜログイン機能は必要なの?
・実際に作ってみる
・modelを作成
・カラムを追加
・データを入れる
・viewを作成
・controllerの実装
・まとめ

はじめに

多くのウェブアプリケーションにはログイン機能が実装されていますが、皆さんはログイン機能を実装したことはあるでしょうか?また、実装する際にはどのように実装していますか?

deviseという非常に便利なログイン機能実装のためのgemがあるので、自分で実装したことはないという方も多いかもしれません。実際に私も今までdeviseに頼りっぱなしでログインの仕組みについて深く考えたことはありませんでした。

確かに、gemを使いこなせれば開発のスピードが格段に上がるので利用しない手はないでしょう。しかし、railsについてきちんと理解する前にあまりにgemに頼りすぎてしまうと自分の技術も向上していかないというのもまた事実だと思います。

そこで今回は普段何気なく使っているログイン機能はどのような仕組みで動いているのかを考え、gemに頼らず自力で実装してみようと思います。

そもそもなぜログイン機能は必要なの?

そもそもの話なのですが、ログイン機能はどうして必要なのでしょうか?もし不要なのであればわざわざ時間をかけて実装するのは面倒なので、まずはユーザーにログインをさせる理由を考えようと思います。

ログインの必要性を考えるために、逆にログイン機能がなかったらどうなるか想像してみてください。あるユーザーがサイトを閲覧していて、そのユーザーがマイページを確認したいという状況を考えます。そのユーザーのマイページを表示させるためには、当然ながらそのユーザーの情報を引っ張ってくるために、今まさにサイトを閲覧しているその人のidを取得する必要があります。ですが、そのidはどうすれば取得できるのでしょうか。ここに来て初めてdeviseのcurrent_userのありがたみを感じると思います。

HTTPというプロトコルはステートレスなプロトコルと言われているように今までのリクエストの情報を全く利用できないため、こちら側で工夫してあげないと今誰がサイトを閲覧しているのかということがわからないのです。その問題を解決するためにあるのがセッションというもので、情報をユーザーのパソコンのウェブブラウザに記憶してくれます。これを使って今閲覧のユーザのidを判別するのですが、このような仕組みがログインになります。

実際に作ってみる

色々聞いたり調べたりしても自分で手を動かしてみないと理解もしにくいと思うので、早速コードを記述していきましょう!今回はとりあえずログイン機能の基本を理解することが目標なので、すでに登録してあるユーザーが名前とパスワードでログインをすることを考えます。わかりやすくするためにパスワードの暗号化などは割愛します。

modelを作成

まずは下準備としてUserモデルを作成しましょう。

command
$rails g model User
Running via Spring preloader in process 7229
      invoke  active_record
      create    db/migrate/20181212114133_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml

カラムを追加 

Userモデルは作られましたので、usersテーブルにユーザーの名前を入れるnameカラムとパスワードを入れるpasswordカラムをそれぞれinteger型で追加しましょう。

マイグレーションファイル
class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
        t.string :name
        t.string :password
        t.timestamps
    end
  end
end
command
$rails db:migrate

データを入れる

今回はログイン機能を作るのが目的のためユーザーの登録画面は作りません。ただ、ユーザーの登録情報がないとログインが成立しないので、データベースにユーザーの情報を直接書き込みましょう。ターミナルで下記のコマンドを打ち込んでください。

command
$ rails c

次に下記のコマンドを打ち込んで、名前:userA パスワード:AAAAAAのuserAさんを作りましょう。

command
irb(main):001:0> User.create(name:"userA",password:"AAAAAA")

このように表示されれば成功です。

 (0.0ms)  begin transaction
  User Create (3.5ms)  INSERT INTO "users" ("name", "password", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "userA"], ["password", "AAAAAA"], ["created_at", "2018-12-13 09:43:58.698249"], ["updated_at", "2018-12-13 09:43:58.698249"]]
   (3.2ms)  commit transaction
=> #<User id: 1, name: "userA", password: "AAAAAA", created_at: "2018-12-13 09:43:58", updated_at: "2018-12-13 09:43:58">

同様にして
名前:userB パスワード:BBBBBBのuserBさん
名前:userC パスワード:CCCCCCのuserCさん
も作っておきましょう。作り終えたら下記のコマンドを打ってデータベースを確認してみましょう。

command
irb(main):005:0> User.all

このように表示されればしっかりとデータが作られています。

 User Load (1.7ms)  SELECT  "users".* FROM "users" LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "userA", password: "AAAAAA", created_at: "2018-12-13 09:43:58", updated_at: "2018-12-13 09:43:58">, #<User id: 2, name: "userB", password: "BBBBBB", created_at: "2018-12-13 09:56:31", updated_at: "2018-12-13 09:56:31">, #<User id: 3, name: "userC", password: "CCCCCC", created_at: "2018-12-13 09:56:48", updated_at: "2018-12-13 09:56:48">]>

viewを作成

次にview画面を作っていきましょう。今回作るのは、すでに登録済みのユーザーがログインを行うためのログイン画面と、正しくログインできた時に飛ぶユーザーのshow画面です。ユーザビリティは全くのゼロですがお許しください。
まずはログイン画面から。

login.html.erb
<h1>ここでログインする</h1>
<%= form_for(:session, url: login_path) do |f| %>
  name<%= f.text_field :name %>
    email<%= f.text_field :email %>
    <%= f.submit 'ログイン' %>
<% end %>

2行目のform_forで少し見慣れない書き方が出てきました。:session, url: login_pathという記述があります。今までであれば:sessionの箇所に:@userのようなインスタンス変数を記述していましたが,セッションにはモデルがないため、そのままではサブミットした後にどのようなURLにPOSTするのかを自動で決めることができません。なのでurl: login_pathのように明記してあげることで、URLを明確に示しています。

次にshow画面

show.html.erb
<h1>ログイン成功後に飛ぶユーザのshow画面</h1>
<%= @current_user.name %>
<%= @current_user.email %>
<%= link_to 'ログアウト', logout_path, method: :delete %>

2行目と3行目に@current_userという記述がありますが、これは後でcontrollerで定義していきます。
一番最後の行にログアウトをするためのリンクも用意しておきましたが、これについても後ほど機能を実装していきます。

controllerの実装

まずはログイン後に表示するshow画面から。画面を表示するだけなので特に記述することはありません。

users_controller.rb
class UsersController < ApplicationController

    def show
    end

end

次にapplication_controllerです。

application_controller.rb
class ApplicationController < ActionController::Base
    before_action :require_sign_in
    before_action :current_user
    def current_user
        @current_user ||= User.find_by(id: session[:user_id])
    end

    def require_sign_in
        redirect_to login_path unless current_user
    end

end

上から順に解説していきます。
before_action :require_sign_inは何をしているのかというと、もしユーザーがサインインしていない場合にはlogin_pathにリダイレクトさせています。unlessという書き方は、そのあとに書いたものがnil、もしくはfalseだった場合に実行するというものです。

次にbefore_action :current_userですが、これはsessions_controllerでも出てくるのでそちらで解説します。

では、sessions_controllerをみてみましょう。

sessions_controller
class SessionsController < ApplicationController
    skip_before_action :require_sign_in, only: [:login]
    skip_before_action :current_user, only: [:login]
    def login
        if params[:session]
            @user = User.find_by(name: params[:session][:name], email: params[:session][:email])                        
            if @user
                session[:user_id] = @user.id
                redirect_to user_path(session[:user_id])
            else
                render("sessions/login")
            end
        end
    end

    def destroy
        session.delete(:user_id)
        @current_user = nil
        redirect_to login_path
    end


    private 
    def session_params
        params.require(:session).permit(:name,:email)
    end
end

こちらも上からみていきます。
1行目のskip_before_action :require_sign_in, only: [:login]についてですが、application_controllerrequire_sign_inが定義されているため、本来ならば全てのアクションの前でrequire_sign_inが実行されてしまいます。ただ、ログインをするページに関してはこれからログインをするところなので、サインインを要求する必要がありません。なのでloginアクションについてのみrequire_sign_inを行わなくていいよ、と言っているのがこの記述です。

同様にskip_before_action :current_user, only: [:login]も、loginの前に関しては実行しなくて良いということを言っています。では、current_userでは何を行なっているのでしょうか。先ほど解説を飛ばしていたので、このタイミングで解説します。

def current_user
        @current_user ||= User.find_by(id: session[:user_id])
end

この中でまず気になるのが||=という記述だと思います。これは@current_userというインスタンス変数がもしnilもしくはfalseだった場合に右辺を代入するというもので、ここでは今ログインしているユーザーのidを持っているユーザーを代入しています。session[:user_id]への代入はloginアクションで行なっているため、後ほど見ていきます。

また、ここでfindではなくわざわざfind_byを使っている理由ですが、これはもしもsession[:user_id]にまだ何も値が代入されていなかった場合にエラーとなるのを防ぐ為です。

これで@current_userにログイン中のユーザーを代入することができました。
次にloginアクションを見ていきましょう。一つ目のif文ですが、これでパラメータが送られているか判定しています。@userには、送られてきた名前とパスワードの組み合わせが一致するユーザがいた場合に、そのユーザー代入しています。

二つ目のif文で、先ほどの@userにユーザーが代入されているかを判定しています。代入されていた場合には、:session[user_id]にそのユーザーのidを代入しています。これらの条件を満たしていない場合には、もう一度ログイン画面を表示するようにしています。

最後にdestroyアクションですが、このアクションでログアウトの機能を実装しています。session.delete(:user_id)の部分でセッションを削除し、@current_user=nil@current_userの中身を空にしています。

まとめ

以上で、非常に簡単にではありますがログイン・ログアウト機能の実装ができました。このままではセキュリティ面での不安がかなり残るのですが、今回はひとまずここまでとします。最後まで読んでいただきありがとうございました!

ひよっこエンジニアがPythonを触ってみた

$
0
0

はじめに

ご覧いただきありがとうございます!
そしてはじめまして!shii_yuです。
プログラミングほぼ未経験者ですがアドベントカレンダーを書かせていただくことになり初投稿をさせていただきます!
Pythonの知識が浅くご指摘があれば是非よろしくお願いします。
未熟ですがよろしくおねがいします。

なぜPython?

アドベントカレンダーを書いてみたかったが、知識もなければアウトプットできるものもない私。
とりあえず初心者が作れるものを作ってみよう...と考え選ばれたのはじゃんけんでした!
if文だけでいけそうだし簡単でしょ!と思っていました。(すみません)
じゃんけんアプリを作ろう!となった時言語で迷ったのですがPythonを選ばせていただきました。
なぜPythonにしたかというとあいうえお = あいうえお(文字列=文字列)が出来るからです:smirk:
プログラミング言語はいろいろありますがcやjavaなどはあいうえお = あいうえおができないらしいのです...。
厳密に言うと出来るらしいのですが文字コードやらなんやらややこしい!!よくわからん!!ってなりました。
JavaScriptもできるのですがイケてそうだしキてそうだしとりあえずPythonにしました。

開発環境について

突然ですが、皆様のパソコンはMacですか?
ちなみに私はMacに憧れるWindowsユーザーです。
環境としましてはwindows10に入っているUbuntu 18.04でpython 3.3.6とpip 9.0.1をインストールして使っています。
環境構築についてはAnacondaがよく使われていると感じました。
今回は私が実装した方法(Macに憧れるwindows10ユーザー向け)での説明をしていきます。

環境構築

Windows Subsystem for Linuxのインストール

  1. 左下にある検索のところから「機能」と入力を行い、候補に現れるWindowsの機能の有効化または無効化を選択。
  2. 以下の画像のWindows Subsystem for Linuxにチェックを入れてokを押し指示に従いインストールした後に再起動を行う。 python1.PNG

Ubuntu 18.04 LTSのインストール

  1. Microsoft Store」を開いていただき、「ubuntu」と検索するとUbuntu 18.04 LTSが出てくるので選択し、インストールを行う。
    pyhonubuntu.PNG

  2. インストールが終わればプログラムを起動し、任意のユーザー名とパスワードを入力(今後必要になる)

Pythonのインストール

  1. ここからはターミナルに以下のコマンドを(1)から順番に打ってもらい、Pythonを導入していく。
(1) $ sudo apt update 
(2) $ sudo apt upgrade 
(3) $ sudo apt -y install python3 
(4) $ python3 -V 

(4)によって表示されたバージョンが,3.6以上であることを確認.
バージョンが表示されない場合はなんらかのウイルス対策ソフトが入っている可能性が大なのでその解除をしてみてください。

(5) $ sudo apt -y install python3-pip 
(6) $ sudo pip3 install --user -U pip 
(7) $ sudo pip3 install --user -U setuptools jupyterlab numpy matplotlib tensorflow 

以上でPythonとpipの導入は終わりです。
ターミナルでjupyter labと打っていただければ、URLがでてくるのでそれをコピーしてブラウザに貼り付ければ以下の画面が出てきます。NotebookのPython3を押していただければソースコードを入力できる画面になり、Pythonを実行できます!
pyhongamen.PNG

じゃんけんアプリについて

環境構築で疲れておしまい!と言いたいところですが、これから本題のじゃんけんについて話していきます!

じゃんけんアプリの流れを考えよう!

作る前に流れを考えてみました。
・qが押されるまで何回でも入力でき、qが入力されると終わる。
・「グー」「チョキ」「パー」の入力をする。
・「勝ち「負け」「あいこ」の判定をする。
・「グー」「チョキ」「パー」以外入力されるともう1度入力できる。
・相手の手はrandomを使ってみる。

じゃんけんアプリに必要なこと

その1 変数の宣言
その2 入力と出力
その3 判定文
その4 繰り返し文
その5 random

とりあえず順番に考えていくことにします!

その1 変数の宣言

基本中の基本中の基本というやつですね。(しかしだいたいここがわけわからん)
私はCやJavaに少し触れてきたので、変数の宣言といえばint a = 0var a = 0他にもなんかごちゃごちゃした書き方があったり、intやstring型、double型などいろいろな型の指定をしなければいけなかったり、まず、変数の宣言だけでも難しかったです。Pythonの宣言は以下のとおりです。

なんと!!!!簡単!!!!!!なんと!!!便利。(型は勝手に考えてくれるだとかなんとか)ちなみに、型を付けたいときは型ヒントとかいうやつでx: intでつけれるみたいですね(1番下のやつです)便利。なぜ334なんでしょう...。

その2 入力と出力

基本中の基本というやつですね。
出力はprintf()で()の中に文字を入れてあげれば出力できます。
入力はinput()で出来ます。
input.PNG

input関数は入力されたデータを文字列として返します。
「グー」「チョキ」「パー」でじゃんけんしたい私にとってはとても嬉しいですね。
整数型に変更したい場合はint(input("入力してください")とする必要があります。

その3 判定文

勝ったか負けたかの判定文が必要です。
Pythonのif文はどうやらインデントで区切られる...?!波括弧({})がいらない!?!?
判定文は以下のようにインデントで区切られるみたいですね。
ifbun.PNG

(他言語では、if文の中身が1行だけのときは波括弧つけなくてもいいのに、つけてたくらいの愛用者でした。)
悲しい。インデントが規格であるらしいですね。これは仕方がないので従うしかないですね。

その4 ループ文

ループ文はwhile文といわゆるfor文があります。今回はwhile文を使用します。
while文はある条件が行われるまで、for文は何回分といった感じなので、while文が向いていると思いました。
ループ文もやはりとは思いましたがインデントで区切られます。Pythonでwhile文を書くと以下のようになります。
while.PNG

インデントでループ文を記述する、絶対ややこしいと思いましたが、仕方がないので従うしかないです。
なれてくると、こっちのほうがやりやすいのでしょうか?永遠の疑問ですね。

その5 random

敵の手を決めるためにrandomな整数がほしいということで、randomを使います。Pythonには標準ライブラリと呼ばれるものに関数random()があるので、import randomの記述を行えばrandom()でrandomな整数がどこでも呼び出すことが出来ます。今回はrandom(1,3)の3つの数字の中でグーチョキパーを決めていきます。

実際にソースコードを書いていこう!

この5つさえあれば簡単にソースコードがかけるはず!!
ってことで書いていきました。
勝手にいくつか機能を付け加えてみました。

janken.ipynb
import random
py_number = random.randint(1,3)
my_hand = input("グーかチョキかパーを入力してください。終了する時はqを押してください。")

while my_hand != "q":
    if py_number == 1:
        py_hand = "グー"
    elif py_number == 2:
        py_hand = "チョキ"
    else:
        py_hand = "パー"

    if  my_hand == py_hand:
        judge = "あいこ"
    else:
        if my_hand == "グー":
            if py_hand == "チョキ":
                judge = "勝ち"
            else:
                judge = "負け"
        elif my_hand == "チョキ":
            if py_hand == "グー":

                judge = "負け"
            else:
                judge = "勝ち"
        elif my_hand == "パー":
            if py_hand == "グー":
                judge = "勝ち"
            else:
                judge = "負け"
        else:
            my_hand = input("グーかチョキかパーを入力してください。")
            continue           

    print(f'あなたの手は{my_hand}で相手の手は{py_hand}なので')    
    print(f'結果は{judge}です。')

    py_number = random.randint(1,3)
    my_hand = input("グーかチョキかパーを入力してください。終了する時はqを押してください。")

実行結果

gutyokipa.PNG

やってみて

頭の中では思い浮かんでも、実際にコードを書くと全然思い通りにいかないなと思いました。
このソースコードを見られた方は「何回inputあるんだ」とも思っていらっしゃるかもしれません。
きれいなコードと言われるものを書くのは難しいとしみじみと感じました。
特に!!!!!どこまでがwhileでループが回っているんだ!!!と何回も無限ループに陥り....
あと、continueがなかなか出てこず、グーチョキパー以外の文字でも判定されるようになったり.....。
じゃんけん簡単!if文だけでできるでしょ!って思ってたのが大間違いでした。

最後に

この記事を読んでくださった方は、プログラミングを始めたばかりの人、プログラミングに深く関わってきてる人、様々な人がいると思います。
私もプログラミングが難しくてよくわからんくて無理やばいとか思うことが何度もあります、なんなら今も思っています。
しかし、それを乗り越えると楽しさが残ると思います。無理もうプログラミングやめたいいと思っている方
プログラミングは楽しみながらしましょう!そして楽しいプログラミングライフを送りましょう!!!

P.S.綺麗なソースコードをかけるようになりたいですね。

【プログラミング質問方法のすゝめ】質問する事にためらいのあるすべての人へ

$
0
0

もくじ

  1. どんな記事?
  2. 読んで得られること
  3. 具体的な方法
  4. 結論

1.どんな記事?

人に聞くのは避けられない....
でも、原因がしょうもない事だったらどうしよう。
こんな事も分からないの?って言われたらどうしよう。
実際に質問してみたら、大した原因ではなかったらどうしよう。

人に質問する時に、色々考えてしまう人/質問する事が怖い人に読んでほしい記事です。

私自身、人に質問するのが怖いです!上記に書いたような事を考えてしまいます!
そのため、自己解決できる幅を広げよう!と考えていました。

しかし、学習初期は自己解決出来る幅も狭いので、質問相手がいるのなら「質問」はした方が効率良かったです。

なので、いかに不安要素を潰しつつ、相手に短時間で意思疎通を行い、求めていた回答を得るかを記事にしました。

2. 得られること

質問しても、意図していない回答が返ってくることが減る。(質問の意図が伝わりやすくなる)

自己解決出来る幅が広がる。

質問力がつく。

3. 具体的な方法

結論としては、以下の三点を意識して質問します。

  • 1.実現したい事
  • 2.問題点
  • 3.試してみた事

なぜこの三点なのか?
私自身、人に質問した際の反省を生かした結果がこの3点でした。

実現したい事を説明する必要性

一言で言うと、意図が伝わりやすくなるからです!無駄な意思疎通が減るので、心理負担が少し減りました!

プログラミング学習初期の私は、質問する際に問題点は必ず説明していました。
問題点を必死に説明して、説明して。。。。。
質問を説明し終わったと思ったところで、

質問相手「うん。それで。。。。。?」

と言われました。
ええ。つまり、実現してほしい事・解決してほしい事が伝わらなかった、もしくは言っていなかったのです。

問題点を説明したら、相手が「実現したい事」を理解してくれるだろう。と思っていました。
相手に理解を丸投げしていました。相手に理解してもらうための、努力をおろそかにしていました。

問題点を説明するだけで、実現したい事を悟ってくれる人もいるかもれません。
しかし、すべての人がそうだとは限りません。

そのため、どの人に質問しても意図が伝わるために「実現したい事」は説明した方がハッピーになれます。

試してみた事を説明するメリット

この項目は、必要ないと思われるかもしれません。

しかし、

質問内容の自己解決の可能性の向上。
質問相手に、幻滅される可能性の削減。
質問相手に、試した事へのフィードバック得られることもあります!

「何を試して、どのような結果が帰って来て、何が出来なかったか」 

これを伝えるために脳内整理するだけでも、解決にいたる事があります!

さらに、「〜で検索して、〜を試してみました。しかし、〜となってしまいました。」

と質問するだけで「ググった?」と聞かれる事を回避できます!

質問する際に説明していて損はないです!

4.まとめ

「〜をしたいのですが(行いたい事)〜で出来ませんでした(問題点)。〜と考えて、〜を実行してみました。しかしながら、〜となってしまいました(試した事)。」と聞くと分かりやすいかもしれないです!

色々試したにもかかわらず、分からなかったら聞いて良いです!

プログラミング学習の高速道路にのりましょ!!

最後まで読んでくださりありがとうございました。

新卒エンジニアがSlackに#Doyaチャンネルを作ったら社内の「グッとくる」が増えた話。

$
0
0

はじめまして

こんにちは。普段は餃子のことを考えながらDMM WEBCAMPの裏で開発をしています。
餃子部の部長です。折角なので餃子について少し語りたいのですが、

弊社で導入したDoyaチャンネルが思いのほか、面白くなってきたので書き綴らせてください。

チャンネルの導入背景

そもそも。 \エンジニアの皆さん。/
いや、エンジニアではなくても仕事をやっている皆さんは、
「何かを達成した時にちゃんとどのように評価されていますか。」

もし「こんな評価されています、おすすめです!」みたいな仕組みがあれば是非教えてください。
私は新卒でエンジニアとして働き始めてから、なかなか自分のやっていることが周りには共有できていないなと課題感を持つことがありました。個人的にはコードを書いて達成感があるのに、周りのメンバーから見たら「毎日パソコンの黒い画面とにらめっこをして何やってるんだろう〜」「何かを実装しているんだろうな。」と思われていたに違いないです。

image.png

そこで社長と1on1をした時に、私の課題から生み出されたのがこのDoyaチャンネルです。

Doyaチャンネル作成

Slackは便利ですね。コストもかからず、1分もたたずにチャンネルを作成できます。

Slack_-_infratop.png

そして折角Doyaをつぶやいでも反応がないのは寂しいので、
リアクションをつけるといったルールだけ決めておきましょう。ここがミソです。

-----------------◇ルール◇-----------------
1. ドヤのタイミングは自由です!自分の成果をドヤりたい時につぶやいてください。
2. 他のメンバーのドヤにはスタンプ・コメント・リアクションしましょう!
3. 周りのメンバーのドヤを見つけて代弁するのも良いです◎
-----------------------------------------

最初はどんなことでも発信して問題ないので、まずは気軽につぶやきます。
コーヒー5杯とかどうでもいいとか思わない
とにかくリアクション芸人ばりにリアクションします。

すると見えてくる、社内メンバーの活躍。

Slack_-_infratop.png

うちのメンバーめちゃめちゃ手厚いサポートだなあ〜
「グッとくる」

そして、新しく見えてくる社内メンバーの活動やキャラクター。

Slack_-_infratop.png

何といってもdisplay: block;なので、存在感抜群になっていました。

他にも変わった部活の取り組みや、売り上げ、活動報告などなど、、、
働くメンバーが増えれば増えるほど、なかなか話せない相手も増えたり、後ろの席の人が何をやっているのか分からなかったりしましよね。ですが、メンバーの頑張りをドヤしたり、自分から発信することで
意外にも「自慢ばっかりするな」というネガティブな空気ではなく、「あの人すごい!」「負けていられない」といったポジティブな空気を生み出すことができました。

チャンネル一つで絶大なる効果

1. Doyaが話の種になる
2. 自分を出せて働けるから、仕事で何か問題があった時でも相談しやすい
3. 風通しの良い雰囲気

「なかなか評価されない...」のではなく、自分から評価されに行くという斬新奇抜なアイディアは意外と社内でも受け入れられます。もしかすると、社内の文化的に合う合わないはあるかと思いますが、興味を持った方は是非!まずは少人数から気軽に取り入れてみてはいかがでしょうか。個人的には、とてもおすすめです。そして、Doyaチャンネルでの成功体験があれば是非コメントいただけると嬉しいです。

最後に

是非一緒に働きたい方、we are hiring!です。
https://www.wantedly.com/companies/infratop3
餃子と共に、お待ちしております!

今後は技術的な記事も投稿して参ります。
最後まで読んでくださり、ありがとうございました。

Lispって何?[Lisp入門の入門]

$
0
0

Lispとは

Lispと聞いて、「は?それ何?」と思ったそこのあなた!是非この記事を読んでみてください!
Lispは、人工知能プログラムやWebアプリ開発に使われてきています。
オライリー社の動物本と呼ばれる本の中でも、Lispは非常に印象的ですよね。
目が5つある象みたいなキャラクターでおなじみ。宇宙から来た動物みたいで、一度見たら忘れないはず。

出典: Land of Lisp

独特なプログラミング言語なので、一度やってみる価値があります。
この記事で入門の入門の文法を学べます。読みながら一緒に書いていきましょう!

はじめに

Common Lisp入門・基本などの記事がありますが、ここでは入門の入門です。
インストールから始まり、基本的な文法を一緒に書きながら学んでいきましょう。
これまでC,Java,Ruby,JavaScriptなどを書いたことがありますが、Lispはホントに特殊です。
やってみての私の感想は2つあります。

①カッコがとにかく多い
最後の閉じカッコが5-6個続くこともあり、対応するカッコがわからなくなってしまいます。
②プログラマーになった気になれる
かなり古い言語であるからか独特です。人によって合う合わないの相性があると思います。でも、書いてる内にプログラマーになった気になれてオススメです笑

特徴

実は、Lispは多くの特徴をもっているんです。
①コンパイルなしで実行可能
プログラムをコンパイルすることなく、その場で実行することができます。
②信者がいる
Lispには、Lisperという「Lispを信奉し伝道する人、熱狂的なファン」が多くいます。それだけ魅力的なのだと思います。
③動的言語である
型を定義しなくてよいです。

概略

1958年 JohnMcCarthyによって考案 (彼は人工知能AIという言葉の生みの親です)
1960-現在 さまざまな方言が生まれる
1975年 Lispの方言Schemeが登場
1984年 Common Lispが登場(さまざまな方言を標準化しようとした
1995年 Paul Grahamと Rovert Morrisは CommonLispで Viawebというアプリを書いた。Viawebでは、ユーザは専門知識なしにWebブラウザを使用して独自の通販サイトを構築しホスト(サイトをIPアドレス上に置く)できた。
1998年 Yahoo!が Viawebを買収し、Yahoo!Storeとしてショッピングモールサービスを提供した。

Yahoo!StoreがLispで書かれていたのは驚きですよね。

準備しよう

インストール

Lispには主に3つの方言(Common Lisp, Schema, Emacs Lisp)があります。方言とは、Lispから派生してできた言語です。今回使うのは、Common Lispの処理系の1つである、CLISPです。何だかケンタッキーのフライドチキンみたいにサクサクしておいしそう笑

Macの場合

Homebrewからインストールすることができます。

$ brew install clisp

基本操作

コンソール画面

$ clisp : 起動

起動するとこんな感じで出てきます。
最後に出てくる[1]>から、コマンドやコードを入力していきます。

$ clisp
  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8

Welcome to GNU CLISP 2.49 (2010-07-07) <http://clisp.cons.org/>

Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2010

Type :h and hit Enter for context help.

[1]>

コードの入力

試しに数やコードを入力してみましょう。
「1」を入力すると1が、足し算のコード「(+ 1 1)」を入力すると計算結果2が返ります。
今度は、文字を入力しましょう。
「a」と入力すると、小文字aが大文字Aに変換され、「Aは値をもっていない」というエラーが起きます。
文字に値が代入されていない場合、文字そのままでは読み取ってくれないので、文字の前にシングルクォーテーションをつけます。
「'a」と入力すると、小文字aが大文字Aに変換・シンボルとして認識され、Aと返されます。

[1]> 1
1
[2]> (+ 1 1)
2
[3]> a

*** - SYSTEM::READ-EVAL-PRINT: variable A has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of A.
STORE-VALUE    :R2      Input a new value for A.
ABORT          :R3      Abort main loop
Break 1 [4]> 'a
A
Break 1 [4]> 'b
B

コマンドの入力

ターミナル上でコードを入力できますが、間違えたらもう一度打ち直さないといけない、終了したら再び使うことができないなどのデメリットがあります。
なので、テキストエディタで記述すると簡単です。
この時に1つ注意が必要です。
この記事では、ファイルの拡張子を「.lsp」としています。「.lisp」という拡張子でも大丈夫です。

(load "ファイル"): ファイルの読み込み

拡張子が.lspのファイルを読み込ませます。
試しにLISPという文字を表示させるlisp.lspを書きましょう。

lisp.lsp
(print 'lisp)

読み込ませた時の前半部分が出力結果、後半部分が読み込みに成功したか(成功したらT)を表示します。

[1]> (load "lisp.lsp")
;; Loading file lisp.lsp ...
LISP
;; Loaded file lisp.lsp
T
(bye): 終了

このコマンドでCLISPのコンソールを終了し、通常のターミナルに戻ります。

[1]> (bye)
Bye.

clispのコンソール画面を出してファイルを読み込むのは、面倒ですよね〜
そこで、ファイル読み込みまでを一括でやってみましょう。

一気にファイル読み込みまでやろう

1つのコマンドを打つだけです。非常に簡単ですね。

$ clisp ファイル名

実際にさきほどのコードで試してみましょう。

$ clisp lisp.lsp
LISP

コードを書いてみよう

四則演算

演算子(+,-,*,/)を前に記述する前置記法(ポーランド記法)になってます。

calculation.lsp
(print (+ 1 2)
(print (- 1 2)
(print (* 1 2)
(print (/ 1 2)

出力結果
3
-1
2
1/2

expt: べき乗

calculation.lsp
(print (expt 3 4))
(print (expt 33 33))

出力結果
81
129110040087761027839616029934664535539337183380513

述語

述語とは真(t)か偽(nil)を返す関数です。

equal: 2つの引数が等しいか

pre.lsp
(print (equal 1 1))
(print (equal 1 2))
(print (equal 'a 'a))

出力結果
t
nil
t

atom: アトムであるか

pre.lsp
(print (atom '(a b c d)))
(print (atom 'a))

出力結果
nil
t

numberp: 数値であるか

pre.lsp
(print (numberp 1))
(print (numberp 'a))

出力結果
t
nil

symbolp: シンボルであるか

pre.lsp
(print (symbolp 1))
(print (symbolp 'a))

出力結果
nil
t

zerop: 0であるか

pre.lsp
(print (zerop 1))
(print (zerop 0))

出力結果
nil
t

oddp: 奇数であるか

pre.lsp
(print (oddp 1))
(print (oddp 2))

出力結果
t
nil

evenp: 偶数であるか

pre.lsp
(print (evenp 1))
(print (evenp 2))

出力結果
nil
t

リスト: 0個以上の要素が()で囲われたもの

first, car: 最初の要素を返す

list.lsp
(print (first '(a b c d)))
(print (car '(a b c d)))
(print (car '(a b)))

出力結果
a
a
a

rest, cdr: 最初の要素以外を返す

list.lsp
(print (rest '(a b c d)))
(print (cdr '(a b c d)))
(print (cdr '(a b)))

出力結果
(b c d)
(b c d)
b

carとcdrについて

ここで、carとcdrのメモリ内部の仕組みについて見ていきます。
1個の箱に1個のデータが入っていて、2個の箱の組をコンス(cons)とします。
リスト(a b c)は3個のコンスから構成されています。
その1つ目のコンスを見てみましょう。
1番目の箱(car部)はデータ部といい、「a」という文字を指すポインタとなっています。
2番目の箱(cdr部)は次ポインタ部といい、リスト(b c)を指すポインタとなっています。
car_cdr.png

図1: リスト(a b c)のメモリ配置

car(カー)= Contents of Address part of Register
cdr(クダー)= Contents of Decrement part of Register
突然この2つの単語が出てきて、「これ何語だよ」と感じた方もいらっしゃるかもしれません。ちゃんと語源があるようです。
1954年のIBM 704という昔のコンピュータで、セルの前半部であるアドレス部を取り出すのがcarで、後半部であるデータ部を取り出すのがcdrだったようです。

second: 2番目の要素を返す

list.lsp
(print (second '(a b c d)))
(print (first (rest '(a b c d)))) ;; firstとrestの組み合わせでも表現できる

出力結果
b
b

third: 3番目の要素を返す

list.lsp
(print (third '(a b c d)))
(print (second (rest '(a b c d)))) ;; secondとrestの組み合わせでも表現できる

出力結果
c
c

fourth: 4番目の要素を返す

10番目の要素を取り出すtenthまで用意されているそうです。

list.lsp
(print (fourth '(a b c d)))
(print (third (rest '(a b c d)))) ;; thirdとrestの組み合わせでも表現できる

出力結果
d
d

cons: 2つの引数からリストを作る

1つ目の引数を、2つ目の引数であるリストの先頭に加えることでリストを作ります。

list.lsp
(print (cons 'a 'b))
(print (cons 'a 'nil))
(print (cons 'a ()))
(print (cons 'a '(b c)))

出力結果
(A . B) ;;真ん中にあるドットが両側の要素をつなげている(ドット対)
(A)     ;;nilはリストを終わらせる
(A)     ;;空リストはnilと同じ意味
(A B C)

list: 引数のリストを作る(3つ以上の引数でも可)

list.lsp
(print (list 'a 'b 'c))
(print (cons 'a (cons 'b 'c)))
(print (cons 'a (cons 'b (cons 'c 'nil))))
(print '(a b c))

出力結果
(A B C)
(A B . C) ;;ドットができてしまう
(A B C)
(A B C)

append: リストを連結する

consと似ていると思った方もいるかもしれません。
1つ目の引数に違いがあります。
基本的にconsは要素、appendはリストを取ります。

list.lsp
(print (append '(a b) '(c d)))

出力結果
(A B C D)

length: リストの長さを返す

list.lsp
(print (length '(a b c)))

出力結果
3

関数

defun: 関数の定義(define function)

Lispでの関数定義はdefunで行います。
試しに階乗(例: 5!=5*4*3*2*1)の関数factを書いてみましょう。

defun.lsp
(defun factorial(n)
  (if (= n 0) 1
    (* n (factorial (- n 1)))))
(print (factorial 5))

出力結果
120

最後に

ハッカーと画家

この記事のためにハッカーと画家を全部読んできました。
簡単にまとめると、筆者のPaul Grahamはこの本で「Lisp最高」と言っています。Lispというマイナーな言語で「Viaweb」を書いたお陰で、競合と差別化を図り成功できたのです。そして、シリコンバレーで「Y Combinator」というシードアクセラレータ(スタートアップ企業を育成する会社)を立ち上げ、AirbnbやDropboxなどに投資し有名になりました。

Lispのマクロが非常に強力だと書いてあったので、次回はマクロについて勉強して書きます。
ここまで読んでいただき、ありがとうございました。

出典: ハッカーと画家

最後に、ハッカーと画家で引用されていたEric Raymond氏の言葉(「How To Become A Hacker」より)と、筆者のPaul Graham氏の言葉をお借りして締めたいと思います。

Lispは、それをものにした時の素晴らしい悟り体験のために勉強しましょう。この体験は、その後の人生でより良いプログラマになる手助けとなるはずです。たとえ、実際にはLispそのものをあまり使われなくても。
引用: 「How To Become A Hacker」 - Eric Raymond

LispのプログラムコードはLISPのデータオブジェクトからできている。それは、ソースコードは文字列でできていて、文字列は言語でサポートされている、というようなつまらない意味じゃない。Lispのコードは、ひとたびパーサによって読まれたら、あなたが解析できるデータ構造になるんだ。
引用: 「ハッカーと画家」 - Paul Graham

参考文献

「Land of Lisp」 - O'REILLY
「ハッカーと画家」 - Paul Graham
「How To Become A Hacker」 - Eric Raymond


It's 書time! [caravanより短いコードで"書き初め"してみた]

$
0
0

書き初めwebアプリ 『 It's 書time! 』

Untitled.gif

こちらのリンク先にあるアプリで、新年の抱負を思い思いに書いてみてください!

↓↓↓↓↓↓↓↓
『 It's 書time! 』
↑↑↑↑↑↑↑↑

書き初めを自分でも実装してみたい方は以下の記事を見てください。
※本当は保存して投稿できる機能をつけたかったのですが、時間と実力の都合上無理でした....





はじめに

突然ですが、日々パソコンを使って文字を書いている皆さんは最後に書き初めをしたのはいつでしょうか?
もともと筆で字を書くことが好きな僕は久々に書き初めをやってみたい!(クリスマスよりもお正月)と思って、今回は書き初めをProcessing.jsというjsでHTMLに実装してみました。

目標

リアルな筆字をできるだけ理解しやすく、コード記述量を少なくしたwebアプリケーションを実装したい。





実装

環境

  • macOS Mojave
  • HTML5
  • css3
  • Processing.min.js v1.4.8

そもそもProcessing.jsとは

Processing.jsは画像、各種データ可視化、動的コンテンツなど描画用に設計されたプログラミング言語であるProcessingのJavaScript移植版である。 Adobe FlashやJavaアプレットを用いることなくウェブブラウザ上で動画、ゲームなどが実装できる。(参照:Processing.js - Wikipedia)

綺麗な動的なデザインをやってみたいとかプログラミング初学者にオススメな言語「Processing」の派生みたいです。

color.gif
(https://www.youtube.com/watch?v=zwWZox1PKw8)
こんなアニメーションができます!





HTML

それではこれからコードを書いていきます。

まずはじめにHTML要素で書き初めに必要な「下敷き」「半紙」を追加します。

sho-time.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>sho-time</title>
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
  <link rel="stylesheet" type="text/css" href="sho-time.css">
</head>

<body>
  <div class="shitajiki">
    <div class="title">
      <h1>It's <ruby><rt>show</rt></ruby> time!</h1>
    </div>
    <div class="hanshi">
    </div>
  </div>
</body>
</html>





css

先程のHTML要素に対してcssで装飾します。

sho-time.css
/*リセットcss*/
* {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}

h1 {
  font-size: 40px;
}
rt {
  font-size: 20px;
}
.title{
  padding: 100px 0 10px;
}
.shitajiki{
  background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
  height: 800px;
  text-align: center;
}
.hanshi {
  width: fit-content;
  margin: 0 auto;
  border: 10px solid black;
}





Processing.jsの導入

ここで、Processing.jsをHTMLに導入をします。
まず 公式サイトからprocessing.min.jsをダウンロード します。(「processing.js」と「processing.min.js」がありますが、容量の軽い後者で今回は進めます。)

ダウンロードしたら今回使うファイルを「sho-time」というフォルダにまとめます。フォルダ構成は以下のとおりです。

sho-time
1 sho-time.html
2 sho-time.css
3 Processing.min.js

Processing.min.jsは、Processingをjsの記述でも扱えるようにしたものです。
また、今回はProcessingを直接書くことで、jsより(個人的な感覚として)シンプルなコードにします!

ファイルを移し替えたら「sho-time.html」ファイルにProcessingのコードを書いていきます。

sho-time.html
.
.
  <link rel="stylesheet" type="text/css" href="sho-time.css">
  <script src="processing.min.js"></script>  ←ここも追加
</head>
.
.
    <div class="hanshi">
      <script type="application/processing">
        //=====「各変数の定義」=====
        float lineWeight = 0.6;
        int i;
        int count = 1;
        String picture;
        int R = 20;        //しずくの大きさ調整用
        int A = 3;         //しずくの丸み調整用

        //=====「初期設定」=====
        void setup(){
          smooth();
          frameRate(60);
          size(1170, 390);
          background(250);
          stroke( 54, 54, 54 );
          fill(54, 54, 54);
        }

        //=====「毎回処理される箇所」=====
        void draw() {
          if (mousePressed) {
            strokeWeight( lineWeight );
            mouseDragged();
          }
        }

        //=====「始筆」=====
        void mousePressed() {
          strokeWeight(2);
          pushMatrix();
          translate(mouseX, mouseY + 3);
          rotate(radians(-150));
          beginShape();
          for (int theta = 10; theta < 361; theta++) {
            float r = 1 / (A * sin(radians(theta)/2)+1);
            vertex(R * r * cos(radians(theta)), R * r * sin(radians(theta)));
          }
          endShape(CLOSE);
          popMatrix();
          redraw();
        }

        //=====「運筆」=====
        void mouseDragged() {
          for (int i = 0; i < 10; i++) {
            float x = random(i);
            float y = sqrt(( sq(i) - sq(x) ));
            line(pmouseX - x, pmouseY + y, mouseX, mouseY);              //右下&左上
            line(pmouseX - x, pmouseY - y, mouseX, mouseY);              //右上&左下
            line(pmouseX + x, pmouseY + y, mouseX + x, mouseY + y);      //右下
            line(pmouseX - x, pmouseY - y, mouseX - x, mouseY - y);      //左上
            line(pmouseX + x, pmouseY - y, mouseX + x, mouseY - y);      //右上
            line(pmouseX - x, pmouseY + y, mouseX - x, mouseY + y);      //左下
            line(pmouseX, pmouseY , mouseX, mouseY);
            line(pmouseX, pmouseY + y, mouseX, mouseY);
          }
        }

        //=====「筆先を整える」=====
        void mouseReleased() {
          lineWeight = 0.6;
        }

        //=====「キー操作」=====
        void keyPressed() {               //"スペース"を押したら「リセット」
          if(key == ' ') {
            background(255);
          }
          if(key == 'r') {                //"r"を押したら墨が「朱色」になる
            stroke( 235, 97, 1 );
            fill( 235, 97, 1 );
          }
          if(key == 'b') {                //"b"を押したら「墨色」になる
            stroke( 54, 54, 54 );
            fill(54, 54, 54);
          }
        }
      </script>
      <canvas id="canvas"></canvas>
    </div>
.
.
.

ここで、jsは別のファイルに書いてコードをまとめたほうがスマートじゃん、と思った方。そのとおりです!
ですが、今回は時間の都合上HTMLファイルに直接書かせていただきます。





以上で書き初めアプリをHTMLに実装することができました!

今回はいかにリアルな筆字を再現するかに時間を費やしてしまって、webアプリケーションの実装まで解説することがありませんでしたが、ぜひ今後もProcessingで遊んでみてください!





最後までお付き合いいただきありがとうございました。

参考文献

【LINEBot】クリスマスは1人映画でええじゃないか

$
0
0

クリスマスって辛くないですか?

辛いですよね。僕は辛いです。え?別に辛くない?、あ、そうですか。
外は寒いし、どこに行ってもカップルや家族づれで、お一人様お断りみたいな雰囲気出てますよね。出てない?いや、出てますよ。
そんな日はさっさと家に帰って映画です。

映画っていいですよ

家で1人暇な時。僕はよく映画を観ます。
今までに500本以上の映画を観て、時間を無駄に浪費してきました。多くの映画に感動させられました。
映画は日常を忘れ、非日常に没頭できる素敵なものです。

だから、クリスマスは家で映画を観ましょう。

何の映画を見ればいいか分からない

分かります。映画って短くても1時間、長いもので3時間以上もありますからね。どうせなら面白い映画を観たい。
でも映画サイトのレビューは当てにならないし、周りに詳しい人はいない。
僕に聞いてくれれば、いくらでもおすすめの映画を教えてあげられるのですが...

課題

・映画は観たい、でもおもしろい映画が分からない。
@Ndo に聞きたいけど、話しかけづらい(そんなことはありませんよ〜)。
・もっと気軽に知りたい(はい、ここ大事!)。

【解決策】LINEBotに聞く

やっとここまでたどり着きました。話の枕が長すぎる!
@NoharaMasato が先にLINEBotについてQiitaに記事を書いているので、完全に二番煎じ。
ですが、クリスマスには僕のBotの方が活躍します。(たぶん)
まずはお試しあれ👇

参考

「Web APIとは? (LINE bot API・グルナビAPI)」
https://qiita.com/NoharaMasato/items/6fb1ac277c965905e019

「今更ながらRails5+line-bot-sdk-ruby+HerokuでLineBot作成してみたら、色々詰まったのでまとめました。」
https://qiita.com/y428_b/items/d2b1a376f5900aea30dc%20

「【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】」
https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39

作ってみましょう

参考サイトを見れば全体の80%は事足ります。
映画情報を取得するための手順は参考にしてください。

1. LineBotAPIの設定

参考サイトを参照。
https://qiita.com/NoharaMasato/items/6fb1ac277c965905e019
https://qiita.com/y428_b/items/d2b1a376f5900aea30dc%20

2. 映画情報をAPIで取得

映画情報を取得するために色々調べたのですが、海外のTheMovieDataBase(TMDB) https://www.themoviedb.org/ と言う映画データベースサイトがAPIを公開していて、それが利用できそうです。使い方も下記のサイトに詳しく載っていました。

https://developers.themoviedb.org/3
https://developers.themoviedb.org/4

まずはユーザー登録をして、サイトの右上のアイコン/リストから自分の映画リストを作成します。
image.png

次に、画面右上の自分のアイコン/設定/APIからAPIキーを生成します。あとで使うので、メモをとるか、その画面を開いておきます。

image.png

3. Railsの実装

Railsアプリケーションの作成

$ rails new film_line_bot

$ cd film_line_bot
Gemfile
gem 'line-bot-api'

パッケージのインストール

$ bundle install

動作の確認

$ rails s

http://localhost:3000 に接続できればOK。

Herokuへデプロイ

下記の記事を参考にしてherokuへデプロイします

「【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】」
https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39

Herokuへログイン

$ heroku login

Herokuにアプリを作成

$ heroku create 好きなアプリ名

git repositoryを作成し、Herokuにデプロイ

$ git init
$ git add .
$ git commit -m "init"
$ git push heroku master

Filmモデルの作成

Filmモデルにenumを実装

$ rails g model Film genre:integer

$ rails g migrate

TMDbのAPIドキュメントを参考にFilmモデルのgenreカラムにenumを適用する

film.rb
class Film < ApplicationRecord
    enum genre: {
        Action: 28,
        Adventure: 12,
        Animation: 16,
        Comedy: 35,
        Crime: 80,
        Documentary: 99,
        Drama: 18,
        Family: 10751,
        Fantasy: 14,
        History: 36,
        Horror: 27,
        Music: 10402,
        Mystery: 9648,
        Romance: 10749,
        SF: 878,
        TVMovie: 10770,
        Thriller: 53,
        War: 10752,
        Western: 37,
    }
end

コントローラーの作成

$ rails g controller linebot
linebot_controller.rb
class LinebotController < ApplicationController
    require 'line/bot'  # gem 'line-bot-api'
    # callbackアクションのCSRFトークン認証を無効
    protect_from_forgery except: :callback
    def client
        @client ||= Line::Bot::Client.new { |config|
            config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
            config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
        }
    end

    def callback
        body = request.body.read

        signature = request.env['HTTP_X_LINE_SIGNATURE']
         unless client.validate_signature(body, signature)
           error 400 do 'Bad Request' end
         end

        events = client.parse_events_from(body)

        # ここでlineに送られてきたイベントを検出
        # messageのtext: 指定すると、返信する文字を決定することができる
        # event.message['text']で送られてきたメッセージを取得することができる
        events.each {|event|
            @genre = event.message['text'].gsub(" ", "") #ここでLINEで送った文章を取得。スペースなどの空白はAPI通信の妨げになるので削除
            uri = URI.parse("https://api.themoviedb.org/4/list/自分の映画リストid?api_key=TMDbのAPIキー") #自分の映画リストにアクセスするためのuriを定義
            json = Net::HTTP.get(uri) #NET::HTTPを利用してAPOを叩く
            results = JSON.parse(json) #返ってきたjsonデータをrubyの配列に変換
            movies = []
            if @genre == "クリスマス" #lineから送られてきたメッセージがクリスマスかどうかで条件分岐
               results['total_pages'].to_i.times do |f|
                 uri = URI.parse("https://api.themoviedb.org/4/list/自分の映画リストid?api_key=TMDbのAPIキ-&page=#{f+1}") #リストから20件しか持って来れないので、ある分だけ繰り返し叩く
                 json = Net::HTTP.get(uri) #NET::HTTPを利用してAPOを叩く
                 results = JSON.parse(json) #返ってきたjsonデータをrubyの配列に変換
                 movies += results['results'] #全映画リストの情報をハッシュの配列で取得
               end
               lists = movies #分かりやすいように入れ替え
            else
               results['total_pages'].to_i.times do |f|
                 uri = URI.parse("https://api.themoviedb.org/4/list/映画リストid?api_key=TMBdのAPIキー&page=#{f+1}")
                 json = Net::HTTP.get(uri) #NET::HTTPを利用してAPOを叩く
                 results = JSON.parse(json) #返ってきたjsonデータをrubyの配列に変換
                 movies += results['results'] #全映画リストの情報をハッシュの配列で取得
               end
               genre_id = Film.genres[@genre] #予めジャンルをenumで列挙型にしておき、送られてきたString型のジャンルを割り当てられた整数に変換する
               lists = movies.select{|x|  x["genre_ids"].include?(genre_id.to_i)} #genre_idが合致する映画だけをフィルタリング
            end
            list = lists.sample # リストから任意のものを一つ選ぶ
            uri = URI.parse("https://api.themoviedb.org/3/movie/#{list['id']}?api_key=TMBdのAPIキー&append_to_response=videos") # listから選んだ映画ついての情報を取得
            json = Net::HTTP.get(uri) #NET::HTTPを利用してAPOを叩く
            results = JSON.parse(json) #返ってきたjsonデータをrubyの配列に変換
            # 映画の情報
            if results['videos']['results'] != nil #古い作品は予告動画が見つからないこともあるため条件分岐
                video = "https://www.youtube.com/embed/#{results['videos']['results'][0]['key']}" #映画の予告動画のurlを生成
            else
                video = "見つかりませんでした"
            end
            movie_title = list['original_title'] #映画のタイトル
            movie_score = list['vote_average'].to_s #float型をstring型に変換

            response = "【タイトル】" +   movie_title + "\n" + "【ジャンル】" + @genre + "\n" + "【スコア】" + movie_score + "\n" + "【Youtube】" + video
            case event #case文 caseの値がwhenと一致する時にwhenの中の文章が実行される(switch文みたいなもの)
            when Line::Bot::Event::Message
                case event.type
                when Line::Bot::Event::MessageType::Text
                  message = {
                     type: 'text',
                     text: response
                  }
                  client.reply_message(event['replyToken'], message)
                end
            end
        }
        head :ok
    end
end

自分の映画リストのidはリストの詳細ページを表示した時のURL末尾に書いてあります。

ルーティング

routes.rb
post '/callback' => 'linebot#callback'

環境変数の設定

$ heroku config:set LINE_CHANNEL_SECRET=自分のChannelSecret
$ heroku config:set LINE_CHANNEL_TOKEN=自分のアクセストークン

Herokuへデプロイ(2回目)

この辺りで詰まったら参考サイトへ👇
https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39

$ git add .
$ git commit -m "update"
$ git push heroku master
$ heroku run rails db:migrate #本番環境でのマイグレーション

この時にhttpsのURLが与えられるのでLINE bot APIのWebhook URLに追加します。
image.png

参考サイトhttps://qiita.com/NoharaMasato/items/6fb1ac277c965905e019 
にもあるように

$ heroku logs -t

でlogを見ながらデプロイやデバッグをすると何が起こっているか分かりやすいです。

うまくいくとこんな感じ

IMG_0647.PNG

今回はジャンルを英語でしか(クリスマスは例外)受け取れませんでしたが、他のAPIと組み合わせれば改良できそうです。おすすめの方法を知っていたらコメントで教えて頂けると嬉しいです。

ちなみに

クリスマスに観たい僕一押しの映画はホームアローンです。いつ観てもお腹が壊れそうなくらい笑えます。

ではでは、ごきげんよう。

gem 'fall_snow'を使ってRailsアプリケーションに雪を降らせる

$
0
0

はじめに

今回のAdventCalendar言い出しっぺのtatsukichiです。

DMM WEBCAMP AdventCalendarの最終日を担当させていただきます。

クリスマスシーズンでいろいろなアプリケーションで雪を降らす演出が年々増えてきていますね。
みなさんが使っているアプリケーションでもよく見かけませんか?

そんな演出を手軽にアプリに導入できたらクリスマスも盛り上がるのではないかと思い、
「雪を降らせるgem」を作ってしまいました。

みなさんの手がけるアプリケーションでも使っていただけたら幸いです。
それでは、簡単ですので使ってみてください。

どんなもの?

たった6行(最小)のコードで雪を降らせることができます。
本当にそれだけです。

こんなものができあがります。
fall_snow.gif

公式

https://rubygems.org/gems/fall_snow

早速やってみる

それでは雪を降らせていきましょう。

1行目

Gemfile

gem 'fall_snow'

bunble installしましょう。

2~5行目

app/assets/stylesheets/application.css

@import 'fall_snow/style.css';
 body{
     background-color: #142744;
 }

cssを読み込みます。
bodyに暗めの色を入れて雪が目立つようにしていますが、お好みで調節してください。

6行目

雪を降らせたいviewファイルに。

<%= fall_snow %>

bodyタグ内であればおそらく動くでしょう。

以上

このような感じでサクッとできてしまいました。
皆さんのRailsアプリケーションでも冬のうちに降らせてみてはいかがでしょうか?

ちなみにこれだけで雪は降るのですが、これ以上に機能は今の所ありませんw
今後の機能拡充に期待しましょう。

gemを作成した話も盛り込もうと思ったのですが、ボリュームが大きくなりそうだったので
いいねが100超えたら書こうと思います。

ありがとうございました。