20191009のRubyに関する記事は29件です。

【Rails/javascript(jQuery)】非同期での投稿削除機能

前回、編集機能の非同期化について記載させていただきましたが、今回は削除機能の非同期化について記事を書かせていただきます。

それでは、まず動画をご覧ください。
alt

Deleteボタンを押すと、削除するかどうかの確認のアラートが出て、さらにアラートのOKボタンを押すと、きちんと削除処理がされていることが確認できますね。(正確にはこの動画だとちゃんと非同期になっているかわからないと思いますが、、)

削除処理の非同期化については、編集処理の非同期化に比べると、記述量も少なく、HTMLを入れ込む必要もないので楽でしたが、いくつか個人的にポイントだと思った箇所がありましたので記載します。

<削除機能実装におけるポイント>
①削除機能の場合、jbilderのファイルやコントローラへのjson.formatの記載は不要
②削除ボタンを押した際のアラートをどのようなビューにするか

①については、当たり前といえば当たり前ですが、これまでの非同期化では、jbuilderとjson.formatをセットで使用していたので、自分は勢いでふたつとも作成してしまいました、、。
ただ、冷静に考えれば、削除処理の場合、動的な情報が入ったHTMLを新たに作成するわけではありませんし、DBから返してもらうデータはないのでいずれも必要ありませんね。
また、削除処理については、基本的に警告表示は必要だと思いますが、アラートをどのようなビューにするのかによってサイトの印象が変わってしまうので、今回は一番簡単なalertメソッドをそのまま使用しましたが、サイトの印象を重視するなら非同期でHTMLを作成するのもありかもしれません。

ということで、以下コードを記載しますので、皆さまの何かのお役に立てば幸いです。

blog_destroy.js
$(function() {
  $(document).on("click", ".delete_id", function (e) {
    e.preventDefault();
    var deleteMessage = confirm('削除してよろしいでしょうか?');
    if(deleteMessage == true) {
      var blog_element = $(this).parents('.content');
      var blog_id = blog_element.attr("data-blog-id");
      var url = location.href + "/" + blog_id;
    $.ajax({
      url: url,
      type: "POST",
      data: {'id': blog_id,
      '_method': 'DELETE'} ,
      dataType: 'json'
    })
    .done(function(data) {
      blog_element.remove();
    })

    .fail(function() {
      alert('blog destroy error');
    })
  }
  });
}); 

index.html.haml
= render partial: 'devise/shared/header'
.contents
  -@blogs.each do|blog|
    .content{"data-blog-id": "#{blog.id}"}
      .content__messages
        .content__message
          .content__message__box{"data-edit-id": "#{blog.id}"}
            - if blog.content.present?
              %p.lower-message__content
                = blog.content
          .content__message__info
            %p.message__upper-info__talker
              ユーザ:
              = blog.user.name
            %p.message__upper-info__date
              投稿日時:
              = blog.created_at.strftime("%Y/%m/%d %H:%M")

          .content__message__edit-and-delete-btn
            %p.edit_id
              Edit

            %p.delete_id
              Delete

      .pict
        =image_tag blog.image.url, class: 'pict__img' if blog.image.present?

= render partial: 'devise/shared/footer'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【50日目】コメントの削除機能、多対多のアソシエーションの設定

コメントの削除機能

destroyアクションの作成

コメントコントローラーにdestroyアクションを記載していきます。

comments_controller.rb
def destroy
  comment = Comment.find(params[:id])
  comment.delete
  redirect_to comment.board, flash: {notice: 'コメントが削除されました' }
end

コメント削除ボタンの作成

コントローラーでアクションを定義したら、viewを作成します。
link_toの第二引数に渡すパスを調べるために、http://localhost:3000/rails/info/routesを確認しますと、
deleteアクションのパスはcomment_pathとなっています。
よって下記のようにリンクを作成します。

_comments.html.erb
<span><%= link_to '削除', comment, method: :delete, data: { confirm: '削除してもよろしいでしょうか?'} %></span>
# 「link_to 'リンク名', リンク先へのURL又はパス, method: :メソッド名」で指定したメソッドの指定したパスに飛べる'リンク'を作成します。
# methodオプションを指定しない場合、第二引数のパスはGETメソッドのものを参照します。
# data-confirm属性を設定すると、リンクを踏んだ際に確認のダイアログボックスを表示させます。

編集機能にもエラーメッセージを表示する。

まず前提として、updateアクションには下記のとおりフィルタリングされています

boards_controller.rb
before_action :set_target_board, only: %i[show edit update destroy]

# 中略

private

def set_target_board
  @board = Board.find(params[:id])
end

これによって、@boardには該当のidのパラメータが渡されております。

その上でupdateアクションを下記のようにします。

boards_controller.rb
def update
  if @board.update(board_params)
    flash[:notice] = "「#{@board.title}」の掲示板を編集しました。"
    redirect_to @board
  else
    redirect_to edit_board_path, flash: {
      board: @board,
      error_messages: @board.errors.full_messages
    }
  end
end

これによって、パラメータが保存できた場合は編集したことを示すメッセージが表示されて、掲示板のshowにリダイレクトします。
バリデーションエラーでboardが保存できなかった場合は、編集画面にリダイレクトしてエラーメッセージのリストを表示します。
又この時、リダイレクト後も掲示板のフォームには編集時に入力したパラメータは保持されます。

多対多のリレーションの考え方

例えば掲示板にタグをつけることを考えます。
ここでは簡略化するため、各テーブルはidとnameのカラムのみを持ちます。

boardsテーブル

id name
1 1日目
2 2日目
3 3日目

tagsテーブル

id name
1 ruby
2 SQL
3 初心者

このとき、例えば
1日目はruby, 初心者
2日目はSQL
3日目はruby, SQL, 初心者
とタグ付けしたかったりした場合、この関係を表そうとすると下記のようになります。

id board tag tag tag
1 1日目 ruby 初心者
2 2日目 SQL
3 3日目 ruby SQL 初心者

でもこれではカラムの数が事前に定められない他、カラムに空白が生まれてしまいます。

そこで「中間テーブル」を用いて、各id同士を繋げて表します。

board_id tag_id
1 1
1 3
2 2
3 1
3 2
3 3

このように表現することで、例えば
board_id = 1 である「1日目」には、
tag_id = 1 である「ruby」と
tag_id = 2 である「初心者」がタグづけされていることがわかります。

では以下でこのアソシエーションをどのように実装するかを見ていきます。

多対多アソシエーションの実装

まず最初に中間テーブルとtagsテーブルのモデルを作成します。

テーブルの作成

tagsテーブル

docker-compose exec web bundle exec rails g model tag name:string

中間テーブル

docker-compose exec web bundle exec rails g model board_tag_relation board:references tag:references

tagがnullであることを

db/migrate/xxxxxx_create_tags.rb
t.string :name, null: false

書き換えたら、マイグレーションを実行します。

docker-compose exec web bundle exec rake db:migrate

モデルのアソシエーションの設定

中間モデルの確認

中間モデルは最初からboardモデルとtagモデルにbelongs_toによってassociateされています。
これによって中間モデルはboard_idとtag_idをカラムに持つことになり、上記で示したような2つのidを結びつけることが可能になります。

app/model/board_tag_relations.rb
class BoardTagRelation < ApplicationRecord
  belongs_to :board
  belongs_to :tag
end

tagモデルの修正

タグから掲示板を関連づける設定をする。

tag.rb
class Tag < ApplicationRecord
  has_many :board_tag_relations
  has_many :boards, through: :board_tag_relations # throughオプションによって中間テーブルを経由して複数のboardを持つことを示している。
end

boardsモデルの修正

掲示板からタグを関連づける設定をする。

board.rb
class Board < ApplicationRecord
has_many :comments
has_many :tag_board_relations
has_many :tag, through: :tag_board_relations

validates :name, presence: true, length: { maximum: 10 }
  validates :title, presence: true, length: { maximum: 30 }
  validates :body, presence: true, length: { maximum: 1000 }
end

次回はアソシエーションのdependentオプションを作成していきます。

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

A server is already running の対処法

Railsでアプリを作成中にa server is already runningが出てlocalサーバが起動しなくなってしまった。

already runningってことはサーバが閉じられてないってこと?

調べてみると
Railsプロジェクト/tmp/pids/server.pidのserver.pidを削除して直るらしい。

server.pidはサーバを起動毎に作成されているようなのでサーバが閉じている時にserver.pidファイルがpidsファイルの中にあるのがそもそもおかしいみたい。

server.pidを削除して新たにサーバを立ち上げ直し。

server.pidファイルが新たに作られる。

解決!

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

csv

書き込み

  • headerのセット
  • パスのセット
require 'csv'


peoples = [
 ['alice', 14, 'femal'],
 ['alice', 15, 'femal'],
 ['alice', 16, 'femal'],
]


headers = ["名前", "年齢", "性別"]

file_path = "./test.csv"

CSV.open(file_path, 'w', headers: headers ) do |row|
  row << headers
  peoples.each do |pe|
    row << pe
  end
end

読み込み

  • headers trueを適応
  • 要素を出力する
require 'csv'


file_path = "./test.csv"


CSV.foreach(file_path, headers: true) do |row|
  p row
end

CSV.foreach(file_path, headers: true) do |row|
    p row["名前"]
    p row["年齢"]
    p row["性別"]
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアル 第5章<復習>

第5章の復習メモです。
個人的に重要と思ったことを書きます。
調べたことや、知っていたことも含めて書きます。

Bootstrap

Twitterが作成したフロントエンドのフレームワーク。
デザインが予め用意されているので、いい感じのデザインを素早く実装できる。

railsで使うには、Gemfileにbootstrap-sassを追加する。

source 'https://rubygems.org'

gem 'rails',          '5.1.6'
gem 'bootstrap-sass', '3.3.7'  # ←追加
.
.
.

追加後、$ bundle installを忘れずに実行する。

また、Bootstrapを読み込ませるため、
CSSファイルには以下を追記する。

~.scss
@import "bootstrap-sprockets";
@import "bootstrap";

補足
railsでは、generate controllerした時、
ビューと一緒に、対応するCSSファイルが作られる。
今回は、簡略化のため、全てのCSSを一つにまとめた物として、
app/assets/stylesheets/custom.scssを作成した。
(カスタムCSSというらしい)

部分テンプレート

ビューファイルのコードの冗長化を防ぐため、
共通する部分を、別ファイルに記載できる。
これを部分テンプレートと呼ぶ。

ファイル名は、
app/views/layouts/_<任意の名前>.html.erb
の形式にする必要がある。

