20200227のRubyに関する記事は30件です。

多対多テーブルの作り方

データベース設計

投稿(postテーブル)に対してタグ(tagテーブル)付けができる機能を作るとします
一つの投稿は複数のタグを持つことができ、一つのタグは複数のタグを持つことができる場合、そのままアソシエーションを組んでしまうとデータベースの中身が複雑になってしまいます。
そのためどの投稿がどのタグに結びついているのかを記憶するテーブルを別に作り、整理します。

内容

postテーブルを作る

ターミナル
rails g model Post image:text

tagテーブルを作る

ターミナル
rails g model Tag name:string

中間テーブルを作る

中間テーブルは各モデルの複数形を名前をスネーク型で繋げます
今回の場合はPosts_tags
postとtagの結びつきを記憶するテーブルのため各idのカラムを作ります
refarencesの型はこの後記述するマイグレーションファイルに記載するforeighn_keyの制約に対応した型になります。
foreighn_keyはpost_idやtag_idカラムに対してpost_idに対応したpostsテーブルのidがあることを保証する制約です。
例)post_id:1 tag_id:1 であればpostsテーブルにid:1の投稿があり、tagsテーブルにもid:1があることを保証しています。

ターミナル
rails g model Posts_tags post_id:refaremces tag_id:refarences

アソシエーション

多対多テーブルのアソシエーションはthroughを使用します。
postとtagは関連しているがposts_tagsを経由して関連することを表します。

ppst.rb
has_many :posts_tags
has_many :tags, through: :posts_tags
tag.rb
has_many :posts_tags
has_many :posts, through: :posts_tags
posts_tag.rb
belongs_to :post 
belongs_to :tag

マイグレーションファイル

マイグレーションファイル
class CreatePostsTags < ActiveRecord::Migration[5.2]
  def change
    create_table :posts_tags do |t|
      t.refarence :post_id, foreign_key: true
      t.refarences :tag_id, foreign_key: true

      t.timestamps
    end
  end
end

完成

これで多対多テーブルの作成が完了します。
※sqliteでrefarences型を使用しようとしたところマイグレーションができませんでした。integer型に変更したところマイグレーションできたので、sqliteではrefarences型は使用できないようです。

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

【Ruby on Rails 5】Instagram風簡易SNSアプリを作ってみよう!(Techpit教材)を終えて

Techpitの【Ruby on Rails 5】教材の感想

TechCommitさんのお年玉企画にて頂いたTechpit教材を使って、Ruby on Rails 5で簡易アプリを作成しました。
簡単ではありますが、どのようなことを学べたのか、その感想を記します。

また、当方実務経験などが全く無く、言語を独学で学んでいる程度なので、経験も知識も非常に浅いことをご了承願います。

教材に取り組む上で必要な知識

まず、【Ruby on Rails 5】instagram風簡易SNSアプリを作ってみよう!という教材は、以下の言語を使用します

  • HTML
  • CSS
  • Javascript
  • Ruby

必要条件はHTML/CSS、及びRubyの基礎知識とされています。

主な目的はRuby on Railsの流れを把握するものであり、
SNSのinstagramを参考にしたアプリ作成を進める内容となっています。

今回の教材に取り掛かるにあたって、HTMLとCSS、Rubyの基礎知識をProgateで補っています。
教材は、2回ほど繰り返し学習を行いました。

※教材内でHTML/CSSは多く使われますが、詳しい解説はありません。

教材の流れと大まかな内容

  • 環境構築
    • Mac -Homebrew
    • Windows -Cloud9
  • Railsアプリケーション作成
    • Gemインストール
    • ローカルサーバー立ち上げ
  • MVC知識
  • Bootstrap導入
  • 機能を付ける
    • サインアップ・サインイン
    • プロフィール
    • 画像投稿
    • いいね
    • コメント
  • デプロイ
    • Herokuを利用してサービス公開
  • pry-railsを用いたデバック方法

どのファイルでどのコードを記述するか、コードの意味から、詳しい情報が書いてある詳細ページのリンクも要所要所あります。

また、各項ごとに質問投稿欄があり、わからないことが運営へ質問出来るようになっていました。

全体を通しての感想

プログラミング超初心者ながら教材を終えて、感想を大きく三つあります。

  • 初心者にも作れるわかりやすさ
  • 使用する知識が多い
  • エラーに慣れてくる

初心者にも作れるわかりやすさ

アプリを作るというと難しそうだなとは思っていましたが、
一つ一つのページを作り、それを組み立てるような流れでわかりやすかったので、
進めていければアプリ作成・公開まで行うことが出来ます。

使用する知識が多い

教材内容全てを理解する、というのは、特に言語など基礎の基礎を学んだだけでは出来ませんでした。使用する言語も多い(ように感じた)ので、基礎知識しか無い段階で全て理解しようとすると相当な時間がかかると思われます。

といっても、そもそもこの教材の一番目的がRuby on Railsの流れを把握することなので、まずは流すように取り組むだけでも、大枠が見えてきます。
細かいことを知りたい時はそのページで質問するでも、リンクを参考にするでも可能なところが良かったです。

エラーに慣れてくる

私が初心者なせいかとは思いますが、教材通りにしているつもりでもどこかしらでエラーが起きて躓いてしまうことが多々ありました。

しかし自分が躓いていることは大体他の人も躓いていることが多かったので、特に各項の質問投稿欄は、同じエラーで解決した人が居たりして、非常に助けになり、また、何度かエラーを繰り返すことでエラーの直し方や着目すべきところも以前より見えるようになった気がします。

まとめ

長くなりましたが、この教材の対象者でもあるように、Ruby on Rails開発の流れをつかみたい人、アプリを作成してみたい人、特にRubyを学んでいる方にはおすすめです。

しかし感想でも触れましたが、やはり言語の面での自分の知識不足が痛かったです。
基礎知識をしっかり固めてやると、もっと入りやすいのかなと思いました。教材としては扱いやすくてとても良かったです。

アプリ作成、そしてサイトの公開まで学べるという点で、知識の色んな活用の仕方が出来るのではと思います。

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

Ruby学習のためのローカル開発環境構築



Ruby学習のために手元のPCにローカル環境構築(VirtualBox、Vagrant)を行った仮定を忘れないようメモ代わりにまとめます。

ローカル開発環境を構築する必要が何故あるのか

サーバーサイド言語(PHP、Ruby、Javaなど)は、サーバー側でプログラムを実行するので、サーバーへの接続を行います。
そのためには、サーバーを借りる必要があるのですが、コストがかかってしまったり、手元のPCの情報を24時間インターネット上に公開するというデメリットもあります。

それならば、同じ環境を自分のPC内に構築すれば、コストもかからず、ネット上に公開せずにWebアプリやWebサイトの開発、テストも行えて便利だよね!ということで、手元に置いておける環境(ローカル環境)が必要になります。

※ちなみにフロントエンド言語(HTML、CSS、JavaScript)は、ブラウザ上で動くため、サーバーとの通信を必要とせず、htmlファイルを用意しブラウザにドラッグ&ドロップすれば表示されるので、動作確認だけならブラウザとhtmlファイルがあれば可能であり、開発環境は必要ない。

※またここでいきなり今回のテーマをぶち壊しますが、ローカル環境構築を作るのではなく、ブラウザ上にサーバー環境を構築してくれるCloud9というサービスもあります。
ので、今後こちらも利用し学習する予定です。今は忘れます。

ローカル環境構築までの流れ

1. VirtualBoxをインストール

VirtualBox:仮想マシン本体ツール。手元のPCにいくつものサーバー(例えば1つを勉強用、もう1つを作りたいWebサーバーの開発用)を立ち上げることができる。



2. Vagrantをインストール

Vagrant:サーバーの設定やOSのインストール作業などをするのが複雑なVirtualBoxを、簡単なコマンドで扱えるようにしてくれるツール。
直接扱っていくのはVagrant、VirtualBoxは直接扱わないが、Vagrantコマンドを通じて裏でVirtualBoxは動いているというイメージ。



3.サーバーを立ち上げる。

サーバー1つにディレクトリ(今回は『MyCentOS』)が1つ必要。(複数のサーバー=複数のディレクトリをまとめられる大元のディレクトリ(今回は『MyVagrant』)を作成すると管理しやすい。)
MyCentOSディレクトリの中でサーバーを立ち上げ、サーバーの設定ファイルをVagrantのコマンドで作る(Vgrantfileを作りIPアドレスの設定などのサーバーの設定をする)
Vgrantfileの設定を読み込んで、裏でVirtualBoxを立ち上げてサーバーを作ってくれるという仕組み。



4.仮装マシンを立ち上げる

ターミナル起動

#ホームディレクトリに移動し、今後複数の仮想マシンを作ることを想定して、それらをまとめるディレクトリ(MyVagrant)を作る
~ $ cd
~ $ mkdir MyVagrant

#作成したディレクトリ(MyVagrant)へ移動。(移動できてればプロンプト部分に『MyVagrant』と表示される)
~$ cd MyVagrant

#仮想マシンを入れるディレクトリ(MyCentOS)を作り、移動。(移動できてればプロンプト部分に『MyCentOS』と表示される)
MyVagrant $ mkdir MyCentOS
MyVagrant $ cd MyCentOS

#仮想マシンを設定するための『Vagrantfile』を作る
MyCentOS $ vagrant init bento/centos-6.8

#Vagrantfileを編集して仮想マシンのIPアドレスを192.168.33.10にする
MyCentOS $ sed -i '' -e 's/# config.vm.network "private_network", ip: "192.168.33.10"/config.vm.network "private_network", ip: "192.168.33.10"/' Vagrantfile

# 仮想マシンを起動
MyCentOS $ vagrant up

# 仮想マシンの状態を確認する。『running』と表示されれば仮想マシンが起動されている。
MyCentOS $ vagrant status



5.仮想マシンの設定

#立ち上げたサーバーにログインする(Vagrantfileのあるディレクトリから)
MyCentOS $ vagrant ssh

#サーバーが立ち上がりOSがインストールできたが、アプリケーションがインストールされていないので行う。OS最新状態にアップデートする。
[vagrant@localhost ~]$ sudo yum -y update

# スクリプト(プログラム)を入手するためのgitをインストール
[vagrant@localhost ~]$ sudo yum -y install git

# gitを使ってアプリケーション設定用のスクリプトをダウンロード。centos6ディレクトリが作成される。
[vagrant@localhost ~]$ git clone https://github.com/dotinstallres/centos6.git

# centos6フォルダへ移動
[vagrant@localhost ~]$ cd centos6

# スクリプトを実行
[vagrant@localhost centos6]$ ./run.sh

# 他の設定を反映
[vagrant@localhost centos6]$ exec $SHELL -l



6.仮装マシン上のファイルを簡単に扱えるようにするため、ファイル転送ツール『Cyberduck』(FTPクライアントソフト)のインストールを行う

7.仮想マシンにアクセスする

Cyberduckの『新規接続』→『SETP』→サーバーに上記で設定したIPアドレスを入力→ユーザ名、パスワード共に『vagrant』→接続
※今後も同じサーバーにアクセスしやすいようにブックマークしておくと便利。
→ブックマークメニュー→新規ブックマーク→ニックネームに『MyCentOS』で完了



8.Ruby学習用のディレクトリ作成

右クリック→『新規フォルダ』→『Ruby_lessons』作成
『Ruby_lessons』へ移動→『新規ファイル(index.rb)』作成
この作成したファイルをクリックすると、設定したエディタが起動する。これでRubyを学習する環境の完成です。


補足

●仮装マシンからのログアウト方法

・ブラウザやファイル転送ツールを終了させる
・ターミナルにて

[vagrant@localhost ruby_lessons]$ exit
#仮想マシンからログアウトしMacの方へ操作が移る

MyCentOS $ vagrant suspend
#仮装マシン停止

MyCentOS $ exit
#ターミナル終了

※ターミナルメニューにて『環境設定』→『プロファイル』→『シェル』→『シェルの終了時:シェルが正常に終了した場合は閉じる』に設定しておくと、ターミナル終了時に自動でウィンドウが閉じる
※上記のように『exit』と入力しなくてもターミナルのウィンドウを閉じても終了はできるがコマンドを入力し正式に終了させる癖をつけておく



●仮想マシンに再びログインする方法
・ターミナル起動

#ホームからMyVagrant(仮想マシンをまとめたディレクトリ)へ移動
~ $ cd MyVgrant

#MyVagrantからMyCentOS(仮装マシンのディレクトリ)へ移動
MyVagrant $ cd MyCentOS

#仮装マシン起動
MyCentOS $ vagrant up

#仮想マシンの状態確認(running表示でOK)
MyCentOS $ vagrant status

#仮想マシンへログイン
MyCentOS $ vagrant ssh
([vagrant@localhost ~]$となればログインできている)

最後に

以上、自分なりに仮定をまとめてみました。拙い文章でしたが、ここまでお読みいただきありがとうございました。
何か誤っている点がありましたら、お教えいただけるとありがたいです。

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

