20200430のRubyに関する記事は19件です。

【�database.yml】DBをDockerとrails server両方で使えるように�設定

Dokerで開発時に、docker-compose rundocker-compose run よりも早いrails serverで一時的に動作確認をしたい場合があるかと思う。
そういった場合にも、以下のconfig/database.yml内の記述だけで対応することができる。

検証環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.3
BuildVersion:   19D76
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin19]
$ rails -v
Rails 5.2.4.2

database.ymlについて

まず、database.ymlとは

config/database.yml(例)
default: &default
  adapter: mysql2
  encoding: utf8
  collation: utf8_general_ci
  pool: 5
  host: <%= ENV['MYSQL_HOST'] || 'localhost' %>
  username: <%= ENV['MYSQL_USERNAME'] || 'root' %>
  password: <%= ENV['MYSQL_PASSWORD'] || '' %>
  socket: /tmp/mysql.sock

主な内容

adapter: 使用するデータベースの種類(postgresql, mysql2等)

encoding: 文字コード

collation: MySQLの文字列と照合順序(ソート順)、”文字コード言語名比較法”で構成される

pool: 確保する接続プールの数

host: データベースが動作しているホスト名またはIPアドレス

username,password: データベースに接続するユーザー名・パスワード

socket: DB通信の接続口

比較演算子: ||

orと同じ働きをする。
Ruby 2.7.0 リファレンスマニュアル 演算子式

文法:

