20190327のRubyに関する記事は14件です。

誰でもプログラムを書けるようになる方法が発見される!なんだこれは?

プログラムを書けるようになる方法を見つけた!なんだこれは?

眼鏡のフレーム端を耳の横に掛けたらなぜか書けるようになった。なんだこれは?
このやり方をやれば誰でもプログラムが書けるようになると思います。頭も良くなった!と思う

プログラムを挫折した経験がある方一度お試しあれ!
15.png
このように耳の横にフレーム端を掛けてやる

自己責任で
メガネのフレームを少し曲げる必要があります。破損しても自己責任で
個人差があると思います。人によってできない人もいると思います。
以上自己責任で試してみてください。

用意するもの
必須
メガネ (耳の横にかけれるもの フレームの横幅が広いものがいいです)
紫外線防止コーティング (眼鏡屋さんで3千円くらいでやってもらえます)

お好み
片方は非球体の方がいい (目が疲れない)
黒縁じゃないほうがいい。(縁が太いと視野が狭くなる)

やり方
フレームの端を耳の横に掛ける
目をつぶり、頭の周りに衛星が回転するイメージをする

メガネをかけていない時 20回ぐらいの回転が限度 疲れてしまう
メガネをかけている時 10回ぐらいの回転が限度 スムーズに回らない うまく回らない
メガネの端を耳の横にかけている時 スムーズに回る 無限に回ることを確認する 

メガネを耳の横にかけてイメージの衛星が無限に回ったら、プログラムが書けるようになっていると思います。

個人差があるため、できない人もいるかもしれません。またある程度この掛け方で生活していく必要があります。普段からイメージトレーニングをやってください。

プログラムが書けるようになったかを確かめてみる。

メガネのこの掛け方をしてプログラムの学習サイトで授業を受けてみましょう
初心者はJavaScriptをお勧めします。

https://dotinstall.com/lessons

途中、掛け方を元に戻してみる。反応を比べてみる

プログラム書けるようになりましたか?理解できるようになりましたか?
プログラムが書けるようになっていた、理解できるようになっていたら、
あなたは今日からプログラマーです。

おめでとう!

近眼じゃない人は?メガネがないとできないの?

近眼じゃなくてもできると思います。近眼になる必要はないです。
今はメガネは必須だと思いますが、近い将来メガネがなくてもできる発明品が出てくるかもしれません。

眼鏡屋さんでメガネのフレームを買う。横幅の広いフレームを選ぶこと
最初についているプラスチックの度の入ってないレンズ
このレンズの上に紫外線防止コーティングを追加してもらう。
3千円くらいでやってもらえます。

これでできると思いますが。個人差もあるので自己責任で
眼鏡を買う前に、眼鏡屋さんで耳の横に掛けて衛星が無限に回るイメージになるか試してみましょう。無限に回るイメージができたらプログラムは書けていると思います。
まずは安いメガネで試すとよいでしょう。

初心者はどのプログラム言語から始めたらいいのか?