呼び出す際は、renderメソッドを用いて、
<%= render 'layouts/<任意の名前>' %>
と記載する。

<例>
部分テンプレート

app/views/layouts/_shim.html.erb
<!--[if lt IE 9]>
  <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js">
  </script>
<![endif]-->

呼び出す側

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application',
                               'data-turbolinks-track': 'reload' %>
    <%= render 'layouts/shim' %>  # ←ここで呼び出している
  </head>
・
・

以下も参考にさせていただきました。
https://qiita.com/taca10/items/dd4a0eae6864e2bdbf3a

アセットパイプライン、Sass

アセットパイプライン

以下を参考にさせていただき、学習しました。
https://qiita.com/hogehoge1234/items/9a94ebc93c5f937502cd

Sass

CSSの強化版(?)で、

  • ネスト
  • 変数の使用

ができるみたい。書き方等、詳細は省略。

リンクの貼り方

ビューファイルでリンクを貼る場合、link_toメソッドを使う。

<%= link_to "文字列", "URL" %>

ルーティングの変更、名前付きルート

ルーティングの変更

generate controllerコマンドでコントローラ等を作成すると、
ルーティングが自動生成される。

config/routes.rb
Rails.application.routes.draw do
  get  'static_pages/about'
end

この場合、ルート/static_pagesにGETリクエストが来ると、
static_pagesコントローラの、aboutアクションにルーティングされる。
即ち、コントローラ名とアクション名に対して、URLが固定されている。

これを、以下のように書き換えることで、URLを自由に決めることができる。

config/routes.rb
Rails.application.routes.draw do
  get  '/about',   to: 'static_pages#about'
end

上の例では、ルート/aboutにGETリクエストが来ると、
static_pagesコントローラの、aboutアクションにルーティングされる。

名前付きルート

railsでは、config/routes.rbに記載したURLに対して、
ルート以降の相対パス、ルートを含めた絶対パスを表す変数が
それぞれ用意され、格納される。
<例>

config/routes.rb
Rails.application.routes.draw do
  get  'static_pages/about'
end

の場合

ルーティングを変更すると、変数名も更新される。
<例>

config/routes.rb
Rails.application.routes.draw do
  get  '/about',   to: 'static_pages#about'
end

の場合

統合テスト

テストについては、以下を参考にさせていただき、学習中です。
https://qiita.com/duka/items/2d724ea2226984cb544f

復習記事を書いてみると、理解不足がとても実感できます・・・

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

Ruby インスタンス変数 の 値 を 順に出力する

目的

  • インスタンス変数を出力する方法を知る。

書き方の例

  • クラスAにはインスタンス変数bcが定義されるものとする。
  • インスタンス変数cの値を順に出力したい。
  • インスタンス12の2個作成するものとする。
  • クラスAから生成したインスタンスのインスタンス変数bcには任意の値が入っているものとする。
  • 下記に処理を記載する。
# クラスAを定義
class A

  # 変数string_1にインスタンス1の内容を格納
  string_1 = A.new(b: "インスタンス変数bに格納される値", c: "インスタンス変数cに格納される値")

  # 変数string_2にインスタンス2の内容を格納
  string_2 = B.new(b: "インスタンス変数bに格納される値", c: "インスタンス変数cに格納される値")

  # 変数stringsに変数string_1とstring_2の内容を配列として格納
  strings = [string_1, string_2]

  # 変数stringsに配列状に格納された内容を一つづつ変数stringに格納
  strings.each do |string|

    # 変数stringに格納された内容のインスタンス変数cのみを出力
    puts string.c

  # each文を閉じる
  end

# クラス定義を閉じる
end

より具体的な例

  • クラスMenuにはインスタンス変数contentpriceが定義されるものとする。
  • インスタンス変数contentの値を順に出力したい。
  • インスタンス12の2個作成するものとする。
  • クラスMenuから生成したインスタンス1のインスタンス変数contentには、文字列「お肉100g」が格納され、インスタンス変数priceには文字列「100円」を格納する。
  • クラスMenuから生成したインスタンス2のインスタンス変数contentには、文字列「お魚1尾」が格納され、インスタンス変数priceには文字列「150円」を格納する。
  • 下記に処理を記載する。
# クラスMenuを定義
class Menu

  # 変数menu_1にインスタンス1の内容を格納
  menu_1 = Menu.new(content: "お肉100g", price: "100円")

  # 変数menu_2にインスタンス2の内容を格納
  menu_2 = Menu.new(content: "お魚一尾", price: "150円")

  # 変数menusに変数menu_1とmenu_2の内容を配列として格納
  menus = [menu_1, menu_2]

  # 変数menusに配列状に格納された内容を一つづつ変数menuに格納
  menus.each do |menu|

    # 変数menuに格納された内容のインスタンス変数cのみを出力
    puts menu.content

  # each文を閉じる
  end

# クラス定義を閉じる
end
  • 下記に前述の処理のコメント無しバージョンを記載する。
class Menu
  menu_1 = Menu.new(content: "お肉100g", price: "100円")
  menu_2 = Menu.new(content: "お魚一尾", price: "150円")
  menus = [menu_1, menu_2]
  menus.each do |menu|
    puts menu.content
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyにおけるプリントデバッグのすゝめ

※ 別に勧めてない ※

皆さん!プリントデバッグ使ってますか!使ってないですよね、どうせ皆さんデバッガでかっこよく問題解決しているんでしょう。だけれども、私はデバッガを使いこなすことができない程度の能力しか持っていないので、99.99%ぐらいの問題はプリントデバッグで地道に値をトレースし、頑張って解決しています。

そんな私のTipsです。

識別子付きで出力

ただ単に変数をpで出力するようなケースだと、次のように出力したい事がありますね。

p "hoge -> #{hoge}"

ただこれは入力が面倒。自分がパット見で区別できればいいので次のようにします。

p [:hoge, hoge]

こうすると、[:hoge, 1]のように出力されるので都合がいいです。

区切り線の表示

いろんな値を出力するときには欠かせない区切り線。
単純に出力する場合は次のようにするかもしれません

p '**************************************'
p hoge
p '**************************************'

だけどこれはちょっと入力が面倒臭い。なので、次のように書きます

p ?**88

?の累乗に見えますが、これは*を88個出力しています。rubyの文字リテラルってやつを利用しています。
区切り線にはなんの文字を使ってもいいので、?-*100とかして、-を100個出力とかでもいいんでしょうが、コードの見た目が?**88のほうがかっこいい!のでほかの文字は使いません。

メソッドチェーンの途中を出力

この方法が必要になるケースはかなり少ないんですが・・。
たとえば意味のない例ですが次の様なメソッドのなげー連鎖がある場合。

b = a.map(&:sono1).map(&:sono2)

このsono1の直後の状態を出力したい時があるんじゃないでしょうか。
tappみたいなgemを使っているならそれでいいのですが、それを使わない環境で手軽にテストしたい場合は、次のようにするでしょうか。

_a = a.map(&:sono1)
p _a
b = _a.map(&:sono2)

でもでも、変数を増やしたり、メソッドチェーンをぶった切って途中でpを使ったり、書き換え量が多くて副作用がうっかり発生するかもしれない。
なので、次のように出力はチェーンに組み込みます。

b = a.map(&:sono1).tap(&method(:p)).map(&:sono2)
# もしくは ↓
b = a.map(&:sono1).tap{|_a|p _a}.map(&:sono2)

どちらかは、好みですね。メソッドチェーンをぶった切って一時的な変数に格納する方法よりは副作用が少ない方法だと思ってます。

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

json ruby 実践

require 'net/http' #標準ライブラリの呼び出し
require 'uri'
require "json" #jsonを使うためのライブラリ


u = URI.parse('http://zipcloud.ibsnet.co.jp/api/search?zipcode=6695221')

res = Net::HTTP.get(URI.parse('http://zipcloud.ibsnet.co.jp/api/search?zipcode=6695221'))

status = JSON.parse(res)["status"]
results = JSON.parse(res)["results"]

p "status:#{status}"
p results

参考記事

https://qiita.com/yertea/items/be6f535fc31d7325ed97

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

Ruby のメソッド定義で引数が「*」だけの場合

OSSライブラリのドキュメントを見ていると以下のようなコードがありました。

def initialize(*)
  super
  # 以下、何らかのコード
end

この * が文法的にどういう意味なのか気になったので調べました。

結論

任意の引数を受け取るが、特にコード内では参照しない(無視する)という意味になるようです。

class Foo
  def bar(*)
    "bar"
  end

  def baz
    "baz"
  end
end

Foo.new.bar("foo", 1, [2]) # => エラーにならない
Foo.new.baz("foo", 1, [2]) # => ArgumentError が発生

メソッド内で super を呼び出すと、親クラスのメソッドに指定された引数をそのまま渡します。
指定された引数の個数が、親クラスのメソッド定義の引数の個数と異なる場合、 ArgumentError が発生します。

class Parent
  def hello(name)
    "Hello #{name}!"
  end
end

class Child < Parent
  def hello(*)
    super
  end
end

Child.new.hello("World") # => "Hello World!"
Child.new.hello # => ArgumentError
Child.new.hello(1, 2) # => ArgumentError

参考

リファレンスマニュアルには以下のように記載されています。
https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#method

def bar(x, *) # 残りの引数を単に無視したいとき
  puts "#{x}"
end
bar(1)        #=> 1
bar(1, 2)     #=> 1
bar(1, 2, 3)  #=> 1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyで[ ](カギ括弧)メソッドを定義する

Array型やHash型などのインスタンス変数の要素を [添字] の記法で取得するためには[]メソッドを定義します。

以下のようにメソッド名を[]で定義すると、添字に指定した値が引数として渡されます。

def [](引数名)
end

使用例

class ConfigModel
  attr_reader :config

  def initialize
    @config = {host: "127.0.0.1", port: 3000}
  end

  def [](key)
    @config[key]
  end
end

config = ConfigModel.new
puts config[:host]
puts config[:port]

出力結果

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

macOSをCatalinaにアップデートしたらdocker-syncが動かなくなって調べたこと

まえおき

Xcodeを過信してたらRubyから苦情もらった(docker-sync動かないorz) - https://qiita.com/CeMoReOn/items/9abb837727af5594fc0f

っていうタイムリーな記事があったので、これは rbenvとかhomebrew経由でRuby入れないとダメなのかなー・・・と思っていたけど、
Mac付属のRubyでも一応動いたのでメモっとく。

ちなみに、次のMacバージョンからそもそもRubyが付属しなくなるという噂 もあるので、この記事の賞味期限はきっとかなり短いだろう。

OSをアップデートした直後