式 `||' 式
式 or 式
左辺を評価し、結果が真であった場合にはその値を返します。左辺の評価結果が偽であった場合には右辺を評価しその評価結果を返します。

config/database.ymlのhost,username,passwordの部分をこれで書くことで、ENV['MYSQL_HOST']等の環境変数がない場合に、localhost等を返してくれる。

終わりに。

最後まで読んで頂きありがとうございます:bow_tone1:
転職の為、未経験の状態からRailsを学習しております。正しい知識を着実に身に着け、実力のあるエンジニアになりたいと考えています。継続して投稿していく中で、その為のインプットも必然的に増え、成長に繋がるかと考えています。
今現在、初心者だからといって言い訳はできないですが、投稿の内容に間違っているところや、付け加えるべきところが多々あるかと思いますので、ご指摘頂けると幸いです。この記事を読んで下さりありがとうございました。

参考にさせて頂いた記事

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

文字列の中に変数を入れる記法

自分はもともとRubyでプログラミングに触れて、eRubyでCGIを書いたりしてたから文字列の中に{}でくくった変数を入れるのを多用していたのですが。

HelloWorld.rb
str1 = "Hello"
str2 = "World!"
print "#{str1}, #{str2}\n"

これをC#で書こうとしたらこうなります。

HelloWorld.cs
var str1 = "Hello";
var str2 = "World!";
Console.Write($"{str1}, {str2}\n") ;

別解でこういう風にも書けるようです。

HelloWorldAlt.cs
var str1 = "Hello";
var str2 = "World!";
Console.Write("{0}, {1}\n", str1, str2);

ところで、最近Javaをはじめたのですが、Javaだとこういう風に書くみたいです。

HelloWorld.java
var str1 = "Hello";
var str2 = "World!";
System.out.printf("%s, %s\n", str1, str2);

後で書いたHelloWorldAlt.csのC#のパターンに近いですね。
ぶっちゃけJavaが一番わかりづらいので、今後C#で書く時もJava表記に近い後者の記法を書いていこうかなと思いました。

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

よくあるサイコロ系問題をRubyで解く

はじめに

競プロはじめたての初心者です。
言語はRubyをよく使っています。

昔、どうしても解くことができなかった問題を、最近遂に解くことができたので、シェアしたいと思います。
※ 著作権の問題上、問題は少し変えています

正解のコードを紹介する前に、私がつまずいたときの考え方とコードを先にシェアしています。
これは、初心者プログラマの考え方を知りたいという熟練者の方の手助けになれば良いなと考えたからです。
少々長くなってしまっているので、答えのみ知りたい方は、「私がつまずいた点」の部分をスキップして読んでもらえばと思います。

サイコロの問題

問題は以下の通りです。

スクリーンショット 2020-04-29 22.11.02.png    スクリーンショット 2020-04-29 23.13.00.png

  1. 上記の展開図からなるサイコロがある
  2. このサイコロを、ある方向にある回数転がす
  3. 最後の上面の数字を出力せよ

入力
入力は、「N,S,E,W」のどれかなる、一つ以上の文字列である。
尚、「N,S,E,W」はそれぞれ上図の矢印方向にサイコロを1回転させることを意味する。

入力例
NW

出力
入力された文字列の長さ分、サイコロを回転させたときの、上面の数字を出力する。

出力例
3

私がつまずいた点

私がなぜ最初にこの問題を解けなかったのか、つまずいた点を紹介します。
初心者はこういうところを難しいと感じているということが伝わればいいなと思います笑

私は、この問題を見た際、サイコロが縦方向と横方向に回転することから、縦方向の数値からなる配列と、横方向の数値からなる配列を作れば良いと考えました。

example.rb
# サイコロが縦に転がった際の数値の配列
tate = [1,2,6,5]
# サイコロが横に転がった際の数値の配列
yoko = [4,6,3,1]

そして、サイコロがN方向に転がったときは、縦の配列の最初の要素(tate[0])を、縦配列の最後(tate[-1])に持ってこれば良いと考えました。
この移動を行なった後の、配列の一番最初の要素が、サイコロの上面の数値になります。
サイコロがS方向に転がった際は、この逆の作業を行います。すなわち、縦配列の最後(tate[-1])を、縦配列の最初(tate[0])に持ってきます。

example.rb
# N方向への回転
# 配列の最初の値を、配列の最後に持ってくる
tate << tate.shift

# S方向への回転
# 配列の最後の値を、配列の最初に持ってくる
tate.unshift(tate.pop)

これで、縦方向にサイコロが回転した場合に、配列の一番目の数値を見れば、サイコロの上面の数値がわかるようになりました。

横方向にサイコロが回転した際も、縦方向に回転した場合と考え方は全く同じです。

example.rb
# E方向への回転
# 配列の最初の値を、配列の最後に持ってくる
yoko << yoko.shift

# W方向への回転
# 配列の最後の値を、配列の最初に持ってくる
yoko.unshift(yoko.pop)

しかし、このコードでは動きません 

すでにお気付きの方もいらっしゃるかもしれませんが、このコードは間違っています。
さて、どこが間違っているでしょう。

サイコロの縦回転と横回転が繰り返されたケースを考えるとわかります。

実はこのコード、次の場合にのみ正しく動きます。

- サイコロが縦回転しかしない場合
- サイコロが横回転しかしない場合

なぜこうなってしまうのでしょう?

サイコロがN方向に1回、E方向に2回、回転した場合を考えてみましょう。
まず、N方向に1回回転すると、サイコロ上面は、2になります。
次に、E方向に2回回転すると、サイコロ上面は、5になります。

しかし、上記のコードでこれを実行すると、6が出力されます。
おかしいですね。

私のコードがうまくいかない理由は、縦方向、もしくは横方向への回転により、縦配列、横配列の値が変化するということを無視しているコードになっているからです。
スクリーンショット 2020-04-29 23.13.00.png
サイコロがN方向に1回、回転した場合、横方向の配列は次のように変わります。

example.rb
# yoko = [4,6,3,1] であったが、
# N方向に1回転した場合は、次のような配列になっていないといけない。
yoko = [4,5,3,2]

このように、縦方向への回転と横方向への回転が組み合わさると、縦配列、横配列の値を動的に変化させないといけないことがわかりました。

最初に問題を解いたときも、ここまではたどり着けたのですが、結局どうすれば良いのかわからず寝ました笑

解答コード in Ruby

つい最近、同じ問題と出会い解法が閃きました(成長の証だと信じたい笑)
そのコードがこちらです。

answer.rb
# サイコロを用意
dice = [*1..6]
# 回転方向(N,S,E,W)の入力結果を配列に
commands = gets.chomp!.split("")

# N方向への回転
def turn_north(dice)
  [dice[1], dice[5], dice[2], dice[3], dice[0], dice[4]]
end

# S方向への回転
def turn_south(dice)
  [dice[5], dice[0], dice[2], dice[3], dice[4], dice[1]]
end

# E方向への回転
def turn_east(dice)
  [dice[3], dice[1], dice[0], dice[5], dice[4], dice[2]]
end

# W方向への回転
def turn_west(dice)
  [dice[2], dice[1], dice[5], dice[0], dice[4], dice[3]]
end

commands.each do |c|
  case c
  when "N"
    dice = turn_north($dice)
  when "S"
    dice = turn_south($dice)
  when "W"
    dice = turn_west($dice)
  when "E"
    dice = turn_east($dice)
  end
end

# サイコロ上面の数値を出力
puts $dice.first

サイコロを回転させるメソッドのロジックがかなりわかりにくいと思うので、説明します。

まず、サイコロの縦方向ごと、横方向ごとに配列を用意するという考え方を捨てました。
理由は上記の通りです。

スクリーンショット 2020-04-29 22.11.02.png    スクリーンショット 2020-04-29 23.13.00.png

発想を変えて、サイコロが回転したときに、上記の展開図の位置にどの数値が入るかを考えました。
今回の場合だと、N方向に1回転した場合、展開図の1のところに数値2がきます。
そして、展開図の2のところには、数値6がきます。
このように、展開図の場所にどの数値が入るかを、1から6まで考えます。
すると、次のような結果になります。

N方向に1回転した場合
- 展開図の1に、数値2
- 展開図の2に、数値6
- 展開図の3に、数値3
- 展開図の4に、数値4
- 展開図の5に、数値1
- 展開図の6に、数値5

この、動きをコードで再現したのが、turn_north(dice)メソッドです。
このメソッドは、引数でサイコロ(今の展開図の数字順に、サイコロの数値が並んでいる配列)を受け取ります。

def turn_north(dice)
  [dice[1], dice[5], dice[2], dice[3], dice[0], dice[4]]
end

そして、引数で受け取った配列に対して、N方向に一回転した際に、サイコロの数値と展開図の位置の変化を戻り値として返しています。

このコードは、N方向に回転した際、展開図の位置に対しサイコロの数値がどのように変化するかという性質を表したものです。
そのため、例えサイコロが途中で横方向に回転しようと、この性質が変わることはありません。
このように考えることによって、正しい出力を出せるコードが出来上がりました。

[追記]
サイコロを転がすメソッドの部分は、Array#values_atメソッドを使えば、よりエレガントに記述できます。
例えば、turn_north(dice)メソッドは次のように書き換えられます。

# 元のコード
def turn_north(dice)
  [dice[1], dice[5], dice[2], dice[3], dice[0], dice[4]]
end

# Array#values_atを用いたコード
def turn_north(dice)
  dice.values_at(1, 5, 2, 3, 0, 4)
end

詳しくは、こちらを参照ください
https://docs.ruby-lang.org/ja/2.7.0/method/Array/i/values_at.html
@scivola さん、ご指摘ありがとうございます!

最後に

今回私が書いたコードは、非常にわかりにくいコードのような気がします。
特に、サイコロが回転したときの処理は、このように長々と説明しないと理解されないと思い、もっと綺麗なコードがあるのではないかと思います。

もし、この記事を読んだ方で、もっと良い方法があるという方は、コメントご教授下さい。

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

Ruby Classの継承を使ってみた。

どうもチャンクノです。
今回はClassの継承を使ってみた話です。
ぶっちゃけ今までどういうところで使えばいいか分かってなかったんですよね。
でも個人開発している中で、ここで使うんじゃね?ってタイミングがあったので使ってみました!

自分の作っているアプリにはTradeモデルがあり、最初はそこに勝率を求めるメソッドを書いていました。

class Trade < ApplicationRecord
  attr_accessor :current_user_id

  def search_user_fx_trades
    Trade.where("(user_id = ?) and (trade_category_id = ?)", current_user_id, 1)
  end

  def win_number_of_fx_trades
    search_user_fx_trades.where("result" => "資産増")
  end

  def win_number_of_fx_daytrades
    win_number_of_fx_trades.where("trade_style_id" => 1).count
  end

  def daytrade_win_rate
    return "0%" if win_number_of_fx_daytrades === 0
    "#{(win_number_of_fx_daytrades / search_user_fx_trades.count.to_f * 
  100).floor(1)}%"
  end

  def fx_win_rate
    return "0%" if win_number_of_fx_trades.count === 0
    "#{(win_number_of_fx_trades.count / search_user_fx_trades.count.to_f * 
     100).floor(1)}%"
  end
end

ほんとはもっと書いてあるんですけど長くなってしまうのでこのぐらいに。
Tradeって他にもスイングトレードとスキャルピングトレードっていうのがあるんですがそれを書き終えた時に、このままいくとメソッドの数えぐいことになるなと気づきました。
まだ年単位や週単位のメソッドが残っていたので。
そこでClassの継承の出番か?と思い新たにClassを作成しました。

class DayTrade < Trade

  def win_number_of_fx_daytrades
    win_number_of_fx_trades.where("trade_style_id" => 1).count
  end

  def daytrade_win_rate
    return "0%" if win_number_of_fx_daytrades === 0
    "#{(win_number_of_fx_daytrades / search_user_fx_trades.count.to_f * 100).floor(1)}%"
  end

end

こんな感じ。
書き方は子クラス < 親クラスです。
これでDayTradeクラスでもTradeクラスのメソッドが使えるようになりました。
子クラスは親クラスの全てを引き継ぐので、attr_accessor :current_user_idなどを、改めて子クラスに記載する必要はありません。
注意点としては、DayTrade内にあるメソッドなので新たにDayTradeインスタンスを作成することと、作成したインスタンスにcurren_userを引数に取らなければいけないということです。

@day_trade = DayTrade.new(current_user)
こうすると

@day_trade.daytrade_win_rate
こんな感じで呼び出せるようになる。

使用する場面があってるかは分かりませんが、こういう感じでそれぞれまとめることで分かりやすくなるんじゃないかなと思います!
なんかメソッドがめちゃくちゃ増えて分かりにくいなと感じてきたら別クラスに切り出せないか検討してみましょう!!
あ、ちなみにちゃんとTrade.rbというファイルとは別にDayTrade.rbというファイルを作ってくださいね!
そんなこと分かってるとは思いますが念の為?

それでは今日はこの辺で!
皆様よきプログラミングライフを?

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

「破壊的メソッド」はRuby用語なのか?

昨日、「データサイエンティストが知るべき破壊的メソッドのすべて」という記事を書いたのですが、友人から次のような意見を貰いました。

微妙に気になったけど、「破壊的メソッド」「非破壊的メソッド」ってプログラミング全般でなくRuby用語で、かつ、「レシーバを変更するメソッド」っていう意味じゃない?
もしあえて名前で呼ぶなら、inplace methodが正しそう。(日本語は見つからない)
https://discuss.pytorch.org/t/what-is-in-place-operation/16244
https://ja.wikipedia.org/wiki/In-placeアルゴリズム

更に、例えばpandasでもinplaceという引数名が使われているので、データサイエンティスト向けならinplaceのほうが馴染みあるかもしれないとも言われました。

たしかに「破壊的」と調べて出てくる記事は、Rubyのものが多いように思います。少し気になったので、いろいろな言語のドキュメントで「破壊的(destructive)」「インプレース(in-place)」という単語で調べて比較してみました。

Pythonの場合: in-placeが優勢

ドキュメントを検索したところ、実際に「インプレース(in-place)」という単語がよく使われているようです。例えば「プログラミング FAQ」の中では次のようにあります。

文字列をインプレースに変更するにはどうしたらいいですか?¶

文字列はイミュータブルなので、それはできません。殆どの場合、組み立てたい個別の部品から単純に新しい文字列を構成するべきです。

一方で、「破壊的(destructive)」という単語も多少は使われています。例えばPython2.7のドキュメントには「破壊的に(destructively)」という単語があります。最新バージョンのドキュメントでは「消去して返します」とありますが、日本語の訳語が変わっただけでした。

集合のアルゴリズムで使われるのと同じように、 popitem() は辞書を破壊的にイテレートするのに便利です。辞書が空であれば、 popitem() の呼び出しは KeyError を送出します。

Python3.8でも「curses --- 文字セル表示を扱うための端末操作」の中に「非破壊的(non-destructive)」という単語が見つかりました。

ウィンドウを destwin の上に重ね書き (overlay) します。ウィンドウは同じサイズである必要はなく、重なっている領域だけが複写されます。この複写は非破壊的です。

少し離れた箇所に「破壊的(destructive)」もあります。

destwin の上にウィンドウの内容を上書き (overwrite) します。ウィンドウは同じサイズである必要はなく、重なっている領域だけが複写されます。この複写は破壊的です。

Rubyの場合: destructive methodという用語が説明されている

Official Ruby FAQの中に「What is a destructive method?」という項目が用意されていました。日本語版の訳は見つかりませんでした。

The plain version creates a copy of the receiver, makes its change to it, and returns the copy. The “bang” version (with the !) modifies the receiver in place.

説明の中に「in place」という単語も使われています。

ドキュメントではあまりヒットしなかったので、るびまも見てみます。やはり「破壊的」という単語がよく使われています。例えば「Ruby コードの感想戦 【第 2 回】 WikiR」より。

force_encoding は ! がついていないので名前だけではわかりづらいですが、破壊的なメソッドです。 そのため、戻り値を text に代入する必要はありません。これで十分です。

「6 月 10 日 午前の部」でin-placeという単語が使われている箇所もありました。

文法上、yield が先に入って、ブロックつき引数を後で導入したとき yield が邪魔になった。あと String で、in-place で mutate するしないがごっちゃになってよくない。Perl からもってきた組み込み変数の 98% は後悔してます。

PHPの場合: 両方とも併用されている

「in-place」と「destructive」という単語が両方とも使われているようです。これらの箇所の日本語への翻訳はまだ行われていない様子?

Ds\Sequence::sort」より

Sorts the sequence in-place, using an optional comparator function.

Ds\PriorityQueue::toArrayより。

注意:

This method is not destructive.

その他の言語

Rubyに影響を与えたSmalltalkも調べてみたかったのですが、はっきりしたこととは分かりませんでした。ただ、Smalltalk-72のドキュメントを見ると、non-destructiveという単語が少なくとも一箇所使われていました。

Note that you can make non-destructive text by using xor ink which complements the background so that reshowing the text crases it while restoring what was
underneath.

また、基本的に変数がイミュータブルなHaskellも調べてみたのですが、destructiveが「過去の互換性を切った言語仕様のアップデート」という意味で使われているものしか見つけられなかったのと、ドキュメント自体を読むのが今の私にとっては骨が折れそうだったので諦めました。

また、Rubyでは「destructive method」という固有名詞に近い使われ方をしているのに対して、他の言語のdestructiveは一般的な形容詞として使われているように見えます。もしかすると、「破壊的メソッド」というのはRubyのコミュニティで使われ始めた言葉が、日本語でプログラミング用語として認識されて広まったものなのかもしれません。機会があればまた調べてみます。

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

テストをまともにやってこなかったから、rspec-railsを使ってモデルの単体テストを初めてまともにやってみた話。

超簡単なモデルの単体テストをやってみる。

前回せっかくミニアプリまで作ったので、今までまともにやってこなかったモデルの単体テストもやってしまおう!

と、今回は、超簡単な単体テストを実施しました。
テスト環境の準備が色々あったので備忘録として記載します。

動作環境

ruby 2.5.1
rails 5.2.4.2

下準備

modelとかmigration等、ここまでの環境は前回記事の下準備をご覧ください。

テスト環境の準備

gemをインストール

.Gemfile
  gem 'rspec-rails'
  gem 'factory_bot_rails'

factory_girlは使ったことないので今回はfactory_botで進めます。

terminal.
$ bundle install

respecをインストール

terminal.
$ rails g rspec:install

.rspecへの追記

.rspec
(以下を追記)
--format documentation

これ、なんの意味があるのかと思ったら、書かないとターミナルにテスト結果がdocument形式で表示されません。

spec/rails_helper.rbへの追記

spec/rails_helper.rb
RSpec.configure do |config|
  (以下を追記)
  config.include FactoryBot::Syntax::Methods #Factory_botのメソッドを使用するため
end

rspecファイルを作成する

terminal.
$ bin/rails g rspec:model item

単体テスト実装

それでは単体テストのコードを書いていきます。

とりあえずFactory_botをつくってみた。

まずは半端な知識でFactory_botを作ります。

spec/factoreis/item.rb
FactoryBot.define do
  factory :item do
    #name に "test" とだけ入れます。
    name    {"test"}
  end
end

とりあえずテストコードも書いてみた。

次に半端な知識でテストコードも書いていきます。

spec/models/item_spec.rb
require 'rails_helper'

RSpec.describe Item, type: :model do
  describe '#create' do
    let(:item) {build(:item)}
    context 'can save' do
      it "is valid with a name" do
        expect(item).to be_valid   #name に "test" が入っていれば成功する(はず)
      end
    end
    context 'can not save' do
      it "is valid without a name" do
        item.name = ""              #name が 空の場合
        expect(item).to be_invalid  #失敗する(はず)
      end
    end
  end
end

テストコード走らせてみた。

Factoryも作った。テストコードも書いた。
次にやることと言ったら、もう決まってる。
いざ。

terminal.
$ bundle exec rspec spec/models/item_spec.rb
terminal.
Item
  #create
    can save
      is valid with a title
    can not save
      is valid without a title (FAILED - 1)

Failures:

  1) Item#create can not save is valid without a title
     Failure/Error: expect(item).to be_invalid
       expected `#<Item id: nil, title: "", created_at: nil, updated_at: nil>.invalid?` to return true, got false
     # ./spec/models/item_spec.rb:14:in `block (4 levels) in <top (required)>'