複数の配列にeachメソッドを使いたい

rubyのzipメソッドを使えば可能

Ruby 2.7.0 リファレンスマニュアルよりサンプルコードを引用

p [1,2,3].zip([4,5,6], [7,8,9])
    # => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

p [1,2].zip([:a,:b,:c], [:A,:B,:C,:D])
    # => [[1, :a, :A], [2, :b, :B]]

p [1,2,3,4,5].zip([:a,:b,:c], [:A,:B,:C,:D])
    # => [[1, :a, :A], [2, :b, :B],
    #     [3, :c, :C], [4, nil, :D], [5, nil, nil]]

p [1,2,3].zip([4,5,6], [7,8,9]) {|ary|
  p ary
}
    # => [1, 4, 7]
    #    [2, 5, 8]
    #    [3, 6, 9]
    #    nil

参考

https://docs.ruby-lang.org/ja/latest/method/Array/i/zip.html

https://freesworder.net/ruby-each-multiple/

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

Ruby/GTK3 - Switch

image.png

gem install gtk3

Switch

Gtk.Switchは、オンまたはオフの2つの状態を持つウィジェットである。
シグナルは notify::active に接続するのがよいとのこと。

image.png

require 'gtk3'

class SwitcherWindow < Gtk::Window
  def initialize
    super
    set_title 'Switch Demo'
    set_border_width 10

    grid = Gtk::Grid.new
    grid.set_column_spacing 6
    grid.set_row_spacing 6
    add grid

    4.times do |i|
      2.times do |j|
        switch = Gtk::Switch.new
        switch.signal_connect('notify::active') { |s| on_switch_activated s }
        switch.set_active [true, false].sample
        grid.attach switch, i, j, 1, 1
      end
    end
  end

  def on_switch_activated(switch)
    state = switch.active? ? 'on' : 'off'
    puts "Switch was turned #{state}"
  end
end

win = SwitcherWindow.new
win.signal_connect('destroy') { Gtk.main_quit }
win.show_all
Gtk.main

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

標準入力で連続した数値を受け取る方法(paiza用)

Rubyで文字列ではなく連続した数値を入力したい

paizaの問題を解くときに標準入力で数値を受け取りたいが、paizaの入力方法ではgets.to_iだけでは不十分だったのでメモとして残します。

NG例

入力は以下である場合
1 2 3

example.rb
number = gets.chomp.split.map(&.to_i)
puts number

map(&.to_i)のように'&'の後に'.'をつける方法がネットによく出ているが、paizaの環境ではsyntaxエラーがでて動かない。

成功例

example.rb
number = gets.chomp.split.map(&:to_i)
puts number

'.'を':'に変えると動いた。

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

bin/rails db:migrateしようとしたらMysql2::Error: Table '*****' doesn't existが出る

経緯

bin/rails g model image

bin/rials g model product

によって作られたマイグレーションファイル
20200227092210_create_images.rb
20200227092647_create_products.rbを,

20200227092210_create_images.rb
(中略)
      t.references :product, foreign_key: true
(中略)

と書いて, bin/rails db:migrate を実行

起きたエラ〜

(中略)
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Table 'プロジェクト名_development.products' doesn't exist: SHOW FULL FIELDS FROM `products`
(中略)

というエラーが発生した。

解決策

 スペルのミスも、文法ミスも、探したけれど見つからないよ♪...
 なので、googleで検索したところ、参考になる記事を発見。

どうやら、
referencesされるマイグレーションファイル
の方が先にmigrateされるべきなようで、


20200227092647_create_products.rbを、
 →2020022709 2209_create_products.rb
  (20200227092210_create_images.rb)
にすると、images.rbよりも早くmigrateされた!!

参照したwebページ

teratail:rails db:migrateができません(おそらくmysqlのカラムがマッチしていない)

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

rails:DM送信機能を簡単に実装〜model〜

ダイレクトメッセージ機能

検索に苦労したのでDM機能について簡単に実装手順を紹介します。

前提として、DM機能の実装には
①トークルームを作成してそこで話す機能を実装する方法(中間テーブルが2つ)
②トークルームを作成しない方法
があります。

今回は②についての説明です。

ER図

スクリーンショット 2020-02-27 19.21.42.png

roomモデルを作らなくても作成できる理由は、routesで設定できます。

手順

usersモデルがある前提で書きていきます。

$ rails g model Messages content:string user:references receive_user:references

Messages modelとmigrateファイルを作成します。

完成したものがこちら

_create_messages.rb
class CreateMessages < ActiveRecord::Migration[5.2]
  def change
    create_table :messages do |t|
      t.string :content
      t.references :user, foreign_key: true
      t.references :receive_user, foreign_key: { to_table: :users }

      t.timestamps
    end
  end
end
_create_messages.rb
{ to_table: :users }

を追記しています。
これが無いと、receive_usersテーブルを参照してしまうので『そんなテーブルは存在しないよ』と言われてしまいます。
そのため、外部キーとしてusersテーブルを参照するという指定を行っています。

$ rails db:migrate

↑します。

続いて、model

models/message.rb
class Message < ApplicationRecord
  belongs_to :user
  belongs_to :receive_user, class_name: 'User'
end
models/message.rb
class_name: User 

を追加します。
これによって、receive_userがReceive_userという存在しないクラスを参照することを防ぎます。

models/user.rb
has_many :messages
has_many :sent_messages, through: :messages, source: :receive_user
has_many :reverses_of_message, class_name: 'Message', foreign_key: 'receive_user_id'
has_many :received_messages, through: :reverses_of_message, source: :user

def sent_messages(other_user, content) #メッセージを送るためのメソッド
    unless self == other_user
      self.messages.find_or_create_by(receive_user_id: other_user.id, content: content)
    end
end

これで完成です!

しっかり作成できるか $ rails cで確認することは忘れないでください?‍♂️

補足

修正依頼などあれば気軽にお申し付けください。

参考記事
https://aka-shin.com/techacademy-mentaring-09/

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

Net::HTTP::Get を用いた HTTP リクエスト - クエリパラメータを簡単に渡す方法

経緯

  • Ruby 2.6.5 の Net::HTTP::Get を使って HTTP リクエスト を実行していたが、 クエリパラメータ を簡単に渡して実行する方法がなかった。
    • Post なら post_form というメソッドがあって、 比較的楽にできる。

コード

class Sample
  def self.get(path, params = {}, header = {})
    uri = URI(path + build_query_string(params))
    request = Net::HTTP::Get.new(uri)
    header.each do |key, value|
      request[key] = value
    end
    response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
      http.request(request)
    end

    response.body
  end

  private
  # @param [Hash] params
  def self.build_query_string(params)
    if params.length === 0
      return ''
    end

    query_string_parts = []
    params.each do |key, value|
      if value.is_a? Array
        value.each do |single_value|
          query_string_parts << "#{key}[]=#{single_value}"
        end
        next
      end
      query_string_parts.push("#{key}=#{value}")
    end
    return '?' + query_string_parts.join('&')
  end
end

注意点

  • {'a': [1, 2, 3]} を Getパラメータ に使った場合は、 ?a[]=1&a[]=2&a[]=3 という文字列が URL に追加される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの基礎

最初に

自身で学習してきた内容を書き出していきます。
基本的には自分自身でわかるような内容になりますので、ご容赦ください。
また、誤っている点がありましたら、コメントにてご指摘ください。

!! (要確認)マークがあるものは実行前に必ずググるべき内容 !!