$ docker-sync-stack start
-bash: /usr/local/bin/docker-sync-stack: /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby: bad interpreter: No such file or directory

前評判のとおり、こんなかんじのエラーは出る。

$ which ruby
/usr/bin/ruby
$ ls -l /usr/bin/ruby
-r-xr-xr-x  1 root  wheel  36624  9 30 05:28 /usr/bin/ruby

Rubyのバイナリ自体はある。

$ ls /System/Library/Frameworks/Ruby.framework/
Resources/ Ruby       Versions/ 
$ ls /System/Library/Frameworks/Ruby.framework/Versions/2.6/
Resources/      Ruby            _CodeSignature/ usr/

このあたりで、「あー、Ruby 2.3じゃなくなったのね」と察しがつく。

docker-syncを入れ直す

$ sudo gem uninstall docker-sync
$ sudo gem install docker-sync
$ docker-sync-stack start
mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/include/ruby.h

You might have to install separate package for the ruby development
environment, ruby-dev or ruby-devel for example.

エラーの種類が変わった。

エラー文言でぐぐると

xcode-select --install とか xcodebuild ... みたいなコマンド叩くといいみたいなのが出てくるが、環境によっては多分それでは無理っぽい。(自分の場合はうまくいかなかった)

Xcode 11にする

これは実際に関係あるのかどうかわからないが、
とりあえず xcode-select --installxcodebuild ... を叩くだけではXcode11にはならない。

e9070a00-c7ec-49d4-a082-c59e7128ec69.jpg

なので、App StoreからXcode 11を落としてくる。

image.png

ついでに、AppleのDeveloper向けサイトからコマンドラインツールも落としてくる。

image.png

11になったことを確認。

image.png

そもそものエラーの出どころを見てみる

mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/include/ruby.h

You might have to install separate package for the ruby development
environment, ruby-dev or ruby-devel for example.

これはそもそもdocker-syncに関係のないエラーだ。ということで、エラーの出どころを見てみる。

/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb
  topdir = File.dirname(File.dirname(__FILE__))
  path = File.expand_path($0)
  until (dir = File.dirname(path)) == path
    if File.identical?(dir, topdir)
      $extmk = true if %r"\A(?:ext|enc|tool|test)\z" =~ File.basename(path)
      break
    end
    path = dir
  end
  $extmk ||= false
  if not $extmk and File.exist?(($hdrdir = RbConfig::CONFIG["rubyhdrdir"]) + "/ruby/ruby.h")
    $topdir = $hdrdir
    $top_srcdir = $hdrdir
    $arch_hdrdir = RbConfig::CONFIG["rubyarchhdrdir"]
  elsif File.exist?(($hdrdir = ($top_srcdir ||= topdir) + "/include")  + "/ruby.h")
    $topdir ||= RbConfig::CONFIG["topdir"]
    $arch_hdrdir = "$(extout)/include/$(arch)"
  else
    abort <<MESSAGE
mkmf.rb can't find header files for ruby at #{$hdrdir}/ruby.h

You might have to install separate package for the ruby development
environment, ruby-dev or ruby-devel for example.
MESSAGE
  end

RbConfig::CONFIG["rubyarchhdrdir"] RbConfig::CONFIG["topdir"] あたりがキモのようだ。

このキーワードでぐぐると

https://www.programqa.com/question/20559255/

こんなページが出てきた。

$ ruby -rrbconfig -e 'puts RbConfig::CONFIG["rubyhdrdir"]'
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0
$ ls /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0
ruby            ruby.h          universal-darwin19

お、ちゃんと ruby.h あるじゃん?

ってことはいけるんじゃね?

あらためてdocker-syncしてみる

$ docker-sync start
          ok  Starting unison for sync panel-workspace-sync

あ、動いたw

まとめ

  • docker-syncをインストールしなおす
  • Xcode 11 + コマンドラインツール 11にする

で解決したのかなぁ・・・

(どこが解決ポイントだったのかは自分でもわかっていない)

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

40手前、気分は高校1年生。プログラミング(Ruby on Rails)を3ヶ月勉強して一口馬主とかPOGで少し役立ちそうな馬の検索WEBサービス(webアプリ)を作ってみた!

粛々と窓際社員を驀進中の小生であるが、ボケーーーっと平日を過ごし、
ただただ週末の競馬を楽しみに生きる人生。

これでいい、とも思うし、これではダメだ。。とも思う。

ただギャンブルとは別のスパイスが欲しいという欲求と、プログラミングをちょっとやってみたい、という秘めた思いを10年越しに実現することにした。

窓際社員であることも、学習を進める上で大いに役立った。

だって・・・・仕事ないから・・・・・。ネットサーフィン感覚で、勉強できるのですもの。。。。。

ついでに毎日腕立てと腹筋を100回以上やることにした。

◆ 作ったサービス

うまBANG!
www.umabang.com

◆ どんなサービス?

血統表に馬名を入れると該当する馬を検索できるサービスです。
とりあえず今は2013-2015年産世代のデータを検索対象にしています。

こんな感じで検索すると

こんな感じで結果がわかる

◇ 動機というか一口馬主をやっていてのペイン

静止画、動画、血統、クラブ、厩舎、などなどなどなど一口馬主の馬選びは様々な要素を考慮する必要があり、
人それぞれ優位に思っていることは違う。
例えば小生は元々は静止画から判断していたが、最近だと動画、そして血統も考慮するようになった。
そう、続ければ続けるほど、何やら様々なものが見えすぎて、シンプルに馬選びを出来なってきているのだ。

そうすると従来は出資しなかった馬に出資したり、どんどん深みにはまっていく。
そこに納得感があれば良いが、必ずしもそうではない。

謎に出資馬を増やしてしまいそうになる誘惑に駆られることも多いが、めちゃくちゃ金持ちでない限り、台所事情はなかなか苦しくなっていく。
小生然り。

ただ血統から絞れることも多く、さらにパズルのように様々な組み合わせを検証したくなる。

・スクリーンヒーローやキングカメハメハ牝馬は成績悪い?
・父 ディープインパクト 母父 stormcat は本当にニックス?
・では父父 サンデーサイレンス  母父 stormcat はどうなの?
・母父 orpen とか注目されているけど?(ポレンティア!)
・fastnetrock ってどうよ?(モーソンピーク!)
・母父 サンデーサイレンスを買っておけば良い簡単なお仕事です(ダイアトニック!アーモンドアイ!ブラストワンピース!)

こんな気持ちを手軽に解決できたらな〜と思ったのがそもそもの動機。
確率的に期待値が低いことを明確にして、そもそも出資しない血統マイルールを今一度持ち直したいと考えている。

◇ 一口馬主をやっていて、できれば出資馬に望むこと

重賞に出走して欲しい!できれば重賞で好走して欲しい!!!
一口馬主を数年やった小生が出資時に最も思うことである。

当該週の重賞はメディアにも多く取り上げられ、様々な方が出資馬の名前を呼んでくれる。
なにものにも代え難い幸せ・・・。

そこで、このサービスでは重賞出走歴のある馬のみで絞り込み検索できるようにした。

加えてなんとなく1000万以上稼いでくれると、最低限楽しませてくれる、ということも感じているので、
最低ラインとして獲得賞金1000万以上の馬が何頭マッチするのかも見られるようにした。
ノルマンディーであればこの辺りが損益分岐点の一つ基準になるだろう。

◇ 改めますと・・・

・牡牝の成績差がわかる。
・重賞出走まで届く可能性をなんとなく調べられる。
・賞金1000万以上獲得する確率もなんとなくわかる。

よろしければ遊んでみてください。もしご変なところがあればコメントをください。

うまBANG!
www.umabang.com

今後これを使ってノルマンディーの馬を見てみようと考えている。
こっちのブログで。

清純派 一口馬主ライフとパドック予想師!誠意の競馬日記と反省
http://paddock.hatenablog.com/

◆ 番外

◇ 今後やりたいこと

対象の馬を増やす
検索時のサジェスト
並び替え
人気ランキングをちゃんとする
検索履歴の保持
サンデーサイレンスの 3x4 4x3 とか馬名と世代を指定して検索できるようにしたい

なんかができればと思います。

◇ どんな勉強をしたか

WEBの講座をやりました。週2回メンターとのビデオMTGがあり、そこで褒めていただけるのでやりがいを持って進められました。

ひさしぶりに他の人から褒められる体験でした。

少しお値段は張りましたが、一口馬主のせいでお金の価値が暴落中なので、躊躇せず申し込めました。

◇ どれくらい勉強したのか

平均すると1日3-4時間程度を2.5ヶ月続けて、このうまBANG!に取り組みました。

◇ 一番辛かった時

スクールの環境が 開発環境 Cloud9 x MySQL 本番環境 Heroku x PostgreSQL だったので、いざ公開しようと思ったら動かなかったのが辛かったです。
さらに、Heroku を MySQL に変更しようとして失敗していた時は絶望に暮れました。

◇ 今つらいこと

ギャグでAmazonアソシエイトとgoogleアドセンスに申し込んでみたのですが、審査で断れています。
一所懸命勉強して作ったのに。。。。。なんだろうこの疎外されている感じ。。。。理由もわからないし。。。。

こういうことから JOKER が生まれるのだと思います。

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

テスト初心者がRSpecを学習するときにとまどったこと

前書き

私はRailsを学びはじめて1年に満たない初学者ですが、現在今まで避けてきたテストを学んでいます。
いろいろと手こずったので、私の経験が誰かの役に立てばと思いこの記事を書いています。

つまづきポイント

何からはじめたら良いかわからない

RSpecどころかテストに触れるのもほぼ初めてだったため、どこからはじめればよいか全くわかりませんでした。書籍、レファレンス、Qiita記事等いろいろな情報を当たった結果、ご存知の方も多いと思いますが、@jnchitoさんの一連の記事が初学者には一番わかりやすかったです。

最初に上記に続く一覧の記事に目を通すと理解が進むと思います。

古い情報が役に立たない

これはプログラミング全般に言えることだと思いますが、改めて最新の情報を参考にすべきだと感じました。
Controller Specが非推奨となっていたり、RSpec3.7よりSystem Specが追加されているなど書きかたが全く異なることが多いのでなるべく新しい情報を参照すべきです。

何をテストすればよいかわからない

これは簡単に論じられる問題ではないと思いますが、最初の学習としては、モデルのテストと何らかの機能のシステムテストを行うとまんべんなく基礎の基礎が学べて良いのかなと感じました。

最後に