Finished in 0.34277 seconds (files took 5.22 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/models/item_spec.rb:12 # Item#create can not save is valid without a title

あれ? エラー?
あ、validation書いてないですねこれ。

validationを追加してみた。

null: falseだけ追加します。

app/models/item.rb
class Item < ApplicationRecord
  (中略)
  validates :name,presence: true #null: false
end

結果

terminal.
$ bundle exec rspec spec/models/item_spec.rb
terminal.
Item
  #create
    can save
      is valid with a title
    can not save
      is valid without a title

Finished in 0.33208 seconds (files took 6.29 seconds to load)
2 examples, 0 failures

上手(?)にできました!

教訓

validationを忘れない!

今回は以上です。

参考にさせていただいた記事

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

Kinx ライブラリ - Net.Http

Network - Http

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

今回は Network(Http) です。

libcurl を抱えているので Http 以外も作れるはずだが、まずは一番欲しいのは Http/Https でしょう。

HTTP/HTTPS リクエスト

各メソッドの意味は後で記載することとして、まずは使い方から。

Http クライアント

Http クライアント・オブジェクトは Net.Http クラスを使用する。

var http = new Net.Http();

HTTP メソッド

headgetpostputdelete をサポート。

HTTP HEAD - head

HEAD メソッドで情報を取得する。SSL に関しては一先ず false にして実行。

var http = new Net.Http();
http.sslVerifyPeer(false);
http.sslVerifyHost(false);
var r = http.head("https://docs.ruby-lang.org/ja/latest/class/Range.html");
if (r.code == 200) {
    System.println(r.toJsonString(true));
}

コールバック関数を指定することもできる。データは取得できたデータ分だけ毎回コールバックされる。その動作はメソッドに関わらず全て同じ動作をする。

また、データは単なるバイト列として扱われるため、日本語などのマルチバイト文字が混じる場合には区切り位置が文字コードの区切りとぴったりあった場所で区切られるとは限らないことに注意。なので、以下のように直接出力した場合、日本語が混じると変になる可能性がある。ヘッダはまだマシだがボディの場合は基本的にはバッファにためていくのが良いかと。

var http = new Net.Http();
http.sslVerifyPeer(false);
http.sslVerifyHost(false);
var r = http.head("https://docs.ruby-lang.org/ja/latest/class/Range.html", &(data) => {
    System.print(data);
});
if (r.code == 200) {    
    ...
}

コールバックでの取得結果は以下の通り。

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 48121
Server: nginx/1.14.2
Content-Type: text/html
Last-Modified: Wed, 15 Apr 2020 00:16:18 GMT
ETag: "5e965252-bbf9"
Cache-Control: public, max-age=43200, s-maxage=172800, stale-while-revalidate=86400, stale-if-error=604800
Accept-Ranges: bytes
Date: Mon, 27 Apr 2020 07:25:51 GMT
Via: 1.1 varnish
Age: 12363
X-Served-By: cache-tyo19947-TYO
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1587972352.977171,VS0,VE1
Vary: Accept-Encoding

復帰値 r にはヘッダ情報をオブジェクト化したオブジェクトが返され、r.code に HTTP ステータスコードが格納されており、r.body にボディの情報がテキストとして格納されている。この構造は他のメソッドでも同様。HEAD ではボディは無いため r.body == "" となる。

上記の例では以下のようになる。

{
    "Last-Modified": "Wed, 15 Apr 2020 00",
    "X-Cache-Hits": "1, 1",
    "body": "",
    "Server": "nginx/1.14.2",
    "code": 200,
    "Date": "Mon, 27 Apr 2020 07",
    "Via": "1.1 varnish",
    "X-Cache": "HIT, HIT",
    "Content-Type": "text/html",
    "Cache-Control": "public, max-age=43200, s-maxage=172800, stale-while-revalidate=86400, stale-if-error=604800",
    "X-Timer": "S1587972352.977171,VS0,VE1",
    "ETag": "\"5e965252-bbf9\"",
    "Vary": "Accept-Encoding",
    "Content-Length": 48121,
    "Accept-Ranges": "bytes",
    "X-Served-By": "cache-tyo19947-TYO",
    "Connection": "keep-alive",
    "Age": 12363
}

head 以外のメソッドでは body にボディ・データがテキストの形で格納される。なので、JSON 形式で受け取る場合などは JSON.parse(r.body) と自分でパースする必要があるので注意。

HTTP GET - get

GET メソッドで情報を取得する。コールバックを指定しなければ全てを受信し、情報を格納したオブジェクトを返す。r.body は文字列なので、JSON 形式等で受信した場合は自分で JSON.parse(r.body) 等を行う。

var http = new Net.Http();
http.sslVerifyPeer(false);
http.sslVerifyHost(false);
var r = http.get("https://docs.ruby-lang.org/ja/latest/class/Range.html");
if (r.code == 200) {
    System.println(r.toJsonString(true));
}

応答はこんな感じ。

{
    "Last-Modified": "Wed, 15 Apr 2020 00",
    "X-Cache-Hits": 1,
    "body": "<!DOCTYPE html>\n<html lang=\"ja-JP\">\n<head>\n ...省略(長い)...",
    "Server": "nginx/1.14.2",
    "code": 200,
    "Date": "Thu, 30 Apr 2020 12",
    "Via": "1.1 varnish",
    "X-Cache": "HIT",
    "Content-Type": "text/html",
    "Cache-Control": "public, max-age=43200, s-maxage=172800, stale-while-revalidate=86400, stale-if-error=604800",
    "X-Timer": "S1588250498.519558,VS0,VE1",
    "ETag": "\"5e965252-bbf9\"",
    "Vary": "Accept-Encoding",
    "Content-Length": 48121,
    "Accept-Ranges": "bytes",
    "X-Served-By": "cache-tyo19929-TYO",
    "Connection": "keep-alive",
    "Age": 102
}

コールバックはヘッダーとボディそれぞれ設定できる。

http.get("https://docs.ruby-lang.org/ja/latest/class/Range.html", {
    header: &(data) => {
        ...
    },
    body: &(data) => {
        ...
    },
});

コールバックに直接関数オブジェクトを指定した場合、ボディのコールバックとして動作する。

http.get("https://docs.ruby-lang.org/ja/latest/class/Range.html", &(data) => {
    ... // ボディ・データが細切れに来る。
});

HTTP POST - post

POST メソッドを発行する。POST するデータは Http#setPostData メソッドで設定する。その他、コールバック等は GET と同じ。

POST はなかなか試す場所が無いのだが、Ruby で以下のサーバを立ち上げてアクセスしてみる。エラーはするが、アクセスされていることは確認できる。

ruby -rwebrick -e 'WEBrick::HTTPServer.new(:DocumentRoot => "./", :Port => 8000).start'

アクセスしてみる。

var http = new Net.Http();
http.setPostData("POST-DATA");
http.post("http://localhost:8000/index.html", System.print);

GET と同じようにボディがコールバックされてくるため、以下の応答となる。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<HTML>
  <HEAD><TITLE>Not Found</TITLE></HEAD>
  <BODY>
    <H1>Not Found</H1>
    `/index.html' not found.
    <HR>
    <ADDRESS>
     WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29) at
     localhost:8000
    </ADDRESS>
  </BODY>
</HTML>

サーバーのログは以下の通り。

[2020-04-21 17:33:53] ERROR `/index.html' not found.
::1 - - [21/Apr/2020:17:33:53 DST] "POST /index.html HTTP/1.1" 404 280
- -> /index.html

HTTP PUT - put

PUTPOST と同じで、メソッド名だけ PUT に変更して実行する。

var http = new Net.Http();
http.setPostData("PUT-DATA");
http.put("http://localhost:8000/index.html", System.print);

HTTP DELETE - delete

DELETEGET と同じで、メソッド名だけ DELETE に変更して実行する。

var http = new Net.Http();
http.delete("http://localhost:8000/index.html", System.print);

verbose

Http#setDebug(callback) を使ってデバッグ情報を取得することができる。以下はデバッグ情報だけ出力させる例。

var http = new Net.Http();
http.setDebug(System.print);
http.delete("http://localhost:8000/index.html");

Curl の Verbose 情報を見ることができる。

*   Trying ::1:8000...
* Connected to localhost (::1) port 8000 (#0)
> DELETE /index.html HTTP/1.1
Host: localhost:8000
Accept: */*

* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Content-Type: text/html; charset=ISO-8859-1
< Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29)
< Date: Tue, 21 Apr 2020 08:49:07 GMT
< Content-Length: 302
< Connection: close
<
* Closing connection 0

ちゃんと DELETE メソッドが発行されてますね。

さらに、setDebugDetail() メソッドで true を指定すると、送受信したデータを表示することも可能。データ量が多くなるので、適宜コントロールすること。

var http = new Net.Http();
http.setDebugDetail(true);
http.setDebug(System.print);
http.delete("http://localhost:8000/index.html");

こんな感じになる。

*   Trying ::1:8000...
* Connected to localhost (::1) port 8000 (#0)
> DELETE /index.html HTTP/1.1
Host: localhost:8000
Accept: */*

* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Content-Type: text/html; charset=ISO-8859-1
< Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29)
< Date: Tue, 21 Apr 2020 09:09:27 GMT
< Content-Length: 302
< Connection: close
<
> Recv data, 0000000302 bytes (0x0000012e)
0000: 3c 21 44 4f 43 54 59 50 45 20 48 54 4d 4c 20 50 <!DOCTYPE HTML P
0010: 55 42 4c 49 43 20 22 2d 2f 2f 57 33 43 2f 2f 44 UBLIC "-//W3C//D
0020: 54 44 20 48 54 4d 4c 20 34 2e 30 2f 2f 45 4e 22 TD HTML 4.0//EN"
0030: 3e 0a 3c 48 54 4d 4c 3e 0a 20 20 3c 48 45 41 44 >.<HTML>.  <HEAD
0040: 3e 3c 54 49 54 4c 45 3e 4d 65 74 68 6f 64 20 4e ><TITLE>Method N
0050: 6f 74 20 41 6c 6c 6f 77 65 64 3c 2f 54 49 54 4c ot Allowed</TITL
0060: 45 3e 3c 2f 48 45 41 44 3e 0a 20 20 3c 42 4f 44 E></HEAD>.  <BOD
0070: 59 3e 0a 20 20 20 20 3c 48 31 3e 4d 65 74 68 6f Y>.    <H1>Metho
0080: 64 20 4e 6f 74 20 41 6c 6c 6f 77 65 64 3c 2f 48 d Not Allowed</H
0090: 31 3e 0a 20 20 20 20 75 6e 73 75 70 70 6f 72 74 1>.    unsupport
00a0: 65 64 20 6d 65 74 68 6f 64 20 60 44 45 4c 45 54 ed method `DELET
00b0: 45 27 2e 0a 20 20 20 20 3c 48 52 3e 0a 20 20 20 E'..    <HR>.
00c0: 20 3c 41 44 44 52 45 53 53 3e 0a 20 20 20 20 20  <ADDRESS>.
00d0: 57 45 42 72 69 63 6b 2f 31 2e 34 2e 32 20 28 52 WEBrick/1.4.2 (R
00e0: 75 62 79 2f 32 2e 35 2e 31 2f 32 30 31 38 2d 30 uby/2.5.1/2018-0
00f0: 33 2d 32 39 29 20 61 74 0a 20 20 20 20 20 6c 6f 3-29) at.     lo
0100: 63 61 6c 68 6f 73 74 3a 38 30 30 30 0a 20 20 20 calhost:8000.
0110: 20 3c 2f 41 44 44 52 45 53 53 3e 0a 20 20 3c 2f  </ADDRESS>.  </
0120: 42 4f 44 59 3e 0a 3c 2f 48 54 4d 4c 3e 0a       BODY>.</HTML>.
* Closing connection 0

setDebugDetail() のオプションに { hex: false } を指定すると、16進データは表示しない形式で取得することもできる。

var http = new Net.Http();
http.setDebugDetail(true, { hex: false });
http.setDebug(System.print);
http.delete("http://localhost:8000/index.html");

16進ダンプが無くなる。

*   Trying ::1:8000...
* Connected to localhost (::1) port 8000 (#0)
> DELETE /index.html HTTP/1.1
Host: localhost:8000
Accept: */*

* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Content-Type: text/html; charset=ISO-8859-1
< Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29)
< Date: Tue, 21 Apr 2020 09:09:48 GMT
< Content-Length: 302
< Connection: close
<
> Recv data, 0000000302 bytes (0x0000012e)
0000: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">.<HTML>.  <HEAD
0040: ><TITLE>Method Not Allowed</TITLE></HEAD>.  <BODY>.    <H1>Metho
0080: d Not Allowed</H1>.    unsupported method `DELETE'..    <HR>.
00c0:  <ADDRESS>.     WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29) at.     lo
0100: calhost:8000.    </ADDRESS>.  </BODY>.</HTML>.
* Closing connection 0