index

  • 基本文法
    • Hello, World!
    • データ型について
  • オブジェクトとメソッド
    • オブジェクトとは
    • メソッドとは
  • 変数と定数
    • 変数とは
    • 定数とは
  • データ型
    • 数値(0除算について
    • 文字列
    • 配列
    • ハッシュ
    • シンボル
  • 条件分岐
    • if文
    • 比較演算子
    • 論理演算子
    • elsif、else
  • 繰り返し処理
    • 文字列や数字をキーボードから入力する方法
      • gets
      • to_i
    • 繰り返し処理や要素を順に取り出す処理
      • while文
      • for文
      • each文
      • break

基本文法

Hello, World!

xxx.rb
puts 'Hello, World!'
ターミナル
ruby xxx.rb(実行するプログラムファイル名)
# 結果
Hello, World!

基本的に、xxx.rbのようなファイルを作りやりたいことを記述、ターミナルで動かす流れ。

ターミナル
touch xxx.rb # ターミナルコマンドでからファイルを作成、ここに処理を記述していく
ruby xxx.rb # 実行
# 結果
# 記述した処理がここに出る

データ型について

文字列型(String「""」)と整数型(Integer「 」)がよく使われる。
詳しくは後日、投稿予定。

xxx.rb
puts 5 + 3
puts "5 + 3"
puts "5" + "3"
ターミナル
ruby xxx.rb
# 結果
8 # 両方integerなので、「5+3」の計算結果
5 + 3 # 計算式をstringで囲っているので、「文字としての"5+3"」が表示される結果
53 # 文字列と文字列を足しているので、「文字としての"5"と"3"」が連続して表示される結果

同じ見た目のメソッドでも、中身が異なる場合があるが、めちゃくちゃ便利。

文字列型、整数型の型変換

to_s 
27.to_s # 数字→文字へ変換,この場合は文字列型の27になる

to_i
"27"to_i # 文字→数字へ変換,この場合は整数型の27になる

オブジェクトとメソッド

オブジェクトとは

操作対象の状態(データ)と便利な機能(メソッド)を1つにまとめたもの
オブジェクトは「~~のもの」

メソッドとは

オブジェクトが持っている固有の機能や処理のこと
メソッドは「~~するもの」

つまり・・・?

puts "XXXYYYY".length
# 結果
7 # 「文字列オブジェクト」を「lengthメソッドで文字数を数え」、結果を「出力(puts)」

他にも、「reverse(文字列を逆にする)」メゾットや、「include?(引数に指定した文字列が含まれているか)」メゾットなどがある。
(メソッドが使えるのかを一覧表示したい場合は、使用可能なメソッド一覧を表示できる「methods」メソッド)
include?の場合は、「true(真)」か「false(偽)」で結果が表示される。

変数と定数

変数とは

オブジェクトにつける名札。
名札をつけることで、欲しいデータを呼び出すことが用意になる。
ただし、変数名は小文字で始まるルールがあり、尚且つ数字は使えない

test = "テストコメントが表示されることを確認しよう"
puts test
# 結果
テストコメントが表示されることを確認しよう

変数名 = 入れたいデータ
ポイント : 「=」は数学的な等しいという意味ではなく、「右のオブジェクトを左の変数に代入する」という意味。

定数とは

あらかじめ変更しないことが保証された数値やデータにつける名札。
基本的に大文字で始まる
⚠️値を書き換えることができない点に注意⚠️
(warning(実行不可能なエラーではないが非推奨な使い方)が表示されて書き換えができてしまうが、エラーが出る時点でアウト。)

Xx = 3.14
puts Xx
# 結果
3.14

Pi = 3.14
puts Pi

Pi = 100
puts Pi
# 結果
3.14
pi.rb:4: warning: already initialized constant Pi
pi.rb:1: warning: previous definition of Pi was here
100 

データ型

数値

「1」「100」「-100」「0」などの整数や、「3.14」などの小数のこと
メソッドとしては、+(足し算)、-(引き算)、/(割り算)、*(掛け算)、%(割り算の余りの計算)などの四則演算。

puts 100 
# 結果
100

puts 100 + 5
# 結果
105

ポイント : 0除算について
任意の数字 を0で割ってはいけないという数学的ルールがプログラミングにも適用されるぞ。
詳しくは後日投稿予定。
[四則演算注意点] 数字を0で割ってはいけないプログラム計算式

文字列

シングルクォートやダブルクォートで囲むことで、文字列として出力。
文字列の中に#{変数}と記述することによって、{}内の変数の値を展開可能。
+を使うと、文字列の連結も可能になり、文字列同士だけでなく、文字列と変数、または変数同士でも、連結できる。

puts "Rubyを学習"
# 結果
Rubyを学習

name = "テストさん"
puts "Hello" + name
# 結果
Helloテストさん

puts "こんにちは#{name}"
# 結果
こんにちはテストさん

配列

配列を使うと、複数のデータをまとめて保存できる。
配列の各要素には、どのようなオブジェクトでも代入でき、同じ配列の要素に異なった種類のオブジェクトを代入することも可能。
しかも、データ型は問われず、文字列でも数値でも代入できる。
ただし、先頭から0、1、2というように「インデックス番号」が割り振られ、「0」から開始することに注意。

names = ["Ruby", "Java", "Python", "PHP"]
puts names[1]

# 結果
Java

ポイント : たまにeach文で回したデータをarray(からの箱的な感じ)に突っ込んだりする。

ハッシュ

ハッシュは、キーと値をセットで保存できるデータの集合。
配列では各要素にインデックス番号が割り振られるが、ハッシュでは任意の値(キー)を割り振ることができる。

tall = {"あい"=>185, "うえ"=>170, "お!"=>150}
puts tall["あい"]
# 結果
185

シンボル

シングルクォートやダブルクォートの代わりにコロンを使用することで、文字列と同様に扱うことが可能。

tall = {:かき=>185, :くけ=>170, :こ!=>150}
puts tall[:こ!]
# 結果
150 

条件分岐

if文

「もしAならばBを実行し、AでなければCを実行」
条件に当てはまればtrue、当てはまらなければfalseという真偽判定ができる。

apple = 110

if apple >= 100
    puts "りんごの値段は100円以上です" #true=>処理される
end

if apple <= 100
    puts "りんごの値段は100円以下です" #false=>処理されない
end

# 結果
りんごの値段は100円以上です # apple = 110のためtrue処理になった

比較演算子

「条件を比較して判定する」時に使用する記号とかのこと。

<   # 右辺の方が大きい
<=  # 右辺の方が大きい、または等しい
>   # 左辺の方が大きい
>=  # 左辺の方が大きい、または等しい

# 数値だけでなく、文字列の比較(同じ文字列かどうか)でも利用できる
==  # 右辺と左辺が等しい
!=  # 右辺と左辺が等しくないとき

論理演算子

「2つ以上の条件を判定する」時に使用する。

!  # 否定
&&  # どちらの条件も正しければtrue
||  # どちらかの条件が正しければtrue 

tall = 180
if tall >= 170 && tall <= 190
    puts "身長は170以上190以下です。"
end
# 結果
身長は170以上190以下です。

elsif、else

if(もし〜)文では、elsif(それとも〜)やelse(当てはまらないなら〜)を使うことで、ifの条件に当てはまらなかった場合の処理を設定できる。

else

apple = "A"

if apple == "A" #もしappleがAなら 
    puts "それはAのリンゴです"
else #ifが当てはまらない場合は
    puts "そのリンゴはAではないです"
end
# 結果
A typeのリンゴです

elsifとelse

apple = "B"

if apple == "A" #もしappleがAなら
    puts "そのりんごはAです"
elsif apple == "B" #それともappleがBなら
    puts "そのりんごはBです"
else #どちらも当てはまらない場合は
    puts "そのりんごはCです"
end
# 結果
そのりんごはBです

よくFizzBuzz問題としてここあたりが使われると思う。

繰り返し処理

全て100%覚える必要はないが、処理ごとの特徴くらいは覚えよう。

文字列や数字をキーボードから入力する方法

gets

キーボード入力された値を取得するメソッド。
getsを使うと、キー入力待ちの状態になり、文字を入力してEnterキーを押すまで次に進まない。
文字列として返されるぞ。

to_i

文字列を整数に変換するメソッド。
例えば、getsメゾットにto_iを使って文字列(String)を整数(Integer)に変換すると計算ができる。

puts "キーボードで数字「2」と数字「3」を入力してください"
  a=gets.to_i
  b=gets.to_i
  puts "a+b=#{a+b}"
# 結果
a+b=5

繰り返し処理や要素を順に取り出す処理

while文

繰り返し処理を行いたいときに使用し、指定した条件式が真(true)の間、繰り返し実行されるのが特徴。

while 条件 do #条件がtrueの間
    処理 #処理を繰り返す
end

# サイコロを振って「6」が出るまで繰り返し実行
dice = 0 # diceに0を代入し、初期値を設定
while dice != 6 do # サイコロの目が6ではない間、diceの初期値は0なので条件を満たす。
                   # 以降はdiceに代入される数によって結果が異なる
    dice = rand(1..6) # 1~6の数字がランダムに出力
    puts dice    
end
# 結果
~
6 # サイコロを振って「6」が出るまで繰り返し実行された最後の数字として6で繰り返し処理が止まる

for文

繰り返し処理を行いたいときに使用し、指定したオブジェクトから順に値を取り出しながら繰り返されるのが特徴。

for 変数 in 範囲、ハッシュ、配列などを指定 do
    処理
end

# for ~ end内で、1から10まで順番に表示する処理を繰り返す実行
for i in 1..10 do # 1..10は、1~10までの範囲を表す
    puts i
end
# 結果
1
2
3
~
9
10 # 1から10まで順番に表示されるため10で止まる

eachメソッド

オブジェクト内の要素を順に取り出すメソッド。
個人的に一番使う繰り返し処理。
繰り返し処理を行いたいときに使用し、データの先頭から順に繰り返して処理されるmのが特徴。

(範囲、ハッシュ、配列などを指定).each do |変数|
  実行する処理
end

apple 130
strawberry180
orange100
という結果が欲しいと仮定

{"apple"=>130, "strawberry"=>180, "orange"=>100}.each do |fruit, price| 
# ハッシュの内容を順にキーをfruit、値をpriceに代入して繰り返す
 puts "#{fruit}#{price}円です。" # 変数展開
end
# 結果
apple130円です。
strawberry180円です。
orange100円です。

地味にポイントなのが、|fruit, price|のように、左から入っていく。
さらに付け加えると、平均点を求める際にも使える。

xxx_controller.rb
ratesum = 0 # 初期値は0
    movie_comments.each do |comment| # movie_commentsを繰り返し、1つ1つをcommentとする
      ratesum += comment.rate # 初期値0に対して、comment.rateという数字を存在するだけ、足し算し続ける
    end

# @averageに平均点を代入
# movie_comments.length(配列の要素数を取得)した時、0なら0,0ではなかったら計算して欲しい処理をしてもらう
    @average = movie_comments.length == 0 ? 0
    : ratesum / movie_comments.length
# comment.rateを足し続けた数字に対して、存在する配列の要素数で割ることで、平均点を割り出す。

break

繰り返し処理を中断することができる。
whileやfor、eachなどの繰り返し文の中で利用する。

i = 0
while i <= 10 do
 if i >5
   break # iが5より大きくなると繰り返しから抜ける
 end
 puts i
 i += 1
end

# 結果
0
1
2
3
4
5

追加あれば、どんどん更新していきます。

駆け出しとして頑張ります。

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

Rubyの基礎について

最初に

自身で学習してきた内容を書き出していきます。
基本的には自分自身でわかるような内容になりますので、ご容赦ください。
また、誤っている点がありましたら、コメントにてご指摘ください。

!! (要確認)マークがあるものは実行前に必ずググるべき内容 !!

index

  • 基本文法
    • Hello, World!
    • データ型について
  • オブジェクトとメソッド
    • オブジェクトとは
    • メソッドとは
  • 変数と定数
    • 変数とは
    • 定数とは
  • データ型
    • 数値(0除算について
    • 文字列
    • 配列
    • ハッシュ
    • シンボル
  • 条件分岐
    • if文
    • 比較演算子
    • 論理演算子
    • elsif、else
  • 繰り返し処理
    • 文字列や数字をキーボードから入力する方法
      • gets
      • to_i
    • 繰り返し処理や要素を順に取り出す処理
      • while文
      • for文
      • each文
      • break

〜主な流れ〜

ターミナル
$ touch xxx.rb # ターミナルコマンドでからファイルを作成、ここに処理を記述していく
$ ruby xxx.rb # 実行
# 結果
# 記述した処理がここに出る

以後は省略。

基本文法

Hello, World!

puts 'Hello, World!'
# 結果
Hello, World!

基本的に、xxx.rbのようなファイルを作りやりたいことを記述、ターミナルで動かす流れ。

データ型について

文字列型(String「""」)と整数型(Integer「 」)がよく使われる。
詳しくは後日、投稿予定。

puts 5 + 3
puts "5 + 3"
puts "5" + "3"
# 結果
8 # 両方integerなので、「5+3」の計算結果
5 + 3 # 計算式をstringで囲っているので、「文字としての"5+3"」が表示される結果
53 # 文字列と文字列を足しているので、「文字としての"5"と"3"」が連続して表示される結果

同じ見た目のメソッドでも、中身が異なる場合があるが、めちゃくちゃ便利。

文字列型、整数型の型変換

to_s 
27.to_s # 数字→文字へ変換,この場合は文字列型の27になる

to_i
"27"to_i # 文字→数字へ変換,この場合は整数型の27になる

オブジェクトとメソッド

オブジェクトとは

操作対象の状態(データ)と便利な機能(メソッド)を1つにまとめたもの
オブジェクトは「~~のもの」

メソッドとは

オブジェクトが持っている固有の機能や処理のこと
メソッドは「~~するもの」

つまり・・・?

puts "XXXYYYY".length
# 結果
7 # 「文字列オブジェクト」を「lengthメソッドで文字数を数え」、結果を「出力(puts)」

他にも、「reverse(文字列を逆にする)」メゾットや、「include?(引数に指定した文字列が含まれているか)」メゾットなどがある。
(メソッドが使えるのかを一覧表示したい場合は、使用可能なメソッド一覧を表示できる「methods」メソッド)
include?の場合は、「true(真)」か「false(偽)」で結果が表示される。

変数と定数

変数とは

オブジェクトにつける名札。
名札をつけることで、欲しいデータを呼び出すことが用意になる。
ただし、変数名は小文字で始まるルールがあり、尚且つ数字は使えない

test = "テストコメントが表示されることを確認しよう"
puts test
# 結果
テストコメントが表示されることを確認しよう

変数名 = 入れたいデータ
ポイント : 「=」は数学的な等しいという意味ではなく、「右のオブジェクトを左の変数に代入する」という意味。

定数とは

あらかじめ変更しないことが保証された数値やデータにつける名札。
基本的に大文字で始まる
⚠️値を書き換えることができない点に注意⚠️
(warning(実行不可能なエラーではないが非推奨な使い方)が表示されて書き換えができてしまうが、エラーが出る時点でアウト。)

Xx = 3.14
puts Xx
# 結果
3.14

Pi = 3.14
puts Pi

Pi = 100
puts Pi
# 結果
3.14
pi.rb:4: warning: already initialized constant Pi
pi.rb:1: warning: previous definition of Pi was here
100 

データ型

数値

「1」「100」「-100」「0」などの整数や、「3.14」などの小数のこと
メソッドとしては、+(足し算)、-(引き算)、/(割り算)、*(掛け算)、%(割り算の余りの計算)などの四則演算。

puts 100 
# 結果
100

puts 100 + 5
# 結果
105

ポイント : 0除算について
任意の数字 を0で割ってはいけないという数学的ルールがプログラミングにも適用されるぞ。
詳しくは後日投稿予定。
[四則演算注意点] 数字を0で割ってはいけないプログラム計算式

文字列

シングルクォートやダブルクォートで囲むことで、文字列として出力。
文字列の中に#{変数}と記述することによって、{}内の変数の値を展開可能。
+を使うと、文字列の連結も可能になり、文字列同士だけでなく、文字列と変数、または変数同士でも、連結できる。

puts "Rubyを学習"
# 結果
Rubyを学習

name = "テストさん"
puts "Hello" + name
# 結果
Helloテストさん

puts "こんにちは#{name}"
# 結果
こんにちはテストさん

配列

配列を使うと、複数のデータをまとめて保存できる。
配列の各要素には、どのようなオブジェクトでも代入でき、同じ配列の要素に異なった種類のオブジェクトを代入することも可能。
しかも、データ型は問われず、文字列でも数値でも代入できる。
ただし、先頭から0、1、2というように「インデックス番号」が割り振られ、「0」から開始することに注意。

names = ["Ruby", "Java", "Python", "PHP"]
puts names[1]

# 結果
Java

ポイント : たまにeach文で回したデータをarray(からの箱的な感じ)に突っ込んだりする。

ハッシュ

ハッシュは、キーと値をセットで保存できるデータの集合。
配列では各要素にインデックス番号が割り振られるが、ハッシュでは任意の値(キー)を割り振ることができる。

tall = {"あい"=>185, "うえ"=>170, "お!"=>150}
puts tall["あい"]
# 結果
185

シンボル

シングルクォートやダブルクォートの代わりにコロンを使用することで、文字列と同様に扱うことが可能。

tall = {:かき=>185, :くけ=>170, :こ!=>150}
puts tall[:こ!]
# 結果
150 

条件分岐

if文

「もしAならばBを実行し、AでなければCを実行」
条件に当てはまればtrue、当てはまらなければfalseという真偽判定ができる。

apple = 110

if apple >= 100
    puts "りんごの値段は100円以上です" #true=>処理される
end

if apple <= 100
    puts "りんごの値段は100円以下です" #false=>処理されない
end

# 結果
りんごの値段は100円以上です # apple = 110のためtrue処理になった

比較演算子

「条件を比較して判定する」時に使用する記号とかのこと。

<   # 右辺の方が大きい
<=  # 右辺の方が大きい、または等しい
>   # 左辺の方が大きい
>=  # 左辺の方が大きい、または等しい

# 数値だけでなく、文字列の比較(同じ文字列かどうか)でも利用できる
==  # 右辺と左辺が等しい
!=  # 右辺と左辺が等しくないとき

論理演算子

「2つ以上の条件を判定する」時に使用する。

!  # 否定
&&  # どちらの条件も正しければtrue
||  # どちらかの条件が正しければtrue 

tall = 180
if tall >= 170 && tall <= 190
    puts "身長は170以上190以下です。"
end
# 結果
身長は170以上190以下です。

elsif、else

if(もし〜)文では、elsif(それとも〜)やelse(当てはまらないなら〜)を使うことで、ifの条件に当てはまらなかった場合の処理を設定できる。

else

apple = "A"

if apple == "A" #もしappleがAなら 
    puts "それはAのリンゴです"
else #ifが当てはまらない場合は
    puts "そのリンゴはAではないです"
end
# 結果
A typeのリンゴです

elsifとelse

apple = "B"

if apple == "A" #もしappleがAなら
    puts "そのりんごはAです"
elsif apple == "B" #それともappleがBなら
    puts "そのりんごはBです"
else #どちらも当てはまらない場合は
    puts "そのりんごはCです"
end
# 結果
そのりんごはBです

よくFizzBuzz問題としてここあたりが使われると思う。

繰り返し処理

全て100%覚える必要はないが、処理ごとの特徴くらいは覚えよう。

文字列や数字をキーボードから入力する方法

gets

キーボード入力された値を取得するメソッド。
getsを使うと、キー入力待ちの状態になり、文字を入力してEnterキーを押すまで次に進まない。
文字列として返されるぞ。

to_i

文字列を整数に変換するメソッド。
例えば、getsメゾットにto_iを使って文字列(String)を整数(Integer)に変換すると計算ができる。

puts "キーボードで数字「2」と数字「3」を入力してください"
  a=gets.to_i
  b=gets.to_i
  puts "a+b=#{a+b}"
# 結果
a+b=5

繰り返し処理や要素を順に取り出す処理

while文

繰り返し処理を行いたいときに使用し、指定した条件式が真(true)の間、繰り返し実行されるのが特徴。

while 条件 do #条件がtrueの間
    処理 #処理を繰り返す
end

# サイコロを振って「6」が出るまで繰り返し実行
dice = 0 # diceに0を代入し、初期値を設定
while dice != 6 do # サイコロの目が6ではない間、diceの初期値は0なので条件を満たす。
                   # 以降はdiceに代入される数によって結果が異なる
    dice = rand(1..6) # 1~6の数字がランダムに出力
    puts dice    
end
# 結果
~
6 # サイコロを振って「6」が出るまで繰り返し実行された最後の数字として6で繰り返し処理が止まる

for文

繰り返し処理を行いたいときに使用し、指定したオブジェクトから順に値を取り出しながら繰り返されるのが特徴。

for 変数 in 範囲、ハッシュ、配列などを指定 do
    処理
end

# for ~ end内で、1から10まで順番に表示する処理を繰り返す実行
for i in 1..10 do # 1..10は、1~10までの範囲を表す
    puts i
end
# 結果
1
2
3
~
9
10 # 1から10まで順番に表示されるため10で止まる

eachメソッド

オブジェクト内の要素を順に取り出すメソッド。
個人的に一番使う繰り返し処理。
繰り返し処理を行いたいときに使用し、データの先頭から順に繰り返して処理されるmのが特徴。

(範囲、ハッシュ、配列などを指定).each do |変数|
  実行する処理
end

apple 130
strawberry180
orange100
という結果が欲しいと仮定

{"apple"=>130, "strawberry"=>180, "orange"=>100}.each do |fruit, price| 
# ハッシュの内容を順にキーをfruit、値をpriceに代入して繰り返す
 puts "#{fruit}#{price}円です。" # 変数展開
end
# 結果
apple130円です。
strawberry180円です。
orange100円です。

地味にポイントなのが、|fruit, price|のように、左から入っていく。
さらに付け加えると、平均点を求める際にも使える。

xxx_controller.rb
ratesum = 0 # 初期値は0
    movie_comments.each do |comment| # movie_commentsを繰り返し、1つ1つをcommentとする
      ratesum += comment.rate # 初期値0に対して、comment.rateという数字を存在するだけ、足し算し続ける
    end

# @averageに平均点を代入
# movie_comments.length(配列の要素数を取得)した時、0なら0,0ではなかったら計算して欲しい処理をしてもらう
    @average = movie_comments.length == 0 ? 0
    : ratesum / movie_comments.length
# comment.rateを足し続けた数字に対して、存在する配列の要素数で割ることで、平均点を割り出す。

break

繰り返し処理を中断することができる。
whileやfor、eachなどの繰り返し文の中で利用する。

i = 0
while i <= 10 do
 if i >5
   break # iが5より大きくなると繰り返しから抜ける
 end
 puts i
 i += 1
end

# 結果
0
1
2
3
4
5

追加あれば、どんどん更新していきます。

駆け出しとして頑張ります。

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

Rubyについて(基礎)

最初に

自身で学習してきた内容を書き出していきます。
基本的には自分自身でわかるような内容になりますので、ご容赦ください。
また、誤っている点がありましたら、コメントにてご指摘ください。

!! (要確認)マークがあるものは実行前に必ずググるべき内容 !!

index

  • 基本文法
    • Hello, World!
    • データ型について
  • オブジェクトとメソッド
    • オブジェクトとは
    • メソッドとは
  • 変数と定数
    • 変数とは
    • 定数とは
  • データ型
    • 数値(0除算について
    • 文字列
    • 配列
    • ハッシュ
    • シンボル
  • 条件分岐
    • if文
    • 比較演算子
    • 論理演算子
    • elsif、else
  • 繰り返し処理
    • 文字列や数字をキーボードから入力する方法
      • gets
      • to_i
    • 繰り返し処理や要素を順に取り出す処理
      • while文
      • for文
      • each文
      • break

〜主な流れ〜

ターミナル
$ touch xxx.rb # ターミナルコマンドでからファイルを作成、ここに処理を記述していく
$ ruby xxx.rb # 実行
# 結果
# 記述した処理がここに出る

以後は省略。

基本文法

Hello, World!

puts 'Hello, World!'
# 結果
Hello, World!

基本的に、xxx.rbのようなファイルを作りやりたいことを記述、ターミナルで動かす流れ。

データ型について

文字列型(String「""」)と整数型(Integer「 」)がよく使われる。
詳しくは後日、投稿予定。

puts 5 + 3
puts "5 + 3"
puts "5" + "3"
# 結果
8 # 両方integerなので、「5+3」の計算結果
5 + 3 # 計算式をstringで囲っているので、「文字としての"5+3"」が表示される結果
53 # 文字列と文字列を足しているので、「文字としての"5"と"3"」が連続して表示される結果

同じ見た目のメソッドでも、中身が異なる場合があるが、めちゃくちゃ便利。

文字列型、整数型の型変換

to_s 
27.to_s # 数字→文字へ変換,この場合は文字列型の27になる

to_i
"27"to_i # 文字→数字へ変換,この場合は整数型の27になる

オブジェクトとメソッド

オブジェクトとは

操作対象の状態(データ)と便利な機能(メソッド)を1つにまとめたもの
オブジェクトは「~~のもの」

メソッドとは

オブジェクトが持っている固有の機能や処理のこと
メソッドは「~~するもの」

つまり・・・?

puts "XXXYYYY".length
# 結果
7 # 「文字列オブジェクト」を「lengthメソッドで文字数を数え」、結果を「出力(puts)」

他にも、「reverse(文字列を逆にする)」メゾットや、「include?(引数に指定した文字列が含まれているか)」メゾットなどがある。
(メソッドが使えるのかを一覧表示したい場合は、使用可能なメソッド一覧を表示できる「methods」メソッド)
include?の場合は、「true(真)」か「false(偽)」で結果が表示される。

変数と定数

変数とは

オブジェクトにつける名札。
名札をつけることで、欲しいデータを呼び出すことが用意になる。
ただし、変数名は小文字で始まるルールがあり、尚且つ数字は使えない

test = "テストコメントが表示されることを確認しよう"
puts test
# 結果
テストコメントが表示されることを確認しよう

変数名 = 入れたいデータ
ポイント : 「=」は数学的な等しいという意味ではなく、「右のオブジェクトを左の変数に代入する」という意味。

定数とは

あらかじめ変更しないことが保証された数値やデータにつける名札。
基本的に大文字で始まる
⚠️値を書き換えることができない点に注意⚠️
(warning(実行不可能なエラーではないが非推奨な使い方)が表示されて書き換えができてしまうが、エラーが出る時点でアウト。)

Xx = 3.14
puts Xx
# 結果
3.14

Pi = 3.14
puts Pi

Pi = 100
puts Pi
# 結果
3.14
pi.rb:4: warning: already initialized constant Pi
pi.rb:1: warning: previous definition of Pi was here
100 

データ型

数値

「1」「100」「-100」「0」などの整数や、「3.14」などの小数のこと
メソッドとしては、+(足し算)、-(引き算)、/(割り算)、*(掛け算)、%(割り算の余りの計算)などの四則演算。

puts 100 
# 結果
100

puts 100 + 5
# 結果
105

ポイント : 0除算について
任意の数字 を0で割ってはいけないという数学的ルールがプログラミングにも適用されるぞ。
詳しくは後日投稿予定。
[四則演算注意点] 数字を0で割ってはいけないプログラム計算式

文字列

シングルクォートやダブルクォートで囲むことで、文字列として出力。
文字列の中に#{変数}と記述することによって、{}内の変数の値を展開可能。
+を使うと、文字列の連結も可能になり、文字列同士だけでなく、文字列と変数、または変数同士でも、連結できる。

puts "Rubyを学習"
# 結果
Rubyを学習

name = "テストさん"
puts "Hello" + name
# 結果
Helloテストさん

puts "こんにちは#{name}"
# 結果
こんにちはテストさん

配列

配列を使うと、複数のデータをまとめて保存できる。
配列の各要素には、どのようなオブジェクトでも代入でき、同じ配列の要素に異なった種類のオブジェクトを代入することも可能。
しかも、データ型は問われず、文字列でも数値でも代入できる。
ただし、先頭から0、1、2というように「インデックス番号」が割り振られ、「0」から開始することに注意。

names = ["Ruby", "Java", "Python", "PHP"]
puts names[1]

# 結果
Java

ポイント : たまにeach文で回したデータをarray(からの箱的な感じ)に突っ込んだりする。

ハッシュ

ハッシュは、キーと値をセットで保存できるデータの集合。
配列では各要素にインデックス番号が割り振られるが、ハッシュでは任意の値(キー)を割り振ることができる。

tall = {"あい"=>185, "うえ"=>170, "お!"=>150}
puts tall["あい"]
# 結果
185

シンボル

シングルクォートやダブルクォートの代わりにコロンを使用することで、文字列と同様に扱うことが可能。

tall = {:かき=>185, :くけ=>170, :こ!=>150}
puts tall[:こ!]
# 結果
150 

条件分岐

if文

「もしAならばBを実行し、AでなければCを実行」
条件に当てはまればtrue、当てはまらなければfalseという真偽判定ができる。

apple = 110

if apple >= 100
    puts "りんごの値段は100円以上です" #true=>処理される
end

if apple <= 100
    puts "りんごの値段は100円以下です" #false=>処理されない
end

# 結果
りんごの値段は100円以上です # apple = 110のためtrue処理になった

比較演算子

「条件を比較して判定する」時に使用する記号とかのこと。

<   # 右辺の方が大きい
<=  # 右辺の方が大きい、または等しい
>   # 左辺の方が大きい
>=  # 左辺の方が大きい、または等しい

# 数値だけでなく、文字列の比較(同じ文字列かどうか)でも利用できる
==  # 右辺と左辺が等しい
!=  # 右辺と左辺が等しくないとき

論理演算子

「2つ以上の条件を判定する」時に使用する。

!  # 否定
&&  # どちらの条件も正しければtrue
||  # どちらかの条件が正しければtrue 

tall = 180
if tall >= 170 && tall <= 190
    puts "身長は170以上190以下です。"
end
# 結果
身長は170以上190以下です。

elsif、else

if(もし〜)文では、elsif(それとも〜)やelse(当てはまらないなら〜)を使うことで、ifの条件に当てはまらなかった場合の処理を設定できる。

else

apple = "A"

if apple == "A" #もしappleがAなら 
    puts "それはAのリンゴです"
else #ifが当てはまらない場合は
    puts "そのリンゴはAではないです"
end
# 結果
A typeのリンゴです

elsifとelse

apple = "B"

if apple == "A" #もしappleがAなら
    puts "そのりんごはAです"
elsif apple == "B" #それともappleがBなら
    puts "そのりんごはBです"
else #どちらも当てはまらない場合は
    puts "そのりんごはCです"
end
# 結果
そのりんごはBです

よくFizzBuzz問題としてここあたりが使われると思う。

繰り返し処理

全て100%覚える必要はないが、処理ごとの特徴くらいは覚えよう。

文字列や数字をキーボードから入力する方法

gets

キーボード入力された値を取得するメソッド。
getsを使うと、キー入力待ちの状態になり、文字を入力してEnterキーを押すまで次に進まない。
文字列として返されるぞ。

to_i

文字列を整数に変換するメソッド。
例えば、getsメゾットにto_iを使って文字列(String)を整数(Integer)に変換すると計算ができる。

puts "キーボードで数字「2」と数字「3」を入力してください"
  a=gets.to_i
  b=gets.to_i
  puts "a+b=#{a+b}"
# 結果
a+b=5

繰り返し処理や要素を順に取り出す処理

while文

繰り返し処理を行いたいときに使用し、指定した条件式が真(true)の間、繰り返し実行されるのが特徴。

while 条件 do #条件がtrueの間
    処理 #処理を繰り返す
end

# サイコロを振って「6」が出るまで繰り返し実行
dice = 0 # diceに0を代入し、初期値を設定
while dice != 6 do # サイコロの目が6ではない間、diceの初期値は0なので条件を満たす。
                   # 以降はdiceに代入される数によって結果が異なる
    dice = rand(1..6) # 1~6の数字がランダムに出力
    puts dice    
end
# 結果
~
6 # サイコロを振って「6」が出るまで繰り返し実行された最後の数字として6で繰り返し処理が止まる

for文

繰り返し処理を行いたいときに使用し、指定したオブジェクトから順に値を取り出しながら繰り返されるのが特徴。

for 変数 in 範囲、ハッシュ、配列などを指定 do
    処理
end

# for ~ end内で、1から10まで順番に表示する処理を繰り返す実行
for i in 1..10 do # 1..10は、1~10までの範囲を表す
    puts i
end
# 結果
1
2
3
~
9
10 # 1から10まで順番に表示されるため10で止まる

eachメソッド

オブジェクト内の要素を順に取り出すメソッド。
個人的に一番使う繰り返し処理。
繰り返し処理を行いたいときに使用し、データの先頭から順に繰り返して処理されるmのが特徴。

(範囲、ハッシュ、配列などを指定).each do |変数|
  実行する処理
end

apple 130
strawberry180
orange100
という結果が欲しいと仮定

{"apple"=>130, "strawberry"=>180, "orange"=>100}.each do |fruit, price| 
# ハッシュの内容を順にキーをfruit、値をpriceに代入して繰り返す
 puts "#{fruit}#{price}円です。" # 変数展開
end
# 結果
apple130円です。
strawberry180円です。
orange100円です。

地味にポイントなのが、|fruit, price|のように、左から入っていく。
さらに付け加えると、平均点を求める際にも使える。

xxx_controller.rb
ratesum = 0 # 初期値は0
    movie_comments.each do |comment| # movie_commentsを繰り返し、1つ1つをcommentとする
      ratesum += comment.rate # 初期値0に対して、comment.rateという数字を存在するだけ、足し算し続ける
    end

# @averageに平均点を代入
# movie_comments.length(配列の要素数を取得)した時、0なら0,0ではなかったら計算して欲しい処理をしてもらう
    @average = movie_comments.length == 0 ? 0
    : ratesum / movie_comments.length
# comment.rateを足し続けた数字に対して、存在する配列の要素数で割ることで、平均点を割り出す。

break

繰り返し処理を中断することができる。
whileやfor、eachなどの繰り返し文の中で利用する。

i = 0
while i <= 10 do
 if i >5
   break # iが5より大きくなると繰り返しから抜ける
 end
 puts i
 i += 1
end

# 結果
0
1
2
3
4
5

追加あれば、どんどん更新していきます。

駆け出しとして頑張ります。

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

rails6 ActionTextの関連付けされない画像データをバックグラウンド削除してみた

はじめに

Rails6の新機能の1つのActionText(リッチテキストコンテンツと編集をかんたんに導入できる)の関連付けされてない画像データをsidekiqでバックグラウンド削除しようという内容です。
削除jobを実行するPostモデル(ポスト)と実行しないArticleモデル(記事)を使って比較していきます。

WEBページ

https://hks-portfolio.work/

GitHub

https://github.com/hironakazuki/aws_on_rails6/

環境

  • Ruby 2.6.3
  • Rails 6.0.2
  • gem aws-sdk-s3 1.60.2

問題点

こちらがActionTextを実装したPostモデルとArticleモデルのER図になります。
er-diagram.png

active_storage_blobsにアップロードした画像が保存され、投稿を完了(save)した時にactive_storage_attachmentsにaction_text_rich_textsとの関連情報が保存されます。

しかし、画像をアップロードした時点で active_storage_blobs に新規レコードが作成されてしまい関連付けされなかった場合にも残り続けてしまいます。

以下sidekiqでjobを実行していないArticleモデルでのgifです。
↓一覧画面の上部にactive_storage_attachments, active_storage_blobsを表示しています
ezgif.com-video-to-gif-compressor.gif

画像添付をやめたのにActiveStorage::Blobの数: 2に増えてしまっています。

本題

関連付けされなかったデータを削除するjobを作成します。

app/jobs/delete_unreferenced_blob_job.rb
class DeleteUnreferencedBlobJob < ApplicationJob
  sidekiq_options queue: :default, retry: 3
  require 'aws-sdk-s3' 

  def perform(*args)
    # 全Blobのidを取得
    blob_ids = ActiveStorage::Blob.pluck(:id)
    # 関連付けされているBlobの取得
    _blob_ids = ActiveStorage::Attachment.pluck(:blob_id).uniq
    # 関連付けされていないBlobの割り出し
    unreferenced_blob_ids = blob_ids - _blob_ids
    # 関連付けされていないBlobの画像ファイルを削除
    if Rails.env.production?
      s3 = Aws::S3::Resource.new(
        region: 'ap-northeast-1',
        credentials: Aws::Credentials.new(
          Rails.application.credentials.dig(:aws, :access_key_id),  # S3用アクセスキー
          Rails.application.credentials.dig(:aws, :secret_access_key)  # S3用シークレットアクセスキー
        )
      )
      bucket = s3.bucket('aws-on-rails6')
      unreferenced_blob_ids.each do |id|
        s3_file_key = ActiveStorage::Blob.find(id).key
        bucket.object(s3_file_key).delete
        bucket.objects({prefix: "variants/#{s3_file_key}"}).batch_delete!
      end
    end
    # 関連付けされていないBlobの削除
    ActiveStorage::Blob.where(id: unreferenced_blob_ids).delete_all
  end
end

本番環境のみs3の画像ファイルも削除しています。

これをPostモデルのsave, update, delete時に実行します。

posts_controller.rb
def create
    if @post.save
            DeleteUnreferencedBlobJob.perform_later
                    .
                    .
def update                                   
    if @post.update
            DeleteUnreferencedBlobJob.perform_later
                    .
                    .
def destroy
    DeleteUndreferencedBlobJob.perform_later if @post.destroy
                    .
                    .

Postモデルで先程と同じことをしてみます。
ezgif.com-video-to-gif (1).gif

ActiveStorage::Blobの数: 1となり削除されているのが確認できました!

注意点

この実装だとユーザーAが画像添付ポストを作成時、ユーザーBが別のポストをsaveやupdateした場合でもユーザーAのsave前の関連付けされていない画像データが消えてしまうので、複数ユーザーが利用するサービスではメンテナンス時などに実行するのが望ましいでしょう。

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

dependent: :destroyをつけていても削除できなかった原因

長時間調べたが解決方法が見つからないという状態が続きました。
私のミスだったので、同じような状態の人に私の経験を伝えれればと思い書きます。

内容

紐付けしたオブジェクトを削除するために、dependent: :destroyをつけても、削除できない時に気をつけて欲しいことを書きます。

問題点

ある特定のUserのみ削除できない

モデルの関係性

UserCartが親子関係で、
CartProductCart_productsと 1対多 です。
Users ー Carts → Cart_products ← Products

class User < ApplicationRecord
  has_one :cart, dependent: :destroy
end

class Cart < ApplicationRecord
  belongs_to :user
  has_many :cart_products, dependent: :destroy
  has_many :products, through: :cart_products
end

class Product < ApplicationRecord
  has_many :cart_products, dependent: :destroy
end

class CartProduct < ApplicationRecord
  belongs_to :cart
  belongs_to :product
end

行っていたこと

関連付けられたオブジェクトもdestroy削除できるように
has_many,has_onedependent: :destroyをつけていた。

エラー文

Cannot delete or update a parent row: a foreign key constraint fails (`app_name_development`.`cart_products`, CONSTRAINT `fk_rails_a4f3e327f3` FOREIGN KEY (`cart_id`) REFERENCES `carts` (`id`))

原因

User has_one CartUser にはCartがは1つのみ紐づくようにしていたのですが、
削除できないUserには2つのCartが紐つかれていた。

そのため
User has_one CartUser has_many Cartsと変更すると削除できた。

+----+---------------------+---------------------+---------+
| id | created_at          | updated_at          | user_id |
+----+---------------------+---------------------+---------+
|  3 | 2020-02-21 07:07:57 | 2020-02-21 07:07:57 |       4 |
| 12 | 2020-02-21 07:29:37 | 2020-02-21 07:29:37 |       7 |
| 13 | 2020-02-21 07:34:26 | 2020-02-21 07:34:26 |       7 |
| 16 | 2020-02-22 07:51:47 | 2020-02-22 07:51:47 |      11 |
+----+---------------------+---------------------+---------+

今後の対処法

モデルを作成する時に、オプションとしてunique: trueを付け、userの重複をなしにする。

XXXXXXX_create_cart.rb
class CreateCarts < ActiveRecord::Migration[5.2]
  def change
    create_table :carts do |t|
      t.references :user, foreign_key: true, unique: true #ここ

      t.timestamps
    end
  end
end

結論

User has_one Cart 関係の
User に1つのみのCartが紐づくようにしていたのですが、
削除できないUserには2つのCartが紐つかれていた。

そのため、以下のようにUser has_one CartUser has_many Cartsと変更すると削除できた。

class User < ApplicationRecord
  has_one :cart, dependent: :destroy
end

⬇︎

class User < ApplicationRecord
  has_many :cart, dependent: :destroy
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでのCookieのSameSite, Secureの対応

概要

2020年2月にChromeのバージョンが80にアップデートされました。
これはCSRFを防ぐためChromeのCookieのSameSite属性をデフォルトでSameSite=Laxにしようというものです。アップデート前はSameSite=Noneと同じ挙動をしていました。

つまりアップデート前までSameSite=Noneでないと正常に動作しないアプリケーションについては明示的にNoneを指定する必要があります。

https://developers-jp.googleblog.com/2019/11/cookie-samesitenone-secure.html
から抜粋

2月のChrome 80 以降、SameSite 値が宣言されていない Cookie は SameSite=Lax として扱われます。
外部アクセスは、SameSite=None; Secure 設定のある Cookie のみ可能になります。
ただし、これらが安全な接続からアクセスされることが条件です。

SameSiteの挙動の説明

SameSite 説明
None クロスドメインでCookieの受け渡しが可能
(ただしSecure=Trueの設定は必須のためhttpの環境だと正常に動作しないはずです)
Lax クロスドメインでGETメソッドであればCookieの受け渡しが可能だがPOSTメソッドは不可
Strict クロスドメインでGET、POSTメソッド両方ともCookieの受け渡しは不可

ただし、これはChromeでの挙動になります。

それ以外のブラウザでは違った挙動をするものがあるので注意が必要です。詳しくは下記のリンク先をご覧ください。
https://www.chromium.org/updates/same-site/incompatible-clients

この中で結構問題があると思われるのは
Versions of Safari and embedded browsers on MacOS 10.14 and all browsers on iOS 12. These versions will erroneously treat cookies marked with SameSite=None as if they were marked SameSite=Strict. This bug has been fixed on newer versions of iOS and MacOS
です。
現時点(2020/2/27)でiOS 12、MacOS 10.14はそれほど古いバージョンではなく使用しているユーザーも多いと思われます。このユーザーに対してはSameSite=Noneと設定しているとSameSite=Strictと同じ挙動をするそうです。
対応するとしたらブラウザによってcookieを書き換える必要がありそうです。

Ruby on RailsでSameSite=Noneを設定する対応例

こちらの記事ではgemでの対応が記載されているのでgemで対応したい方はこちらが良いと思います。
(rails_same_site_cookie gemで、RailsアプリにChrome 80向けのSameSite属性を指定する)

以降はRuby on Railsでの実装例を記載します。

最初にrack gemのバージョンを確認します。
rackが2.0系だとSameSite=Noneの対応が入っておらずエラーになるのでrackのバージョンアップをおこなうかRack::Utilsのモンキーパッチ以下のように作成します。
rackのバージョンアップ時に削除漏れがあるといけないので内容にTODOを記載しておきましょう。

# -*- encoding: binary -*-
# TODO: rack2.1.0以降だとsamesite=Noneの設定が入っているのでソースファイルごと削除する
module Rack
  # Rack::Utils contains a grab-bag of useful methods for writing web
  # applications adopted from all kinds of Ruby libraries.

  module Utils
    def add_cookie_to_header(header, key, value)
      case value
      when Hash
        domain  = "; domain=#{value[:domain]}"   if value[:domain]
        path    = "; path=#{value[:path]}"       if value[:path]
        max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
        # There is an RFC mess in the area of date formatting for Cookies. Not
        # only are there contradicting RFCs and examples within RFC text, but
        # there are also numerous conflicting names of fields and partially
        # cross-applicable specifications.
        #
        # These are best described in RFC 2616 3.3.1. This RFC text also
        # specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
        # fixed length format with space-date delimited fields.
        #
        # See also RFC 1123 section 5.2.14.
        #
        # RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
        # in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
        # the space delimited format. These formats are compliant with RFC 2822.
        #
        # For reference, all involved RFCs are:
        # RFC 822
        # RFC 1123
        # RFC 2109
        # RFC 2616
        # RFC 2822
        # RFC 2965
        # RFC 6265
        expires = "; expires=" +
          rfc2822(value[:expires].clone.gmtime) if value[:expires]
        secure = "; secure"  if value[:secure]
        httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
        same_site =
          case value[:same_site]
          when false, nil
            nil
          when :none, 'None', :None
            '; SameSite=None'.freeze
          when :lax, 'Lax', :Lax
            '; SameSite=Lax'.freeze
          when true, :strict, 'Strict', :Strict
            '; SameSite=Strict'.freeze
          else
            raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
          end
        value = value[:value]
      end
      value = [value] unless Array === value

      cookie = "#{escape(key)}=#{value.map { |v| escape v }.join('&')}#{domain}" \
        "#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"

      case header
      when nil, ''
        cookie
      when String
        [header, cookie].join("\n")
      when Array
        (header + [cookie]).join("\n")
      else
        raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
      end
    end
    module_function :add_cookie_to_header
  end
end

修正箇所は↓の部分を追記しただけです。

when :none, 'None', :None
            '; SameSite=None'.freeze

rackが2.1系以降であれば対応されているので上記の対応は不要です。

使い方はこんな感じでいけると思います。

cookies[:hogehoge] = { value: "sample value", expires: 1.hour.from_now, same_site: "None", secure: true }

環境によってSecure=True or Falseを設定したい場合

以下のようなClassを作成します。

# frozen_string_literal: true

class SecureCookieWithSameSiteLax

  def self.secure?
    Rails.env.staging? || Rails.env.production?
  end

end

使い方はこんな感じです。

secure = SecureCookieWithSameSiteLax.secure?

cookies[:hogehoge] = { value: "sample value", expires: 1.hour.from_now, same_site: "Lax", secure: secure }

production環境、staging環境ではSameSite=Lax Secure=True
development環境ではSameSite=Lax Secure=False
としたい場合の実装方法となります。

SameSite=Noneを設定しなくても問題なく動作するアプリケーションであればLaxStrictを明示的にセットしてsecureな設定にしておくのが良いと思います。

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

チーム開発を始める際にチーム全員でgithubを共有する方法(rails)

チーム開発をする際にgithubを全員で共有する方法

某プログラミングスクールにて、チーム開発を始める際に全員でgithubを共有するのに苦戦したので分かりやすく丁寧にまとめたいと思います。

まず用語の説明をします。

ブラウザ上でのgithub = リモート
ブラウザ上でのgithubでのリポジトリ = リモートリポジトリ (リポジトリとは、chat-spaceなどのアプリケーションを指します。)
自分のPC = ローカル
github desktop = ローカルリポジトリ

まず簡単に流れを掴み、それから1つ1つの詳細を説明していきます。

流れ

1)チームの一人(これからオーナーと呼びます)が自分のローカルでアプリケーションを立ち上げる。
2)オーナーがそのアプリケーションを自分のgithubリモートと繋げる。
3)オーナーがチームメンバーを自分が作ったリモートリポジトリに招待する。
4)メンバーたちがそのリモートリポジトリに入り、その雛形のコードを自分のローカルにコピーする。
5)全員が何かしらの変更点を自分のエディターに書き、pushし、全員がちゃんと1つのリモートリポジトリにつながっているか確認する
6)全員がブランチを切って作業を開始する。