C# .NetCore WindowsFormをお勧めします。(画像参照)
GUIなのでわかりやすい。書籍もたくさん出ています。(Visual C# 2018逆引き大全 555の極意はお勧めです)

C#は動画学習サイトUdemyで学習できます
https://www.udemy.com/jp/

(iOSやアンドロイドをやりたい人はその言語を最初に初めてもいいと思います)

そのあと

ゲーム Unity C#
Web Asp.net MVC C#
機械学習 Tensorflow for C#
Iot Windows 10 IoT Core C#
iOS SwiftかObjective-C
Android KotlinかJava

をやるとよいでしょう
csforms02.png

便利ツール

タスク管理ソフト
https://trello.com/ja

オンラインチャット Slack
https://slack.com/intl/ja/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bcryptがインストールしてるのに出来てないと出るエラーと対策

概要

Railsチュートリアルを進行中に躓いたbcryptに関するエラーと対策を記載していきます。

内容

まずは、インストール手順を説明します。

Gemfileに追加したいライブラリとそのバージョンを追加する。

Gemfile
source 'https://rubygems.org'

gem 'rails',        '5.0.0.1'

gem 'bcrypt',         '3.1.11' #追加
.
.
.

そしてコマンドを実行してインストール

$ bundle install

ここまでは、問題ない。rails test などのコマンドを実行すると
bcryptをインストールしたにも関わらず、インストールしてないと出てくる。

You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install
・
・
・

チュートリアル進行中にこのエラーで嵌る人は結構いるみたいです。

対策

色んな方のQiitaを参考にして改善に試みてみましたが、全く原因も対策も分からず
次の日になってbundle installしてrails testを実行してみると正常に動作しました...。

ただ、IDEはCloud9を使用していて、30分でサーバーが切れるので
サーバーの再起動などが関わっている可能性もあるので実行してみる価値はあると思います。

Weblickの再起動などの対策方法もあるみたいなので調べてみてください。
参考URLに自分が実行していなかった方法が記載されていたので、是非試してみてください。

参考URL

https://qiita.com/ricoirico/items/f3600abea6eb221a62df

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

もそ、プログラミングを学ぶ【Ruby & Rails復習編】〜本引数と仮引数の違いってなに?〜

相変わらずエラー問題に苦戦中の もそ。
やさしい先輩たちから「この部分はRubyの復習をしてみるといいかも」というアドバイスをもらい早速Rubyを復習しています。

すると早速、
「げっ、ここって意味がよくわからなくてスルーしたところじゃん...」
となっていたところが出てきました。
Railsを学んだのでなんとな〜く分からんでもないのですが、理解を深めるべくリベンジしました。

今日はそんなRubyの本引数と仮引数について書いていきます。

引数について

まずは引数(ひきすう)についておさらいします。

引数は、プログラムでメソッドなどに渡すことのできる値です。
変数の値には使える範囲が決まっていて、これをスコープと呼びます。
たとえば、あるdef~end内で定義した変数は、ほかのメソッドでそのまま使うことはできません

引数を使うことでメソッドの外にある変数、つまりスコープ外にある変数をメソッドの中で使うことができます。

そして引数には、メソッドを呼び出す部分に記述する本引数とメソッドを定義している部分に記述する仮引数の2つがあります。
コードを実際に見てみましょう。

 def multi(input)()内は仮引数
    puts input * input
   end

 puts "何か数字を入力してください"
   input = gets.to_i

 multi(input)()内は本引数

こんな感じです。
コードが読み込まれて処理される順番は③→②→①となります。
③と②でスコープを定義して、引数を使うことで①に変数を渡しています。

ひとつポイントなのが、仮引数の名前は本引数と違ってもいいということ。
先ほどのコードの流れは、
③のinputという本メソッド内に②gets.to_iという数値を入れて、①で結果を表示する 
というものでした。
これは方程式と同じで、仮の名前に数値を代入しているので、「x=aとして x+1*10=aを解きなさい」という数学の問題と同じような感覚です。

なので先ほどのコードは、

def multi(number)
    puts number * number
    return number * number
  end

  puts "何か数字を入力してください"
  value = gets.to_i

  multi(value)

と表すことができます。

スコープの範囲に注意

次に、このようなコードで考えてみましょう。

 def rename(name)
    name = "もそと#{name}"
   end

 name = "ポチ"
   rename(name)
   puts name

このコードを実行すると、出力結果は「ポチ」だけになります。
では、「もそとポチ」と出力するにはどうすればいいでしょうか?

このコードのポイントは、何回も出てきている"name"の記述。
同じ名前が出てきているのですが、実は①と②で中身が違います

...意味分かんないですよね。私もわからないです。

というわけで、LINEでポチに泣きつきました。

無題1160 3.jpeg

「え、これ外側で名前変えてるだけだからでしょ(即答)」

ここでポチのいう外側は②の部分。

 def rename(name)
    name = "もそと#{name}"
   end

 name = "ポチ"
   rename(name)
   puts name

②のコードでは、
「name=ポチとして、rename(ポチ)にします。ではnameを出してください」
と記述しています。
最初にname=ポチと言っているので、ここで指すnameの中身は「ポチ」になります。

では①の部分はどうでしょうか。
「rename(name)を定義します。name=”もそとポチ”です」

よくよく考えてみてください。
これ、②と①でnameで指示しているnameの中身が違いますよね。

なので出力結果を「もそとポチ」にするには、

 def rename(name)
    name = "もそと#{name}"
   end

 name = "ポチ"
   name=rename(name)
   puts name

このように記述すればよいのです!なるほど〜!

--
ふわふわしていた引数の考え方が、やっときちんと理解できた...気がします(理解したとは言っていません)
配列と本引数/仮引数はややこしいとポチも言っていたので、ここは焦らず少しずつ理解を深めていこうと思います。
果たして理解できる日は来るのか...もその修行は続く。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

数分経ってもCloud9でherokuコマンドが使えるようにする

環境

Railsチュートリアルの推奨環境

Cloud9のIDE
heroku

原因


Cloud9のIDEは30分アクセスが無いとサーバーが落ちるようになっている。

(恥ずかしながら全く知りませんでした。)

また、ターミナル毎に秘密鍵やherokuが設定されているため
サーバーを立ち上げ直したり、別ターミナルを開くと登録されていない状態に戻る

$ heroku create
bash: heroku: command not found

Railsチュートリアル 第1章 1.5.1 herokuのセットアップ で紹介されている通り
cloud9上でherokuをインストールする事が出来ますが、毎回無駄な処理が必要となる。

source <(curl -sL https://cdn.learnenough.com/heroku_install)

さらに詳しい原因については参考URLをご覧ください。

現在の自分の知識では、理解が追いつかなかったので説明が出来ません。

日々精進をして理解出来るようになりたいと思います。

対策


ターミナル起動時にPOSTが自動で設定されるようにする必要がある。
$ vi ~/.bashrc

# 以下をファイルの最後に追記

PATH=/usr/local/heroku/bin:$PATH

# こちらの記載でも可

source $HOME/.profile > /dev/null

参考URL

https://qiita.com/tathuhi10/items/281c0d8e03438e0e752c

https://railstutorial.jp/chapters/beginning?version=5.1#sec-heroku_setup

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不足分をnilで埋めた配列を取得

String#split などで配列を作成する場合、指定した長さより実際の長さが短いことがある。

長さ5の配列を作りたい
"a,b,c,d,e,f,g".split(',', 5)
#=> ["a", "b", "c", "d", "e,f,g"]

"a,b,c,d".split(',', 5)
#=> ["a", "b", "c", "d"]   # 足りない…

#first#take を使っても数を増やすことはできない。

"a,b,c,d".split(',', 5).first(5)
#=> ["a", "b", "c", "d"]

"a,b,c,d".split(',', 5).take(5)
#=> ["a", "b", "c", "d"]

Array#values_at なら増やせる。

"a,b,c,d".split(',', 5).values_at(0...5)
#=> ["a", "b", "c", "d", nil]

破壊的に拡張したいなら、 Array#[]= で範囲外に代入する手がある。

"a,b,c".split(',', 5).tap { |a| a[5-1] = a[5-1] }
#=> ["a", "b", "c", nil, nil]

nil でなくデフォルト値があるのなら、 #zip でも良いかもしれない。

[*"1".."5"].zip("a,b,c".split(',', 5)).map { |v0,v| v || v0 }
#=> ["a", "b", "c", "4", "5"]

|| を使う場合は、偽の値( nilfalse )が既に入っていても書き換えてしまうので注意。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] 配列、eachメソッド

eachメソッド

今回は配列内に共通の処理を行うときに使うeachメソッドを説明していきたいと思います。

配列.rb
example = ["ゆーいち", 25, "エンジニア"]

例えばこのような配列があったとします。配列内の一つの要素を取り出す場合はexample[0]のように番号を指定してあげるのでした。
もしここら辺がわからないという場合はこちらをご確認ください。

今回は配列内全てに対して処理を行います。

eachメソッドの定義

それでは配列内の全ての要素を順番に取り出し表示するという処理を書いていきます。

配列.rb
example = ["ゆーいち", 25, "エンジニア"]

example.each do |ex|
  puts ex
end
# =>ゆーいち
  #25
  # エンジニア

これで配列内から要素を順番に取り出しそれぞれに対してputsという処理をする事ができました。

まず定義の仕方としては
配列名.each do |変数|
  処理
 end 

このように定義します。
このとき変数名はなんでも構いません。
言い方を変えるとこの処理は、配列内の0番目の要素を取り出し変数にその要素が代入され処理をする。次に1番目の要素を...
この作業を配列内に要素がなくなるまで繰り返す
と言ったほうがわかりやすいかもしれません。

配列内に処理をする要素がなくなった時点で終了します。

上の例でいうと、まず0番目の要素である"ゆーいち"が変数exに代入されex = "ゆーいち"の関係になりputs exなので"ゆーいち"が表示されます。次に25が取り出され...と言った感じですね。

each_with_indexメソッド

少し発展的なものとしてeach_with_indexメソッドも紹介していきます。
eachメソッドと処理の仕方は変わりませんがこのメソッドは取り出した要素の番号をつける事ができます。
例えば上の例で
  [0]ゆーいち
  [1]25
  [2]エンジニア
と出力したい場合

配列.rb
example = ["ゆーいち", 25, "エンジニア"]

example.each_with_index do |ex, index|
  puts "[#{index}]#{ex}"
end
# =>[0]ゆーいち
  #[1]25
  # [2]エンジニア

これで出力できました。変わったところは変数が一つ増えていますね一つ目の変数には配列内の要素が、二つ目には0から始まる番号を定義しています。

最後に

今回はeachメソッドについて説明していきました。例では要素が3つしかなかったので簡単でしたが、多くの要素を持っている配列だと一つ一つ取り出して行くのは無理がありますね。そういったときにeachメソッドを使いましょう。

最後まで読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

while文による繰り返し (Ruby)

はじめに

初学者のアウトプット記事です。

while文によるループ処理

プログラムを終了させず同じ処理をずっと繰り返すループ処理を行う方法はwhile文の条件式をtrueにしておくこと。これでwhileが繰り返すための条件が常に真となり、永遠にwhile内の処理が繰り返される。

while true do    
    # 条件式がtrueになることでプログラムは終了せず繰り返し処理が行われる
    ## 処理
  end

しかしこれでは無限ループとなり、処理が終わることがない。ループの処理から抜けるためにはexitメソッドを呼び出す。

while true do
    exit  # ここのexitでプログラムが終わる
    puts "こんにちは" # これは呼ばれない
  end

exitメソッドはプログラム自体を強制終了させるのでexitメソッド以降の処理は行われない。

while true do
    input = gets.chomp         # ユーザーの入力をinputに代入
    if input == "exit"     # exitと入力されたら
      exit                     # exitメソッドによりプログラムを終了
    end
    puts "ループ中"
  end

これはexitと入力するまでずっと処理を続けるプログラム。whileの条件式をtrueにすることでexitを呼ぶまで同じ処理を行い続ける。

1〜10まで順に足し合わせて出力するプログラム

  number = 1        
  while number <= 10 do #numberが10以下なら繰り返す
    puts number      #number 出力
    number += 1      #出力後numberに+1して繰り返し
  end

訂正修正ありましたらご指摘お願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecでRedmineのsudoモードをテストする

# spec/support/time_helpers.rb
RSpec.configure do |config|
  # travel_toを使えるようにする
  # https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to
  config.include ActiveSupport::Testing::TimeHelpers, type: :feature
end
RSpec.describe 'sudoモードが有効になっているとパスワードを入力しないと操作ができない', type: :feature do
  include ActiveSupport::Testing::TimeHelpers

  background do
    # sudoモードを有効にする
    allow(Redmine::SudoMode).to receive(:enabled?).and_return(true)
  end

  scenario '操作しようとしたらsudoモードの期限が切れていたのでパスワードを再入力して操作を完了する' do
    visit パスワードの再入力が求められるフォームがある_path
    fill_in 'パスワードの再入力が求められるフィールド', with: 'なにか'

    # Redmine::SudoMode.timeoutの値はデフォルトだと15分に設定されている。
    # そのため20分後に操作をしてパスワードの再入力が求められることを確認する。
    travel_to(20.minutes.from_now) do
      click_on '送信'
      expect(page).to have_content('この操作を続行するにはパスワードを入力してください')
      fill_in 'パスワード', with: 'password'
      click_on '送信'
    end

    expect(page).to have_content('操作が成功した際に表示されるであろう文言')
  end
end

参考

https://github.com/redmine/redmine/blob/f0d579adebfe3c5da93135fbaa3d9ec503d06855/test/integration/sudo_mode_test.rb#L9
https://github.com/redmine/redmine/blob/f0d579adebfe3c5da93135fbaa3d9ec503d06855/test/integration/sudo_mode_test.rb#L197-L200

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

大学データとログイン機能gem deviseを組み合わせる

ここ3週間の一区切りとして、今週は
scaffoldで作成した大学データと、gemのdevise、Bootstrap等を組み合わせる。

前回までのdeviseを使ったサイトに、大学データを一部コピーして、作成する予定だったが、
Active Record等がエラーを起こしたため、今回はrails newから作成した。
やってることの多くは以前のと重複しているので、詰まった所、初めてな所を、今日は書く。

やった事

  • #Railsの命名規則(単数形と複数形)
  • DBのカラム定義を後から変更
  • render partial: 部分テンプレの参照
  • validation
  • Postgresqlにはunsined型は存在しない

使用環境

  • ホストOS: Windows10 Home
  • 仮想環境: Ubuntu Bento/Bionic
  • Ruby:2.51
  • Rails: 5.2.2
    • gem 'devise' : ログイン等の機能用
    • gem 'kaminari' : ページネーション
  • DB: PostgreSQL

Railsの命名規則(単数形と複数形)

rails gコマンドで、controller名やmodel名を指定する際に、混乱した。

# rails generate scaffold model名の単数形 フィールド名の型と並び
# rails g controller controller名の複数形
# カラムの追加
# rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
  • model名は単数形で、頭文字を大文字にする
    • scaffoldの場合、modelが基準
    • modelは設計書であり、(テーブル1つに付き)1つなため
  • controller名は複数形で、頭文字を大文字にする。
    • 1つのcontrollerに複数のactionが含まれるため

DBのカラム定義を後から変更

rails g scaffoldコマンド時に、ClubStudentの外部キーの定義をreferecesとミスタイプしていた。

修正方法:app/db/migrate下のファイルを修正

app/db/migrate/20190326030303_create_club_students.rb
class CreateClubStudents < ActiveRecord::Migration[5.2]
  def change
    create_table :club_students do |t|
     #スペリングミス
     #t.refereces :student
     #訂正分
      t.references :student
      t.references :club, foreign_key: true
      t.timestamps
    end
  end
end

なお、ALTTER TABLEコマンドを使って、あとから修正する方法は
DB内のデータを書き換えるだけで、アプリ自体のファイル等は編集されない。

mysql
# ALTER TABLE テーブル名 MODIFY COLUMN カラム名 新しい定義
ALTER TABLE ClubStudent MODIFY COLUMN student references

つまり、原因の根本的な部分を修正できないので、駄目

render partial: 部分テンプレの参照

render レンダリング(render) - railsドキュメント

全てのページのヘッダー(上部)に、ログアウトや他のstudentやclubs等のリンクを乗せる
tempsnip.jpg

共通して表示させるので、/app/views/layouts/application.html.erb を編集する。
なお、部分テンプレファイル名は『_』アンダーバー始まり

/app/views/layouts/application.html.erb
<body>
# <%= render :partial => '部分テンプレ名' %>
  <%= render :partial => 'shared/header' %>
</body>

表示させたいリンクを書きこむ。

/app/views/shared/_header.html.erb
<%= link_to 'Student list', students_path %> 
<%= link_to 'subjects list', subjects_path %> 
<%= link_to 'clubs list', clubs_path %> 
<%= link_to 'exam_result list', exam_results_path %>
<%= link_to 'club_stdent list', club_students_path %>
<%= link_to 'Log Out', destroy_student_session_path, method: :delete %>

validation

参考リンク Active Record Validations
バリデーションは有効なデータだけをDBに保存するのを確実にするための最善策。

今回の実装先:clubの新規作成ページ
newclub_form.JPG

validate条件

空でないこと

validates :name, presence: true
# 因みに、空が条件ならば
# validates :name, absence: true

入力文字の長さ

文字の最大長は、データ型を要参照。varcharなら255文字まで

/app/models/club.rb
# 2文字以上
validates :name, length:{minimum:2}
# 255文字以上
validates :name, length:{maximum:255}

exclusion含まない

授業ではしなかったが、クラブ名末尾に『部』を入れない、という条件を追加してみる

/app/models/club.rb
validates :name, exclusion: { in: %w(部 サークル) }
# 『含む』ならinclusion

実装結果

空白や文字列長、『サークル』という語には、validatesが発動した
validate-clubnew.JPG

ただ、現状だと、『テニスサークル』の様に文字列と連結すると、validateが動かない
あとまわし

Club was successfully updated.

Name: サークル部

type "unsigned" does not exist (※Postgresql)

validatesの実装していく最中に、エラーに気づいた

studentのeditページで更新すると、

ActiveRecord::StatementInvalid in StudentsController#show

PG::UndefinedObject: ERROR: type "unsigned" does not exist LINE 1: ...id as subject_id, CAST(AVG(exam_results.score) as unsigned) ... ^ : SELECT subjects.id as subject_id, CAST(AVG(exam_results.score) as unsigned) as avg_score, MAX(exam_results.score) as max_score, MIN(exam_results.score) as min_score FROM "students" INNER JOIN "exam_results" ON "exam_results"."student_id" = "students"."id" INNER JOIN "subjects" ON "subjects"."id" = "exam_results"."subject_id" GROUP BY subjects.id ORDER BY subjects.id

と、エラーを吐き、因みに、ブラウザの戻るボタンで戻ると、更新されている。
また、エラー原因であると思わる、StudentController#showは

app/controllers/students_controller.rb#show
def show
    @students = Student.joins(:subjects)
                       .select('students.name, students.email, students.age, students.gender, students.opinion, subjects.id as subject_id')
                       .select('exam_results.name as exam_result_name, subjects.name as subject_name, exam_results.score')
                       .select('CAST((exam_results.score / subjects.max_score) * 100 as unsigned) as ratio')
                       .where(id: params[:id])

    avg_result = Student.joins(:subjects)
                        .select('subjects.id as subject_id')
                        .select('CAST(AVG(exam_results.score) as unsigned) as avg_score')
                        .select('MAX(exam_results.score) as max_score')
                        .select('MIN(exam_results.score) as min_score')
                        .group('subjects.id')
                        .order('subjects.id')
(以下略)

因みに、このcontrollerは、以前の大学データのcontrollerからコピーしてきたものだ。
つまり、MySQLで動くアプリのcontroller。

unsigned (MySQL)

  • MySQLにおいては正と負の整数を扱うことができる。
  • unsignedを指定すると、正の数しか格納できなくなり、代わりに範囲が2倍になる。
  • unsignedにした値が負になると、エラーを起こす
    • UNSIGNEDは、マイナス値が入らないだけでなく、マイナスになる計算もできない。
    • CASTで一時的に型を変える事で回避は可能。

Postgresqlにはunsined型は存在しない(最重要)

対応策

まだ、試験結果のデータを入れてないので、功を奏すか分からないけれども

  • unsignedをint等の型に置き換える
    • 今回は試験点数を扱っていて、intで事足りると思われる。
    • ただ、MySQLでint unsignedだと、範囲が正の方向に2倍になっている。
    • 扱う数によっては、intより1つ上ののbigintに変える必要がある
  • CAST as unsignedの部分を消す
    • MySQLでCAST as unsingedは、一時的に型を指定している

前回の大学データに倣って、今回はcast as intに変更した

app/controllers/students_controller.rb
# (該当部分だけ抜き出し)
.select('CAST((exam_results.score / subjects.max_score) * 100 as int) as ratio')

.select('CAST(AVG(exam_results.score) as int) as avg_score') 

正常に、studentデータのedit、updateが機能した。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Scaffoldで作成したサイトにgem devise等を組み合わせていく

今週からは、scaffoldで作成した大学データと、gemのdevise、Bootstrap等を組み合わせる。

やった事

  • Railsの命名規則(単数形と複数形)
  • DBのカラム定義を後から変更
  • render partial: 部分テンプレの参照
  • validation
  • UNSIGNEDという型が存在しないPostgreSQL

使用環境

  • ホストOS: Windows10 Home
  • 仮想環境: Ubuntu Bento/Bionic
  • Ruby:2.51
  • Rails: 5.2.2
    • gem 'devise' : ログイン等の機能用
    • gem 'kaminari' : ページネーション
  • DB: PostgreSQL

Railsの命名規則(単数形と複数形)

rails gコマンドで、controller名やmodel名を指定する際に、混乱した。

# rails generate scaffold model名の単数形 フィールド名の型と並び
# rails g controller controller名の複数形
# カラムの追加
# rails generate migration AddカラムToモデル名の複数形 フィールド名と並び
  • model名は単数形で、頭文字を大文字にする
    • scaffoldの場合、modelが基準
    • modelは設計書であり、(テーブル1つに付き)1つなため
  • controller名は複数形で、頭文字を大文字にする。
    • 1つのcontrollerに複数のactionが含まれるため

DBのカラム定義を後から変更

rails g scaffoldコマンド時に、ClubStudentの外部キーの定義をreferecesとミスタイプしていた。

修正方法:app/db/migrate下のファイルを修正

app/db/migrate/20190326030303_create_club_students.rb
class CreateClubStudents < ActiveRecord::Migration[5.2]
  def change
    create_table :club_students do |t|
     #スペリングミス
     #t.refereces :student
     #訂正分
      t.references :student
      t.references :club, foreign_key: true
      t.timestamps
    end
  end
end

なお、ALTTER TABLEコマンドを使って、あとから修正する方法は
DB内のデータを書き換えるだけで、アプリ自体のファイル等は編集されない。

mysql
# ALTER TABLE テーブル名 MODIFY COLUMN カラム名 新しい定義
ALTER TABLE ClubStudent MODIFY COLUMN student references

つまり、原因の根本的な部分を修正できないので、駄目

render partial: 部分テンプレの参照

render レンダリング(render) - railsドキュメント

全てのページのヘッダー(上部)に、ログアウトや他のstudentやclubs等のリンクを乗せる
tempsnip.jpg

共通して表示させるので、/app/views/layouts/application.html.erb を編集する。
なお、部分テンプレファイル名は『_』アンダーバー始まり

/app/views/layouts/application.html.erb
<body>
# <%= render :partial => '部分テンプレ名' %>
  <%= render :partial => 'shared/header' %>
</body>

表示させたいリンクを書きこむ。

/app/views/shared/_header.html.erb
<%= link_to 'Student list', students_path %> 
<%= link_to 'subjects list', subjects_path %> 
<%= link_to 'clubs list', clubs_path %> 
<%= link_to 'exam_result list', exam_results_path %>
<%= link_to 'club_stdent list', club_students_path %>
<%= link_to 'Log Out', destroy_student_session_path, method: :delete %>

validation

参考リンク Active Record Validations
バリデーションは有効なデータだけをDBに保存するのを確実にするための最善策。

今回の実装先:clubの新規作成ページ
newclub_form.JPG

validate条件

空でないこと

validates :name, presence: true
# 因みに、空が条件ならば
# validates :name, absence: true

入力文字の長さ

文字の最大長は、データ型を要参照。varcharなら255文字まで

/app/models/club.rb
# 2文字以上
validates :name, length:{minimum:2}
# 255文字以上
validates :name, length:{maximum:255}

exclusion含まない

授業ではしなかったが、クラブ名末尾に『部』を入れない、という条件を追加してみる

/app/models/club.rb
validates :name, exclusion: { in: %w(部 サークル) }
# 『含む』ならinclusion

実装結果

空白や文字列長、『サークル』という語には、validatesが発動した
validate-clubnew.JPG

ただ、現状だと、『テニスサークル』の様に文字列と連結すると、validateが動かない
あとまわし

Club was successfully updated.

Name: サークル部

type "unsigned" does not exist (※Postgresql)

validatesの実装していく最中に、エラーに気づいた

studentのeditページで更新すると、

ActiveRecord::StatementInvalid in StudentsController#show

PG::UndefinedObject: ERROR: type "unsigned" does not exist LINE 1: ...id as subject_id, CAST(AVG(exam_results.score) as unsigned) ... ^ : SELECT subjects.id as subject_id, CAST(AVG(exam_results.score) as unsigned) as avg_score, MAX(exam_results.score) as max_score, MIN(exam_results.score) as min_score FROM "students" INNER JOIN "exam_results" ON "exam_results"."student_id" = "students"."id" INNER JOIN "subjects" ON "subjects"."id" = "exam_results"."subject_id" GROUP BY subjects.id ORDER BY subjects.id

と、エラーを吐き、因みに、ブラウザの戻るボタンで戻ると、更新されている。
また、エラー原因であると思わる、StudentController#showは

app/controllers/students_controller.rb#show
def show
    @students = Student.joins(:subjects)
                       .select('students.name, students.email, students.age, students.gender, students.opinion, subjects.id as subject_id')
                       .select('exam_results.name as exam_result_name, subjects.name as subject_name, exam_results.score')
                       .select('CAST((exam_results.score / subjects.max_score) * 100 as unsigned) as ratio')
                       .where(id: params[:id])

    avg_result = Student.joins(:subjects)
                        .select('subjects.id as subject_id')
                        .select('CAST(AVG(exam_results.score) as unsigned) as avg_score')
                        .select('MAX(exam_results.score) as max_score')
                        .select('MIN(exam_results.score) as min_score')
                        .group('subjects.id')
                        .order('subjects.id')
(以下略)

因みに、このcontrollerは、以前の大学データのcontrollerからコピーしてきたものだ。
つまり、MySQLで動くアプリのcontroller。

unsigned (MySQL)

  • MySQLにおいては正と負の整数を扱うことができる。
  • unsignedを指定すると、正の数しか格納できなくなり、代わりに範囲が2倍になる。
  • unsignedにした値が負になると、エラーを起こす
    • UNSIGNEDは、マイナス値が入らないだけでなく、マイナスになる計算もできない。
    • CASTで一時的に型を変える事で回避は可能。

Postgresqlにはunsined型は存在しない(最重要)

対応策

まだ、試験結果のデータを入れてないので、功を奏すか分からないけれども

  • unsignedをint等の型に置き換える
    • 今回は試験点数を扱っていて、intで事足りると思われる。
    • ただ、MySQLでint unsignedだと、範囲が正の方向に2倍になっている。
    • 扱う数によっては、intより1つ上ののbigintに変える必要がある
  • CAST as unsignedの部分を消す
    • MySQLでCAST as unsingedは、一時的に型を指定している

前回の大学データに倣って、今回はcast as intに変更した

app/controllers/students_controller.rb
# (該当部分だけ抜き出し)
.select('CAST((exam_results.score / subjects.max_score) * 100 as int) as ratio')

.select('CAST(AVG(exam_results.score) as int) as avg_score') 

正常に、studentデータのedit、updateが機能した。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Parallel 的ゾンビを殺した話

TL; DR

v1.16.0 から、 parallel の gem 使ってるときに timeout とか使って親を殺しても、ちゃんと子供達も kill されるようになりました。

詳細

parallel の gem は、便利なんだけれども、親スレッドを kill してもその子供達は普通に活動し続ける。 thread であろうとも process であろうとも。

例:

t = Thread.new do
  Parallel.map(0..1000, in_threads: 2) do |i|
    p i
    sleep 1
  end
end
sleep 1
t.kill
sleep
# 0 
# 1                                   
# 3                               
# 2                         
# 5
# 4
# => kill しているにも関わらず 1000 まで出力される。      

たとえば、悪名高い timeout と組み合わせるとこうなる。

begin
  Timeout.timeout(1) do
    Parallel.map(0..1000, in_threads: 2) do |i|
      p i
      sleep 1
    end
  end
rescue Timeout::Error
  p :timeout
end
sleep
# 0
# 1
# 2
# 3
# :timeout
# 4
# 5
# 6
# 7
# 8
# => 1000 まで続く

これは、あんまりにもあれなので、 parallel 本家に以下の PR を出してマージしてもらった。

https://github.com/grosser/parallel/pull/248

これによって、 Timeout やら 親スレッドの kill やらが行われた時に、その子供のスレッドやプロセスについても正しく kill されるようになった。

begin
  Timeout.timeout(1) do
    Parallel.map(0..1000, in_threads: 2) do |i|
      p i
      sleep 1
    end
  end
rescue Timeout::Error
  p :timeout
end
sleep
# 0
# 1
# 3
# :timeout
# 2
# => ここでとまる

Concurrent Ruby を使えよ、という話は若干ある

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails勉強ネタ resources で名前付きヘルパー変わること確認

普段何気なくroutes.rbにてresourcesメソッドを使用しているが、
色々確認する。

usersのコントローラーを作成(定番の7つのメソッド)

$ rails g controller users index new show create edit update destroy
users_controller.rb
class UsersController < ApplicationController
  def index
  end

  def new
  end

  def show
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end
end
Rails.application.routes.draw do
  get 'users/index'
  get 'users/new'
  get 'users/show'
  get 'users/create'
  get 'users/edit'
  get 'users/update'
  get 'users/destroy'
end

この状態でrails routesを実行すると。

$ rails routes
       Prefix Verb URI Pattern              Controller#Action
  users_index GET  /users/index(.:format)   users#index
    users_new GET  /users/new(.:format)     users#new
   users_show GET  /users/show(.:format)    users#show
 users_create GET  /users/create(.:format)  users#create
   users_edit GET  /users/edit(.:format)    users#edit
 users_update GET  /users/update(.:format)  users#update
users_destroy GET  /users/destroy(.:format) users#destroy

全部users_'アクション名'という名前付きヘルパーになって、メソッドは全部GETです。
(空気を読んでPOSTとかDELETEなどになるのかと思っていた・・・)

resoureceメソッド使う

routes.rb
Rails.application.routes.draw do
  # get 'users/index'
  # get 'users/new'
  # get 'users/show'
  # get 'users/create'
  # get 'users/edit'
  # get 'users/update'
  # get 'users/destroy'

  resources :users
end

この状態でrails routes実行すると

$ rails routes
WARNING: Nokogiri was built against LibXML version 2.9.7, but has dynamically loaded 2.9.4
   Prefix Verb   URI Pattern               Controller#Action
    users GET    /users(.:format)          users#index
          POST   /users(.:format)          users#create
 new_user GET    /users/new(.:format)      users#new
edit_user GET    /users/:id/edit(.:format) users#edit
     user GET    /users/:id(.:format)      users#show
          PATCH  /users/:id(.:format)      users#update
          PUT    /users/:id(.:format)      users#update
          DELETE /users/:id(.:format)      users#destroy

Restfulな形になった。
ニュアンス的にcreateuser_path のPOSTなイメージがあるが、
users_pathである事に注意(間違えそう)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails勉強ネタ resources での名前付きヘルパー確認

普段何気なくroutes.rbにてresourcesメソッドを使用しているが、
色々確認する。

usersのコントローラーを作成(定番の7つのメソッド)

$ rails g controller users index new show create edit update destroy
users_controller.rb
class UsersController < ApplicationController
  def index
  end

  def new
  end

  def show
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end
end
routes.rb
Rails.application.routes.draw do
  get 'users/index'
  get 'users/new'
  get 'users/show'
  get 'users/create'
  get 'users/edit'
  get 'users/update'
  get 'users/destroy'
end

この状態でrails routesを実行すると。

$ rails routes
       Prefix Verb URI Pattern              Controller#Action
  users_index GET  /users/index(.:format)   users#index
    users_new GET  /users/new(.:format)     users#new
   users_show GET  /users/show(.:format)    users#show
 users_create GET  /users/create(.:format)  users#create
   users_edit GET  /users/edit(.:format)    users#edit
 users_update GET  /users/update(.:format)  users#update
users_destroy GET  /users/destroy(.:format) users#destroy

全部users_'アクション名'という名前付きヘルパーになって、メソッドは全部GETです。
(空気を読んでPOSTとかDELETEなどになるのかと思っていた・・・)

resoureceメソッド使う

routes.rb
Rails.application.routes.draw do
  # get 'users/index'
  # get 'users/new'
  # get 'users/show'
  # get 'users/create'
  # get 'users/edit'
  # get 'users/update'
  # get 'users/destroy'

  resources :users
end

この状態でrails routes実行すると

$ rails routes
   Prefix Verb   URI Pattern               Controller#Action
    users GET    /users(.:format)          users#index
          POST   /users(.:format)          users#create
 new_user GET    /users/new(.:format)      users#new
edit_user GET    /users/:id/edit(.:format) users#edit
     user GET    /users/:id(.:format)      users#show
          PATCH  /users/:id(.:format)      users#update
          PUT    /users/:id(.:format)      users#update
          DELETE /users/:id(.:format)      users#destroy

Restfulな形になった。
ニュアンス的にcreateuser_path のPOSTなイメージがあるが、
users_pathである事に注意(間違えそう)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CentOS7にRubyをインストール

いつも忘れがちなので、自分用のメモも兼ねてインストール方法を投稿します。

前準備

CentOS7はMinimal ISOでインストール済みであること

必要なパッケージのインストール
$ sudo yum -y install git bzip2 gcc gcc-c++ openssl-devel readline-devel zlib-devel
$ sudo yum -y install epel-release
$ sudo yum -y install nodejs
rbenvをダウンロード
$ sudo git clone https://github.com/rbenv/rbenv.git /usr/local/rbenv
$ sudo git clone https://github.com/rbenv/ruby-build.git /usr/local/rbenv/plugins/ruby-build
rbenvの環境設定

/etc/profile.d/rbenv.shを作成し、以下の内容を追加します。

export RBENV_ROOT=/usr/local/rbenv
export PATH=${RBENV_ROOT}/bin:$PATH
eval "$(rbenv init --no-rehash -)"

rootユーザで以下の実行が必要

# source /etc/profile.d/rbenv.sh

sudoでrbenvを実行するためにsudorderにRBENV_ROOTとsecure_pathの追記が必要

# visudo
...
Defaults env_keep += "RBENV_ROOT"
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/rbenv/bin:/usr/local/rbenv/shims
...

Rubyのインストール

バージョンをチェック
$ rbenv install -l
...
  2.6.0-rc1
  2.6.0-rc2
  2.6.0
  2.6.1
  2.6.2
  2.7.0-dev
  jruby-1.5.6
...
インストール実行

バージョン2.6.2をインストールしてみます。

$ sudo rbenv install 2.6.2

使用するRubyのバージョンを設定します。

$ sudo rbenv global 2.6.2
$ sudo rbenv rehash

これでインストール完了です。

後処理

必要なgemをインストール

bundlerはRailsを動かすために最低限必要なのでインストールします。

$ sudo gem install bundler --no-doc
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む