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

ひな祭り[Ruby]

ひな祭り[Ruby]問題

問題

ひな祭りの準備をしましょう。あなたが持っている 10 体の人形を "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" で表します。ひな壇の各段 (計 3 段) に並べる人形の数が与えられるので、各段にならべる人形の記号を改行区切りで出力してください。人形は必ずもとの A, B, C, ... の順番で並べます。

例)

各段にならべる人形の数: 2, 3, 5

→ 人形の並べ方:

AB
CDE
FGHIJ

入力される値

入力は以下のフォーマットで与えられます。

h_1 h_2 h_3
ひな壇の 1 段目、2 段目、3 段目に並べる人形の数を表す整数 h_1, h_2, h_3 がこの順に半角スペース区切りで与えられます。入力は 1 行となり、末尾に改行が 1 つ入ります。

期待する出力

ひな壇への人形の並べ方を以下の形式で出力してください。

s_1
s_2
s_3
ひな壇の 1 段目、2 段目、3 段目に並べる人形を表す文字列 s_1, s_2, s_3 をこの順に改行区切りで出力してください。

s_i (1 ≦ i ≦ 3) は i 段目に並べる人形の記号をアルファベット順に結合した文字列です。

入力例1

2 3 5

出力例1

AB
CDE
FGHIJ

入力例2

1 1 8

出力例2

A
B
CDEFGHIJ

入力例3

3 4 3

出力例3

ABC
DEFG
HIJ

アドバイス頂いた結果

ns = gets.split.map(&:to_i)
str = "ABCDEFGHIJ"
i = 0
ns.each do |n|
  puts str[i, n]
  i += n
end

このほかにも Array#shift で求めるコードも!

以上!

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

【Ruby ~基本(コマンド/メソッド)~】勉強メモ

Rubyの復習。
ほぼ自分の勉強メモです。
過度な期待はしないでください。

Rubyファイル

Rubyファイルには、.rbという拡張子を付ける

irbコマンド

irbコマンドは、ターミナルから直接Rubyのプログラムを動かすことができる機能。
ちなみに、irbは「Interactive Ruby」の略。

ターミナル
sample@sampleMBP ~ % irb
// エンターキーで実行
irb(main):001:0> "Hello! こんにちは!"
// このように表示されれば成功
=> "Hello! こんにちは!"
// 終了するときは、exitと記述して実行
irb(main):002:0> exit
sample@sampleMBP ~ % 


rubyコマンド

rubyコマンドはRubyに関するさまざまな操作が実行できるコマンド。
Rubyファイルをプログラムとして実行するためには、
ターミナルにruby [実行したいRubyファイルのパス]と打ち込む。

ターミナル
// Rubyファイルを実行するには、ファイルがあるディレクトリ迄移動する
// 今回で有ればsampleディレクトリにあるsample.rbというRubyファイルを実行
sample@sampleMBP sample % ruby sample.rb
"hello, GitHubDesktop."


出力命令

「puts "○○"」と書くと、putsの後の○○という文字がコンソールに出力される。

×××.rb
# 文字の出力
puts "Hello World"
# 数値の出力
puts 77
# 数値の計算
puts 7 + 7
# 文字の連結
puts "Hello" + "World"
出力結果
Hello World

77

14

Hello World


入力命令

  • getsメソッド

ターミナルに値の入力機能を起動するメソッド。
getsをコードに記述すると、記述した行を読み込んだ段階で入力機能が起動し、
ターミナル画面は入力待ちの状態になる。

ちなみに、getsメソッドで入力された値は、末尾に改行がついた文字列になる。

×××.rb
input = gets

puts "明日の天気は#{input}です。"
ターミナル
~ % ruby ×××.rb
# 入力した文字
晴れ
# 出力結果
明日の天気は晴れ
です。


  • chompメソッド

chompメソッドは、文字列の末尾に存在する改行を取り除いた文字列を返す。

×××.rb
input = gets

puts "明日の天気は#{input}です。"
ターミナル
~ % ruby ×××.rb
# 入力した文字
くもり
# 出力結果 改行されずに1行で出力される
明日の天気はくもりです。



メソッド

  • lengthメソッド

lengthメソッドは、文字列の文字数を数える。

×××.rb
puts "Hello World".length
出力結果
11


  • to_sメソッド

数値を文字列に変換する。
ちなみに、 to_sのsの意味は「string(文字列)」

ターミナル
# 数値「20」にto_sメソッドをつけて実行
irb(main):001:0> 20.to_s
=> "20"


  • to_iメソッド

文字列を数値に変換する。
ちなみに、 to_iのiの意味は「integer(整数)」

ターミナル
# 文字列「30」にto_iメソッドをつけて実行
irb(main):001:0> "30".to_i
=> 30


バックスラッシュ記法

(バックスラッシュ) から始まる文字の記法のこと。
バックスラッシュの入力は、option + ¥
バックスラッシュ記法が適応されるのは文字を "(ダブルクォーテーション)で囲んだときの。

\n で → 改行

×××.rb
puts "ようこそ!\n日本へ!"
出力結果
ようこそ!
日本へ!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Active Storage の導入まとめ

はじめに

・Active Strage の導入を自分なりにまとめたもので、メモのようなものです。
・投稿者は初学者ですので誤った情報を投稿してしまうことがあります。その時はぜひ、遠慮会釈なしにご指摘いただけると幸いです。

Active Strageとは

ファイルアップロード機能を手軽に実装できるGemのこと。
元々はインストールが必要だったが、Rails5.2.0以降、初期段階から統合される事となった。このGemを用いると、ファイルアップロードのメソッドを使用できたり、画像を保存するテーブルを簡単に作成できる。

Active Strageのお供

Active Strageと一緒に導入すると画像の加工の幅が広がる物を3つ列挙します。

・ImageMagick
・MiniMagick
・ImageProcessing

ImageMagickはソフトウェアで、MiniMagickとImageProcessingはGemになります。

ImageMagick
コマンドラインから画像に処理を加えることができるツールのこと。
画像の作成やサイズ変更、保存形式の変更などが可能となる。
Homebrewよりインストールする。

MiniMagick
ImageMagickをRubyで扱えるようにしてくれる接合の役割を担うGemのこと。

ImageProcessing
ImageMagickでの画像サイズ機能を拡張してくれるGemのこと。

導入の手順

1、ImageMagickをHomebrewからインストールするために、下記のコードをターミナルに入力する。

% brew install imagemagick

2、MiniMagickとImageProcessingをインストール。

Gemfile.
gem 'mini_magick'
gem 'image_processing', '~> 1.2'
bundle install

'~> 1.2'と表記があるが、これは、1.2.0以上、1.3.0未満のバージョンを指定、という意味。

3、ローカルサーバーを再起動

rails s

4、Active Strageをインストール
導入手順1でrailsアプリの中にActive Strageを入れた。
ここでのインストールはActive Strageを用いるための周辺準備のためのインストール。

rails active_storage:install

これにより、Active Strageに関連したマイグレーションファイルが作成される。

続けて、以下のようにmigrateをすることでテーブルが作成される。

rails db:migrate
余談 ※読み飛ばし可*

導入手順1と4はどちらもインストールであるが、何が違うのかを自分なりに解説。

アパートの引越しで例えるならば、

導入手順1は「仲介業者と賃貸の契約」
導入手順4は「家具や消耗品を部屋へ搬入」

である。

仲介業者と契約を結んだだけでは(導入手順1)その部屋で快適には過ごせない。冷蔵庫に電子レンジ、シャンプーにお皿などの生活用具を部屋に運搬して(導入手順2)初めて生活ができる空間となる。

まとめ

Active Strageを調べていく中で、CarrierWaveという、同じく画像アップロード実装のGemを見つけました。
CarrierWaveは10年ほど前から利用されている古い技術ということで今はActive Strageが主流なのかと思いましたが、意外にもCarrierWaveも現役選手でした。
Active Strageはシンプルなファイルアップロード。
CarrierWaveはアップロード+細かな設定。
それぞれ別の役割がしっかりと備わっているようなので、CarrierWaveのことも調べて実装したいと思います。

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

【Week 6】my_help, ruby_second

はじめに

マルチスケールシミュレーション特論の講義メモです.講義メモのインデックスはコチラ

授業内で 学習メモがpublicになっていて,LGTMをもらえている という指示があったので,拙い内容ですが投稿しています.

チャート式 Ruby

参照記事はコチラ

variable

前回の講義ではコマンドライン引数として受け取った文字を ARGV[0] を用いて出力するプログラム hello_name.rb を作成した.

(参考) hello_name.rb

puts "Hello #{ARGV[0]}."

今回は受け取った引数 ARGV[0] を name という変数 (variable) に代入し,出力するプログラムを作成する.

まずは変数 name を用意する必要がある.Ruby では変数の型宣言の必要がない(Python も然り)ため,変数は以下のように定義できる.

name = ARGV[0]

コマンドライン引数を変数に代入し,出力するプログラム name_variable.rb を作成したい.はじめに変数 name に "Rudy" を代入して,出力するプログラムを作成する.

name = "Rudy"
puts "Hello #{name}."

次に ARGV[0] を変数 name に代入し,出力するプログラムにリファクタリングする.

name = ARGV[0]
puts "Hello #{name}."

これを実行すると

> ruby name_variable.rb Hiroki.
Hello Hiroki.

期待どおり受け取った引数を変数に代入し,出力するプログラムを作成できた.

method

これまでの講義で引数や変数を用いて "Hello xxx." を出力するプログラムを作成してきたが,これらをメソッド化したい.

Rubyではメソッド (method) を定義することで,関数やプロシージャなどを実装できる.メソッドは以下のように定義できる.

(参考)メソッドの例

def hello(name)
  p name
end

name_variable.rb を参考に,"Hello xxx." を出力するメソッドをプログラム hello_method.rb に作成する.

まずはじめに hello メソッドを作成・呼び出してみる.

def hello (name)
end

name = ARGV[0]
hello (name)

メソッド内に処理を書いていないので勿論実行しても何も出力されない.メソッドの引数を出力するように以下のようにメソッドに追記する.

def hello (name)
  puts name
end

name = ARGV[0]
hello (name)

これを実行すると

> ruby hello_method.rb Rudy
Rudy

コマンドラインで受け取った引数を出力するメソッドを作成できた.あとは出力に文字列を加えればよいので,

def hello(name)
  puts "Hello #{name}."
end

name = ARGV[0]
hello(name)

実行すると

> Ruby hello_method.rb Rudy
Hello Rudy.

期待どおり "Hello xxx." を出力する hello メソッドを作成できた.

TDD

まずTDDとは…

Test Driven Development: テスト駆動開発

以下の手順でコーディングを行う

  • テストを作る (test)
  • エラーを出す (red)
  • エラーをなくす (green)
  • code を brush up する (refactoring)

最終目標は 動く綺麗なコードを作る こと,らしいです.自分もどちらかというと TDD に近いスタイルだった.

折角なので hello_method.rb をリファクタリングしていく.まずはメソッド名が hello と何をするメソッドか分かりづらいので, puts_hello に変更する.

def puts_hello name
  puts "Hello #{name}."
end

name = ARGV[0]
puts_hello name

続いて command の option に名前を指定し忘れた場合,問い合わせるようにしたい.追加する処理は以下のようになる.

name = ARGV[0]
if name == nil
  puts "What\'s your name?"
  name = gets.chomp
end

上記の処理を gets_name メソッドとしてプロシージャにまとめる.hello_method.rb をリファクタリングした後のプログラム hello.rb は以下のとおり.

def puts_hello name
  puts "Hello #{name}."
end

def gets_name
  name = ARGV[0]
  if name == nil
    puts "What\'s your name?"
    name = gets.chomp
  end
  return name
end

name = gets_name
puts_hello name