それでは早速初めていきます。

1)チームの一人が自分のローカルでアプリケーションを立ち上げる。

チームの一人(基本的に誰でもOK)がローカルで普段何かアプリケーションを立ち上げる時にやっているように
rails newでアプリを立ち上げます。この際、できればバージョンを指定していた方が安全なので今回は5.2.3を指定して作っていく設定で行います。

今回作るアプリの名前はsampleと言う名前で作る設定で行います。

$ rails _5.2.3_ new sample -d mysql

2)オーナーがそのアプリケーションを自分のgithubリモートと繋げる。

次に、このアプリケーションをgithubに繋げます。

$ cd sample
$ git init   //現在のsampleをgit下に置きます
$ git add .   //全てのファイルを追加します
$ git status  //ちゃんと全てのファイルが入った確認できます

すると、このように全てのファイルがgit下に入ったことが確認できます。

Screenshot from Gyazo

次に、このgit下に移動できたファイル達をgithub desktopに追加していきます。

$ git commit -m "initial commit"

そして次にgithub desktopを開きます。
すると左上に
Screenshot from Gyazo

と言う表示があるので
Current Repositoryをクリックしてください。(この時点でsampleと表示されていますが気にしなくて大丈夫です)
その後
Add ボタンをクリックし、

Add Existing Repository...