Fiber

Fiber を使ってデータをループで処理させることもできる。EventMachine 的な使い方を想定しているが、まだ EventMachine は無いので今後の検討項目。

var http = new Net.Http();
http.sslVerifyPeer(false);
http.sslVerifyHost(false);
var fiber = new Fiber(&{
    http.get("https://docs.ruby-lang.org/ja/latest/class/Range.html", &(data) => {
        yield data;
    });
});
var total = "";
while (true) {
    var d = fiber.resume();
    break if (!fiber.isAlive());
    total += d;
    System.println("read %d bytes, total %d bytes" % d.length() % total.length());
}
System.println(total);

こんな感じで取得できます。

read 1371 bytes, total 1371 bytes
read 1371 bytes, total 2742 bytes
read 1371 bytes, total 4113 bytes
read 1371 bytes, total 5484 bytes
read 1371 bytes, total 6855 bytes
read 1371 bytes, total 8226 bytes
read 1371 bytes, total 9597 bytes
read 1371 bytes, total 10968 bytes
read 1371 bytes, total 12339 bytes
read 1371 bytes, total 13710 bytes
read 1371 bytes, total 15081 bytes
read 1371 bytes, total 16452 bytes
read 1371 bytes, total 17823 bytes
read 1371 bytes, total 19194 bytes
read 1371 bytes, total 20565 bytes
read 1371 bytes, total 21936 bytes
read 1371 bytes, total 23307 bytes
read 1371 bytes, total 24678 bytes
read 1371 bytes, total 26049 bytes
read 1371 bytes, total 27420 bytes
read 1371 bytes, total 28791 bytes
read 1371 bytes, total 30162 bytes
read 1371 bytes, total 31533 bytes
read 1371 bytes, total 32904 bytes
read 1371 bytes, total 34275 bytes
read 1371 bytes, total 35646 bytes
read 1371 bytes, total 37017 bytes
read 1371 bytes, total 38388 bytes
read 1371 bytes, total 39759 bytes
read 1371 bytes, total 41130 bytes
read 1371 bytes, total 42501 bytes
read 1371 bytes, total 43872 bytes
read 1371 bytes, total 45243 bytes
read 1371 bytes, total 46614 bytes
read 1371 bytes, total 47985 bytes
read 136 bytes, total 48121 bytes
<!DOCTYPE html>
<html lang="ja-JP">
<head>
<!-- Global Site Tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-620926-3"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments)};
  gtag('js', new Date());

  gtag('config', 'UA-620926-3');