htllo_method.rb と比較して,よりまとまりのある分かり易いプログラムになった.

Ruby 開発周辺情報

参照記事はコチラ

myhelp org-mode

bash や emacs については前回の講義メモにまとめています.

【Week 5】bash, ruby_first

また,org-mode によるCUIでのメモについて,詳しい内容は以下の記事に随時追加していきます.

【Memo】Emacs Org-Mode

次回の講義内容 <2020-11-04 Wed>

次回の講義は

だそうです.


  • source ~/grad_members_20f/members/e79a93e5b7b1/posts/class/c6_20201028.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Week 6】my_help, ruby_second

はじめに

マルチスケールシミュレーション特論の講義メモです.講義メモのインデックスはコチラ

チャート式 Ruby

参照記事はコチラ

variable

前回の講義ではコマンドライン引数として受け取った文字を ARGV[0] を用いて出力するプログラム hello_name.rb を作成した.

(参考) hello_name.rb

puts "Hello #{ARGV[0]}."

今回は受け取った引数 ARGV[0] を name という変数 (variable) に代入し,出力するプログラムを作成する.

まずは変数 name を用意する必要がある.Ruby では変数の型宣言の必要がない(Python も然り)ため,変数は以下のように定義できる.

name = ARGV[0]

コマンドライン引数を変数に代入し,出力するプログラム name_variable.rb を作成したい.はじめに変数 name に "Rudy" を代入して,出力するプログラムを作成する.

name = "Rudy"
puts "Hello #{name}."

次に ARGV[0] を変数 name に代入し,出力するプログラムにリファクタリングする.

name = ARGV[0]
puts "Hello #{name}."

これを実行すると

> ruby name_variable.rb Hiroki.
Hello Hiroki.

期待どおり受け取った引数を変数に代入し,出力するプログラムを作成できた.

method

これまでの講義で引数や変数を用いて "Hello xxx." を出力するプログラムを作成してきたが,これらをメソッド化したい.

Rubyではメソッド (method) を定義することで,関数やプロシージャなどを実装できる.メソッドは以下のように定義できる.

(参考)メソッドの例

def hello(name)
  p name
end

name_variable.rb を参考に,"Hello xxx." を出力するメソッドをプログラム hello_method.rb に作成する.

まずはじめに hello メソッドを作成・呼び出してみる.

def hello (name)
end

name = ARGV[0]
hello (name)

メソッド内に処理を書いていないので勿論実行しても何も出力されない.メソッドの引数を出力するように以下のようにメソッドに追記する.

def hello (name)
  puts name
end

name = ARGV[0]
hello (name)

これを実行すると

> ruby hello_method.rb Rudy
Rudy

コマンドラインで受け取った引数を出力するメソッドを作成できた.あとは出力に文字列を加えればよいので,

def hello(name)
  puts "Hello #{name}."
end

name = ARGV[0]
hello(name)

実行すると

> Ruby hello_method.rb Rudy
Hello Rudy.

期待どおり "Hello xxx." を出力する hello メソッドを作成できた.

TDD

まずTDDとは…

Test Driven Development: テスト駆動開発

以下の手順でコーディングを行う

  • テストを作る (test)
  • エラーを出す (red)
  • エラーをなくす (green)
  • code を brush up する (refactoring)

最終目標は 動く綺麗なコードを作る こと,らしいです.自分もどちらかというと TDD に近いスタイルだった.

折角なので hello_method.rb をリファクタリングしていく.まずはメソッド名が hello と何をするメソッドか分かりづらいので, puts_hello に変更する.

def puts_hello name
  puts "Hello #{name}."
end

name = ARGV[0]
puts_hello name

続いて command の option に名前を指定し忘れた場合,問い合わせるようにしたい.追加する処理は以下のようになる.

name = ARGV[0]
if name == nil
  puts "What\'s your name?"
  name = gets.chomp
end

上記の処理を gets_name メソッドとしてプロシージャにまとめる.hello_method.rb をリファクタリングした後のプログラム hello.rb は以下のとおり.

def puts_hello name
  puts "Hello #{name}."
end

def gets_name
  name = ARGV[0]
  if name == nil
    puts "What\'s your name?"
    name = gets.chomp
  end
  return name
end

name = gets_name
puts_hello name

htllo_method.rb と比較して,よりまとまりのある分かり易いプログラムになった.

Ruby 開発周辺情報

参照記事はコチラ

myhelp org-mode

bash や emacs については前回の講義メモにまとめています.

【Week 5】bash, ruby_first

また,org-mode によるCUIでのメモについて,詳しい内容は以下の記事に随時追加していきます.

【Memo】Emacs Org-Mode

次回の講義内容 <2020-11-04 Wed>

次回の講義は

だそうです.


  • source ~/grad_members_20f/members/e79a93e5b7b1/posts/class/c6_20201028.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]オーバーライド(継承)について勉強してみた![初心者]

オーバーライドとは?

親クラス(スーパークラス)で定義されたメソッドについて、小クラス(サブクラス)で同じ名前で再定義すること。

クラスの継承とは?

他のクラスをベースとして新しいクラスを作成することをクラスの継承と言います。

利用したいクラスが複数存在する場合、全てを1から作成していると骨が折れますし、共通する部分を複数のクラスに重複して定義する必要があります。
そこで、あるクラスが既に作成されている場合、そのクラスを拡張することで、新しいクラスを作成することができます。

クラスの継承は以下のように記載します。

class 子クラス名 < 親クラス名
end

継承の具体例

class Days
  def study
    puts "勉強する"
  end
end

class Holiday < Days
  def sleep
    puts "寝過ごす"
  end
end

holiday = Holiday.new
holiday.study
実行結果
勉強する

オーバーライドの具体例

class Days
  def study
    puts "勉強する"
  end
end

class Holiday < Days
  def sleep
    puts "寝過ごす"
  end

  def study
    super
    puts "休日だろうと勉強しなければ"
  end
end

holiday = Holiday.new
holiday.study
実行結果
勉強する
休日だろうと勉強しなければ

サブクラスにメソッド定義がない場合

サブクラスで定義していないメソッドが呼び出された時は、スーパークラスに同じ名前のメソッドがある場合に限り、スーパークラスのメソッドが呼び出せます。
具体例を以下に載せました。

class Days
  def study
    puts "勉強する"
  end
end

class Holiday < Days

end

holiday = Holiday.new
holiday.study
実行結果
勉強する

superclassメソッド

全てのクラスは、クラスメソッドとしてsuperclassメソッドを持っています。
また、スーパークラスのメソッドを引数なしで呼び出したい時には、super()も使用できます。

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

【Ajax】jQueryを使って一覧ページでいいね機能

はじめに

一覧ページでのいいね機能を実装しようとしましたが、一番上の星をいいねしたら全てのチームにいいねがついてしまうという問題が発生しました。この問題が起きたことで大変学習になったので、思考の整理とアウトプットとして記事にまとめさせていただきました。また、いいね機能は多くのアプリで実装することが多いと思います。同じような問題が起きている方や別の実装方法など、ご意見いただけたら幸いです!

概要

まず大前提にテーブルは、users, teams, likesの3つでlikes
usersteamsの中間テーブルになっています。
AjaxでHTTPメソッドのGET, POST, DELETEができるように実装していきます。

起きている問題・解決したいこと(仮説)

起きている問題と解決したいことを簡単に書いていきたいと思います。

起きている問題

  • 全ての記事にいいねがついてしまう。
  • 全てのチームのidを取得していたつもりが一番上のチームのidしか取得できていない。(デベロッパーツールで確認)

解決したいことと仮説

  • 配列にして取得できていると思ったができていないので配列から一つずつチームを取得できるようにする。
  • いいね対象を判断できるようにクラスをつけないといけない。
  • クリックした対象に動的idが付与されていれば実装できるのではないか。

Ajaxで実装すること

  • いいねの表示
  • いいねされたら黄色い星を表示
  • いいねされているものをクリックしたら白い星を表示

LikesController

show, create, destroyアクションを使用しています。
Railsでの定義と違うところはリクエストに対して返すのがjson形式のデータということです。

LikesController
class LikesController < ApplicationController
  before_action :authenticate_user!
  before_action :set_like

  def show
    like_status = current_user.has_liked?(@team)
    render json: { hasLiked: like_status }
  end


  def create
    @team.likes.create!(user_id: current_user.id)

    render json: { status: 'ok' }
  end

  def destroy
    like = @team.likes.find_by!(user_id: current_user.id)
    like.destroy!

    render json: { status: 'ok' }
  end

  private

  def set_like
    @team = Team.find(params[:team_id])
  end

end

user.rb

has_liked?user.rbで定義しています。showアクション内の処理のようにcurrent_userがteamをlikeしてる?と直感的にわかりやすいと思い、定義しております。

user.rb
def has_liked?(team)
  likes.exists?(team_id: team.id)
end

index.html.haml

HTTPリクエストを送るにはJavaScriptでidを取得できるようにしなくてはいけないので、テンプレートにカスタムデータを記述しています。data-<情報>とすることで任意の属性を付与することができます。今回はチームに対していいねをしたいのでteamidが必要です。
routesを確認するとどこにリクエストを送れば良いかよくわかります。

たとえばですが、以下index.html.hamlのように記述してデベロッパーツールで確認すると

  • data-team-id="1"
  • id="active-star1"

このようにdata属性とチームのidが入ったidを取得することができます。
チームのidが入ったidは一覧ページでのいいね機能なので、クリックしたときに特定のチームをいいねするという状況になります。それを判定するために記述しております。実際にjQueryのコードを見ていただいたほうが早いと思いますが、簡単に説明させていただきますと、active-starというidだけだと、active-starというidは複数あるので、全てのチームにいいねをつける処理が実行されてしまいます。そのため、動的なid(今回はチームのid)を付与しております。数字の1の部分がチームのidです。

ここで重要なのは、

  • カスタムデータ
  • hiddenでいいねの表示を隠していること

hiddenを使っている理由はいいねの状態を確認し表示するということをAjaxで表示するためです。

index.html.haml
.hidden.active-star{id: "active-star#{teams.id}", data: {team_id: tams.id}}
          = image_tag 'star-yellow.png'
.hidden.in-active-star{id: "in-active-star#{teams.id}", data: {team_id: teams.id}}
          = image_tag 'star-white.png'
css
.hidden {
  display: none;
}

jQuery

本題のいいね機能の実装部分です。
記述が冗長になってしまったので、コメントを入れております。重要なのはcsrfTokenをリクエスト時に持たせることです。これをしないと422 (Unprocessable Entity)というエラーが起きます。なぜかというとGETと違ってPOSTなどの処理はデータベースの変更をするリクエストなので、簡単に操作されては困るため制約がついています。そのため、rails-ujsを使ってaxiosでリクエスト時にcsrfTokenというのを持たせるようにしております。

jQuery
import $ from 'jquery'
import axios from 'axios'

import { csrfToken } from 'rails-ujs'
// リクエスト時にCSRFトークンを持たせる
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken()