をクリックすると

Screenshot from Gyazo

このような表示が出てくるかと思います。

ここで
Choose...

をクリックし、自分が作っているsampleファイルを選択し、
Add Repository

を選択すると、

Screenshot from Gyazo

このような画面が出てくると思います。

ここで左の列の

Screenshot from Gyazo

の欄のSummary(required)のところに、initial commit と記載し、
下の青いボタンのCommit to masterが押せるようになりますので、クリックしてください。

すると

Screenshot from Gyazo

このようにローカルに変更点はもうないよ。と言う表示が出ます。

そして、右側にある

Screenshot from Gyazo

で、Pubulish repositoryをクリックしてください。

すると

Screenshot from Gyazo

この表示が出ます。

Nameはそのままで大丈夫です。

Descriptionは空欄で大丈夫です。

その下のチェックボタンはこのコードをプライベートにする。と書いていますが、
今回はチーム開発をするため、このコードを共有しないといけません。

よって、チェックボタンは外してください。

Screenshot from Gyazo
この状態が正解です。

そしてPublish Repositoryをクリックしましょう。

そうすると、github ブラウザ(リモート)に、リモートリポジトリが作成されます。

おめでとうございます。

自分のgithubブラウザに行き、確認してください。
Screenshot from Gyazo

このように自分のアプリケーションが表示できましたでしょうか。