</script>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../style.css">
<link rel="stylesheet" href="../syntax-highlight.css">
<link rel="icon" type="image/png" href="../rurema.png">

<link rel="canonical" href="https://docs.ruby-lang.org/ja/latest/class/Range.html">

<title>class Range (Ruby 2.7.0 リファレンスマニュアル)</title>
...(以下省略)

インスタンス・メソッド

Net.Http クラス・インスタンスに定義されているメソッドは以下の通り。正直、証明書関係はちゃんとテストできていないが、libcurl のマニュアル通りの実装をしてみた。基本的には対応する CURL オプションを設定しているだけです。何かあれば教えていただけると助かります。

メソッド 意味
sslVerifyPeer(tf) true, 証明書を sslSetCaInfo() メソッド、または sslSetCaPath() メソッドで証明ディレクトリを指定する。 false, 左記の検証を行わない。
sslVerifyHost(verify) true, SSL ピア証明書に一般名が存在するかどうかを調べ、その名前がホスト名と一致することを検証する。false, 左記の検証を行わない。
sslSetCaInfo(path) 接続先を検証するための証明書を保持するファイル名。 sslVerifyPeer() とともに使用する。
sslSetCaPath(path) 複数の証明書ファイルを保持するディレクトリ。 sslVerifyPeer() とともに使用する。
setDebugDetail(tf, opts) true, デバッグ情報に送受信データを含める。 opts として { hex: false } を指定すると送受信データに 16 進ダンプを含めない。
setUserPassword(user, pass) 接続に使用するユーザー名とパスワード。
setProxy(url) リクエストを経由させる HTTP プロキシの URL。
setProxyUserPassword(user, pass) プロキシに接続するためのユーザー名とパスワード。
setTimeout(millisec) タイムアウトをミリ秒で指定する。
setDebug(callback) デバッグ情報を受け取るコールバックを指定する。コールバックを指定するとデバッグ情報を取得するよう設定される。
addHeader(key, value) 設定する HTTP ヘッダフィールドを keyvalue で指定する。
removeHeader(key) key で示されるヘッダを削除する。
setRedirect(tf, opts) サーバーが HTTP ヘッダの一部として送ってくる "Location: " ヘッダの内容をたどる。opts として { max: n } を指定するとリダイレクトする回数の上限を設定できる。
setPostData(data) POST または PUT で送信するデータを設定する。
head(url, callback) HEAD メソッドを発行する。コールバックを指定しなかった場合、取得したヘッダデータのオブジェクトを返す。
get(url, callbacks) GET メソッドを発行する。コールバックはヘッダ・ボディ用関数、または { header: f1, body: f2 } でそれぞれ指定可能。コールバックを指定しなかった場合、取得したデータのオブジェクトを返す。
post(url, callbacks) POST メソッドを発行する。コールバックはヘッダ・ボディ用関数、または { header: f1, body: f2 } でそれぞれ指定可能。コールバックを指定しなかった場合、取得したデータのオブジェクトを返す。
put(url, callbacks) PUT メソッドを発行する。コールバックはヘッダ・ボディ用関数、または { header: f1, body: f2 } でそれぞれ指定可能。コールバックを指定しなかった場合、取得したデータのオブジェクトを返す。
delete(url, callbacks) DELETE メソッドを発行する。コールバックはヘッダ・ボディ用関数、または { header: f1, body: f2 } でそれぞれ指定可能。コールバックを指定しなかった場合、取得したデータのオブジェクトを返す。

おわりに

ネットワーク系は色々使い道があるので今後充実させたいカテゴリー。まずは一番使いたい HTTP を用意してみました。libcurl ではできないものも含めてサポートしていきたい。SNMP とか、SSH とか。