document.addEventListener('DOMContentLoaded', () => {

  // ロード時にいいねされていない星を配列で取得
  $('.in-active-star').each(function (index, element) {
    // テンプレートで記述したカスタムデータを取得
    let likeData = $(element).data()
    // カスタムデータからチームIDを取得
    let getId = likeData.teamId
    // カスタムデータを入れてGETリクエストを送る
    axios.get(`/teams/${getId}/like`)
      // リクエストを送ったらレスポンスが返ってくる
      .then((response) => {
        // responseでrenderされたlikeの状態を取得(true or false)
        const inActiveStatus = response.data.hasLiked
        // falseであればいいねされていない => 白い星を表示するために、'hidden'を取り外す
        if ( inActiveStatus === false ) {
          $(element).removeClass('hidden')
        } 
      })
  })

  // ロード時にいいねされている星を配列で取得
  $('.active-star').each(function (index, element) {
    // テンプレートで記述したカスタムデータを取得
    let likeData = $(element).data()
    // カスタムデータからチームIDを取得
    let getId = likeData.teamId
    // カスタムデータを入れてGETリクエストを送る
    axios.get(`/teams/${getId}/like`)
      // リクエストを送ったらレスポンスが返ってくる
      .then((response) => {
        // responseでrenderされたlikeの状態を取得(true or false)
        const activeStatus = response.data.hasLiked
        // trueであればいいねされている => 黄色い星を表示するために、'hidden'を取り外す
        if ( activeStatus === true) {
          $(element).removeClass('hidden')
        } 
      })
  })

  // #create いいねをつけたいときの処理
  $('.in-active-star').on('click', (e) => {
    e.preventDefault();
    let dataset = $(e.currentTarget).data()
    // クリックした要素のidを取得
    let teamId = dataset.teamId
    // teamIdを使いPOSTリクエストを送る
    axios.post(`/teams/${teamId}/like`)
    .then((response) => {
      // リクエスト成功なら処理を行う
      if (response.data.status === 'ok') {
        $(`#in-active-star${teamId}`).addClass('hidden');
        $(`#active-star${teamId}`).removeClass('hidden');
      }
    })
    // エラー時の処理
    .catch((e) => {
      window.alert('Error')
      console.log(e)
    })

  })

  // #destroy いいねを外したいときの処理
  $('.active-star').on('click', (e) => {
    e.preventDefault();
    let dataset = $(e.currentTarget).data()
    // クリックした要素のidを取得
    let teamId = dataset.teamId
    // teamIdを使いdeleteメソッドを使う
    axios.delete(`/teams/${teamId}/like`)
    .then((response) => {
      // リクエスト成功なら処理を行う
      if (response.data.status === 'ok') {
        $(`#active-star${teamId}`).addClass('hidden');
        $(`#in-active-star${teamId}`).removeClass('hidden');
      }
    })
    // エラー時の処理
    .catch((e) => {
      window.alert('Error')
      console.log(e)
    })
  })

})

まとめ

  • Ajax処理はデベロッパーツールを使いdebuggerconsole.log()を使い値が取れているか確認をしっかりすると開発が捗る。
  • 詳細ページのように一つしかいいねがないときは意識していなかったが、一覧ページなど複数ある中の一つというように特定したいときは個別のidを付与することで実装できる。
  • POSTやDELETE時にはデータベースの操作をすることからCSRFトークンというものが必要になる。

最後に

今回はjQueryを使って実装しました。Vue.jsの学習も始めたので、生JSやjQueryをもっと理解したら開発に使っていきたいなと思っています。このように実装するのもひとつの方法だと思いますが、他にもたくさんの実装方法があると思います。他のライブラリやフレームワークではどのように実装するのか、また一度書いたコードもリファクタリングを積極的にやっていけたらと思います!

参考文献

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

【Rails】いいね機能をjQueryで実装

はじめに

一覧ページでのいいね機能を実装しようとしましたが、一番上の星をいいねしたら全てのチームにいいねがついてしまうという問題が発生しました。この問題が起きたことで大変学習になったので、思考の整理とアウトプットとして記事にまとめさせていただきました。また、いいね機能は多くのアプリで実装することが多いと思います。同じような問題が起きている方や別の実装方法など、ご意見いただけたら幸いです!

概要

まず大前提にテーブルは、users, teams, likesの3つでlikes
usersteamsの中間テーブルになっています。
AjaxでHTTPメソッドのGET, POST, DELETEができるように実装していきます。

起きている問題・解決したいこと(仮説)

起きている問題と解決したいことを簡単に書いていきたいと思います。

起きている問題

  • 全ての記事にいいねがついてしまう。
  • 全てのチームのidを取得していたつもりが一番上のチームのidしか取得できていない。(デベロッパーツールで確認)

解決したいことと仮説

  • 配列にして取得できていると思ったができていないので配列から一つずつチームを取得できるようにする。
  • いいね対象を判断できるようにクラスをつけないといけない。
  • クリックした対象に動的idが付与されていれば実装できるのではないか。

Ajaxで実装すること

  • いいねの表示
  • いいねされたら黄色い星を表示
  • いいねされているものをクリックしたら白い星を表示

LikesController

show, create, destroyアクションを使用しています。
Railsでの定義と違うところはリクエストに対して返すのがjson形式のデータということです。

LikesController
class LikesController < ApplicationController
  before_action :authenticate_user!
  before_action :set_like

  def show
    like_status = current_user.has_liked?(@team)
    render json: { hasLiked: like_status }
  end


  def create
    @team.likes.create!(user_id: current_user.id)

    render json: { status: 'ok' }
  end

  def destroy
    like = @team.likes.find_by!(user_id: current_user.id)
    like.destroy!

    render json: { status: 'ok' }
  end

  private

  def set_like
    @team = Team.find(params[:team_id])
  end

end

user.rb

has_liked?user.rbで定義しています。showアクション内の処理のようにcurrent_userがteamをlikeしてる?と直感的にわかりやすいと思い、定義しております。

user.rb
def has_liked?(team)
  likes.exists?(team_id: team.id)
end

index.html.haml

HTTPリクエストを送るにはJavaScriptでidを取得できるようにしなくてはいけないので、テンプレートにカスタムデータを記述しています。data-<情報>とすることで任意の属性を付与することができます。今回はチームに対していいねをしたいのでteamidが必要です。
routesを確認するとどこにリクエストを送れば良いかよくわかります。

たとえばですが、以下index.html.hamlのように記述してデベロッパーツールで確認すると

  • data-team-id="1"
  • id="active-star1"

このようにdata属性とチームのidが入ったidを取得することができます。
チームのidが入ったidは一覧ページでのいいね機能なので、クリックしたときに特定のチームをいいねするという状況になります。それを判定するために記述しております。実際にjQueryのコードを見ていただいたほうが早いと思いますが、簡単に説明させていただきますと、active-starというidだけだと、active-starというidは複数あるので、全てのチームにいいねをつける処理が実行されてしまいます。そのため、動的なid(今回はチームのid)を付与しております。数字の1の部分がチームのidです。

ここで重要なのは、

  • カスタムデータ
  • hiddenでいいねの表示を隠していること

hiddenを使っている理由はいいねの状態を確認し表示するということをAjaxで表示するためです。

index.html.haml
.hidden.active-star{id: "active-star#{teams.id}", data: {team_id: tams.id}}
          = image_tag 'star-yellow.png'
.hidden.in-active-star{id: "in-active-star#{teams.id}", data: {team_id: teams.id}}
          = image_tag 'star-white.png'
css
.hidden {
  display: none;
}

jQuery

本題のいいね機能の実装部分です。
記述が冗長になってしまったので、コメントを入れております。重要なのはcsrfTokenをリクエスト時に持たせることです。これをしないと422 (Unprocessable Entity)というエラーが起きます。なぜかというとGETと違ってPOSTなどの処理はデータベースの変更をするリクエストなので、簡単に操作されては困るため制約がついています。そのため、rails-ujsを使ってaxiosでリクエスト時にcsrfTokenというのを持たせるようにしております。

jQuery
import $ from 'jquery'
import axios from 'axios'

import { csrfToken } from 'rails-ujs'
// リクエスト時にCSRFトークンを持たせる
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken()

document.addEventListener('DOMContentLoaded', () => {

  // ロード時にいいねされていない星を配列で取得
  $('.in-active-star').each(function (index, element) {
    // テンプレートで記述したカスタムデータを取得
    let likeData = $(element).data()
    // カスタムデータからチームIDを取得
    let getId = likeData.teamId
    // カスタムデータを入れてGETリクエストを送る
    axios.get(`/teams/${getId}/like`)
      // リクエストを送ったらレスポンスが返ってくる
      .then((response) => {
        // responseでrenderされたlikeの状態を取得(true or false)
        const inActiveStatus = response.data.hasLiked
        // falseであればいいねされていない => 白い星を表示するために、'hidden'を取り外す
        if ( inActiveStatus === false ) {
          $(element).removeClass('hidden')
        } 
      })
  })

  // ロード時にいいねされている星を配列で取得
  $('.active-star').each(function (index, element) {
    // テンプレートで記述したカスタムデータを取得
    let likeData = $(element).data()
    // カスタムデータからチームIDを取得
    let getId = likeData.teamId
    // カスタムデータを入れてGETリクエストを送る
    axios.get(`/teams/${getId}/like`)
      // リクエストを送ったらレスポンスが返ってくる
      .then((response) => {
        // responseでrenderされたlikeの状態を取得(true or false)
        const activeStatus = response.data.hasLiked
        // trueであればいいねされている => 黄色い星を表示するために、'hidden'を取り外す
        if ( activeStatus === true) {
          $(element).removeClass('hidden')
        } 
      })
  })

  // #create いいねをつけたいときの処理
  $('.in-active-star').on('click', (e) => {
    e.preventDefault();
    let dataset = $(e.currentTarget).data()
    // クリックした要素のidを取得
    let teamId = dataset.teamId
    // teamIdを使いPOSTリクエストを送る
    axios.post(`/teams/${teamId}/like`)
    .then((response) => {
      // リクエスト成功なら処理を行う
      if (response.data.status === 'ok') {
        $(`#in-active-star${teamId}`).addClass('hidden');
        $(`#active-star${teamId}`).removeClass('hidden');
      }
    })
    // エラー時の処理
    .catch((e) => {
      window.alert('Error')
      console.log(e)
    })

  })

  // #destroy いいねを外したいときの処理
  $('.active-star').on('click', (e) => {
    e.preventDefault();
    let dataset = $(e.currentTarget).data()
    // クリックした要素のidを取得
    let teamId = dataset.teamId
    // teamIdを使いdeleteメソッドを使う
    axios.delete(`/teams/${teamId}/like`)
    .then((response) => {
      // リクエスト成功なら処理を行う
      if (response.data.status === 'ok') {
        $(`#active-star${teamId}`).addClass('hidden');
        $(`#in-active-star${teamId}`).removeClass('hidden');
      }
    })
    // エラー時の処理
    .catch((e) => {
      window.alert('Error')
      console.log(e)
    })
  })

})

まとめ

  • Ajax処理はデベロッパーツールを使いdebuggerconsole.log()を使い値が取れているか確認をしっかりすると開発が捗る。
  • 詳細ページのように一つしかいいねがないときは意識していなかったが、一覧ページなど複数ある中の一つというように特定したいときは個別のidを付与することで実装できる。
  • POSTやDELETE時にはデータベースの操作をすることからCSRFトークンというものが必要になる。

最後に

今回はjQueryを使って実装しました。Vue.jsの学習も始めたので、生JSやjQueryをもっと理解したら開発に使っていきたいなと思っています。このように実装するのもひとつの方法だと思いますが、他にもたくさんの実装方法があると思います。他のライブラリやフレームワークではどのように実装するのか、また一度書いたコードもリファクタリングを積極的にやっていけたらと思います!

参考文献

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

【Week 5】bash, ruby_first

はじめに

マルチスケールシミュレーション特論の講義メモです.講義メモのインデックスはコチラ

授業内で 学習メモがpublicになっていて,LGTMをもらえている という指示があったので,拙い内容ですが投稿しています.

概要

今回から Ruby の講義に入っていく.講義のメモは各回

  • チャート式 Ruby
  • Ruby 開発周辺情報

の2点を軸としてまとめる.

チャート式 Ruby 一覧

チャート式 Rubyで扱う内容は以下を予定している.

  1. puts (出力)
  2. variable and method (変数とメソッド)
  3. if-else, case, array and each (条件分岐と配列まわり)
  4. gem (ライブラリの使い方)
  5. recursion (再帰)
  6. class
  7. 仕上げ問題