3)オーナーがチームメンバーを自分が作ったリモートリポジトリに招待する。

次に、このリモートリポジトリにメンバーを招待します。

まずこの画面でSetting ボタンをクリックしてください

Screenshot from Gyazo

するとこの画面に飛びますので、次に

Screenshot from Gyazo

Manage accessをクリックしてください。

するとパスワードを要求されますので入力してください。

すると

Screenshot from Gyazo

このような画面があるページに飛びます。

ここでInvite a collaboratorをクリックしましょう。
すると検索画面が出てきますので、メンバー達のgithubでのアカウント名を正しく記入し、追加していきます。

追加されたメンバー達は、githubmに登録した時に使用したメールアドレスにメールが届きますので確認してください。

すると、メールには
View Invitationと書かれているのでクリックします。 (※メールはPCで閲覧しましょう)
すると、githubのページに飛びますので、そこで
Accept Invitationをクリックすると、無事、オーナーが作ったリポジトリに参加できます。

4)メンバーたちがそのリモートリポジトリに入り、その雛形のコードを自分のローカルにコピーする。

お待たせしました。
ここからはやっとメンバー達も作業開始できます。
メンバー達はそのリポジトリに行ったあと、

Screenshot from Gyazo

この
Clone or download
をクリックし、URLをコピーします。

そしてgithub desktopに行きます。

Screenshot from Gyazo
そして左上にCurrent Repsitoryをクリックします。

そして
Add
をクリックし
Clone Repositoryをクリックします。

するとこのような画面が出てきます。

Screenshot from Gyazo

ここではURLを選択してください。

Screenshot from Gyazo

するとこのようなページに変わりますので

上の欄にはgithubブラウザでコピーしたURLをペーストします。

下のLocal Pathは、このコピーしたファイルをどこに入れるかを自分で決めることができます。

これで全員のローカル環境に、オーナーが作ったローカルと同じものがリモートを通じて繋げることができました。

ここからは実際に確認作業をします。

5)全員が1つのリモートリポジトリにつながっているか確認する.

これは簡単です。一人一人がマスターブランチで適当にREAD.Meなどに記載を加え、commit、pushしてください。
すると、一人一人が変えた場所がgithub desktopのhistryに反映されているはずです。

6)全員がブランチを切って作業を開始する。

ここからは実際にコードをバリバリ書いていく作業です。
全員ブランチを切り、各々の作業をしてください。

これで作業が開始できます。

以上になります。頑張ってください!

間違っている点やご指摘もお待ちしております。

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

COTOHA でキーワードの抽出 (Ruby)

COTOHA API Portal の使用例です。

key_word.rb
#! /usr/bin/ruby
# -*- encoding: utf-8 -*-
#
#   key_word.rb
#
#                   Feb/27/2020
#
# ---------------------------------------------------------------------
require 'faraday'
require 'json'
#
load 'get_config.rb'
load 'get_token.rb'
# ---------------------------------------------------------------------
def read_string_proc (file_in)
#
    str_out=""
    File.open(file_in,"r:UTF-8") do |ff|
        while line=ff.gets
            str_out += line
        end
    end
#
    return  str_out
end
# ---------------------------------------------------------------------
STDERR.puts "*** 開始 ***"
#
file_in = ARGV[0]
puts file_in
doc = read_string_proc (file_in)
#
config = get_config_proc()
#
access_token = get_token_proc(config)
#   
#
headers={
    "Content-Type": "application/json",
    "Authorization": "Bearer " + access_token
    }
#
data = {
    "document": doc,
    "type": "default"
    }

str_json = JSON.generate(data)
#
url = config['url_base'] + "v1/keyword"
#
con = Faraday.new 
res = con.post do |req|
    req.url url
    req.headers = headers
    req.body = str_json
    end
#
    puts res.status
    dict_aa=JSON.parse(res.body)
#
    dict_aa['result'].each {|unit|
        str_out = unit['form'] + "\t" + unit['score'].to_s
        puts str_out
    }
#
STDERR.puts "*** 終了 ***"
# ---------------------------------------------------------------------

get_config.rb get_token.rb はこちら
COTOHA API で構文解析 (Ruby)

実行結果

$ ./key_word.rb akai_rousoku.txt
*** 開始 ***
akai_rousoku.txt
200
猿 135.52966
蝋燭  83.9601
花火  78.08584
亀 43.078
火 42.81965
*** 終了 ***
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

COTOHA で固有名詞の抽出 (Ruby)

COTOHA API Portal の使用例です。

proper_noun.rb
#! /usr/bin/ruby
# -*- encoding: utf-8 -*-
#
#   proper_noun.rb
#
#                   Feb/27/2020
#
# ---------------------------------------------------------------------
require 'faraday'
require 'json'
#
load 'get_config.rb'
load 'get_token.rb'
# ---------------------------------------------------------------------
STDERR.puts "*** 開始 ***"
#
config = get_config_proc()
#
access_token = get_token_proc(config)
#   
sentence = "特急はくたかで富山に向かいます。それから、金沢に行って、兼六園に行きます。"
#
headers={
    "Content-Type": "application/json",
    "Authorization": "Bearer " + access_token
    }
#
data = {
    "sentence": sentence,
    "type": "default"
    }

str_json = JSON.generate(data)
#
url = config['url_base'] + "v1/ne"
#
con = Faraday.new 
res = con.post do |req|
    req.url url
    req.headers = headers
    req.body = str_json
    end
#
    puts res.status
    dict_aa=JSON.parse(res.body)
#
    dict_aa['result'].each {|unit|
        puts unit['form']
    }
#
STDERR.puts "*** 終了 ***"
# ---------------------------------------------------------------------

get_config.rb get_token.rb はこちら
COTOHA API で構文解析 (Ruby)

実行結果

$ ./proper_noun.rb
*** 開始 ***
200
富山
金沢
兼六園
*** 終了 ***
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

COTOHA API で構文解析 (Ruby)

COTOHA API Portal の使用例です。

parsing.rb
#! /usr/bin/ruby
# -*- encoding: utf-8 -*-
#
#   parsing.rb
#
#                   Feb/27/2020
#
# ---------------------------------------------------------------------
require 'faraday'
require 'json'
#
load 'get_config.rb'
load 'get_token.rb'
# ---------------------------------------------------------------------
STDERR.puts "*** 開始 ***"
#
config = get_config_proc()
#
access_token = get_token_proc(config)
#   
sentence = "特急はくたか"
#
headers={
    "Content-Type": "application/json",
    "Authorization": "Bearer " + access_token
    }