ではまた次回。

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

Railsのform_withを使った時にページが更新されない時

form_withとは

Railsアプリケーションでformを作成する時にform_for/form_tagを使っていました。

任意のmodelに紐づくフォームを作成したい場合にはform_forを使い、modelに紐づかない場合にはform_withを使います。

form_forを使えばPOSTの送信先urlなどを全て自動で行いますが、form_forの場合は全て明示します。

HTMLタグのヘルパーのようなイメージで使います。

【Rails】form_for/form_tagの違い・使い分けをまとめた

しかし、Rails5.1以降は2つのメソッドを統合したform_withが登場し、form_withの利用が推奨されています。

form_withでページが更新されない

form_withを使ってユーザのログインフォームを作っていました。

form_withにはUserModelを紐づけており、バリデーションやログイン認証の結果にエラーがあれば、viewに反映せるようにしていました。

views/users/_form.html.erb
<%= form_with model: @user, url: user_path do |f| %>

    <%= render 'shared/error_messages',  object: f.object %>

    <%= f.label :name %>
    <%= f.text_field :user_name, class: "form-control" %>

    <%= f.label :email %>
    <%= f.email_field :email, class: "form-control" %>

    <%= f.label :password %>
    <%= f.password_field :password, class: "form-control" %>

    <%= f.label :password_confirmation, "Confirmation" %>
    <%= f.password_field :password_confirmation, class: "form-control" %>

    <%= f.submit yield(:btn_text), class: "btn btn-primary" %>
<% end %>

ところが、エラーは発生しているのにviewには反映されない問題に衝突しました。

これは、form_withでのパラメータの送信はajaxで行われているためです。

HTMLのフォームとしてPOSTしたい場合はlocal: trueオプションを付け加えます。

views/users/_form.html.erb
<%= form_with model: @user, url: user_path, local: true do |f| %>
  :
<% end %>

これでバリデーションなどのエラー結果を表示するviewを表示できます。

都度フォームにオプションをつけるのが面倒な場合は、config/initializeディレクトリに適当なファイルを作成し以下のコードを追記します。

config/initialize/some_file.rb
Rails.application.configure do
    config.action_view.form_with_generates_remote_forms = false
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]RuboCopが遅いときにやること

はじめに

AllCopsで対象外のファイルを指定したら急に動作が遅くなったRuboCopさんの動作速度を改善する方法。

原因

AllCopsで除外ファイルを指定する前は明らかな不要フォルダは検索してなかった。
除外ファイルを指定すると、指定ファイル以外は全部検索していた。

解決策

検索不要なフォルダを指定する。実際のやつはこんな感じ↓

rubocop.yml
AllCops:
  TargetRubyVersion : 2.6
  Exclude:
    - 'db/schema.rb'
    - 'db/migrate/*'
    - !ruby/regexp /old_and_unused\.rb$/
    - 'bin/*'
    - 'node_modules/**/*'
    - 'config/**/*'
    - 'public/**/*'
    - 'tmp/**/*'
    - 'log/**/*'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby と Perl と Java と Python で解く AtCoder ATC 002 A

はじめに

AtCoder Typical Contest(ATC) とは、競技プログラミングにおける、典型問題を出題するコンテストです。
AtCoder さん、ありがとうございます。

今回のお題

AtCoder Typical Contest 002 A - 幅優先探索

今回のテーマ、幅優先探索

Ruby

DFS(深さ優先探索)とBFS(幅優先探索)の違いについて、いろいろあると思いますが、ここではデータの流れに注目します。

DFS BFS
後入れ先出し 先入れ先出し
データ構造 スタック キュー
データを入れる push push
データを取り出す pop shift
ruby.rb
r, c = gets.split.map(&:to_i)
sy, sx = gets.split.map(&:to_i)
gy, gx = gets.split.map(&:to_i)
cm = Array.new(r + 1).map{Array.new(c + 1, 0)}
1.upto(r) do |i|
  s = gets.chomp
  1.upto(c) do |j|
    cm[i][j] = -1 if s[j - 1] == '.'
  end
end
que = []
que.push(sy)
que.push(sx)
cm[sy][sx] = 0
while que.size > 0
  y = que.shift
  x = que.shift
  if cm[y + 1][x] == -1
    cm[y + 1][x] = cm[y][x] + 1
    que.push(y + 1)
    que.push(x)
  end
  if cm[y - 1][x] == -1
    cm[y - 1][x] = cm[y][x] + 1
    que.push(y - 1)
    que.push(x)
  end
  if cm[y][x + 1] == -1
    cm[y][x + 1] = cm[y][x] + 1
    que.push(y)
    que.push(x + 1)
  end
  if cm[y][x - 1] == -1
    cm[y][x - 1] = cm[y][x] + 1
    que.push(y)
    que.push(x - 1)
  end
end
puts cm[gy][gx]

que に push して shift して que が空になるまで while で回す要領です。

末尾にデータを追加 先頭のデータを取り出す
push shift
que.rb
que.push(sy)
que.push(sx)

xy 座標を別々に push していますが、リファレンスで配列ごと渡してもいいと思います。

array.rb
  if cm[y + 1][x] == -1
  if cm[y - 1][x] == -1
  if cm[y][x + 1] == -1
  if cm[y][x - 1] == -1

上下左右をチェックしていますが、出題によっては右と下のみに減ったりします。

Python

python.py
from collections import deque

r, c = map(int, input().split())
sy, sx = map(int, input().split())
gy, gx = map(int, input().split())
cm = [[0 for j in range(c + 1)] for i in range(r + 1)]
for i in range(1, r + 1):
    s = input()
    for j in range(1, c + 1):
        if s[j - 1] == ".":
            cm[i][j] = -1
que = deque([])
que.append(sy)
que.append(sx)
cm[sy][sx] = 0
while len(que) > 0:
    y = que.popleft()
    x = que.popleft()
    if cm[y + 1][x] == -1:
        cm[y + 1][x] = cm[y][x] + 1
        que.append(y + 1)
        que.append(x)
    if cm[y - 1][x] == -1:
        cm[y - 1][x] = cm[y][x] + 1
        que.append(y - 1)
        que.append(x)
    if cm[y][x + 1] == -1:
        cm[y][x + 1] = cm[y][x] + 1
        que.append(y)
        que.append(x + 1)
    if cm[y][x - 1] == -1:
        cm[y][x - 1] = cm[y][x] + 1
        que.append(y)
        que.append(x - 1)
print(cm[gy][gx])

deque の場合

末尾にデータを追加 先頭のデータを取り出す
append popleft

Perl

perl.pl
chomp (my ($r, $c) = split / /, <STDIN>);
chomp (my ($sy, $sx) = split / /, <STDIN>);
chomp (my ($gy, $gx) = split / /, <STDIN>);
my @cm;
for my $i (1..$r) {
  chomp (my $s = <STDIN>);
  for my $j (1..$c) {
    $cm[$i][$j] = -1 if substr($s, $j - 1, 1) eq '.';
  }
}
my @que;
push @que, $sy;
push @que, $sx;
$cm[$sy][$sx] = 0;
while(@que) {
  my $y = shift @que;
  my $x = shift @que;
  if ($cm[$y + 1][$x] == -1) {
    $cm[$y + 1][$x] = $cm[$y][$x] + 1;
    push @que, $y + 1;
    push @que, $x;
  }
  if ($cm[$y - 1][$x] == -1) {
    $cm[$y - 1][$x] = $cm[$y][$x] + 1;
    push @que, $y - 1;
    push @que, $x;
  }
  if ($cm[$y][$x + 1] == -1) {
    $cm[$y][$x + 1] = $cm[$y][$x] + 1;
    push @que, $y;
    push @que, $x + 1;
  }
  if ($cm[$y][$x - 1] == -1) {
    $cm[$y][$x - 1] = $cm[$y][$x] + 1;
    push @que, $y;
    push @que, $x - 1;
  }
}
print $cm[$gy][$gx], "\n";
末尾にデータを追加 先頭のデータを取り出す
push shift

Java