今まで何となくテストめんどくさいなあと感じていたのですが、(おそらくRails Tutorialが原因)やってみると意外と面白かったです。
特にCapybaraでブラウザが自動操作されるのは見ていて興奮しました。
本当にまだ基本しかわかっていませんが、頑張って勉強を続けていきたいと思います。

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

Rails6 のちょい足しな新機能を試す92(config.disable_sandbox 編)

はじめに

Rails 6 に追加された新機能を試す第92段。 今回は、 config.disable_sandbox 編です。
Rails 6 では、 rails console --sandbox を実行したときに、エラーメッセージを表示して終了するオプション config.disable_sandbox が追加されました。
本番環境などで、 rails console --sandbox したときにトランザクションログが大きくなりすぎて、メモリが不足してサービスがダウンしてしまうことを防ぐ目的で導入されたようです。

Ruby 2.6.4, Rails 6.0.0 で確認しました。

$ rails --version
Rails 6.0.0

プロジェクトを作る

$ rails new rails_sandbox
$ cd rails_sandbox

config.disable_sandbox を設定する

今回は、 config/environments/development.rb で設定します。

config/environments/development.rb
Rails.application.configure do
  ...
  config.disable_sandbox = true
end

rails console を実行する

--sandbox オプションつきで、 rails console を実行してみます。

$ bin/rails c --sandbox
Running via Spring preloader in process 48
Error: Unable to start console in sandbox mode as sandbox mode is disabled (config.disable_sandbox is true).

エラーメッセージが表示されて実行できないことが確認できます。

試したソース

試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try092_console_sandbox

参考情報

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

【小学生でもわかる】クラスの概念「クラスはたい焼き」(オブジェクト指向)

オブジェクト指向におけるクラス

オブジェクト指向の言語に共通するクラスの概念について具体的な例えで解説

どんな人向けの記事?

ProgateでHTML/CSSなどのマークアップ言語から入って
「なんだプログラミングって簡単じゃん♪俺才能ある♪」
ってなった後、オブジェクト指向型言語に触れて...

「...???...思ってたんと違う...」

ってなった人向けの記事

【本題】クラスは「たい焼き」

クラス = たい焼き機

どんな模様・形のたい焼きにするのか決めるのがクラス設計。
たくさんのたい焼き(たくさんのインスタンス)を作れる

インスタンス = たい焼き

クラスを元にできたもの。

継承

たい焼き機をもとにして、タコ焼き機やベビーカステラ焼き機を作ること(クラスの継承)
たかがたい焼き機でも、どんな材質の鉄板を使うか、コンロで使うには厚みをどれくらいにしないといけないのかなども考えなければいけないので、一から作るより継承して作った方が速くて確実だよね。という話

まとめ

クラス = たい焼き機
インスタンス = たい焼き

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

現在修正中【小学生でもわかる】クラスの概念「クラスはたい焼き」(オブジェクト指向)

オブジェクト指向におけるクラス

オブジェクト指向の言語に共通するクラスの概念について具体的な例えで解説

どんな人向けの記事?

ProgateでHTML/CSSなどのマークアップ言語から入って
「なんだプログラミングって簡単じゃん♪俺才能ある♪」
ってなった後、オブジェクト指向型言語に触れて...

「...???...思ってたんと違う...」

ってなった人向けの記事

【本題】クラスは「たい焼き」

クラス = たい焼き機

どんな模様・形のたい焼きにするのか決めるのがクラス設計。
たくさんのたい焼き(たくさんのインスタンス)を作れる

インスタンス = たい焼き

クラスを元にできたもの。

継承

たい焼き機をもとにして、タコ焼き機やベビーカステラ焼き機を作ること(クラスの継承)
たかがたい焼き機でも、どんな材質の鉄板を使うか、コンロで使うには厚みをどれくらいにしないといけないのかなども考えなければいけないので、一から作るより継承して作った方が速くて確実だよね。という話

まとめ

クラス = たい焼き機
インスタンス = たい焼き

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

君はRailsの「Yay! You’re on Rails!」の画面がどこで記述されているか知っているか?

完全にあおりタイトルでごめんなさい。

結論

ここにあります。
「Yay! You’re on Rails!」で全文検索したら出てきました。
僕にとっては意外な場所でした。

/Users/hoge/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのテスト環境でのいろいろなエラーでつらい

railsのテスト環境でやったときのいろいろなエラーの詰め合わせの記事です。
タイトルが抽象的ですみません。

rails s -e test

2019-10-06 18:13:28 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.
=> Booting Puma
=> Rails 5.2.3 application starting in test 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.1 (ruby 2.6.3-p62), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: test
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

http://0.0.0.0:3000/
へアクセス。

Puma caught this error: No route matches [GET] "/" (ActionController::RoutingError)
# 以下、略

こんな、エラがー起こる。

config/routes.rb
root 'rails/welcome#index'

を追加する。
「Yay! You’re on Rails!」のおなじみの画面が出て安心する。

ちなみにこのページのファイルはこのパスにあるようだ。

/Users/hoge/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb

curlで叩いてみる

curl localhost:3000/blogs/index/

Puma caught this error: PG::UndefinedTable: ERROR:  relation "blogs" does not exist
LINE 1: SELECT "blogs".* FROM "blogs"
                              ^
: SELECT "blogs".* FROM "blogs" (ActiveRecord::StatementInvalid)
# 以下、略。

なんだかすごい。エラーが出ている。
落ち着いてみる。

冒頭に
Puma caught this error: PG::UndefinedTable: ERROR: relation "blogs" does not existとある。
データベースがなんかおかしい?

単にrails sだと正常に返ってくる。

DBがないのか?
test用のDBを作成する。

rails db:create RAILS_ENV=test

2019-10-06 17:55:03 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.
Database 'todo_slim_test' already exists

DBはあるようだ。
test用にmigrateしなくてはいけないのか?

 $ rails db:migrate RAILS_ENV=test

2019-10-06 17:55:36 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.
== 20190818133341 CreateBlogs: migrating ======================================
-- create_table(:blogs)
   -> 0.0645s
== 20190818133341 CreateBlogs: migrated (0.0646s) =============================

ついでにseedsもやっておく。

 $ rails db:seed RAILS_ENV=test
2019-10-06 17:55:50 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.

再度やる。

$ rails s -e test
2019-10-06 17:55:58 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.
=> Booting Puma
=> Rails 5.2.3 application starting in test 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.1 (ruby 2.6.3-p62), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: test
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
2019-10-06 17:56:05 +0900: Rack app error handling request { GET /blogs/index }
#<AbstractController::ActionNotFound: The action 'show' could not be found for BlogsController>

なんだか、まだなにかしなくてはいけないようだ。
developではこんなことを言われないので、DBがおかしいのかと疑ってみる。
テスト環境のDBの状態を見に行く。

rails db RAILS_ENV=test

DEPRECATION WARNING: Passing the environment's name as a regular argument is deprecated and will be removed in the next Rails version. 

Please, use the -e option instead. (called from <top (required)> at /Users/hoge/product/ruby_sandbox/rails-blog-slim/bin/rails:9)
config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:

  * development - set it to false
  * test - set it to false (unless you use a tool that preloads your test environment)
  * production - set it to true

-eオプションつけてやってと言われた。

dbを初期化する。
migrateもする。

rails db:migrate:reset
rails db:migrate
rails db -e test

つないで、DBができていることを確認した。