Ruby 開発周辺情報 一覧

Ruby 開発周辺情報で扱う内容は以下を予定している.

  1. bash + emacs
  2. myhelp
  3. bundler
  4. rake
  5. rubular
  6. thor
  7. rubcoop

チャート式 Ruby

参照記事はコチラ

puts

今回から Ruby の講義に入る.はじめに学ぶ内容はプログラミングの初歩中の初歩である文字列の出力についてである.まずはみんな大好き

Hello World.

を出力するプログラムを作ってみる.

プログラムを管理するためにcodes ディレクトリを作成し, puts_hello_world.rb を編集する.

> mkdir codes
> cd codes
> emacs puts_hello_world.rb

puts_hello_world.rb は以下のとおり.

puts "hello world."

これを実行してみると,

> ruby puts_hello_world.rb
hello world.

期待通り hello world. が出力された.

Ruby で文字を出力できるメソッドは puts だけでない.以下にいくつか例を挙げる.

メソッド 使い方
print 引数の値を出力. 改行なし
puts 引数の値を出力.改行あり
p デバッグ用の出力.引数のオブジェクトがわかる
pp require 'pp'が必要.p と同じデバッグ用
printf c 言語と同等.複数の引数を渡すことができる

上記のメソッドを比較するプログラム p_print_hello_world.rb を作成する.p_print_hello_world.rb は以下のとおり.

puts "hello world."
print "hello world."
p "hello world."
pp "hello world."
printf "hello world."

これを実行してみると,

> ruby p_print_hello_world.rb 
hello world.
hello world."hello world."
"hello world."
hello world

各々出力が異なるので考察してみる.

> puts "hello world."
hello world. 
# 

> print "hello world."
hello world. (改行なし)
#

> p "hello world."
"hello world."
# 

> pp "hello world."
"hello world."
# 

> printf "hello world."
hello world
#

ARGV[0]

続いてコマンドライン引数を用いて

> ruby hello_name.rb Rudy

と実行したときに,

Hello Rudy.

と出力するプログラムを作ってみる.

Ruby でコマンドライン引数を扱うには ARGV[]を使う.python でも sys.argv (sys モジュールの argv メソッド) を使うので,コマンドライン引数を使いたいときはどの言語も argv で検索すれば良さそう.とりあえず hello_name.py を作成し,

puts ARGV[0]

を実行してみる.

> ruby hello_name.rb Rudy
Rudy

確かにコマンドラインの値を出力することができた.

ARG[0]は引数 (argment) 配列のインデックス番号 0 を指している.unix shell 上では command の option として引数を直接渡せるように用意されているらしい.

次にコマンドライン引数と文字列を組み合わせて出力させたい.Ruby ではいくつか方法があるので以下にまとめる.

メソッド プログラム
puts puts "Hello " + ARGV[0]
puts puts "Hello #{ARGV[0]}"
print print "Hello #{ARGV[0]}\n"
print print "Hello " + ARGV[0] + "\n"

先に書いたように printf でも問題なく出力できる.Ruby on Rails で Web アプリケーションを作った際に一番お世話になった2番目のプログラムで書いたものを実行する.

> ruby hello_name.rb Rudy
Hello Rudy.

無事コマンドライン引数を用いて出力をするプログラムを作成できた.

リダイレクト

実行結果を別ファイルに保存するには,以下のようにリダイレクトを用いるとよい.

> ruby hello_name.rb Hiroki > hello_name.txt

'>' はリダイレクト(redirect)といい,出力を txt に変更したものが指定したファイル先に保存される.

Hello Hiroki.

また,'<' の場合は 指定したファイルを入力として与えることができる.ファイルの中身は cat (concatinate)

cat hello_name.txt

で指定したファイルの中身を見ることができる.

Ruby 開発周辺情報

参照記事はコチラ

bash + emacs

shell

  • command 関連
    • command [objects]
    • command [options] [objects]
  • directory 関連
    • open [file] # ファイルを開く
    • mkdir [DIR] # ディレクトリの作成 (make directory)
    • pwd # カレントディレクトリ(print working directory)
    • cd [DIR] # ディレクトリの移動 (change directory)
    • cd .. # 一つ上のディレクトリへ移動
    • ls -al # ファイルやディレクトリの表示 (list all, long)
  • process 関連
    • ps, top # process status
    • fg, bg # fore, back ground
    • kill -9 PID # kill process id

などのコマンドで操作するCLI.(command line interface)

emacs

key-bind が充実しているテキストエディタ.というよりも key-bind での操作が前提のテキストエディタといった方が適している.emacs による一連の編集動作は以下のとおり.

> emacs hoge.org    # emacsの起動

# ファイル編集後
c-x c-s    # ファイルのセーブ

c-x c-c (quit) or c-z (stop)    # emacsの終了 

quit は emacs を終了,stop はプロセスを残して一時終了する.stop の場合は

> fg

で再び編集中の emacs を呼び出すことができる.

key-bind について,より詳しい内容は以下の記事に随時追加していきます.

【Memo】Emacs key-bind (Editing)

git

git について,詳しい内容は以下の記事に随時追加していきます.

【Memo】github & Qiita

次回の講義内容 <2020-10-28 Wed>

次回の講義は

だそうです.


  • source ~/grad_members_20f/members/e79a93e5b7b1/posts/class/c5_20201021.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Roman Numerals

Mac OS X-10.15.7 ruby-2.7.1p83

Roman Numerals

講義ページリンク

roman numerals

課題

アラビア数字を受け取って,それに対応したローマ数字を返すプログラムの作成ついでに整数(Integer)クラスを拡張する

アラビア数字からローマ数字への変換方法

  • 各桁を表すのに使用する文字は,1,5,次の桁の1(10)を表す3つ
    • 例えば1桁目では(I(1), V(5), X(10)), 2桁目では10倍した(X(10), L(50), C(100))を用いる
  • 各桁では,その桁の値に応じて次のルールに従って文字を並べる
    • 1~3: 1の文字を数字分並べる(III)
    • 4: 5の文字の左に1の文字(IV)
    • 5: 5の文字(V)
    • 6~8: 5の文字の右に,数字と5の差の分だけ1の文字を並べる(VIII)
    • 9: 次の桁の1の文字の左に1の文字(IX)
  • 各桁の文字を並べる(439 => CDXXXIX)

解答例

#!/usr/bin/env ruby
# frozen_string_literal: true

class Integer
  def to_roman
    if !between?(1, 3999)
      puts "#{self} is not supported in #{__method__}."
      exit
    else
      roman = []
      roman_symbols = %w[I V X L C D M]
      digits.to_a.each_with_index do |n, i|
    ivx = roman_symbols.slice(2 * i, 3)
    case n
    when 1..3 then roman.push(ivx[0] * n)
    when 4 then roman.push(ivx[0] + ivx[1])
    when 5 then roman.push(ivx[1])
    when 6..8 then roman.push(ivx[1] + (ivx[0] * (n - 5)))
    when 9 then roman.push(ivx[0] + ivx[2])
    end
      end
    end
    roman.reverse.join
  end
end

test_data = [1, 2, 4, 5, 6, 9, 10, 11, 14, 15, 19, 38, 42, 49, 51, 97, 99, 439, 483, 499, 732, 961, 999, 1999, 4000]
test_data.each do |n|
  puts "#{n}\t#{n.to_roman}"
end

出力

> ruby roman_numerals.rb
1       I
2       II
4       IV
5       V
6       VI
9       IX
10      X
11      XI
14      XIV
15      XV
19      XIX
38      XXXVIII
42      XLII
49      XLIX
51      LI
97      XCVII
99      XCIX
439     CDXXXIX
483     CDLXXXIII
499     CDXCIX
732     DCCXXXII
961     CMLXI
999     CMXCIX
1999    MCMXCIX
4000 is not supported in to_roman.

NOTE

  • Integerクラス内で self.digits にアクセスすると,自身の各桁の値を取得できる
    • 上記のコードでは self. はrubocopに削除されたのでいらないのかも
  • 更に .to_a で配列として取得できる
  • 配列に対して .each_with_index でpythonのenumerateみたいに数値とインデックスを取得できる
  • pythonみたいにリスト内包表記とかlambdaとかmapとかが使えるともう少し簡潔に書けるだろうが書き方がわからないのでとりあえずこのまま
  • あとqiitaにpostしたらなぜか解答例のソースコードのインデントがおかしくなってる…

  • source ~/multiscalesim_toku/grad_members_20f/members/lynd2299/roman_numerals.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Roman Numerals

Mac OS X-10.15.7 ruby-2.7.1p83

Roman Numerals

講義ページリンク

roman numerals

課題

アラビア数字を受け取って,それに対応したローマ数字を返すプログラムの作成ついでに整数(Integer)クラスを拡張する

アラビア数字からローマ数字への変換方法

  • 各桁を表すのに使用する文字は,1,5,次の桁の1(10)を表す3つ
    • 例えば1桁目では(I(1), V(5), X(10)), 2桁目では10倍した(X(10), L(50), C(100))を用いる
  • 各桁では,その桁の値に応じて次のルールに従って文字を並べる
    • 1~3: 1の文字を数字分並べる(III)
    • 4: 5の文字の左に1の文字(IV)
    • 5: 5の文字(V)
    • 6~8: 5の文字の右に,数字と5の差の分だけ1の文字を並べる(VIII)
    • 9: 次の桁の1の文字の左に1の文字(IX)
  • 各桁の文字を並べる(439 => CDXXXIX)

解答例

#!/usr/bin/env ruby
# frozen_string_literal: true

class Integer
  def to_roman
    if !between?(1, 3999)
      puts "#{self} is not supported in #{__method__}."
      exit
    else
      roman = []
      roman_symbols = %w[I V X L C D M]
      digits.to_a.each_with_index do |n, i|
    ivx = roman_symbols.slice(2 * i, 3)
    case n
    when 1..3 then roman.push(ivx[0] * n)
    when 4 then roman.push(ivx[0] + ivx[1])
    when 5 then roman.push(ivx[1])
    when 6..8 then roman.push(ivx[1] + (ivx[0] * (n - 5)))
    when 9 then roman.push(ivx[0] + ivx[2])
    end
      end
    end
    roman.reverse.join
  end
end

test_data = [1, 2, 4, 5, 6, 9, 10, 11, 14, 15, 19, 38, 42, 49, 51, 97, 99, 439, 483, 499, 732, 961, 999, 1999, 4000]
test_data.each do |n|
  puts "#{n}\t#{n.to_roman}"
end

出力

> ruby roman_numerals.rb
1       I
2       II
4       IV
5       V
6       VI
9       IX
10      X
11      XI
14      XIV
15      XV
19      XIX
38      XXXVIII
42      XLII
49      XLIX
51      LI
97      XCVII
99      XCIX
439     CDXXXIX
483     CDLXXXIII
499     CDXCIX
732     DCCXXXII
961     CMLXI
999     CMXCIX
1999    MCMXCIX
4000 is not supported in to_roman.

NOTE

  • Integerクラス内で self.digits にアクセスすると,自身の各桁の値を取得できる
    • 上記のコードでは self. はrubocopに削除されたのでいらないのかも
  • 更に .to_a で配列として取得できる
  • 配列に対して .each_with_index でpythonのenumerateみたいに数値とインデックスを取得できる
  • pythonみたいにリスト内包表記とかlambdaとかmapとかが使えるともう少し簡潔に書けるだろうが書き方がわからないのでとりあえずこのまま

  • source ~/multiscalesim_toku/grad_members_20f/members/lynd2299/roman_numerals.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Roman Numerals

Mac OS X-10.15.7 ruby-2.7.1p83

Roman Numerals