#
data = {
    "sentence": sentence,
    "type": "default"
    }

str_json = JSON.generate(data)
#
url = config['url_base'] + "v1/parse"
#
con = Faraday.new 
res = con.post do |req|
    req.url url
    req.headers = headers
    req.body = str_json
    end
#
    puts res.status
    dict_aa=JSON.parse(res.body)
#
    dict_aa['result'].each {|unit|
        unit['tokens'].each {|token|
            str_out = token['form'] + "\t" + token['pos']
            puts str_out
        }
    }
#
STDERR.puts "*** 終了 ***"
# ---------------------------------------------------------------------
get_config.rb
# -*- encoding: utf-8 -*-
#
#   get_config.rb
#
#                   Feb/27/2020
#
# ---------------------------------------------------------------------
require 'dotenv'
#
# ---------------------------------------------------------------------
def get_config_proc()
    Dotenv.load
    config = {}
    config["grantType"] = "client_credentials"
    config["clientId"] = ENV["CLIENT_ID"]
    config["clientSecret"] = ENV["CLIENT_SECRET"]
    config["url_publish"] = ENV['ACCESS_TOKEN_PUBLISH_URL']
    config["url_base"] = ENV['DEVELOPER_API_BASE_URL']
#
    return config
end
# ---------------------------------------------------------------------
get_token.rb
# -*- encoding: utf-8 -*-
#
#   get_token.rb
#
#                   Feb/27/2020
#
# ---------------------------------------------------------------------
require 'faraday'
require 'json'
#
# ---------------------------------------------------------------------
def get_token_proc(config)
    str_json = JSON.generate(config)
#
    headers={
        "Content-Type": "application/json"
        }
#
    con = Faraday.new 
    res = con.post do |req|
        req.url config['url_publish']
        req.headers['Content-Type'] = 'application/json'
        req.body = str_json
    end
#
    dict_aa=JSON.parse(res.body)
    access_token = dict_aa['access_token']
#
    return access_token
end
# ---------------------------------------------------------------------

実行結果

$ ./parsing.rb 
*** 開始 ***
200
特急  名詞
は 動詞語幹
く 動詞接尾辞
たか  名詞
*** 終了 ***
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Before Rails Tutorial4章 ログイン機能

本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用
4章:ログイン機能

3章で作成した、蔵書管理アプリ bukukore にログイン機能を付与していきます。
ログイン機能は、 Devise という GEM を用いて構築していきます。

Deviseの使い方はこちら

Devise

bukukore ディレクトリ内の、Gemfile を立ち上げて下記のコードを追記してください。

Gemfile(追記)
gem 'devise'

Gemfile に追記したら、 bundle install していきましょう。

terminal
$ bundle install --path vendor/bundle

devise のインストールが完了したら、deviseを適応させていきます。

terminal
$ rails g devise:install
(省略)
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

deviseをインストールすると、上記のようなメッセージが表示されます。
1つ目のメッセージに従い、デフォルトのURL を設定します。今回は指示通りで良いので、
config/environments/development.rb の一番下に下記のコードを追加します。

config/environments/development.rb(追加)
(省略)
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

2つ目のメッセージは、3つ目のメッセージは、のちほど追加します。
4つ目のメッセージでViewを作成するのですが、そのまえにModelをつくっておきます。
devise で使いたいのは User登録機能なので 下記のコマンドを実行します。

config/initializers/devise.rb(235行目あたりの該当箇所を書換)
config.scoped_views = true

config.scoped_views を false から true に書き換えてください。
(devise をどのフォルダにインストールするかの設定です。)
使い方はこちら

terminal
$ rails g devise User
  create   db/migrate/20200216064430_devise_create_users.rb
  create   app/models/user.rb
   route   devise_for :users

これで user.rb の他に、devise に関するマイグレーションファイルと、ルーティングが追加されています。
ルーティングは、 rails routes を実行することで確認できます。
マイグレーションファイルは、 db/migrate の中にあります。
devise では機能毎に ON / OFF 設定可能です。

モジュール名    各機能の説明
database_authenticatable データベースに保存されたパスワードが正しいかどうかの検証とを行ってくれます。またパスワードの暗号化も同時に行ってくれます。
registerable ユーザー自身がアカウント登録、編集、削除することを許可します。
recoverable パスワードをリセットできるようにし、メールで通知します。
rememberable 30日間ログインしたままにするというような永続ログインを可能にします。ログイン画面の下のチェックボックスにチェックすることで永続ログインを有効化できます。
trackable ユーザーのサインイン回数や、サインイン時間、IPアドレスなどを記録できるようにします。
validatable Emailやパスワードのバリデーションを可能にします。独自に定義したバリデーションを追加することもできます。
confirmable メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を可能にします。また、サインイン中にアカウントが認証済みかどうかを検証します。
lockable 一定回数ログインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。
timeoutable 一定時間活動していないアカウントのログインを破棄します。
omniauthable TwitterやFacebookなどのSNS認証を追加したい場合に使用します。
db/migrate/2020(省略)_devise_create_users.rb
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      t.string   :unlock_token # Only if unlock strategy is :email or :both
      t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    add_index :users, :confirmation_token,   unique: true
    add_index :users, :unlock_token,         unique: true
  end
end

上記のようにすべての機能をコメントアウトしておきます。(講義終了後に、興味ある学生が拡張して遊べるための配慮)
今回はメール認証を行いたいので Confirmable を使用します。
user.rb もすべての機能が使用できるように変更しておきます。

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

マイグレーションファイル を変更したので適応させましょう。
マイグレーションファイルの実行は下記のコマンドでしたね。

terminal
$ rails db:migrate
== 20200220202020 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0021s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0008s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0010s
== 20200220202020 DeviseCreateUsers: migrated (0.0041s) =======================

rails db:migrate した後は、 rails sever を再起動しておいてください。

マイグレーションを実行することでデータベースの中に、usersテーブルが作成されます。
4つ目のメッセージに書いてあるように、Devise が使用するView(アカウント作成関連) も作っていきます。

terminal
$ rails g devise:views user
  app/views/devise/shared/_links.html.erb (リンク用パーシャル)
  app/views/devise/confirmations/new.html.erb (認証メールの再送信画面)
  app/views/devise/passwords/edit.html.erb (パスワード変更画面)
  app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面)
  app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面)
  app/views/devise/registrations/new.html.erb (ユーザー登録画面)
  app/views/devise/sessions/new.html.erb (ログイン画面)
  app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面)
  app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文)
  app/views/devise/mailer/password_change.html.erb (メール用パスワード変更完了文)
  app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文)
  app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)

上記コマンドを実行することで、 Devise の機能により、ユーザー作成関連の View が自動生成されます。
この時点でアカウント作成画面などにアクセスは可能ですが、メール認証機能は実装していないのでアカウントは作れません。
http://localhost:3000/users/sign_up

いまのうちに、新規登録、ログインへのリンクを用意しておきましょう。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Bukukore</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? %>  
      <%= link_to 'ユーザー編集', edit_user_registration_path %> |  
      <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>  
    <% else %>  
      <%= link_to "新規登録", new_user_registration_path %> |  
      <%= link_to "ログイン", new_user_session_path %>  
    <% end %>  
    <%= yield %>
  </body>
</html>

メール認証

Google 二段階認証

config/initializers/devise.rb の21行目あたりにある
config.mailer_sender を自分の gmail アドレスに変更します。

config/initializers/devise.rb
config.mailer_sender = '[自分のアドレスに置き換えます]@gmail.com'  

最後に開発環境の設定ファイルの最後に以下を追加します。

config/environments/development.rb(追記)
(省略)
ActionMailer::Base.smtp_settings = {  
  address: 'smtp.gmail.com',  
  port: 587,  
  user_name: '[自分のアドレスに置き換えます]@gmail.com',  
  password: '先程取得した二段階認証のアプリパスワード',
  authentication: 'plain',  
  enable_starttls_auto: true  
}  

以上でメール認証処理は完了です。

「こんなの自力でわかるわけない!自力開発なんてできない」
と思うかもしれませんが、GEMの使い方はリファレンスにすべて書いてあるのでご安心ください。
今後、リファレンスを読む癖、調べる癖をつけていきましょう。
https://github.com/heartcombo/devise#getting-started
https://qiita.com/gakkie/items/6ef70c0788c3cbff81ee

実際にユーザー登録を行い、メールが届くことを確認できます。
http://localhost:3000/users/sign_up
届いたメールに記載されている URL をクリックすることで、認証完了です。
rails server のログにも URL が書いてあるので、そちらをコピペしても認証可能です。

どういう仕組みで、メールの認証やログイン・ログアウトが行われているのか詳しくは
https://railstutorial.jp/
で学べます。

ユーザーの一覧と詳細表示ページ

Userの一覧をみるページ、詳細情報をみるページを作成していきましょう。
Model はすでに作っているので、 Controller と View を作成します。

terminal
$ rails g controller Users index show