$ curl localhost:3000/blogs/index
[{"id":1,"title":"今日のできごと","article":"ごはんを食べて寝た","created_at":"2019-10-06T09:02:16.207Z","updated_at":"2019-10-06T09:02:16.207Z"}][

値が返ってきた。
いろいろ試した結果、たぶんDBが良くなかったんだろうという結論。

雑なポエム的なまとめ

test環境はdevelopと結構違ってつらい。
動きがおかしかったらDBをresetしてtest用に作り直す。

参考サイト
Ruby - rails db:migrate RAILS_ENV=test ができない|teratail

Ruby - rails s -e testでlocalhost:3000を見るとエラーが出てしまいます。エラーを消したいです。|teratail

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

Ruby、Rails を学ぶ上でのおすすめの本まとめ

これは何?

Ruby、Rails に関して何によってどう学んできましたか?というのをよく聞かれるので、まとめた。
あくまで個人の経験に基づく感想です。

(書籍へのリンクはアフィリエイトではありません)

対象

とりあえず Ruby でちょっとしたスクリプトは書けるし、Rails も軽く触ったことはあるよ、という方。
(全くRuby触ったことない人向けではないと思います)

Rubyの言語仕様を学ぶ

効能

  • Ruby っぽい書き方ってどんな書き方なんだろう?を学べる(map 使うとか)。
  • Rails を読む上でのおまじないが減る。
    • なぜ attr_accessor を書くとセッター/ゲッターが生えるのかとか、モデルがActiveRecord を継承していることの意味とかがわかるようになり不安が減る。

紹介

  • Effective Ruby
    • リファレンス的な本ではなく、もっと現実的に使われる文脈を意識して書かれているように感じた。読みやすかった。
    • 写経しやすいのが良い。書いて身につけることができる。
  • メタプログラミングRuby第2版
    • メタプログラミングという切り口で Ruby の言語仕様を教えてくれる本だと思った。
    • クラス定義の話(継承ツリーとか特異クラスとかinclude/extendとかの話)はこの本が一番分かりやすかった。
    • 「ブロックがクロージャである」という表現を直感的に理解できるようになった。

設計力を高める

効能

  • 以下のような疑問に対する自分なりの意見を持てるようになる。
    • model/controller/view 以外にも app ディレクトリ以下に service とか作ることがあるっぽいけど、どこに何を書くのが美しいの?
    • controller/model が肥大化してしまってつらいんだけどどうすればいいの?
  • 実装に関して戦略レベルで大きなヘマをすることが減る。したがってコードレビューで大きくちゃぶ台返しされることが減る。

注意事項

  • ちょっと読んでみて「難しくてほとんど全部よくわからん、」「言っている意味はわかるがこれを学んでなにか意味があるの?」となったら今読むべきではないと考えたほうが良いかもしれません。
    • 知識というか考え方を学ぶ本だと私は解釈しており、理解できていない状態で読んでもほとんど学びはないのではないか?となっている。
    • 具体的に開発を通じて悩んだ経験があると、「なるほどこういうふうに書けるのね!」となりやすいが、経験があまりないとピンとこないので記憶に残らない、ということもありそう。

紹介

  • リファクタリング:Rubyエディション
    • 神本。しかし絶版っぽい?
    • 写経しやすいのが良い。書いて身につけることができる。
    • リファクタリングの本なので、before の状態から after の状態に帰る方法としてどのような方法があるのかを学ぶことができる。
    • before の状態を見てその問題点を列挙する力、after の候補を列挙する力、after の候補から最も良いものを選び出す力、を身につけるとアウトプットの質が大幅に改善される。
  • Ruby によるデザインパターン
    • 絶版っぽい?
    • デザインパターンを Ruby で実装してみた、みたいなもの。
    • 『リファクタリング:Ruby』 でしっかり学んでいると、だいたい「まぁそうだよね」、となるかもしれない。
    • 「デザインパターンって言葉よく聞くけど何なのだろう?ちょっと学んでみたいな」みたいな人には特に向いているかと思う。
  • オブジェクト指向設計実践ガイド―Rubyでわかる進化しつづける柔軟なアプリケーションの育て方(サンディメッツ本)
    • 絶版じゃない!!!
    • 上の2つの本で学んだこととかぶるところが多いので途中で読むのをやめたが、良本。
    • 登場する具体例が自転車なので、自転車好きなら良いかも。個人的には特に興味も知識もないので、オブジェクトの例としてギアとかそういうの出てきてもピンとこなくてちょっとつらかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

データ yaml json

yaml

  • 構造化したデータを表現するモノ

記事

https://magazine.rubyist.net/articles/0009/0009-YAML.html

基本
require 'yaml'

yaml_data = <<-DATA
- RED
- GREEN
- BLU

DATA

YAML.load(yaml_data)
# リテラル
yaml_data = <<-DATA
- RED
- GREEN
- BLU

DATA

p YAML.load(yaml_data)


# リテラル出力
  Desktop ruby sample.rb
["RED", "GREEN", "BLU"]
"\ndefault: &default\n  adapter: mysql2\n  host: 127.0.0.1\n  encoding: utf8\n  pool: 5\n  username: root\n  password:\n  reconnect: false\n\ndevelopment:\n  <<: *default\n  database: development\n\ntest:\n  <<: *default\n  database: test\n\n\n"


#yml形式

database = <<-DATA

default: &default
  adapter: mysql2
  host: 127.0.0.1
  encoding: utf8
  pool: 5
  username: root
  password:
  reconnect: false

development:
  <<: *default
  database: development

test:
  <<: *default
  database: test


DATA

p YAML.load(database)


#yml形式 出力

  Desktop ruby sample.rb
["RED", "GREEN", "BLU"]
{"default"=>{"adapter"=>"mysql2", "host"=>"127.0.0.1", "encoding"=>"utf8", "pool"=>5, "username"=>"root", "password"=>nil, "reconnect"=>false}, "development"=>{"adapter"=>"mysql2", "host"=>"127.0.0.1", "encoding"=>"utf8", "pool"=>5, "username"=>"root", "password"=>nil, "reconnect"=>false, "database"=>"development"}, "test"=>{"adapter"=>"mysql2", "host"=>"127.0.0.1", "encoding"=>"utf8", "pool"=>5, "username"=>"root", "password"=>nil, "reconnect"=>false, "database"=>"test"}}

json

  • json(JavaScript Object Notation)とは

javascriptのオブジェクト

require 'json'

yaml_data = <<-DATA
["Red","Blue","Red"]

DATA

JSON.load(yaml_data)
# json は基本はオブジェクト

example.json

{
    "ruby": {
      "rails":30000
    },
    "python": {
      "django":25000
    }
  }

require 'json'

File.open("example.json") do |file|
  hash = JSON.load(file)
  p hash
end


hash = JSON.parse('{"ruby":{"rails":30000},"python":{"django":25000}}')
p hash
{"ruby"=>{"rails"=>30000}, "python"=>{"django"=>25000}}

# 文字列型のjsonもある

- parse

hash = JSON.parse('{"ruby":{"rails":30000},"python":{"django":25000}}')

p hash
{"ruby"=>{"rails"=>30000}, "python"=>{"django"=>25000}}

  • json 作成
generate
ハッシュ → json

require 'json'
hash = JSON.generate({"ruby":{"rails":30000},"python":{"django":25000}})
p hash
to_json
# ruby で用意してくれている

require 'json'
hash = {"ruby":{"rails":30000},"python":{"django":25000}}.to_json
p hash

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

【devise】confirmableを利用しているとき、メールアドレスを変更する際のメールによる確認作業をスキップする

config/initializers/devise.rbを以下のように変更して保存。

config/initializers/devise.rb
  ...

  # If true, requires any email changes to be confirmed (exactly the same way as
  # initial account confirmation) to be applied. Requires additional unconfirmed_email
  # db field (see migrations). Until confirmed, new email is stored in
  # unconfirmed_email column, and copied to email column on successful confirmation.
- config.reconfirmable = true
+ config.reconfirmable = false

  ...

この時点でサーバーを起動していたなら再起動する。これでユーザー情報を変更する際に、確認作業なくメールアドレスを変更できる。

もしusersテーブルにunconfirmed_emailカラムが残っているなら、不要なので削除する。

$ rails g migration remove_unconfirmed_email_from_users unconfirmed_email:string
$ rails db:migrate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【devise】confirmableを適用しているとき、メールアドレスを変更する際のメールによる確認作業をスキップする

config/initializers/devise.rbを以下のように変更して保存。

config/initializers/devise.rb
  ...

  # If true, requires any email changes to be confirmed (exactly the same way as
  # initial account confirmation) to be applied. Requires additional unconfirmed_email
  # db field (see migrations). Until confirmed, new email is stored in
  # unconfirmed_email column, and copied to email column on successful confirmation.
- config.reconfirmable = true
+ config.reconfirmable = false

  ...

この時点でサーバーを起動していたなら再起動する。これでユーザー情報を編集する際に、メールアドレスを確認作業なく変更できる。

もしusersテーブルにunconfirmed_emailカラムが残っているなら、不要なので削除する。

$ rails g migration remove_unconfirmed_email_from_users unconfirmed_email:string
$ rails db:migrate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【devise】confirmableを適用時の、メールアドレスを変更する際のメール確認をスキップする

config/initializers/devise.rbを以下のように変更して保存。

config/initializers/devise.rb
  ...

  # If true, requires any email changes to be confirmed (exactly the same way as
  # initial account confirmation) to be applied. Requires additional unconfirmed_email
  # db field (see migrations). Until confirmed, new email is stored in
  # unconfirmed_email column, and copied to email column on successful confirmation.
- config.reconfirmable = true
+ config.reconfirmable = false

  ...

この時点でサーバーを起動していたなら再起動する。これでユーザー情報を編集する際に、メールアドレスをメールによる確認作業なしで変更できる。

もしusersテーブルにunconfirmed_emailカラムが残っているなら、不要なので削除する。

$ rails g migration remove_unconfirmed_email_from_users unconfirmed_email:string
$ rails db:migrate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Deviseでログイン機能を追加・日本語化・Bootstrap適用まで

Deviseの日本語化やBootstrapの導入方法の記事はよく見かけます。ところが……

  • ビューファイルを作成するときに,結局どのコマンドを使えばよいか分からない:sweat:
$ rails g devise:views
$ rails g devise:i18n:views
$ rails g devise:views:bootstrap_templates
  • 全て導入したはずなのにメール文が日本語化されない:cry:

など,順序を間違えると問題が発生します。そこで,ログイン画面を最低限のスタイルで実装するところまでをまとめてみました。

開発環境

  • macOS Mojave 10.14.6
  • Ruby 2.6.4
  • Rails 5.2.3
  • Bootstrap 4.3.1
  • Devise 4.7.1 (confirmableなどの導入過程は記載していません)

0. 準備

以下の前提で進めていきます。(-d postgresqlはお好みで)

$ rails _5.2.3_ new devise_sample -d postgresql
$ rails g controller homes index
config/routes.rb
Rails.application.routes.draw do
  root 'homes#index'
end
app/views/layouts/application.html.erb
(略)
  <body>
    <%= render 'shared/header' %>
    <%= yield %>
  </body>
app/views/homes/index.html.erb
<%= render 'shared/flash_messages' %>
app/views/shared/_flash_messages.html.erb
<% flash.each do |name, msg| %>
  <div class="alert alert-<%= name %>" role="alert" id="alert">
    <a href="#" class="close" data-dismiss="alert">×</a>
    <%= msg %>
  </div>
<% end %>
app/views/shared/_header.html.erb
<header>
  <nav class="navbar navbar-expand navbar-light">
    <%= link_to "Deviseサンプル", root_path, class: 'navbar-brand' %>
    <div id="Navber">
      <ul class="navbar-nav">
        <% if user_signed_in? %>
          <li class="nav-item active">
            <%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: 'nav-link' %>
          </li>
      <% else %>
          <li class="nav-item active">
            <%= link_to "新規登録", new_user_registration_path, class: 'nav-link' %>
          </li>
          <li class="nav-item active">
            <%= link_to "ログイン", new_user_session_path, class: 'nav-link' %>
          </li>
        <% end %>
      </ul>
    </div>
  </nav>
</header>

1. Gemの追加

  • Gemfileに以下を追加して$ bundle install
gem 'bootstrap', '~> 4.3.1'
gem 'devise'
gem 'devise-i18n'
gem 'devise-i18n-views'
gem 'devise-bootstrap-views', '~> 1.0'
gem 'jquery-rails'
gem 'rails-i18n'

2. Bootstrapの導入

  • 【公式】twbs/bootstrap-rubygem

  • application.cssの拡張子をscssに変更

  • application.scssから,*= require_tree .*= require_selfを削除

  • application.scss@import "bootstrap";を追加

  • スタイルも追加

app/assets/stylesheets/application.scss
@import "bootstrap";

// ログイン画面

.container-login {
  @extend .container-fluid;
  max-width: 576px;
  padding: 2rem;
}

// 「ログインしました」などのフラッシュ用スタイル

.alert-notice {
  @extend .alert-info;
}

.alert-alert {
  @extend .alert-danger;
}
  • application.jsに3つ追加
app/assets/javascripts/application.js
//= require jquery3
//= require popper
//= require bootstrap-sprockets

3. Deviseの導入

  • 【公式】plataformatec/devise

  • Deviseをインストール(userの箇所は,任意のモデル名でOKです)

rails g devise:install
rails g devise user
rails db:create db:migrate
  • 問題がなければ,$ rails sの後,http://localhost:3000からログインボタンを押せば,ログイン画面が表示されます。

スクリーンショット 2019-10-08 9.37.05.png

  • gem 'devise-bootstrap-views'を追加しているので, Bootstrapもある程度適用されています。
    • 公式(hisea/devise-bootstrap-views
    • 横幅一杯表示されるのはちょっと……と思われるかもしれませんが,さらに見た目を整える作業は最後にします。

4. Deviseの日本語化

config/application.rb
module AssociationTutorial
  class Application < Rails::Application
    # 以下を追加すれば日本語に
+   config.i18n.default_locale = :ja
    # タイムゾーンも変更するなら,以下を追加
+   config.time_zone = 'Asia/Tokyo'
  end
end
  • サーバーを落として$ rails sで再起動すれば日本語に変更されます。

スクリーンショット 2019-10-08 10.04.53.png

5. メールを送信できるようにする(Gmailかつ開発環境のみ対応)

  • 送信元の表示名を変更しておきます。
    • タイトル名はこの設定が反映されますが,メールアドレスの箇所は実際の送信するアドレスになります。
config/initializers/devise.rb
Devise.setup do |config|
-  config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
+  config.mailer_sender = ''タイトル名 <noreply@example.com>'
end
  • Gmailから送信する場合,安全性の低いアプリのアクセスを有効にする必要があります。

  • development.rbconfig.action_mailer.raise_delivery_errors = falseの箇所を置き換えます。

    • メールアドレスやパスワードをgitの管理下に入れるのを避け,credentials管理にしておきます。本番環境で無駄にエラーが出ないようダミー情報を入れておきます。
config/environments/development.rb
  # 削除 config.action_mailer.raise_delivery_errors = false
  # 以下に置き換え
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

  if Rails.application.credentials.gmail.present?
    mail_address = Rails.application.credentials.gmail[:address]
    password = Rails.application.credentials.gmail[:password]
  else
    mail_address = 'admin@example.com'
    password = 'password'
  end

  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
      enable_starttls_auto: true,
      address: "smtp.gmail.com",
      port: 587,
      user_name: mail_address,
      password: password,
      authentication: "plain"
  }
  • $ EDITOR=vi rails credentials:editで,credentialsの中に,メールアドレスとパスワードを記載します。
gmail:
  address: 送信に使用するメールアドレス
  password: 送信に使用するパスワード
  • メール送信の確認をするには,あらかじめ新規登録をした上で,ログイン画面のパスワードを忘れましたか?をクリックしてパスワード再設定メールを送信すればOKです。

スクリーンショット 2019-10-08 12.59.31.png

 日本語にはなっているもののどうも不自然ですね……これは次の作業で解決します。

6. 日本語訳を変更

日本語訳を変更したい場合は,次のコマンドでconfig/locales/devise.views.ja.ymlを作成し,編集すればOKです。

rails g devise:i18n:locale ja
  • なお,このコマンドの時点で,パスワード再設定メールの本文が自然なものに変わります。

スクリーンショット 2019-10-08 13.15.17.png

  • 例えばアカウント登録新規登録に変更したい場合は,devise.views.ja.ymlの該当文字を置換すればOKです。

7. ログイン画面などの変更

  • まず,次のコマンドでビューファイルを作成します。
rails g devise:i18n:views
rails g devise:views:bootstrap_templates -f
  • 【参考】それぞれのコマンドの最後に例えばuserをつけることで,usersディレクトリ内にファイルを作成することもできますが,その場合は,次の3つの作業を行わないと反映されません。

    • devise.views.ja.yml30行目のdeviseusersに変更
    • config/initializers/devise.rbにあるconfig.scoped_views = falseのコメントアウトを外してtrueに変更
    • サーバーを落として$ rails sで再起動
  • 例えば以下のファイルを<div>で囲むと,無駄に横長になっている状態を改善できます。

    • ここから先はお好みで。
ファイル名
app/views/devise/confirmations
app/views/devise/passwords
app/views/devise/registrations
app/views/devise/sessions
app/views/devise/unlocks
<div class="container-login">
# 元のプログラム
</div>

スクリーンショット 2019-10-08 14.54.38.png

app/helpers/devise_helper.rb
module DeviseHelper
  def bootstrap_devise_error_messages!
    return "" if resource.errors.empty?

    html = ""
    messages = resource.errors.full_messages.each do |errmsg|
      html += <<-EOF
      <div class="alert alert-danger alert-dismissible" role="alert">
        <button type="button" class="close" data-dismiss="alert">
          <span aria-hidden="true">&times;</span>
          <span class="sr-only">close</span>
        </button>
        #{errmsg}
      </div>
      EOF
    end
    html.html_safe
  end
end

〜〜〜〜〜変更前〜〜〜〜〜
スクリーンショット 2019-10-08 15.13.37.png
〜〜〜〜〜変更後〜〜〜〜〜
スクリーンショット 2019-10-08 15.13.06.png

  • ログイン画面だけエラーメッセージが表示されないので,追加しておきます。
app/views/devise/sessions/new.html.erb
 <div class="container-login">
   <h1><%= t('.sign_in') %></h1>
+  <%= render 'shared/flash_messages' %>

   <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>

スクリーンショット 2019-10-08 15.18.45.png

  • app/views/deviseディレクトリ内のファイルのbtn btn-primarybtn btn-primary btn-blockに置換すればボタンの横幅が自然になります。

〜〜〜〜〜変更前〜〜〜〜〜
スクリーンショット 2019-10-08 15.26.43.png
〜〜〜〜〜変更後〜〜〜〜〜
スクリーンショット 2019-10-08 15.26.51.png

  • バリデーションはフロント側にも簡単に入れられます。
    • f.email_fieldf.password_fieldrequired: trueを入れることで空欄投稿できなくなります。
    • 新規登録(アカウント登録)画面では,例えば,f.password_fieldrequired: true, minlength: '6', maxlength: '30'を追加すれば,文字数のバリデーションも追加できます。
app/views/devise/registrations/new.html.erb
     <div class="form-group">
       <%= f.label :password %>
-      <%= f.password_field :password, autocomplete: 'current-password',
-                                      class: 'form-control' %>
+      <%= f.password_field :password, autocomplete: 'current-password',
+                                      class: 'form-control',
+                                      required: true,
+                                      minlength: '6',
+                                      maxlength: '30' %>

スクリーンショット 2019-10-09 10.04.34.png

  • _links.html.erbを編集して,一番下のリンクをボタンにしてみます。
app/views/devise/shared/_links.html.erb
<hr class="border-dark my-5">
<div class="form-group">
  <%- if controller_name != 'sessions' %>
    <%= link_to t(".sign_in"), new_session_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
    <%= link_to t(".sign_up"), new_registration_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
    <%= link_to t(".forgot_your_password"), new_password_path(resource_name), class: 'btn btn-secondary btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
    <%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name), class: 'btn btn-secondary btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
    <%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name), class: 'btn btn-secondary btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.omniauthable? %>
    <%- resource_class.omniauth_providers.each do |provider| %>
      <%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider), class: 'btn btn-info btn-block' %><br />
    <% end -%>
  <% end -%>