講義ページリンク

roman numerals

課題

アラビア数字を受け取って,それに対応したローマ数字を返すプログラムの作成ついでに整数(Integer)クラスを拡張する

アラビア数字からローマ数字への変換方法

  • 各桁を表すのに使用する文字は,1,5,次の桁の1(10)を表す3つ
    • 例えば1桁目では(I(1), V(5), X(10)), 2桁目では10倍した(X(10), L(50), C(100))を用いる
  • 各桁では,その桁の値に応じて次のルールに従って文字を並べる
    • 1~3: 1の文字を数字分並べる(III)
    • 4: 5の文字の左に1の文字(IV)
    • 5: 5の文字(V)
    • 6~8: 5の文字の右に,数字と5の差の分だけ1の文字を並べる(VIII)
    • 9: 次の桁の1の文字の左に1の文字(IX)
  • 各桁の文字を並べる(439 => CDXXXIX)

解答例

#!/usr/bin/env ruby
# frozen_string_literal: true

class Integer
  def to_roman
    if !between?(1, 3999)
      puts "#{self} is not supported in #{__method__}."
      exit
    else
      roman = []
      roman_symbols = %w[I V X L C D M]
      digits.to_a.each_with_index do |n, i|
    ivx = roman_symbols.slice(2 * i, 3)
    case n
    when 1..3 then roman.push(ivx[0] * n)
    when 4 then roman.push(ivx[0] + ivx[1])
    when 5 then roman.push(ivx[1])
    when 6..8 then roman.push(ivx[1] + (ivx[0] * (n - 5)))
    when 9 then roman.push(ivx[0] + ivx[2])
    end
      end
    end
    roman.reverse.join
  end
end

test_data = [1, 2, 4, 5, 6, 9, 10, 11, 14, 15, 19, 38, 42, 49, 51, 97, 99, 439, 483, 499, 732, 961, 999, 1999, 4000]
test_data.each do |n|
  puts "#{n}\t#{n.to_roman}"
end

出力

> ruby roman_numerals.rb
1       I
2       II
4       IV
5       V
6       VI
9       IX
10      X
11      XI
14      XIV
15      XV
19      XIX
38      XXXVIII
42      XLII
49      XLIX
51      LI
97      XCVII
99      XCIX
439     CDXXXIX
483     CDLXXXIII
499     CDXCIX
732     DCCXXXII
961     CMLXI
999     CMXCIX
1999    MCMXCIX
4000 is not supported in to_roman.

NOTE

  • Integerクラス内で self.digits にアクセスすると,自身の各桁の値を取得できる
    • 上記のコードでは self. はrubocopに削除されたのでいらないのかも
  • 更に .to_a で配列として取得できる
  • 配列に対して .each_with_index

  • source ~/multiscalesim_toku/grad_members_20f/members/lynd2299/roman_numerals.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveRecordでは present? の代わりに exists? を使おう

小ネタです。

条件に該当するレコードが存在するかを確認したい

ActiveRecordで条件に存在するレコードが存在するかを確認するのに、つい、

Model.where(conditions).present?

と書いてしまいがちですが、これはパフォーマンス上の問題が生じる可能性があります。

.present? は ActiveSupport によってモンキーパッチされたメソッドです。
Mode.where(conditions) のような ActiveRecord_Relation クラスのオブジェクトに .present? を適用するとどうなるか?

すると条件に該当するレコードを全てDBから取得して、(Rails上のモデルの)配列として評価することになります。
配列に要素が存在すれば true、 しなければ false ですね。

なぜこれはダメなのか

一見すると問題なさそうですし、実際結果自体は正しいのですが、条件に該当するレコードが1つでも存在することを確認できれば良いわけで、全件を取得する必要はありません。
100万レコードが条件に該当した場合、その100万レコードがDBからピックアップされ、その100万レコード分のデータがRails側に転送され、100万のモデルが作成された上で捨てられる。という壮大な無駄が発生します。結果、いつまでたっても結果が返ってこない。というパフォーマンス上の障害を引き起こしてしまうのです。

こういうのはレコード数の少ない開発環境では顕在化しずらく、レコード数の多い本番環境でいきなり顕在化してパニックになることがあります。

では、どうする?

.exists? メソッドを使いましょう。

Model.where(conditions).exists?

こちらを使うと、SQLが exists 式を使うものに代わります。
exists 式は条件に該当するレコードが1件でも存在すればDBがそこで探索を打ち切ってくれるので、無駄に全件を取ってくることはありません。
DBレベルで true または false で返してくれるのでオーバヘッドが最小で済みます。

present? の逆は?

.present? の逆である .blank? に対応するメソッドは .empty? になります。
.empty? を使えばSQL上は exists 式を使います。

ただ、条件に該当するレコードが存在しないことを確認するには、結局全件のチェックが必要になるので両者に速度的な差は少ないかもしれません。

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

TableExportで好きな位置にExcelやCSV出力ボタンを配置する

はじめに

JQueryプラグイン「TableExport」ではテーブルを簡単にExcel、CSV、テキストファイルなどで出力できます。ただ、出力ボタンの位置は上下で固定っぽくて不便だったので、改善策を備忘録として残しておきます。

TableExport自体の使い方はこちらのサイトが分かりやすいので参考にされてください。

開発環境は以下の通り

  • Ruby 2.6.3
  • Rails 5.2.4.4
  • jQuery 1.12.4

(Ruby on Rails使用してますが、今回の記事ではjQueryのみ取り上げているのであまり関係ないかと思います)

なお、bootstrapを使えばデフォルトでオシャレなボタンにできますが今回は使いません。

問題点



Export to xlsxボタンがとんでもないところにありますね。
普通のテーブルだともう少しマシかもしれませんが、このテーブルは見出し固定のスクロール式で、テーブルを横にスライドするとボタンも一緒に流されてしまうので余計見にくい代物になってしまいます。。



一応オプションでボタンの位置を指定できますが、前述した通り上下の指定しかできないので改善には至りませんでした。

$(".table").tableExport({
    ...
    position: "bottom",
    ...
});

解決策

至ってシンプルです。

①tableExport()で生成されるボタンを隠して代理ボタンを配置する
②代理ボタンが押されたらtrigger()でもともとのボタンが押されたことにする

htmlファイル
...

<table id="export-table">
    ...
</table>

<button type="button" id="export-btn">Excel出力</button>

...

jqueryファイル
...

$('#export-table').tableExport({
    formats: ["xlsx"],
    bootstrap: false
});

$('#export-table caption').hide();

$('#export-btn').on('click', function(){
    $('.table caption button').trigger('click');  
});

...

うまくいきました!
もちろんCSVやテキストファイルの出力にも使用できます。

おわりに

TableExportはとても簡単に外部ファイルを出力できるプラグインですが、扱っている記事自体がとても少ないので、こういうちょっとした解決策っていうのはもっと増えていくと嬉しいですね。

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

チャート式 ruby-V (Recursive Fibonacci)

目的

ruby のコードを実際に書いてみよう.今回は第 V 弾.

お題:Fibonacci数列

fib(n) = fib(n-1)+fib(n-2)
0 1 1 2 3 5 8 13 21 ...

を recursion (再帰) で求める.

解説

以下のような関数 fib をつくる

  • 入力 : n (項の番号)
  • 出力 : 第 n 項の値

今回は TDD (Test Driven Development : テスト駆動開発) を意識して解説を書く.

  • テストを作る
  • red : エラーを出す
  • green : エラーをなくす (green)
  • refactoring : code を綺麗にする (refactoring)

fib(0) = 0

初項 (0) を関数 fib に渡して, 初項の値 0 が返ってくるようにする.

ではまず, テストをつくって, red.

emacs fibonacci.rb

でファイルを作成&オープンし,

p fib(0)

と書いて,

ruby fibonacci.rb

で実行.もちろん, そんな関数作ってないのでエラーがくる.

そこで, green.

def fib(n)
    if n==0
      return 0
    end
end

これでひとまずエラーがなくなって, 実行が通る.

次に, refactoring.

前回つくった "assert_equal_final.rb" の assertion を試す.

# 「require './assert_equal_final.rb'」でも良い
require './assert_equal_final'
assert_equal(0, fib(0))

うまくいった.

fib(1) = 1

次は第 1 項に対しての処理.

ではまず, テストをつくって, red.

assert_equal(1, fib(1))

もちろん失敗.

じゃあ, green.

n が 1 の時の処理を書き足して,

def fib(n)
    if n==0
      return 0
    end
    if n==1
      return 1
    end
end

これで実行通る.

refactoring

テストの文が重複してきたので,

[[0,0],[1,1]].each do |pair|
  assert_equal(pair[0], fib(pair[1]))
end

こうする.もちろん実行通る.

fib(2) = fib(1) + fib(0) = 0+1 = 1

続いては, 第 2 項に対する処理.

ではまず, テストをつくって, red.

[[0,0],[1,1],[2,1]].each do |pair|
  assert_equal(pair[0], fib(pair[1]))
end

そうすると,

$ ruby fibonacci.rb 
expected :: 0
result :: 0
succeeded in assert_equal.
expected :: 1
result :: 1
succeeded in assert_equal.
expected :: 2
result :: 1
failed in assert_equal.

もちろん失敗.

green

じゃあ n が 2 に対する処理を追加.

def fib(n)
    if n==0
      return 0
    end
    if n<=2
      return 1
    end
end

あれ, うまくいかない.これはテストの書き方が悪い.

# 「assert_equal(pair[0], fib(pair[1]))」 ではなく
assert_equal(pair[1], fib(pair[0]))

これで ok.

refactoring

そもそも pair[0], pair[1] が分かりづらくて良くなかった.

[[0,0],[1,1],[2,1]].each do |index, expected|
    assert_equal(expected, fib(index))
end

こうやって, 明示的にするととても分かりやすい.

refactoring

そしてこのあたりで, 「あれ, code ごちゃついてきた」 となる.じゃあ短くしよう.

def fib(n)
  return 0 if n==0
  return 1 if n<=2
end

こうすると, 短い上にわかりやすい. 素晴らしい.

fib(3) = fib(2) + fib(1) = 1+1 = 2

さっきと同様に red & green.

code は,

def fib(n)
  return 0 if n==0
  return 1 if n<=2
  return 2
end

今はとりあえずこれで ok.

refactoring

ここで code を修正.

def fib(n)
  return 0 if n==0
  return 1 if n<=2
  return fib(2) + fib(1)
end

これで良いよね.

fib(4) = fib(3) + fib(2) = 2+1 = 3

red & green & refactoring.

def fib(n)
  return 0 if n==0
  return 1 if n<=2
  return fib(2) + fib(1)
  return fib(3) + fib(2)
end

こう書いてみると,

def fib(n)
  return 0 if n==0
  return 1 if n<=2
  return fib(n-1) + fib(n)
end

こうで良いよねって気づく. もっと言うと,

def fib(n)
  return 0 if n==0
  return 1 if n==1 # n<=2ではなく
  return fib(n-1) + fib(n)
end

これで良いよね. そして, fib(4) 以降 (fib(5), fib(6), …) も出来上がり.

まとめ

TDD で良い感じに Fibonacci 数列の code を書けた. 素晴らしい.

今回書いた code の全体

最終的にこうなった. ファイル名は "fibonacci.rb".

require './assert_equal_final'

def fib(n)
  return 0 if n==0
  return 1 if n==1
  return fib(n-1) + fib(n-2)
end

if $PROGRAM_NAME == __FILE__
  [[0,0],[1,1],[2,1],[3,2],[4,3],
   [5,5],[6,8],[7,13],[8,21],[9,34]].each do |index, expected|
    assert_equal(expected, fib(index))
  end
end

参考ページ