2つ目のメッセージにあったルート設定もここでしておきましょう。
ユーザーの一覧ページ(users#index)をTOPページにしたいので、下記コードを追加します。

config/routes.rb(追加)
(省略)
  root to: 'users#index'

これで、root(最初のページ)にアクセスがあった時に、usersControllerのindexアクションを呼び出します。
http://localhost:3000/

課題 MVCの復習

1, User Model を使い、users テーブルから全データを取り出し
2, user index ページに表示してみましょう。
3, user show ページにユーザー情報を表示してください http://localhost:3000/users/show/1

ヒント: Model は Controller に記述します。
一度何も見ずにやってみてみると理解度チェックになります。
曖昧な場合は、前章の内容を確認してください。

1の解答例はここをクリックすると表示されます
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
  end
end

2の解答例はここをクリックすると表示されます
app/views/users/index.html.erb
<h1>ユーザーの一覧</h1>

<% @users.each do |user| %>
  <p><%= user[:id] %></p>
  <p><%= user[:email] %></p>
<% end %>

3の解答例はここをクリックすると表示されます
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end
end
config/routes.rb(書き換え)
#   get 'users/show'(コメントアウト)
  get 'users/show/:id', to: 'users#show'
app/views/users/show.html.erb
<h1>ユーザーの情報</h1>

<p><%= @user.id %></p>
<p><%= @user.email %></p>

新しいカラムの追加方法

現在 id,email と2つのカラムがあります。これだけでは誰か分かりづらいので
名前を登録するために、 name カラムを追加してみましょう。

terminal
$ rails g migration AddNameToUsers name:string

migration コマンドのまとめはこちら

上記のコマンドで、usersテーブルに name カラムを追加するためのマイグレーションファイルが自動生成されます。

db/migrate/2020(省略)_add_name_to_users.rb(自動生成)
class AddNameToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :name, :string
  end
end

マイグレーションファイルを実行します。

terminal
$ rails db:migrate
== 20200222020202 AddNameToUsers: migrating ===================================
-- add_column(:users, :name, :string)
   -> 0.0017s
== 20200222020202 AddNameToUsers: migrated (0.0018s) ==========================

users テーブルに、nameカラムを追加することができました。

ストロングパラメータに name を許可する

しかし、追加しただけでは保存できないようになっています。
これは rails が自動的行っているセキュリティ対策の為です
ストロングパラメータについて

保存できるようにするためには、ストロングパラメータへの追加が必要です。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :permitted_parameters, if: :devise_controller?

  private

  def permitted_parameters
    devise_parameter_sanitizer.permit :sign_up, keys: [:name]
    devise_parameter_sanitizer.permit :account_update, keys: [:name]
  end

end 

permitted_parameters アクションの中を見てください。
devise_parameter_sanitizer.permit 【許可する場所】, keys: 【許可するカラム名】
というようにして、許可を与えます。

before_action は、このファイルが呼び出されたときに最初に実行されるものです。

これで新たに、名前を保存できるようになりました。

課題

1, 登録画面に、名前を登録するためのフォームを追加してください。
   http://localhost:3000/users/sign_up
2, 実際に名前を登録し、 show ページで名前を表示してください。
   http://localhost:3000/users/show/1 (必要があればidは登録した番号に変更してください)

BookテーブルとUserテーブルの関連付け

続いて、2章で学習したテーブルの関連付けの復習をやっていきます。
詳細ページに、そのユーザーが登録した、本の情報を表示していきましょう。

http://localhost:3000/users/show/1 ←ユーザーの詳細ページ

http://localhost:3000/books/new ←本の情報登録ページ
http://localhost:3000/books/show/1 ←登録内容を確認

課題

ユーザーの詳細ページに、登録した本の情報が表示されるようにしてください。

解答例はここをクリックすると表示されます
app/models/user.rb(1行追加)
class User < ApplicationRecord
   has_many :books

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :confirmable, :lockable, :timeoutable
end
app/models/book.rb
class Book < ApplicationRecord
  belongs_to :user
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @books = @user.books
  end
end
app/views/users/show.html.erb
<p><%= @user.id %></p>
<p><%= @user.email %></p>
<p><%= @user.name %></p>

<% @books.each do |book| %>
  <p>本のタイトル: <%= book.title %></p>
  <p>著者: <%= book.author %></p>
<% end %>

https://bbbbruno.qrunch.io/entries/wcNZqbMwA5SRBqQj

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

COTOHA アクセストークンの取得 (Ruby)

COTOHA API Portal の使用例です。

access_token.rb
#! /usr/bin/ruby
# -*- encoding: utf-8 -*-
#
#   access_token.rb
#
#                   Feb/27/2020
#
# ---------------------------------------------------------------------
require 'faraday'
require 'json'
require 'dotenv'
#
# ---------------------------------------------------------------------
STDERR.puts "*** 開始 ***"
#
Dotenv.load
#
data = {
    "grantType": "client_credentials",
    "clientId": ENV["CLIENT_ID"],
    "clientSecret": ENV["CLIENT_SECRET"]
    }
#
str_json = JSON.generate(data)
#
url = ENV['ACCESS_TOKEN_PUBLISH_URL']

headers={
    "Content-Type": "application/json"
    }
#
con = Faraday.new 
res = con.post do |req|
    req.url url
    req.headers['Content-Type'] = 'application/json'
    req.body = str_json
end

puts    res.status
puts    res.body
puts    "-----------" 

dict_aa=JSON.parse(res.body)
puts    dict_aa['access_token']
#
STDERR.puts "*** 終了 ***"
# ---------------------------------------------------------------------

実行結果

$ ./access_token.rb 
*** 開始 ***
201

          {
            "access_token": "oleleUiGwUIVjbvH*******", 
            "token_type": "bearer",
            "expires_in": "86399" ,
            "scope": "" ,    
            "issued_at": "1582767850104"           
           }

-----------
oleleUiGwUIVjbvHLonJD2OA3I8q
*** 終了 ***

使ったバージョン

$ ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyでvalidationするdry-validationの基本のキ

はじめに

RubyでAPIを作成する際にvalidationをいい感じにしてくれるgemを探したところ、dry-validationに行き着きました。
ただし、公式のリファレンスではよく分からないとか調べても古いバージョンの話だったとかがあったので、メモとして執筆時点で分かったことをメモとして残します。

環境

dry-validation 1.4
https://dry-rb.org/gems/dry-validation/1.4/

アプリケーションで使用するための基底クラスの作成

デフォルトだとメッセージが英語なので、日本語にするためにはconfigの設定が必要です。
なので基底クラスを使ってまとめて設定します。

class ApplicationContract < Dry::Validation::Contract
  config.messages.default_locale = :ja
  config.messages.backend = :yaml
  config.messages.load_paths << 'config/contract/error.yaml'
end

メッセージはconfig.messages.load_pathsに設定した箇所に置いてください。
中身は以下のような感じです。
詳しいconfigの意味とかは公式のリファレンスで十分なので、そちらをご覧ください。

https://dry-rb.org/gems/dry-validation/1.4/messages/#using-localized-messages-backend

ja:
  dry_validation:
    errors:
      filled?: '必須です'
      int?: '数字じゃありません'
      date?: '日付じゃありません'
      string?: '文字列じゃありません'

一番上にロケールを書く以外はフォーマットに沿って書いてあげる必要があります。

validation class

基本的な設定ができたので、実際にvalidationするためのクラスを例に使い方を書いていきます。
まずはサンプルをご覧ください。

class FormContract < ApplicationContract
  params do
    required(:start_date).filled(:date)
    required(:end_date).filled(:date)
    optional(:id).filled(:integer)
    required(:name).maybe(:string)
  end

  rule(:end_date, :start_date) do
    key(:start_date).failure('開始日は終了日よりも後ろにしてください') if values[:end_date] < values[:start_date]
  end

単項目チェック

単項目はprams部分に記述します。

  params do
    required(:start_date).value(:date)
    required(:end_date).filled(:date)
    optional(:id).filled(:integer)
    required(:name).maybe(:string)
  end 

パラメータチェック

パラメータが必須であるか任意であるかを記述します。
必須である場合はrequired、任意である場合はoptionalで宣言します。

値チェック

値チェックはいくつか種類があります。
ここでは簡単なチェックのみで、パラメータのチェックを正規表現使ってやりたいといったチェックは別で行う必要があります。(後述)

filled

値の存在チェック + 型変換しての型チェックです。
値が存在しない場合は存在しないエラーとなり、値があり型変換後の型が違ったら型エラーとなります

# emptyエラー
{locale}.dry_validation.errors.filled?

# 型エラー
{locale}.dry_validation.errors.int?
{locale}.dry_validation.errors.str?
{locale}.dry_validation.errors.date?

ちなみにfilledの引数に型を入れてるから型チェックしてくれるだけで、引数がなければ型チェックをスキップすることも可能です。

# 型チェックあり
required(:end_date).filled(:date)

# 型チェックなし
required(:end_date).filled()

value

厳密な型チェックです。

こちらはfilledと違って厳密な型チェックが行われます。
厳密な型チェックなので、formのパラメータをそのまま与えてvalidationするという用途には向いてません。
(formから渡ってきた値はstring扱いとなるため)

型チェックのみのため、型が違ったエラーしか返ってきません。

{locale}.dry_validation.errors.int?
{locale}.dry_validation.errors.str?
{locale}.dry_validation.errors.date?

maybe

こちらも厳密な型チェックですが、値がnilの場合はチェックされません。
ただし、stringのblankはチェック対象となるため、注意が必要となります。

{locale}.dry_validation.errors.int?
{locale}.dry_validation.errors.str?
{locale}.dry_validation.errors.date?

チェックをしたくない場合

チェックを行いたくない場合は何も書かなければOKです。
パラメータとしては必須だけど、2つの値のどちらかが入っていれば良いといったケースですね。

相関チェックや複雑なチェック

そういうのはruleに記載していきます。
エラーが発生した場合は、どのkeyにどんなエラーがあったかを設定していく形です。
ruleに設定するキーも1〜n個引数が取れるので、全部こっちでvalidationさせることも可能です。

  rule(:end_date, :start_date) do
    key(:start_date).failure('開始日は終了日よりも後ろにしてください') if values[:end_date] < values[:start_date]
  end

呼び出し方

呼び出しはnewしてcallすれば良いです。
下のサンプルはrailsのparamsをそのまま引数として使う場合です。
callにはHashを引数としてあげればあとはやってくれます。

FormContract.new.call(params.permit!.to_h)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バリデーションエラーの表示とredirect_toとrenderの違い

flashメッセージは表示されるけど、バリデーションエラーが表示されなくて困っているのでteratailに質問を投げてみたところ、エラーメッセージを表示させるにはrenderを使うというご指摘をいただいたので、備忘録として記事に致します。

before

questions.controller.rb
class QuestionsController < ApplicationController

  def new
    @questions = Question.all
    @question = Question.new
  end

  def create
    @question = Question.new(question_params)
    begin
    @question.save!
      flash[:notice] = '投稿に成功しました'
      redirect_to new_question_path
    rescue
      flash.now[:alert] = '投稿に失敗しました'
      redirect_to new_question_path
    end
  end

  private
  def question_params
    params.require(:question).permit(:title, :detail)
  end

after

questions.controller.rb
def new
   @questions = Question.all
   @question = Question.new
 end

 def create
    @questions = Question.all
    @question = Question.new(question_params)
    begin
      @question.save!
      flash[:notice] = '投稿に成功しました'
      redirect_to new_question_path
    rescue
      flash.now[:alert] = '投稿に失敗しました'
      render :new
    end
  end

private
  def question_params
    params.require(:question).permit(:title, :detail)
  end

beforeでは例外処理になった場合に

redirect_to new_question_path

によって、newアクションまで遡ってしまっています。遡ってしまった結果、モデルのインスタンスが失われてしまうので、save!で格納されたエラーメッセージも無かったことになるみたいです。 更新ボタンを押した処理に近いです。

afterではリダイレクトしない場合もnewと同じ情報が必要なので、beginの前はnewと同じコードを入れています。

rescue
 flash.now[:alert] = '投稿に失敗しました'
 render :new

ここをrender :newとすることで、リダイレクトせずにnewのviewファイルを表示しています。こうすることでモデルのインスタンスも失われないので、エラーメッセージも無くなりません。

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

【Rails】Slackへの通知をActionMailerのようなerbテンプレートを使う形で実装する

以下のGemを利用してSlackで通知を実装する際ActionMailerのようにerbを使ってviewを分けられないかと検討した。

https://github.com/stevenosloan/slack-notifier

app/views/admin_slack_notifier/receive_message_from_user.text.erb
【<%= @subject %>】

■□–––––––––––––––––––––□■
▼送信者情報
<%= @message.user.name %>

▼送信内容
<%= @message.content %>

■□–––––––––––––––––––––□■

app/libs/slack_notifier.rb
module SlackNotifier
  CONFIG = YAML.load_file(Rails.root.join('config', 'slack.yml'))[Rails.env]

  class << self
    def post(to, text, options = {})
      options = options.symbolize_keys
      dry_run = options.key?(:dry_run) ? options[:dry_run] : CONFIG['dry_run']
      post_options = format_options(to, text, options)
      notifier = Slack::Notifier.new CONFIG['webhook_url']
      notifier.post post_options unless dry_run
    end

    private

    # rubocop:disable Metrics/AbcSize
    def format_options(to, text, options)
      post_options = {
        'channel' => (options[:channel].presence || CONFIG[to.to_s]['channel']),
        'icon_emoji' => (options[:icon_emoji].presence || CONFIG[to.to_s]['icon_emoji']),
        'username' => (options[:username].presence || CONFIG[to.to_s]['username']),
        'text' => text
      }
      post_options[:title] = options[:title] if options.key?(:title)
      post_options
    end
    # rubocop:enable Metrics/AbcSize
  end
end


app/libs/admin_slack_notifier.rb
module AdminSlackNotifier

  class << self

    def receive_message_from_user(message)
      @message = message
      @subject = 'ユーザーより新しいメッセージが送信されました'
      post_with_template 'admin_channel', __method__, binding
    end

    private

    def post_with_template(to, view_name, binding)
      erb = Rails.root.join('app', 'views', name.underscore, "#{view_name}.text.erb").read
      text = ERB.new(erb).result(binding)
      SlackNotifier.post to, text
    end
  end
end

config/slack.yml
development: &default
  dry_run: true
  username: &username Admin
  webhook_url: WEBHOOK_URL
  admin_channel:
    channel: admin_channel
    icon_emoji: ":crystal_ball:"
    username: *username
staging:
  <<: *default
  dry_run: false
production:
  <<: *default
  dry_run: false
test:
  <<: *default

以下のような形でAction名と同様のviewを習得してSlackの通知を送信することができる。

sample.rb
    AdminSlackNotifier.receive_message_from_user(message)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Rails チュートリアル学習記録 第3章

第2章については特に書く内容がなかったため学習記録は付けませんでした

3.1

heroku createを実行するとbash: heroku: command not foundというエラーメッセージ。
https://qiita.com/RyuGotoo/items/7d44f7aa51f7c90ad0b7
↑こちらの記事参考に
nvm install node
npm install -g heroku-cli
を実行後、あらためてheroku createを実行すると上手くいった。

3.2

特になし

3.3

特になし

3.4

特になし

3.5

特になし

3.6

特になし

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

正規表現について 電話番号で解説

正規表現とは

正規表現とは、メタ文字と文字列を組み合わせてあらゆる文字列パターンを表現することです。
この記事では携帯電話番号を例にしてどのように書けばいいのか解説します。

早速例を出します。以下は携帯電話番号の妥当性について検証しています。

・携帯電話の番号である条件
1. 先頭が070、080、090のいずれかであること
2. 4桁目が ハイフン であること
3. 5桁目から8桁目が数字であること
4. 9桁目が ハイフン であること
5. 10桁目から13桁目が数字であること

5つの条件を満たす文字列パターンを書くと以下のようになります。
/^0[789]0-\d{4}-\d[4]\

解説

それでは1つずつ解説していきます。

・先頭の / と末尾の /
この中に表現したい文字列パターンを入力します。

・^0
先頭がの文字が0であること
・[789]0
2文字目が7,8,9のいずれかであること
これで条件1の「先頭が070、080、090のいずれかであること」が表現できました。

・ - (ハイフン)
4桁目がハイフンであること
これで条件2の「4桁目がハイフンであること」を満たすことができます。

・ \d{4}
4桁の数字であること
この記述により、条件3の「5桁目から8桁目が数字であること」となります。

9桁目から13桁目については解説が被ることになるた割愛します。

まとめ

以上で携帯電話番号の妥当性を確認できます。
画面から入力された値に対してこのチェックを入れることで、テーブルに予期せぬ値が格納されることを防ぐことが出来るわけですね!

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

Example title

Example

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

Example title

Example

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

Example title

Example

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

Example title

Example

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