</div>

スクリーンショット 2019-10-08 15.36.29.png

  • さらに,リンクのログイン新規登録(アカウント登録)を次のように変更してみます。
# 上2つを次に置き換え

  <%- if controller_name != 'sessions' %>
    <%= link_to "アカウントをお持ちの方", new_session_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
    <%= link_to "アカウントをお持ちでない方", new_registration_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

スクリーンショット 2019-10-08 15.41.51.png

単純なスタイルですが,ログイン画面がだいぶ整ったのではないでしょうか。

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

アンチパターン:Railsで外部キーとassociation名は同じにしない

要約

  • belongs_to :nameforeign_key :nameが同じだと、その逆のhas_manyをとるときにどうがんばってもうまく取れなかった
  • 同じにしない
  • 外部キーには_idつける

詳細

  • 以下のコードだとto_messagesfrom_messagesがとれない
class User < ApplicationRecord
  has_many :to_messages, class_name: 'Message', foreign_key: 'to', dependent: :destroy
  has_many :from_messages, class_name: 'Message', foreign_key: 'from', dependent: :destroy
end

class Message < ApplicationRecord
  has_many :to, class_name: 'User', foreign_key: 'to'
  has_many :from, class_name: 'User', foreign_key: 'from'
end

>Message.last.to
=>#<User id: 373...>
>User.last.to_messages
=>[]
  • has_manyの名前を変えたらいけた
class Message < ApplicationRecord
  has_many :to_user, class_name: 'User', foreign_key: 'to'
  has_many :from_user, class_name: 'User', foreign_key: 'from'
end
  • そもそもforeign_key_idがいいね
  • 自分では慣例的にやらないけどレビューであがってくることはあるので覚えておく
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

YOLPの2点間距離APIで緯度1度分・経度1度分の距離を調べる

概要

  • 対象の位置に緯度・経度を1度 (または0.1度、0.01度、0.001度) 足したときの距離を Ruby + YOLP API で調べる
  • 距離の計算には YOLP(地図):2点間距離API を使用する
  • 可視化のために YOLP(地図):Yahoo!スタティックマップAPI を使用して地図画像を出力する
  • 対象位置は「日本へそ公園 (兵庫県西脇市上比延町)」「名古屋駅」「納沙布岬 (のさっぷみさき) 離島を除けば日本の本土最東端」「荒崎 (あらさき) 沖縄本島最南端に位置する岬」の4箇所とする

ソースコード

動作確認環境: Ruby 2.6.5

require 'json'
require 'open-uri'

class Measure

  def initialize(appid)
    @appid = appid
  end

  # point: 基準となる位置など
  # cond: 移動量(緯度経度)など
  def output(point, cond)
    target = []
    cond[:list].each_with_index do |info, i|
      # 緯度経度に移動分の緯度経度を足す
      t = {:lat => point[:lat] + info[:lat], :lon => point[:lon] + info[:lon]}
      # 2点間の距離を求める
      t[:dist] = distance(point, t)
      # ピン名をセット
      t[:pin] = info[:pin]
      # 移動量(緯度経度)に応じてどれだけの距離があるか出力
      puts "#{point[:name]}(#{point[:lat]},#{point[:lon]}): 移動量(#{info[:lat]},#{info[:lon]}) => 距離#{t[:dist]}km"
      # 地図上に載せる情報を追加
      target << t
    end
    # 地図画像を出力
    open("distmap_#{point[:name]}_#{cond[:name]}.png", 'wb').write(map(point, target))
  end

  # 2点間の距離を求める
  def distance(p, t)
    # YOLP(地図):2点間距離API - Yahoo!デベロッパーネットワーク
    # https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/distance.html
    c = "#{p[:lon]},#{p[:lat]} #{t[:lon]},#{t[:lat]}" # 経度・緯度の順番
    url = 'https://map.yahooapis.jp/dist/V1/distance?' +
      URI.encode_www_form({
        'appid'       => @appid,
        'coordinates' => c,
        'output'      => 'json',
      })
    json = open(url).read
    # kmで小数点第6位までの値が入っている
    JSON.parse(json)['Feature'][0]['Geometry']['Distance']
  end

  # 地図画像を返す
  # pointのpとtargetのt
  def map(point, target)
    # YOLP(地図):Yahoo!スタティックマップAPI - Yahoo!デベロッパーネットワーク
    # https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/static.html
    # 緯度・経度の順番
    pinp = "#{point[:lat]},#{point[:lon]},#{point[:lat]}\n#{point[:lon]}"
    pint = {}
    target.each do |t|
      # 緯度・経度の順番
      pint["pin#{t[:pin]}"] = "#{t[:lat]},#{t[:lon]},#{t[:lat]}\n#{t[:lon]}\n#{t[:dist]}km"
    end
    url = 'https://map.yahooapis.jp/map/V1/static?' +
      URI.encode_www_form({
        'appid'  => @appid,
        'pinp'   => pinp,
        'width'  => '600',
        'height' => '600',
      }.merge(pint))
    open(url).read
  end