チャート式ruby-V(Recursive Fibonacci)


  • source ~/Lecture/multiscale_simulation/grad_members_20f/members/gagagagazelle/docs/c5_fibonacci.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでTimecop/TimeHelpersを使って時刻を変える方法について

この記事は、Happy Elements Advent Calendar 2020の5日目です。
RailsでTimecop/TimeHelpersを使って時刻を変える方法についての記事です。

はじめに

ソーシャルゲームでは、イベントが yyyy年mm月dd日に始まるなど、特定の時刻になると発動することが定番です。

例: 架空のソーシャルゲームのイベントカレンダー
image.png

これらのようなイベントの動作を確認するために、サーバ側で時刻を変えることができると、動作テストがしやすくなります。
- 例1 12月2日 15:00開始のガチャAの動作確認をするために、サーバの時間を12/2 16時にセットする
- 例2 バグ報告があったので、1ヶ月前のイベントの時間にセットしてバグ調査をする など

サーバ側で時刻を変える方法について、以下の3つの方法に絞って調査しました。
- Timecop
- ActiveSupport::Testing::TimeHelpers
- libfaketime

この記事では、それぞれがどのようなものか、をまとめました。

Timecop

https://github.com/travisjeffery/timecop
こちらはRailsのgemです。
Time/Date/DateTimeクラスのオーバーライドとして実装されているものでした
https://github.com/travisjeffery/timecop/blob/master/lib/timecop/time_extensions.rb

調査を進めると、TimeHelpersという物がRailsに組み込まれていることがわかりました。
TimeHelpersは、Timecopより機能としては少なく、時刻を固定させるのみです。
Timecopは、固定の他に、過去や未来に設定した後に時刻が進むなども可能です。

TimecopとTimeHelperでは、Timecopの方がリッチな機能である、と言えると思います。

  • 標準のTimeHelpersで十分である
  • gemを入れたくない

などの場合は、TimeHelpersの利用が選択肢に上がってくると思います。
2020年現在もメンテナンスはされているようです。

ActiveSupport::Testing::TimeHelpers

https://edgeapi.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html

Rails4.1以降で標準のものです。
時刻を固定・解除するのみです。

libfaketime

https://github.com/wolfcw/libfaketime

こちらはRailsの話ではなく、OSにインストールして動くサービスです。
システムコールを改変するものです

  • メリット
    • プログラムを変えなくていい
  • デメリット
    • 導入が少し手間

システムコールとは?

libfaketimeでシステムコールの話が出てきたので、ここではシステムコールについて。

strace というコマンドを使って、コマンドを打つと大量にログが出てきます。これがシステムコールです(fstat, close, read, ...etc)。
libfaketimeは、このシステムコールを書き換えて時刻を変えるので、TimecopやTimeHelpersよりも深い部分で動いていると言えると思います。

strace date

execve("/bin/date", ["date"], 0x7ffc3bc97870 /* 28 vars */) = 0
brk(NULL)                               = 0x55b92bdc6000
openat(AT_FDCWD, "/usr/local/lib/faketime/libfaketime.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0$\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=66512, ...}) = 0
...
read(4, "@2020-6-16 20:30:00\n", 4096)  = 20
close(4)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(1, "Tue Jun 16 20:30:00 JST 2020\n", 29Tue Jun 16 20:30:00 JST 2020
) = 29
close(1)                                = 0
close(2)                                = 0
munmap(0x7f46f7935000, 8)               = 0
munmap(0x7f46f7936000, 32)              = 0
exit_group(0)                           = ?
+++ exited with 0 +++

余談

調査中に、もしTimecopとlibfaketimeを同時に使った場合どうなるのだろう、と思いました。
実験してみたところ、Timecopで設定した時刻がRailsでは得られました。

libfaketimeで改変された時間を、さらにTimecopが上書きするという順序になります。
イメージ図は以下の通りです。

image.png

まとめ

自分が運営中のタイトルでは、開発者の手元では、TimeHelpersを使ってRailsの時刻を変えながらイベントの動作確認などに利用しています。libfaketimeも使えるようになっており、必要であればそちらも利用しています。
こちらの記事では、サーバ側で時刻を変える方法について、Timecop, TimeHelpers, libfaketimeを調査しました。
調査してわかりましたが、どれが優れているというものではありませんでした。
それぞれ、やりたいことの要件に合わせて選択すれば良いと思いました。
もしRailsで時刻を変えたいけど、どうしようと悩んで調べている方がいらっしゃいましたら、
参考にしていただけると幸いです。

(注) 記事の中の図は自分で用意しました

参考

https://andycroll.com/ruby/replace-timecop-with-rails-time-helpers-in-rspec/
https://techracho.bpsinc.jp/penguin10/2018_12_25/67780
https://qiita.com/ktrkmk/items/b1361dd43d22dcf5627e
https://qiita.com/Targityen/items/67682d6c80cdcbe1186c

終わりに

Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です!

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
https://recruit.happyelements.co.jp/

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

Railsでタグ機能を実装

はじめに

現在作成しているアプリでタグ機能を実装したのでその実装方法を残しておきます。
Railsにはタグ機能の実装を簡単にしてくれる acts-as-taggable-on というgemがありますが、関連付けの練習も踏まえて自前で実装します。
現在作っているアプリのPK,FKのみを示したER図は以下のようになります。
スクリーンショット 2020-11-25 9.00.20.png

実行環境

この記事は以下の環境で動作確認しています。
ruby 2.7.1
rails 6.0.3
DB MySQL

モデルの作成

Profileモデルは既に作ってあるという前提で進めていきます。
まず、tagモデルとtag_relastionshipモデルを作成します。

$ rails g model tag name:string
$ rails g model tag_relationship profile:references tag:references

複合キーインデックスを張ります。
こうすることにより、同じタグを二回保存できないようにします。

XXXXXXXXXXXXXX_create_tag_relationships.rb
class CreateTagRelationships < ActiveRecord::Migration[6.0]
  def change
    create_table :tag_relationships do |t|
      t.references :profile, foreign_key: true
      t.references :tag, foreign_key: true

      t.timestamps
    end
    add_index :tag_relationships, [:profile_id, :tag_id], unique: true
  end
end

タグ名は必ず入力して欲しいのでnull:falseにします。

XXXXXXXXXXXXXX_create_tags.rb
class CreateTags < ActiveRecord::Migration[6.0]
  def change
    create_table :tags do |t|
      t.string :name, null: false

      t.timestamps
    end
  end
end

モデルの関連付けとバリデーション

基本的な中間テーブルを用いる多対多の実装です。

中間デーブル経由でタグに紐付くprofileの情報を取得できるように has_many throughも定義します。これに関してはprofileモデルでも同じです。
タグ名はユニークで必ず保持していて欲しいので以下のようなバリデーションにします。

tag.rb
class Tag < ApplicationRecord
  has_many :tag_relationships, dependent: :destroy
  has_many :profiles, through: :tag_relationships

  validates :name, uniqueness: true, presence: true
end
tag_relationship.rb
class TagRelationship < ApplicationRecord
  belongs_to :profile
  belongs_to :tag

  validates :tag_id, presence: true
  validates :profile_id, presence: true
end
profile.rb
class Profile < ApplicationRecord
  belongs_to :user
  has_many :tag_relationships, dependent: :destroy
  has_many :tags, through: :tag_relationships
end

viewの作成

現在作成しているアプリではプロフィールの新規登録の際にタグも登録して欲しいのでprofiles/new.html.erbで実装します。
タグの部分だけ抜粋して載せます。

f,text_field :tagとすることでparams[:profile][:tag]でパラメーターを受け取れるようにします。

profiles/new.html.erb
<div class="input-field col s12">
  <i class="material-icons prefix">local_offer</i>
  <%= f.text_field :tag, placeholder: "タグを複数つけるには' , 'で区切ってください" %>
</div>

表示する際はeachで配列で保存されているタグを繰り返し表示します。

profiles/show.html.erb
<% @user_profile.tags.each do |tag| %>
  <div class="chip">
    <%= tag.name %>
    <i class="close material-icons">close</i>
  </div>
<% end %>

コントローラーの作成

ユーザーとプロフィールはhas_oneを用いて一対一の関係にしているので、buildする際は「インスタンス名.build_アソシエーション名」としています。

プロフィール情報と一緒に送られてきたタグを保存できるようにします。

profiles_controller.rb
  def new
    @user_profile = current_user.build_profile
  end

  def create
    @user_profile = current_user.build_profile(profile_params) # profile_paramsはストロングパラメーター 
    tag_list = params[:profile][:tag].split(',') # viewでカンマ区切りで入力してもらうことで、入力された値をsplit(',')で配列にしている。
    if @user_profile.save
      @user_profile.save_tags(tag_list) # save_tagsというクラスメソッドを使って保存している。
      flash[:notice] = "プロフィールの設定が完了しました"
      redirect_to root_url
    else
      render 'new'
    end
  end

save_tagsメソッドは下記に示します。

profile.rb
  def save_tags(profile_tag)
    profile_tag.each do |tag|
      new_tag = Tag.find_or_create_by(name: tag)
      self.tags << new_tag
    end
  end

「find_or_create_by」メソッドは引数で指定した値があればそれを取得し、なければ作成します。名前の通り、findかcreateするメソッドです。
self.tags << profile_tag ではプロフィールに関連したタグの配列に新たなタグを追加しています。
<< だけでなく、pushメソッドを使っても同じように要素を追加することができます。

タグの編集機能

プロフィールを編集する際にタグも変更できるようにします。

viewの一部を抜粋します。

profiles/edit.html.erb
<div class="input-field col s12">
  <i class="material-icons prefix">local_offer</i>
  <%= f.text_field :tag, value: @tag_list, placeholder: "タグを複数つけるには' , 'で区切ってください" %>
</div>

value: @tag_listとすることで既存の値を表示します。

editアクションではviewで既存の値を表示するために@tag_listを記述します。
pluck関数を使うことによってレシーバのカラムを簡単に取得します。
今回は@user_profile.tags.pluck(:name)としているのでプロフィールに関連したタグのnameカラムを配列で取得します。
join(',')で取得した配列を「,」で区切った文字列にします。

profiles_controller.rb
  def edit
    @user = Profile.find(params[:id]).user
    @user_profile = @user.profile
    @tag_list = @user_profile.tags.pluck(:name).join(',')
  end

  def update
    @user = Profile.find(params[:id]).user
    @user_profile = @user.profile
    tag_list = params[:profile][:tag].split(',')
    if @user_profile.update(profile_params)
      @user_profile.save_tags(tag_list)
      flash[:notice] = "プロフィールの変更が完了しました"
      redirect_to root_url
    else
      render 'edit'
    end
  end

今回は繰り返しの処理が単純なのでpluck関数を使わなくても「&:メソッド」を使えばタグの名前を同程度の記述量で取得することができます。
@user_profile.tags.map(&:name).join(',')
しかし、今回の場合、pluckは指定したカラムのみをSQLで取ってくるのでmapより早いと思われます。(間違っていたらご指摘いただければ幸いです)なのでこのままpluckを使用します。

save_tagsメソッドを更新でも使えるようにします。

profile.rb
  def save_tags(profile_tag)
    current_tags = self.tags.pluck(:name) unless self.tags.nil?
    old_tags = current_tags - profile_tag
    new_tags = profile_tag - current_tags

    # 古いタグを削除
    old_tags.each do |old_tag|
      self.tags.delete(Tag.find_by(name: old_tag))
    end

    # 新しいタグを追加
    new_tags.each do |new_tag|
      add_tag = Tag.find_or_create_by(name: new_tag)
      self.tags << add_tag
    end
  end

文字列の配列でも下記の例のように引き算できます。
a = ["first", "second", "third"]
b = ["first", "third", "forth"]
a - b => ["second"]
b - a => ["forth"]
これを用いて古いタグ、新しいタグを分けてそれぞれ処理します。

最後に

これでタグの作成、編集機能は完成です。
まだユーザーに取って良い形とは言えないのでjsを使うなどしてユーザーにとって使いやすいものに改善していこうと思います。

参考

pluckメソッドが便利な件について
pluckとmapの違いを調査する
Railsでタグ機能をgemを使わずに実装した際のメモ

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

Ruby CLIにて、実行部をクラス内でコードする

ロジックをクラス内で実装し、処理はメインメソッドにまとめたいときに使用した書き方です。
Rubyで使い捨てCLIを作成したときにお世話になりました。

class SomethingTool
  def run
  end
end

if __FILE__ == $0
  something_tool = SomethingTool.new
  something_tool.run
end

if __FILE__ == $0は、明示的に「ここがメインの処理」と示すために使ってます。
詳しい説明は下記の記事を参考にして下さい。
[Ruby] if FILE == $0 ってなんなの!?

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

Railsの1対多の作成

本投稿の目的

・Rails学習の議事録です。


学習に使った教材

Udemyの以下2つの教材を参考にまとめました。
"はじめてのRuby on Rails入門-RubyとRailsを基礎から学びWebアプリケーションをネットに公開しよう"
"フルスタックエンジニアが教える 即戦力Railsエンジニア養成講座"


○1対多とは?

・紐づいたmodel間での関係の名称
・ex)ツイートmodelとコメントmodle
・この時,ツイート1つに対して複数コメントが可能
・この関係を"1対多"の関係と呼ぶ