java.java
import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int r = Integer.parseInt(sc.next());
        int c = Integer.parseInt(sc.next());
        int sy = Integer.parseInt(sc.next());
        int sx = Integer.parseInt(sc.next());
        int gy = Integer.parseInt(sc.next());
        int gx = Integer.parseInt(sc.next());
        int cm[][] = new int[r + 1][c + 1];
        for (int i = 1; i <= r; i++) {
            String s = sc.next();
            for (int j = 1; j <= c; j++) {
                if (".".equals(s.substring(j - 1, j))) {
                    cm[i][j] = -1;
                }
            }
        }
        sc.close();
        Deque<Integer> que = new ArrayDeque<>();
        que.add(sy);
        que.add(sx);
        cm[sy][sx] = 0;
        while (que.size() > 0) {
            int y = que.poll();
            int x = que.poll();
            if (cm[y + 1][x] == -1) {
                cm[y + 1][x] = cm[y][x] + 1;
                que.add(y + 1);
                que.add(x);
            }
            if (cm[y - 1][x] == -1) {
                cm[y - 1][x] = cm[y][x] + 1;
                que.add(y - 1);
                que.add(x);
            }
            if (cm[y][x + 1] == -1) {
                cm[y][x + 1] = cm[y][x] + 1;
                que.add(y);
                que.add(x + 1);
            }
            if (cm[y][x - 1] == -1) {
                cm[y][x - 1] = cm[y][x] + 1;
                que.add(y);
                que.add(x - 1);
            }
        }
        System.out.println(cm[gy][gx]);
    }
}

Deque の場合

末尾にデータを追加 先頭のデータを取り出す
add poll
Ruby Python Perl Java
コード長 791 Byte 935 Byte 915 Byte 1660 Byte
実行時間 10 ms 24 ms 5 ms 106 ms
メモリ 1788 KB 3436 KB 512 KB 23636 KB

まとめ

  • ATC 002 A を解いた
  • Ruby に詳しくなった
  • Python に詳しくなった
  • Perl に詳しくなった
  • Java に詳しくなった

参照したサイト
Pythonのスタックとキューには何を使えばいいのか(各データ構造の速度比較)

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

rails routesで覚えのないroutingが出力される。

問題のルーティング

こんなの設定してない、、、

$ rails routes
       rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                  action_mailbox/ingresses/mandrill/inbound_emails#create
        rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                  action_mailbox/ingresses/postmark/inbound_emails#create
           rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                     action_mailbox/ingresses/relay/inbound_emails#create
        rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                  action_mailbox/ingresses/sendgrid/inbound_emails#create
         rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                              action_mailbox/ingresses/mailgun/inbound_emails#create
       rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#index
                                      POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#create
    new_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/new(.:format)                             rails/conductor/action_mailbox/inbound_emails#new
   edit_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)                        rails/conductor/action_mailbox/inbound_emails#edit
        rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#show
                                      PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#destroy
rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                      rails/conductor/action_mailbox/reroutes#create
                   rails_service_blob GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
            rails_blob_representation GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
                   rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                              active_storage/disk#show
            update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                      active_storage/disk#update
                 rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                           active_storage/direct_uploads#create

解決方法

$ rails new プロジェクトの名前 --skip-active-storage --skip-action-mailer --skip-action-mailbox

rails newでプロジェクトを立ち上げる際にオプションを付け加える事で解決。
既にrails newした後はapplication.rbファイルにコードを記述する事で表示されなくなりました。

Application.rb

class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0

    # CODE YOU SHOULD ADD vvvvvv
    # ここから
     initializer(:remove_action_mailbox_and_activestorage_routes, after: :add_routing_paths) { |app|
     app.routes_reloader.paths.delete_if {|path| path =~ /activestorage/}
     app.routes_reloader.paths.delete_if {|path| path =~ /actionmailbox/ }
    } ←# ここまで追加
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
  end
end

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

Rubyとは

Ruby

特徴

・記述量が少ない
・JavaやPHPといった他の言語同様、オブジェクト指向プログラミング言語である。
・スクリプト言語であるため、コンパイルする必要がない
・日本で生まれたプログラミング言語である。

記述量が少ない

他の言語であれば6行〜10行必要な場合でも、Rubyは1行で済む場合がある。
記述量が少ないということは作業の短縮になり、開発者にとって非常に助かると言う利点がある。

オブジェクト指向とは

プログラムの動作を属性値とメソッドを持ったパーツの組み合わせで実現すると言う考え方である。

スクリプト言語とは

プログラミング言語のうち、プログラムの記述や実行を比較的簡易に行うことができる言語の総称。インタプリンタ型(ソースコードを即座に実行開始できる)型のため、コンパイラ(一括してから実行する方式)に比べ、開発や修正をテンポよく進めることができる。

デメリットして

処理速度が他の言語に比べて遅いため、大規模アプリや処理速度が重要なサービスには向いていない。

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

【Rails】Kaminariで同一ページにもっと見るを複数実装する

通常の場合

home_controller.rb
def index
  @cats = Cat.page(params[:page]).per(1)
end
app/views/home/index.html.erb
<%= paginate @cats %>

複数の場合

home_controller.rb
def index
  @cats = Cat.page(params[:cats_page]).per(1)
  @dogs = Dog.page(params[:dogs_page]).per(1)
end
app/views/home/index.html.erb
<%= paginate @cats, param_name: 'cats_page' %>
<%= paginate @dogs, param_name: 'dogs_page' %>

ajaxを利用したもっと見るの場合

home_controller.rb
def index
  @cats = Cat.page(params[:page]).per(1)
  @dogs = Dog.page(params[:page]).per(1)
  return unless request.xhr?
  case params[:type]
  when 'dog', 'cat'
    render "#{params[:type]}"
  end
end
app/views/home/index.html.erb
<%= link_to_next_page @cats, 'もっと見る', remote: true, params: { type: :cat }, id: 'more-cat' %>
<%= link_to_next_page @dogs, 'もっと見る', remote: true, params: { type: :dog }, id: 'more-dog' %>
app/views/home/cat.js.erb
$('.cat-wrap').append('<%= escape_javascript(render 'cat', object: @cats) %>');
$('#more-cat').replaceWith('<%= escape_javascript(link_to_next_page(@cats, 'もっと見る', params: { type: :cat }, remote: true, id: 'more-cat')) %>');

app/views/home/dog.js.erb
$('.dog-wrap').append('<%= escape_javascript(render 'dog', object: @dogs) %>');
$('#more-dog').replaceWith('<%= escape_javascript(link_to_next_page(@dogs, 'もっと見る', params: { type: :dog }, remote: true, id: 'more-dog')) %>');

参考

https://qiita.com/Coolucky/items/bde74b020b8d37ccf426
https://www.rubydoc.info/github/amatsuda/kaminari/Kaminari/ActionViewExtension

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

Address already in use - bind(2) for "0.0.0.0" port 3000 (Errno::EADDRINUSE) が出たとき

エラー内容: Address already in use - bind(2) for "0.0.0.0" port 3000 (Errno::EADDRINUSE)

解決策

3000portで繋いでいたのでそのプロセスをkillしたら直った。
割とよくあるエラーでいつもコマンドを忘れるのでメモ。。

$ kill -9 $(lsof -i tcp:3000 -t)

lsofコマンドを先にインストールしておいた方がいいかも。

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

[Rails]FullCalenderで投稿一覧も表示させつつ、マイページでは自分の投稿だけに絞ってカレンダーを表示する

目的

FullCalenderを使って、Twitter+マイページでは自分の投稿をカレンダーで見る
ということを実装する

前提

フルカレンダーを実装し、カレンダーが表示できている
下記のサイトを参考に作りました。
https://qiita.com/sasasoni/items/fb0bc1644ece888ae1d4
https://qiita.com/imp555sti/items/ee9809768f6dc9439ab5

やること

FullCalenderをまずは準備しておきます。
そして自分が一番詰まったのは、indexではフォローしている人全員を表示する。
しかし自分のマイページのカレンダーでは、自分の投稿のみに限定する。
ということです。

どうやらFullCalenderの使い方としてindexアクションで定めた@eventsをカレンダーで表示するようなので
indexでは投稿一覧を@eventsではなく、別の名前を与えて。
ユーザーの投稿一覧を@eventsとしてあげます。