end

# 基準となる場所
points = [
  # 日本へそ公園 (兵庫県西脇市上比延町)
  {:name => 'Heso',    :lat => 35,        :lon => 135},
  # 名古屋駅
  {:name => 'Nagoya',  :lat => 35.170634, :lon => 136.881751},
  # 納沙布岬 (のさっぷみさき) 離島を除けば日本の本土最東端
  {:name => 'Nosappu', :lat => 43.385056, :lon => 145.816278},
  # 荒崎 (あらさき) 沖縄本島最南端に位置する岬
  {:name => 'Arasaki', :lat => 26.075,    :lon => 127.680833},
]

conds = [
  { # 緯度経度に0.001度足す
    :name => '3',
    :list => [
      {:pin => 'n', :lat => 0.001, :lon => 0.000},
      {:pin => 'e', :lat => 0.000, :lon => 0.001},
    ]
  },
  { # 緯度経度に0.01度足す
    :name => '2',
    :list => [
      {:pin => 'n', :lat => 0.01, :lon => 0.00},
      {:pin => 'e', :lat => 0.00, :lon => 0.01},
    ]
  },
  { # 緯度経度に0.1度足す
    :name => '1',
    :list => [
      {:pin => 'n', :lat => 0.1, :lon => 0.0},
      {:pin => 'e', :lat => 0.0, :lon => 0.1},
    ]
  },
  { # 緯度経度に1度足す
    :name => '0',
    :list => [
      {:pin => 'n', :lat => 1, :lon => 0},
      {:pin => 'e', :lat => 0, :lon => 1},
    ]
  },
]

appid = '<YOUR APPLICATION ID>'

m = Measure.new(appid)

points.each do |p|
  conds.each do |cond|
    m.output(p, cond)
  end
end

出力結果

  • 日本では、緯度(南北方向の移動)の1度分はおおよそ110kmほどの距離になる
  • 日本では、経度(東西方向の移動)の1度分はおおよそ90kmほどの距離になる
  • 北海道は高緯度のため、経度方向の距離が小さくなる
  • 沖縄は低緯度のため、経度方向の距離が大きくなる
Heso(35,135): 移動量(0.001,0.0) => 距離0.11094km
Heso(35,135): 移動量(0.0,0.001) => 距離0.091288km
Heso(35,135): 移動量(0.01,0.0) => 距離1.109405km
Heso(35,135): 移動量(0.0,0.01) => 距離0.912882km
Heso(35,135): 移動量(0.1,0.0) => 距離11.094135km
Heso(35,135): 移動量(0.0,0.1) => 距離9.128817km
Heso(35,135): 移動量(1,0) => 距離110.949629km
Heso(35,135): 移動量(0,1) => 距離91.287788km
Nagoya(35.170634,136.881751): 移動量(0.001,0.0) => 距離0.110944km
Nagoya(35.170634,136.881751): 移動量(0.0,0.001) => 距離0.091098km
Nagoya(35.170634,136.881751): 移動量(0.01,0.0) => 距離1.109437km
Nagoya(35.170634,136.881751): 移動量(0.0,0.01) => 距離0.910983km
Nagoya(35.170634,136.881751): 移動量(0.1,0.0) => 距離11.094448km
Nagoya(35.170634,136.881751): 移動量(0.0,0.1) => 距離9.109825km
Nagoya(35.170634,136.881751): 移動量(1,0) => 距離110.952776km
Nagoya(35.170634,136.881751): 移動量(0,1) => 距離91.097874km
Nosappu(43.385056,145.816278): 移動量(0.001,0.0) => 距離0.1111km
Nosappu(43.385056,145.816278): 移動量(0.0,0.001) => 距離0.08103km
Nosappu(43.385056,145.816278): 移動量(0.01,0.0) => 距離1.111002km
Nosappu(43.385056,145.816278): 移動量(0.0,0.01) => 距離0.810299km
Nosappu(43.385056,145.816278): 移動量(0.1,0.0) => 距離11.110106km
Nosappu(43.385056,145.816278): 移動量(0.0,0.1) => 距離8.102994km
Nosappu(43.385056,145.816278): 移動量(1,0) => 距離111.109843km
Nosappu(43.385056,145.816278): 移動量(0,1) => 距離81.029456km
Arasaki(26.075,127.680833): 移動量(0.001,0.0) => 距離0.110789km
Arasaki(26.075,127.680833): 移動量(0.0,0.001) => 距離0.100054km
Arasaki(26.075,127.680833): 移動量(0.01,0.0) => 距離1.107891km
Arasaki(26.075,127.680833): 移動量(0.0,0.01) => 距離1.000541km
Arasaki(26.075,127.680833): 移動量(0.1,0.0) => 距離11.078981km
Arasaki(26.075,127.680833): 移動量(0.0,0.1) => 距離10.005405km
Arasaki(26.075,127.680833): 移動量(1,0) => 距離110.796788km
Arasaki(26.075,127.680833): 移動量(0,1) => 距離100.053811km

出力結果 (地図画像)

日本へそ公園 (兵庫県西脇市上比延町)

  • 緯度35度
  • 経度135度

移動量1度
Heso(35,135): 移動量(1,0) => 距離110.949629km
Heso(35,135): 移動量(0,1) => 距離91.287788km
distmap_Heso_0.png

移動量0.1度
Heso(35,135): 移動量(0.1,0.0) => 距離11.094135km
Heso(35,135): 移動量(0.0,0.1) => 距離9.128817km
distmap_Heso_1.png

移動量0.01度
Heso(35,135): 移動量(0.01,0.0) => 距離1.109405km
Heso(35,135): 移動量(0.0,0.01) => 距離0.912882km
distmap_Heso_2.png

移動量0.001度
Heso(35,135): 移動量(0.001,0.0) => 距離0.11094km
Heso(35,135): 移動量(0.0,0.001) => 距離0.091288km
distmap_Heso_3.png

名古屋駅

  • 緯度35.170634度
  • 経度136.881751度

移動量1度
Nagoya(35.170634,136.881751): 移動量(1,0) => 距離110.952776km
Nagoya(35.170634,136.881751): 移動量(0,1) => 距離91.097874km
distmap_Nagoya_0.png

移動量0.1度
Nagoya(35.170634,136.881751): 移動量(0.1,0.0) => 距離11.094448km
Nagoya(35.170634,136.881751): 移動量(0.0,0.1) => 距離9.109825km
distmap_Nagoya_1.png

移動量0.01度
Nagoya(35.170634,136.881751): 移動量(0.01,0.0) => 距離1.109437km
Nagoya(35.170634,136.881751): 移動量(0.0,0.01) => 距離0.910983km
distmap_Nagoya_2.png

移動量0.001度
Nagoya(35.170634,136.881751): 移動量(0.001,0.0) => 距離0.110944km
Nagoya(35.170634,136.881751): 移動量(0.0,0.001) => 距離0.091098km
distmap_Nagoya_3.png

納沙布岬 (のさっぷみさき) 離島を除けば日本の本土最東端

  • 緯度43.385056度
  • 経度145.816278度

移動量1度
Nosappu(43.385056,145.816278): 移動量(1,0) => 距離111.109843km
Nosappu(43.385056,145.816278): 移動量(0,1) => 距離81.029456km
distmap_Nosappu_0.png

移動量0.1度
Nosappu(43.385056,145.816278): 移動量(0.1,0.0) => 距離11.110106km
Nosappu(43.385056,145.816278): 移動量(0.0,0.1) => 距離8.102994km
distmap_Nosappu_1.png

移動量0.01度
Nosappu(43.385056,145.816278): 移動量(0.01,0.0) => 距離1.111002km
Nosappu(43.385056,145.816278): 移動量(0.0,0.01) => 距離0.810299km
distmap_Nosappu_2.png

移動量0.001度
Nosappu(43.385056,145.816278): 移動量(0.001,0.0) => 距離0.1111km
Nosappu(43.385056,145.816278): 移動量(0.0,0.001) => 距離0.08103km
distmap_Nosappu_3.png

荒崎 (あらさき) 沖縄本島最南端に位置する岬

  • 緯度26.075度
  • 経度127.680833度

移動量1度
Arasaki(26.075,127.680833): 移動量(1,0) => 距離110.796788km
Arasaki(26.075,127.680833): 移動量(0,1) => 距離100.053811km
distmap_Arasaki_0.png

移動量0.1度
Arasaki(26.075,127.680833): 移動量(0.1,0.0) => 距離11.078981km
Arasaki(26.075,127.680833): 移動量(0.0,0.1) => 距離10.005405km
distmap_Arasaki_1.png

移動量0.01度
Arasaki(26.075,127.680833): 移動量(0.01,0.0) => 距離1.107891km
Arasaki(26.075,127.680833): 移動量(0.0,0.01) => 距離1.000541km
distmap_Arasaki_2.png

移動量0.001度
Arasaki(26.075,127.680833): 移動量(0.001,0.0) => 距離0.110789km
Arasaki(26.075,127.680833): 移動量(0.0,0.001) => 距離0.100054km
distmap_Arasaki_3.png

参考資料

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

マイグレーションファイルの命名規則

目的

マイグレーションファイルを作成する際に
作成のみでなく閲覧者などにもわかりやすい命名にするため規則などを記載していく

taskテーブルを作成

rails g migration CreateTasks name:string content:text

マイグレーションファイル例
class CreateTasks < ActiveRecord::Migration[5.2]
def change
    create_table :tasks do |t|
      t.string :name
      t.text :content
    end
end

taskテーブルにカラムを追加