【説明の簡略のために書き省略記号を使用】
・素となるmodel = model(1)
・紐づくmodel = model(多)
*(コーディング例では任意のmodel名が入る)

【1対多の関係におけるmodel関係の名称を2つ示す】
・has many = model(1)から見たmodel(多)の関係
・belongs to = model(多)からみたmodel(1)の関係

○model(他)作成後の操作

①model(多)の作成

qiita.rb
rails g model model() model(1): references

【解説】
○model(1): references
⇨ referencesでmodel(1)を紐けるように設定
⇨ これで,model(多)には, model名(1)_id というcolumnが作成される

【例:Q&Aアプリケーションのmodelを想定】

qiita.rb
rails g model answer questin: references

【解説】
⇨ answer model へ question model を紐付ける
⇨ questionのcolumnに answer_id が作成される

②model(多)のmigrationファイルへの記述

・model(多)を紐づけで作成しておけば自動記述される

qiita.rb
class Create+model() < ActiveRecord::Migration[5.0]
  def change
    create_table :model()s do |t|
      t.references :model(1), foreign_key: true
      t.string :column1, null: false
      t.text :column2, null: false

      t.timestamps
    end
  end
end

【解説】
○null: faluse
⇨ column値に 空 を受け付けない

○t.reference :model(1), foreign_key: true
⇨ model(1)のid にないと,model(1)_idとしてmodel(多)で保存できない

③model(多)のdbの作成

rails db:migrate

④model(多)のmodelファイルへの記述(自動記述)

・model(多)作成時に,以下が,model(多)のmodelファイルへ自動で記述される

qiita.rb
class model() < ApplicationRecord
  belongs_to :model(1)
end

【解説】
○belongs_to :model(1)
⇨ (*model(1)名称は単数係)

【例:answer modelに紐づくquestion modelの場合】

qiita.rb
class Answer < ApplicationRecord
  belongs_to :question
end

○belongs_to :question⇨ 親がmodel(1) で 子がmodel(多)
⇨ (model(多)から見るとmodel(1)は1つに定まる)
⇨ (子供は複数人いるが,親や一位に定まるというイメージ)

⑤model(1)のmodelファイルへの記述(手動記述)

・model(1)については,追加で以下の記述を追加する

qiita.rb
    has_many :model()s, dependent: :destroy

【解説】
○has_many :model(多)s
⇨ 一番上の行に記述する
⇨ (*モデル名(多)は複数形)

○dependent: :destroy
⇨ model(1)が削除されると紐づいたmodel(多)は自動で消される という設定

【例:Answerの素となるQuesitionの場合】

qiita.rb
class Question < ApplicationRecord
    has_many :answers, dependent: :destroy
end

⑥rails routesの更新

・config/routes.rb ファイルを開く
・内容を次のように更新 (do 移行が追加で更新する部分)
*(model名は複数形)

qiita.rb
resources :model(1)s do
 resources :model()s
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マルチスケールシミュレーション特論:第 10 回をまとめてみた

ruby-2.5.5p157

フィボナッチ数列(問題)

  • 本日は Fibonacci 数列についての問題を解く

    fib(n) = fib(n-1)+fib(n-2)
    0 1 1 2 3 5 8 13 21 ...
    

    を recursion(再帰)で求めよ

進め方

前回と同様に テスト駆動開発 で今後の学習を進めて行く

  • まずは初項から計算を行う

    • red: 表示を行うred → p fib(0)エラーがおこる
    • green: def をしてみる

      def fib(n)
        if n==0
          return 0
        end
      end
      
    • refactoring: assert_equal.rb の assertion(確認)を試しておく

  • 次は、2 項目を求める

    • これは初項と同様に求める
  • 次は 3 項目 = 2 項目 + 1 項目

この処理を逐一行っていく。エラーが現れたら随時処理を行っていく

プログラム(最終版)

上記のようにプログラムを書き進めていくと以下のようなプログラムができます

def fib(n)
 return 0 if n==0
 return 1 if n==1
 return fib(n-1) + fib(n-2)
 end

 require './assert_equal'
 # assert_equal.rb  は同じディレクトリにある事を想定
 [[0,0],[1,1],[2,1],[3,2],[4,3],
 [5,5],[6,8],[7,13],[8,21]].each do |index, expected|
   puts assert_equal(expected, fib(index))
 end

class 化

次にクラス化について学習を行ったオブジェクト指向という考え方がある。このキーとなる考え方が,

  • 隠蔽(capsulation)
  • 継承(inheritance)
  • 多形(polymorphism)

である

クラス化とは

クラス化について

簡単にまとめると、オブジェクト指向プログラミングにおけるクラス(英:class)とは、オブジェクトを生成するための設計図あるいはひな形に相当するものである

プログラムをクラス化すると

まずは class なしのプログラム

def puts_hello name
  puts "Hello #{name}."
end
def gets_name
  name = ARGV[0] || 'world'
  return name
end

次にクラスありのプログラム

class Hello
  def initialize
    # インスタンス変数
    @name = gets_name(name)
    puts_hello
  end

  def puts_hello
    puts "Hello #{@name}."
  end

  def gets_name
    name = ARGV[0] || 'world'
    return name
  end
end

インスタンス変数について

  • 宣言

変数の初めに「@」をつけることで宣言を行うことができる

  • スコープ

インスタンスメソッド内でのみ使用できる変数。インスタンスごとに異なる値を持つことができ、メソッドを超えて参照することができる


  • source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/class10.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プルダウン型セレクトメニューに、DBから項目を持ってくるには??

はじめに

・セレクトボックス(プルダウン型)を作りたい
・メニュー項目に異なるテーブルの情報を持ってくるには??

こうした部分で実際に悩んだので、メモとして残しておきます。
*結論から言うと、collection_selectでうまくいきます!!

まずやったこと

1. モデルの関連付け

今回だと、商品モデルとジャンルモデルを紐付けました

product.rb
class Product < ApplicationRecord

  belongs_to :genre
end
genre.rb
class Genre < ApplicationRecord

  has_many :products, dependent: :destroy
end

***今回、モデルのアソシエーションを行いましたが、やる必要はなかったです!!***

2. 使用するコントローラで変数を定義

今回の場合、productsコントローラを使用するので、
newアクションに、フォーム部分での受け皿となる@newproductを定義し,
実行したいcreateアクションも定義します。

products_controller.rb
  def new
    @newproduct = Product.new
  end

  # 商品新規登録
  def create
    @newproduct = Product.new(product_params)
    @newproduct.save
    redirect_to リダイレクト先
  end

プルダウンメニューを作ろう

まず、collection_selectの使い方をみましょう。
以下の記事を参考にしました!!
[Rails 4.x] FormのSelect プルダウンメニューの項目をDBから引っ張ってくる方法

collection_selectの文法

<%= f.collection_select :属性名, 表示用の配列データ, :valueとして扱うカラム名, :表示用のカラム名, オプション %>

とりあえずcollection_selectを使ってみた

3. メニューの項目をジャンルテーブルのDBから持ってくる。

~admin/products/new/html/erb
 <%= form_with model: @newproduct local: true do |f| %>
  <div class="form-group">
    <%= f.label :genre_id, 'ジャンル' %>
    <%= f.collection_select :genre_id, Genre.all, :id, :name, :include_blank => '選択してください', required: true %>
  </div>
<% end %>

これでうまくプルダウンメニューを表示できました!!

気になったこと

・表示用の配列データは、モデルの関連付けが行われているから、Genre.allを持ってこられるのだろうか?
・属性名のgenre_idはラベルと共通にすべきなのか??

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

マルチスケールシミュレーション特論:第 10 回をまとめてみた

ruby-2.5.5p157

フィボナッチ数列(問題)

  • 本日は Fibonacci 数列についての問題を解く

    fib(n) = fib(n-1)+fib(n-2)
    0 1 1 2 3 5 8 13 21 ...
    

    を recursion(再帰)で求めよ

進め方

前回と同様に テスト駆動開発 で今後の学習を進めて行く

  • まずは初項から計算を行う

    • red: 表示を行うred → p fib(0)エラーがおこる
    • green: def をしてみる

      def fib(n)
        if n==0
          return 0
        end
      end
      
    • refactoring: assert_equal.rb の assertion(確認)を試しておく

  • 次は、2 項目を求める

    • これは初項と同様に求める
  • 次は 3 項目 = 2 項目 + 1 項目

この処理を逐一行っていく。エラーが現れたら随時処理を行っていく

プログラム(最終版)

上記のようにプログラムを書き進めていくと以下のようなプログラムができます

def fib(n)
 return 0 if n==0
 return 1 if n==1
 return fib(n-1) + fib(n-2)
 end

 require './assert_equal'
 # assert_equal.rb  は同じディレクトリにある事を想定
 [[0,0],[1,1],[2,1],[3,2],[4,3],
 [5,5],[6,8],[7,13],[8,21]].each do |index, expected|
   puts assert_equal(expected, fib(index))
 end

  • source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/class10.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マルチスケールシミュレーション特論:第 10 回をまとめてみた

ruby-2.5.5p157

フィボナッチ数列(問題)

  • 本日は Fibonacci 数列についての問題を解く

    fib(n) = fib(n-1)+fib(n-2)
    0 1 1 2 3 5 8 13 21 ...
    

    を recursion(再帰)で求めよ

進め方

前回と同様に テスト駆動開発 で今後の学習を進めて行く

  • まずは初項から計算を行う

    • red: 表示を行うred → p fib(0)エラーがおこる
    • green: def をしてみる

      def fib(n)
        if n==0
          return 0
        end
      end
      
    • refactoring: assert_equal.rb の assertion(確認)を試しておく

  • 次は、2 項目を求める

    • これは初項と同様に求める
  • 次は 3 項目 = 2 項目 + 1 項目

この処理を逐一行っていく。エラーが現れたら随時処理を行っていく

プログラム(最終版)

上記のようにプログラムを書き進めていくと以下のようなプログラムができます

def fib(n)
 return 0 if n==0
 return 1 if n==1
 return fib(n-1) + fib(n-2)
 end

 require './assert_equal'
 # assert_equal.rb  は同じディレクトリにある事を想定
 [[0,0],[1,1],[2,1],[3,2],[4,3],
 [5,5],[6,8],[7,13],[8,21]].each do |index, expected|
   puts assert_equal(expected, fib(index))
 end

  • source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/class10.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マルチスケールシミュレーション特論:第 10 回をまとめてみた