index.html.erb
def index
    @all_events = Event.all.includes(:user)
    @user = User.find(current_user.id)

   #フォローしているユーザーを取得
    @follow_users = @user.followings.map { |f| f[:id] }
    @follow_users << current_user.id

   #フォローユーザーの投稿のみ表示
    @events_onlyfollow = @all_events.where(user_id: @follow_users).order("created_at DESC")
    # 自分の投稿のみ
    @events = Event.where(user_id: current_user.id)
  end

まだまだ理解が浅くちゃんとできている訳ではないと思いますが。。。
とりあえずカレンダーが自分の投稿だけで絞れたのでひとまずOK

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

Ruby と Perl と Java と Python で解く AtCoder ABC 047 C

はじめに

AtCoder Problems の Recommendation を利用して、過去の問題を解いています。
AtCoder さん、AtCoder Problems さん、ありがとうございます。

今回のお題

AtCoder Beginner Contest 047 C - 一次元リバーシ
Difficulty: 650

今回のテーマ、正規表現

Ruby

例えば、WWWWBBWBBB を W B でそれぞれまとめますと WBWB となり、3回で一色にできることが分かります。
こういう文字列の処理は正規表現を使用すると簡単に解けます。
AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~で有名な C - 白昼夢 も正規表現ですとスッキリ解けます。

ruby.rb
s = gets.chomp
s.gsub!(/W+/, "W")
s.gsub!(/B+/, "B")
puts s.size - 1

W+ は1文字以上の連続したW を表現しています。

Python

python.py
import re

s = input()
s = re.sub(r'W+', "W", s)
s = re.sub(r'B+', "B", s)
print(len(s) - 1)

Python で正規表現を使用する場合、import re が必要です。

Perl

perl.pl
chomp (my $s = <STDIN>);
$s =~ s/W+/W/g;
$s =~ s/B+/B/g;
print length($s) - 1, "\n";

Java

java.java
import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        sc.close();
        s = s.replaceAll("W+", "W");
        s = s.replaceAll("B+", "B");
        System.out.println(s.length() - 1);
    }
}
Ruby Python Perl Java
コード長 71 Byte 97 Byte 87 Byte 310 Byte
実行時間 36 ms 38 ms 21 ms 239 ms
メモリ 10076 KB 4468 KB 640 KB 35160 KB

まとめ

  • ABC 063 C をスッキリ解いた
  • Ruby に詳しくなった
  • Python に詳しくなった
  • Perl に詳しくなった
  • Java に詳しくなった

参照したサイト
pythonで、とっても便利な正規表現を!
Java での正規表現の使い方メモ
instance method String#gsub
ABC049C - 白昼夢を簡単に導く方法が知りたい(Golang)

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

resource resourcesって何が違うの??

Railsルーターの目的

そもそもルーティングは何をしてるんや。という話。
Railsのルーターは受け取ったURLを認識し、適切なコントローラ内アクションやRackアプリケーションに割り当てます。ルーターは、ビューでこれらのパスやURLを直接ハードコードすることを避けるためにパスやURLを生成することもできます。

つまり受け取ったURLを捌いて指示を出す、司令塔ですよってこと。

resources と resource の違い

①URLにidを必要とするかしないのか
②resourceの場合単数なので、一覧みたいな概念はなく、indexが作られない

resources 複数

リソースをいくつも定義しなければならない場合は、以下のように簡単にわかりやすくできる。get,new,create,edit,update,destroy,showがresourcesでまとめられる!(only、exceptで限定することも可能)

resources :photos, :books, :videos

上の記法は以下と完全に同一です。

resources :photos
resources :books
resources :videos

ポイントとしては、リソース名がbooks、photosのような複数形になっていることにも注意
リソースが複数なのでidを持たないと識別できない

resource 単数

ユーザーがページを表示する際にidを一切参照しないリソースが使われることがあります。たとえば、/profileでは常に「現在ログインしているユーザー自身」のプロファイルを表示し、他のユーザーidを参照する必要がないとします。このような場合には、単数形リソース (singular resource) を使ってshowアクションに (/profile/:idではなく) /profileを割り当てることができます。

get 'profile', to: 'users#show'

結局どういうこと?

①URLにidを必要とするかしないのか

下記のようにresourcesが複数だとidが必要
resourceが単数だとidは要らない。

/mobile/posts/:id(.:format)  ←postは何個も存在するためidが必要 
/mobile/profile/edit(.:format) ←profileは一つしか存在しないため、idがいらない

②resourceの場合単数なので、一覧みたいな概念はなく、indexが作られない

routes.eb
resource :users

生成されるルーティング

 new_users GET    /users/new(.:format)  users#new
edit_users GET    /users/edit(.:format) users#edit
     users GET    /users(.:format)      users#show
           PATCH  /users(.:format)      users#update
           PUT    /users(.:format)      users#update
           DELETE /users(.:format)      users#destroy
           POST   /users(.:format)      users#create

参考文献

https://railsguides.jp/routing.html#%E5%8D%98%E6%95%B0%E5%BD%A2%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9

https://qiita.com/ryuuuuuuuuuu/items/e5960c7fecad4ef1301b

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

[devise]値を配列の形で保存する

背景

deviseを使っている上で、配列の形でデータを送りたいと考えたが、なかなかやり方が見つからず苦労してやっと実装出来たので、備忘録や他の人の役に立てればと思い書きました。

実装方法

deviseのデフォルトで設定されたカラム以外を保存する際に以下のコードを書いてあげるが、
配列を渡す時は「:category_ids => []」のように空の配列を一緒に付けてあげればうまく保存されます。

application_controller.rb
#中略
  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :self_introduction, :sex, :img_name, :category_ids => []])

    devise_parameter_sanitizer.permit(:account_update, keys: [:name, :self_introduction, :sex, :img_name, :category_ids => []])
  end
#中略

すごく時間がかかりましたが以外と簡単だったことに驚きました。。
deviseはやはり便利な分難しいですね

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

【Favorite】Rails いいね機能 実装

画面収録 2020-04-30 0.34.38.mov.gif

【ゴール】

ユーザーに投稿に対していいね機能を実装する
いいねを押すとハートが赤くなる

参考:https://qiita.com/nojinoji/items/2c66499848d882c31ffa

【メリット】

UI、UXの向上
アソシエーションの理解度向上

【開発環境】

■ Mac OS catalina
■ Ruby on Rails (5.2.4.2)
■ Virtual Box:6.1
■ Vagrant: 2.2.7

【実装】 "user" "post"機能実装は割愛

  1. favorite model作成
mac.terminal
$ rails g model Favorite
  1. アソシエーション 追記

※ user : favorite = 1 : 多 
※ post : favorite = 1 : 多

config/post.rb
has_many :favorites
def favorited_by?(user)
    favorites.where(user_id: user.id).exists?
end
config/user.rb
has_many :favorites
config/favorite.rb
belongs_to :user
belongs_to :post

route 追記
※ "favorite" は "post" に関連しているのでルートをネスト(親子)させる

config/routes.rb
resources :posts do
    resources :favorites , only: [:create , :destroy]
end

favorite controller作成
※ "create" , "destroy"も一緒に作成

mac.terminal
$ rails g controller Favorites create destroy

favorite controller記述
① , ②で user_id post_id をparameterに渡す

favorites_controller.rb
class FavoritesController < ApplicationController
  def create
    @post = Post.find(params[:post_id]) ①
    favorite = @post.favorites.new(user_id: current_user.id) ②
    favorite.save
    flash[:success] = "Liked post"
    redirect_to request.referer
  end

  def destroy
    @post = Post.find(params[:post_id]) ①
    favorite = current_user.favorites.find_by(post_id: @post.id) ②
    favorite.destroy
    redirect_to request.referer
  end
end

view記述
※ modelで記述 ”@post.favorited_by?” を使用

post/show.html/erb
<% if @post.favorited_by?(current_user) %>
   <%= link_to post_favorite_path(@post), method: :DELETE do %>
   <i class="fa fa-heart" aria-hidden="true" style="color: red;"></i>
   <%= @post.favorites.count %>like
   <% end %>
<% else %>
  <%= link_to post_favorites_path(@post) , method: :POST do %>
  <i class="fa fa-heart-o" aria-hidden="true"></i>
  <%= @post.favorites.count %>like
  <% end %>
<% end %>

以上

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