rails g migration AddStateToTasks state:string
rails g migration add_state_to_tasks state:string

マイグレーションファイル例
class AddStateToTask < ActiveRecord::Migration[5.2]
  def change
    #[形式]add_column(テーブル名,カラム名,データ型)
    add_column :tasks, :state, :string
  end
end

add_columnには次のオプションを指定することができる。
・ null:true … NOT NULL制約を削除
・ null:true … NOT NULL制約を追加
・ limit: size … ファイルのサイズに対する制限を設定
・default:[val]… [val]に設定した値をレコード作成じのカラムのデフォルト値とする

taskテーブルに追加したカラム名を変更

rails g migration RenameFromStateToTasks
rails g migration rename_state_to_tasks

マイグレーションファイル例
class RenameFromStateToTasks < ActiveRecord::Migration[5.2]
  def change
    #[形式]rename_column(テーブル名,変更前のカラム名,変更後のカラム名)
    rename_column :tasks,:state,:status
  end
end

taskテーブルに追加したカラムのデータ型を変更

rails g migration ChangeStatusOfTasks
rails g migration change_datatype_status_of_tasks

マイグレーションファイル例
class ChangeDatatypeStatusOfTasks < ActiveRecord::Migration[5.2]
  def up
    #[形式]change_column(テーブル名,カラム名,データタイプ,オプション)
    change_column :tasks, :status, :integer

    # オプション
    # limit - カラム長の最大数
    # change_column :tasks, :status, :integer, limit: 120

    # default - カラムのデフォルト値を設定。NULLにしたい場合は、nilを指定
    # change_column :tasks, :status, :integer, default: "タイトルがありません"

    # null - null制約を設定。false -> null制約がON。true -> null制約がOFF
    # change_column :tasks, :status, :integer, null: true
  end
end

taskテーブルに追加したカラムにインデックスやユニーク制約の追加

rails g migration AddIndexStatusToTasks
rails g migration add_index_Status_to_tasks

マイグレーションファイル例
class AddIndexStatusToTasks < ActiveRecord::Migration[5.2]
  def change
    add_index  :tasks, :status
   #add_index  :tasks, :status ,unique: true ユニーク制約も付加可能
  end
end

taskテーブルに追加したカラムにNULL制約の追加

rails g migration ChangeNotNullToTasks
rails g migration change_notnull_to_tasks

マイグレーションファイル例
class ChangeNotNullToTasks < ActiveRecord::Migration[5.2]
  def change
    #tasksテーブルのstatusにNOT NULL制約を追加
    change_column_null :tasks, :status, false
  end
end

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

【49日目】Rails学習日誌 コメントの保存、表示、パーシャル作成、バリデーション、エラーの表示

68 コメント保存処理の実装 続き

コメントのコントローラーの編集 

binding.pryでコメント入力時のパラメーターを確認
Rails flogによってパラメータが下記のように見やすく表示される

  Parameters: 

{

                  "utf8" => "✓",

    "authenticity_token" => "(略)",

               "comment" => {

        "board_id" => "5",

            "name" => "例",

         "comment" => "コメント"

    },

                "commit" => "送信"

}

ストロングパラメータの設定

保存できるパラメータはboard_id, name, commentのみにする

comments_controll.rb
private

def comment_params
  params.require(:comment).permit(:board_id, :name, :comment)
end

createアクションの作成

ストロングパラメータによって弾かれるかどうかをif文で分岐
分岐した結果、成功をエラーをflashメッセージを用いてリダイレクト先に表示

comments_controll.rb
def create
  # コメントオブジェクトを作成
  # フォームに入力されたパラメータで初期化
  comment = Comment.new(comment_params) 
  # コメントの保存。保存前にバリデーションチェック
  # 保存の可否によってif文で分岐
  if comment.save 
    flash[:notice] = 'コメントを投稿しました' #flashでリダイレクト先に一時的にメッセージを渡す
    redirect_to comment.board 
  else
  # 以下はRails5.1以降専用
  # コメントが保存できなかった場合入力したコメントオブジェクトとエラーメッセージのリストを返す。
  # コメントオブジェクトの中身をリダイレクト先のフォームに戻したり、エラー表示は今後作る
    flash[:comment] = comment 
    flash[:error_messages] = comment.errors.full_messages
    redirect_back fallback_location: comment.board # redirect_backで一つ前の画面に戻るようリダイレクト
  end
end

69 コメントの表示

showの問題を修正

修正前のshowアクション

boards_controller.rb
def show
  @comment = @board.comments.new
end

この状態では、新しく作成されたが保存していない空のコメントが含まれてしまう。
=>常にからのコメントがコメント欄に表示されてしまうので修正する。

boards_controller.rb
def show
  @comment = Comment.new(board_id: @board.id)
end

コメントの初期化の際にboard_idを用いることで、該当のboardのコメントで保存されたコメントのみを表示するようにした。

viewの作成

show.html.erb
<div class="p-comment__list">
  <div class="p-comment_listTitle">コメント</div>
  <%= render @board.comments %> 
# 特定のモデル(comment)のオブジェクトのリスト(comments)をrenderの引数に渡した場合、自動的にモデル名(comment)のviewが使用される。
# オブジェクトの数だけ繰り返しviewがレンダリングされる。
# 今回のrenderの対象は「comments」なので、/app/views/comments/_comments.html.erbがオブジェクトの数レンダリングされる
</div>

## コメント表示用のパーシャルの作成
/app/views/commentsを作成し、_comment.html.erbを作成。

_comment.html.erb
<div class="p-comment__item">
  <p><%= simple_format(comment.comment) %></p> # simple_formatヘルパーで改行を<br>タグに変換してくれる
  <div class="p-comment__bottomLine">
    <span><%= comment.name %></span>
    <span><%= comment.created_at.to_s(:datetime_jp) %></span> #以前作った日本語時刻表示
  </div>
</div>

リファクタリング

commentを投稿するためのviewはcommentsディレクトリの下にある。
しかし、_comment_formはboardsディレクトリの下にある。
=>役割的にcommentsディレクトリ配下にあるべきなので修正する。

_form.html.erb としてcomments配下にコピーした上で、shownのパーシャルの呼び出し方を変えれば完了。

show.html.erb
<%= render partial: 'comments/form', locals: { comment: @comment } %>

70 コメントモデルへのバリデーション追加

モデルにバリデーションを作成

comment.rb
validates :name, presence: true, length: { maximum: 10 }
validates :comment, presence: true, length: { maximum: 1000 }

## エラーメッセージを作成
comments_controller.rbのcreateアクションのelse以下がバリデーションエラーの表示になる
以前作成した掲示板のcreate時のエラーメッセージを流用する

app/views/sharedディレクトリを作成
_error_messages.html.erbを作成

<% if flash[:error_messages] %>
  <div class="alert alert-danger">
    <ul>
      <% flash[:error_messages].each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

各viewのエラー呼び出しを編集
まずはboardsから
こちらではすでに作成している_form.html.erb内にエラー表示の部分があるため、sharedを呼び出すように書き換える。

_form.html.erb
<%= render 'shared/error_messages' %>

commentsにおいても_form.html.erbの先頭にsharedを呼び出すように上記と同じ記載をする。

エラーの日本語化

config/locales/ja.yml を編集

ja.yml
ja:
  activerecord:
    attributes:
      board:
        name: 名前
        title: タイトル
        body: 本文
      comment:
        name: 名前
        comment: コメント
   views:
     pagination:
       first: '最初'
       last: '最後'
       previous: '前'
       next: '次'
       truncate: '...'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsとDeviseのメモ

users/registrations/以下やusers/sessions/以下のviewの変更が反映されない

usersディレクトリではなくdeviseのもともとのディレクトリを読みに行ってしまっている。
routes.rbで明示的に示す必要がある。

routes.rb
devise_for :users,controllers{
  registrations: "users/registratons",
  sessions:"users/sessions",
}

メール(mailer)の場合はすこしやり方が違う。
views/users/mailer以下のファイルを編集したら以下の設定を行う。

controllers/users/mailer.rb
class Users::Mailer < Devise::Mailer
end

を追加

config/initializers/devise.rb
config.mailer = Users::Mailer

を追加

http://yrfreelance.com/2019/01/13/rails-devise-mail-template/

SendGridでメールを送信する

starterは適宜変更
sendgridアドオンは無料のdynoでも使えるがクレカの登録が必要.

settings > billing でクレジットカードを登録できる.
メール送信は400通/月まで無料

heroku addons:create sendgrid:starter
確認
heroku config:get sendgrid_username
heroku config:get sendgrid_password

以下を追加する。

config/environments/production.rb
 config.action_mailer.default_url_options ={ host:"yourappname.herokuapp.com"}
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.smtp_settings ={
  user_name:ENV["SENDGRID_USERNAME"],
  password:ENV["SENDGRID_PASSWORD"],
  domain:"heroku.com",
  adress:"smtp.sendgrid.net",
  port:587,
  authentication: :plain,
  enable_starttls_auto:true,
  }

あるいは、以下を追加する

config/environment.rb
ActionMailer::Base.smtp_settings = {
    :address        => 'smtp.sendgrid.net',
    :port           => '587',
    :authentication => :plain,
    :user_name      => ENV['SENDGRID_USERNAME'],
    :password       => ENV['SENDGRID_PASSWORD'],
    :domain         => 'heroku.com',
    :enable_starttls_auto => true
}
models/user.rb
#devise_for に :confirmable を追加
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,:confirmable

Error: connect ECONNREFUSED
がでたら、上記の環境変数が適切に設定できているか確認

migration fileを作成

bundle exec rails g migration addConfirmColumnToUser cconfirmed_at:datetime confirmation_token:string confirmation_sent_at:datetime
db/migrate/timestamps_add_confirm_column_to_user.rb
def change
    add_column :users, :confirmation_token, :string
    add_column :users, :confirmed_at, :datetime
    add_column :users, :confirmation_sent_at, :datetime
    add_index :users, :confirmation_token, unique: true
end

以下を追加
これがtrueだと、method missing エラーが起きる.
デフォルトでtrueになっていた気がするので注意。

config/initializers/devise.rb
config.reconfirmable = false
bundle exec rails db:migrate

Mailerが送信する認証用のアドレスをhttpからhttpsにする

httpとhttpsのサイトは別のサイト扱いされるのでパスワード保存がきかなくなってUXが低下するからhttpsのサービスを作っているならリンクもhttps化したほうがいい。

views/users/mailer/...
#link_to の confirmation_url(@resource, :confirmation_token => @token)を
#  confirmation_url(@resource, :confirmation_token => @token, protocol: "https")に変える。

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