ruby-2.5.5p157

フィボナッチ数列(問題)

  • 本日は Fibonacci 数列についての問題を解く

    fib(n) = fib(n-1)+fib(n-2)
    0 1 1 2 3 5 8 13 21 ...
    

    を recursion(再帰)で求めよ

進め方

前回と同様に テスト駆動開発 で今後の学習を進めて行く

  • まずは初項から計算を行う

    • red: 表示を行うred → p fib(0)エラーがおこる
    • green: def をしてみる

      def fib(n)
        if n==0
          return 0
        end
      end
      
    • refactoring: assert_equal.rb の assertion(確認)を試しておく

  • 次は、2 項目を求める

    • これは初項と同様に求める
  • 次は 3 項目 = 2 項目 + 1 項目

この処理を逐一行っていく。エラーが現れたら随時処理を行っていく

プログラム(最終版)

上記のようにプログラムを書き進めていくと以下のようなプログラムができます

def fib(n)
 return 0 if n==0
 return 1 if n==1
 return fib(n-1) + fib(n-2)
 end

 require './assert_equal'
 # assert_equal.rbは同じディレクトリにある事を想定
 [[0,0],[1,1],[2,1],[3,2],[4,3],
 [5,5],[6,8],[7,13],[8,21]].each do |index, expected|
   puts assert_equal(expected, fib(index))
 end

  • source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/class10.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

google recruit

課題

https://qiita.com/daddygongon/items/ba94b0f2a73990fc6a07

問題

{e(自然対数の底)の値で連続する10桁の数のうち,最初の素数} をrubyで求めよ.

解答

ソースコード

#!/usr/bin/env ruby
# frozen_string_literal: true

napier_number = <<~EOS
  2.71828182845904523536028747135266249775\
  7247093699959574966967627724076630353547\
  5945713821785251664274274663919320030599\
  2181741359662904357290033429526059563073\
  81323286279434907632338298807531952510190
EOS

digits = 10

def prime?(n)
  return false if n.even?

  (3...Math.sqrt(n)).step(2).each do |i|
    return false if n % i == 0
  end

  true
end

(2...napier_number.length - digits).each do |i|
  n = napier_number[i, digits].to_i
  if prime?(n)
    puts "#{n} is the first 10 digit prime number."
    break
  end
end

実行結果

(*'-') < ./codes/google_recruit.rb
7427466391 is the first 10 digit prime number.

学んだこと

ヒアドキュメント

https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html#here

<<[(-|~)]["'`]識別子["'`]
   ...
識別子

で、文字列を表現することができる。

prime

require 'prime' することによって、 Prime.prime?(n) で素数判定できる。

#!/usr/bin/env ruby
# frozen_string_literal: true

require 'prime'

napier_number = <<~EOS
  2.71828182845904523536028747135266249775\
  7247093699959574966967627724076630353547\
  5945713821785251664274274663919320030599\
  2181741359662904357290033429526059563073\
  81323286279434907632338298807531952510190
EOS

(2...napier_number.length - 10).each do |i|
  n = napier_number[i, 10].to_i
  p n if Prime.prime?(n)
end

  • source ~/go/src/github.com/TeamNishitani/grad_members_20f/members/iPolyomino/google_recruit.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マルチスケールシミュレーション特論:第 10 回をまとめてみた

ruby-2.5.5p157

フィボナッチ数列(問題)

  • 本日は Fibonacci 数列についての問題を解く

    fib(n) = fib(n-1)+fib(n-2)
    0 1 1 2 3 5 8 13 21 ...
    

    を recursion(再帰)で求めよ

進め方

前回と同様に テスト駆動開発 で今後の学習を進めて行く

  • まずは初項から計算を行う

    • red: 表示を行うred → p fib(0)エラーがおこる
    • green: def をしてみる

      def fib(n)
        if n==0
          return 0
        end
      end
      
    • refactoring: assert_equal.rb の assertion(確認)を試しておく

  • 次は、2 項目を求める

    • これは初項と同様に求める
  • 次は 3 項目 = 2 項目 + 1 項目

この処理を逐一行っていく。エラーが現れたら随時処理を行っていく

プログラム(最終版)

上記のようにプログラムを書き進めていくと以下のようなプログラムができます

def fib(n)
 return 0 if n==0
 return 1 if n==1
 return fib(n-1) + fib(n-2)
 end

 require './assert_equal'
 [[0,0],[1,1],[2,1],[3,2],[4,3],
 [5,5],[6,8],[7,13],[8,21]].each do |index, expected|
   puts assert_equal(expected, fib(index))
 end

  • source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/class10.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

第8回

Ubuntu-20.04.1 ruby-2.7.0p0

assert_equalを作る

イコールかそうでないかをお知らせしてくれるメソッド.

文字の色付け

成功したかどうかを色で端的に分かった方が良い.

require 'colorize'

puts '出力1'.green  #緑色に
puts '出力2'.red    #赤色に
puts '出力1'.blue   #青色に

とりあえずassert_equalとassert_not_equalを作る

#{method}でメソッド名が取れる.

require 'colorize'

def assert_equal(expected, result)
  puts "expected :: #{expected}"
  puts "result :: #{result}"

  if expected == result
    print "succeeded in #{__method__}.\n".green
  else
    print "failed in #{__method__}.\n".red
  end
end

def assert_not_equal(expected, result)
  puts "expected :: #{expected}"
  puts "result :: #{result}"

  if expected == result
    print "failed in #{__method__}.\n".red
  else
    print "succeeded in #{__method__}.\n".green
  end
end

assert_equal(1, 1)
assert_equal(1, 2)
assert_not_equal(1, 2)
assert_not_equal(1, 1)

重複部分の整理

assert_equal とassert_not_equalで重複してる部分があるなぁ…

require 'colorize'
def puts_vals(expected,result)
  puts "expected :: #{expected}"
  puts "result :: #{result}"
end

def assert_equal(expected, result)
  puts_vals(expected, result)

  if expected == result
    print "succeeded in #{__method__}.\n".green
  else
    print "failed in #{__method__}.\n".red
  end
end

def assert_not_equal(expected, result)
  puts_vals(expected, result)

  if expected == result
    print "failed in #{__method__}.\n".red
  else
    print "succeeded in #{__method__}.\n".green
  end
end

assert_equal(1, 1)
assert_equal(1, 2)
assert_not_equal(1, 2)
assert_not_equal(1, 1)

と重複部分は別の関数に.

別のスクリプトでassert_equalを使う

その前に

別スクリプトで使いたいのはassert_equal及びassert_not_equal.

assert_equal(1, 1)
assert_equal(1, 2)
assert_not_equal(1, 2) 
assert_not_equal(1, 1)

の部分は要らない.

if $PROGRAM_NAME == __FILE__
  assert_equal(1, 1)
  assert_equal(1, 2)
  assert_not_equal(1, 2)
  assert_not_equal(1, 1)
end

とすることで,書かれているファイル名と動いているファイル名が一致した時のみ実行させることができる.

使う

先ほどの変更を加えたassert_equal

require 'colorize'
def puts_vals(expected,result)
  puts "expected :: #{expected}"
  puts "result :: #{result}"
end

def assert_equal(expected, result)
  puts_vals(expected, result)

  if expected == result
    print "succeeded in #{__method__}.\n".green
  else
    print "failed in #{__method__}.\n".red
  end
end

def assert_not_equal(expected, result)
  puts_vals(expected, result)

  if expected == result
    print "failed in #{__method__}.\n".red
  else
    print "succeeded in #{__method__}.\n".green
  end
end

if $PROGRAM_NAME == __FILE__
  assert_equal(1, 1)
  assert_equal(1, 2)
  assert_not_equal(1, 2)
  assert_not_equal(1, 1)
end

をrequireで呼び出して使用する.

require './assert_equal'

assert_equal(4, 2 * 2)
assert_not_equal('Muku', 'Miku')

call_equal.rbを作成し,実行.

> ruby call_equal.rb
expected :: 4
result :: 4
succeeded in assert_equal.
expected :: Muku
result :: Miku
succeeded in assert_not_equal.

と別スクリプトでも使用することができた.

参考サイト

この記事は以下のサイトを参考に作成しました.


  • source ~/grad_members_20f/members/musutafakemaru/8.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript基礎まとめ③

1.オブジェクト

Javascriptにおけるオブジェクトは
大きなオブジェクトの中に、またオブジェクトがあり、その中にまたオブジェクト、
プロパティがある。

例えば

let human = {
  men:{
   name: "yamada",
   age : 20,
  }
 }

 console.log(human.men.name)


///yamadaと表示されます///

ここでオブジェクト名がhuman、プロパティ名がnameになる。

プロパティを追加、今ある値を変更するときには

let human = {
  men:{
   name: "yamada",
   age : 20,
  }
 }
human.age = 25
/// age を変更///
human['address'] = 'Tokyo'
///addressのプロパティを追加///

となる。

2.documentオブジェクト

document.getElementById("id名")

document.getElementsByClassName("class名")

document.querySelectorAll("セレクタ名")

document.querySelector("セレクタ名")


3.イベント発火
〇〇したら**するの
〇〇したら =イベント
〇〇したら**するの一連の流れをイベント発火という。

イベント発火に使う関数はaddEventListenerメソッド。

要素.addEventListener('イベント名', 関数)

イベント名は「クリックしたら」とか「カーソルが来たら」とか
それぞれ定義されている。

〇〇したら**するの
**するもう少し深堀りすると
例えばカーソルが指定の要素に乗っているときは色が変わり、
要素から外れたときには色が戻るととかは

要素.setAttribute(name, value)
/// nameのvalue(値)を指定する///
要素.removeAttribute(name, value)
/// nameのvalue(値)をとりのぞく////

普段使っているサイトを見ていくとJavaScriptかなり使われていそうだなと
基礎を勉強していくだけでも感じました。

間違い等あればコメントお願いいたします!

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

assert_equal rubular

assert_equal

テスト駆動開発で進めるときに、あるプログラムの処理結果が予測された値と正しいかを確認するために用いられる。

assert_equal(expected, actual) で書く。

#!/usr/bin/env ruby
# frozen_string_literal: true

def assert_equal(expected, result)
  return expected == result
end

p assert_equal(1, 1 + 1)

自分で作らずとも、~assert_equal~ できる

class Test::Unit::TestCase を使うと使える。

require 'test/unit'
class Dummy < Test::Unit::TestCase
  def test_assert
    assert_equal(2, 1 + 1)
  end
end

colorize

ターミナルの出力に色付けするときに使える。

'true'.green みたいに String に対して適用する。

require 'colorize'

def assert_equal(expected, result)
  if expected == result
    puts 'true'.green
  else
    puts 'false'.red
  end
end

assert_equal(1, 1)

Rubular

正規表現の確認ができるウェブサイトがある。

https://rubular.com/


  • source ~/go/src/github.com/TeamNishitani/grad_members_20f/members/iPolyomino/c9_assert_equal.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Heroku上のRailsアプリにScoutを導入

Herokuの管理画面からアドオンを追加

ダッシュボードから操作する方法と、コマンドラインから操作する方法があります。
アドオンを追加した時点で、環境変数が自動でセットされます。

ダッシュボードから操作する場合

  • Herokuのダッシュボードにアクセス
  • Resourcesタブを開く
  • Add-onsの下の検索窓に'Scout'と入力
  • Provisionをクリック

コマンドラインから操作する場合

Heroku CLIがインストールされている状態で以下のコマンドを叩く

$ heroku addons:create scout

Gemを追加

Gemfile
gem 'scout_apm'
$ bundle install

これだけ。

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