20200219のRailsに関する記事は16件です。

【初学者のつまずきを記録】RailsでのMySQL接続:mysql.sock問題

はじめに

RailsでのMySQL接続手順について、備忘録的にまとめようと思います。
作業自体はごく基本的な内容かと思いますが、
初学者である僕が実際につまずいたことを重点的に記載しました。
僕と同じ初学者の方のご参考の一助となればうれしいです。
後半部分ではつまずいた原因について検討してみました。

※僕自身は「RailsのDBを(初めから| |後から)MySQLに変更する」を参考にしながら進めました。

前準備:MySQLを指定してRailsアプリを作成

Railsの仕様上、データベースを指定しない場合は自動的にSQLiteとなる。

$ rails new アプリ名 -d mysql

作成されたconfig/database.ymlを確認。

config/database.yml
default: &default
  adapter: mysql2
          ()

確かにMySQLとなっていることを確認。

MySQLとの接続

手順通り接続を試みる。が、エラー発生。

$ mysql -u root
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

mysql.sockを通じての接続ができなかったとのこと。

調べてみると、mysql.sockファイル自体が存在しないことが問題らしい。
確かに、tmpフォルダ内を確認してみても存在しない。下記実行で作成。

$ sudo touch /tmp/mysql.sock

その上で接続を試みても同じようなエラー発生。

$ mysql -u root             
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (38)

そもそも、mysql.sockファイルとは何だろう?
調べてみると、サーバーとクライアントの間で通信を行う際の仲介役的な役割らしい。
ここで、MySQLサーバーを起動していないことに気づきました。
というかエラー分にもしっかり MySQL serverとありますね。

MySQLサーバーを起動。

$ mysql.server start
Starting MySQL
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/xxxx.pid).

PIDファイルがないとのお達し。
指定された場所に作成をする。

$ touch /usr/local/var/mysql/xxxx.pid

その上でサーバー起動を試みるも、、、

$ mysql.server start                            
Starting MySQL
.rm: /tmp/mysql.sock: Permission denied
2020-02-17T12:13:34.6NZ mysqld_safe Fatal error: Can't remove the socket file:
/tmp/mysql.sock.
Please remove the file manually
         (略)

別のエラーが発生。
mysql.sockを削除しろと??
ないと言うから作ったのに。

言われるがままに削除し、
どこか不本意な気持ちのまま再度サーバー起動。成功。

$ mysql.server start                            
Starting MySQL
. SUCCESS! 
$ mysql -u root                                 
Welcome to the MySQL monitor. 
      (略)
mysql> 

MySQLへの接続も成功したようだ。釈然としない。

成功した理由を考えてみた

調べてみると、mysql.sockはMySQLサーバー起動時に自動的に作成されるファイルであり、
手動で作成する類のものではないらしい。
つまり、一番始めに発生した下記エラーは、mysql.sockを作成していないことが問題ではなく、
Can't connect:つなげない状態」であったことが問題であったと思われる。

$ mysql -u root
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

よって、mysql.sockがない状態でも、
PIDファイルを作成してサーバー起動を成功させることによって(=ここでmysql.sockが自動作成され)
MySQLへの接続も成功した、、、という理解でいいのだろうか。

では手動作成のmysql.sockは何が問題であったのか?

調べてみるとどうやら手動で作成したmysql.sockは権限に問題があるようだ。
検証のために再度手動でmysql.sockを作成し、
自動作成(サーバー起動時)されたmysql.sockと$ ls -lの出力を比較してみた。

#手動
-rw-r--r--  1 root            wheel   0  2 17 22:56 mysql.sock

#自動作成
srwxrwxrwx  1 xxxx(ユーザー名)  wheel   0  2 17 22:45 mysql.sock

なるほど違う。

手動で作成された方はrootユーザーのみが書き込みの権限を持っている。
書き込みの権限がないとうまくいかないので、(参考:MySQL の設定上の考慮事項)
rootユーザー以外での実行ができない状態となっている??

ならばと手動作成した上でrootユーザー権限でサーバー起動を試みてみる。

$ sudo mysql.server start   
Starting MySQL
. ERROR! The server quit without updating PID file (/usr/local/var/mysql/xxxx.pid).

だめかー。

では手動作成したものを自動作成と同じ権限に変えてみる。
これでダメな理由はないはず。

$ sudo chmod 777 mysql.sock  #権限を変更
$ sudo chown xxxx(ユーザー名) mysql.sock  #所有者も変更
$ mysql.server start                            
Starting MySQL
. SUCCESS! 

成功した!

手動でmysql.sockを作成した場合でも
権限周りを調整することによりサーバー起動ができることの確認がとれました。

まとめ

解決方法を色々とググりながら進めましたが、
「まずはじっくりとエラー分を解釈すること」の大切さを痛感しました。
またlinux等の知識が不足しており、後半の検証部分は気持ちとしてしこりが残っております。
(rootユーザー権限でサーバー起動できなかったあたり等)
今後の課題にするようにしたいと思います。

はじめての記事投稿でしたが、思考を整理するとても良い機会となりました。
誤り・認識違い、アドバイス等ご指摘いただけましたらとてもありがたいです。

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

RSpecを導入した時、詰まったこと

エラー内容

Rspecを使ったテストを実行する際、以下のようなエラーが発生。

An error occurred while loading ./spec/models/board_spec.rb.
Failure/Error: config.include FactoryBot::Syntax::Methods

NameError:
  uninitialized constant FactoryBot

行ったこと

参考
http://yurafuca.hatenablog.com/entry/2018/06/28/190842
https://qiita.com/Sa2Knight/items/90d32c9b8493d6bf94b3
・gemにrspec-railsを導入済み
・rspecの初期ファイルを作成済み

エラー原因

spec/rails_helper.rbの一番最初の行にいきなりconfig.include FactoryBot::Syntax::Methodsを記入してしまった。
下記が正しい書き方。

RSpec.configure do |config|
  # 中略
  config.include FactoryBot::Syntax::Methods
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecを導入した時、詰まったこと(NameError: uninitialized constant FactoryBot)

エラー内容

Rspecを使ったテストを実行する際、以下のようなエラーが発生。

An error occurred while loading ./spec/models/board_spec.rb.
Failure/Error: config.include FactoryBot::Syntax::Methods

NameError:
  uninitialized constant FactoryBot

行ったこと

参考
http://yurafuca.hatenablog.com/entry/2018/06/28/190842
https://qiita.com/Sa2Knight/items/90d32c9b8493d6bf94b3
・gemにrspec-railsを導入済み
・rspecの初期ファイルを作成済み

エラー原因

spec/rails_helper.rbの一番最初の行にいきなりconfig.include FactoryBot::Syntax::Methodsを記入してしまった。
下記が正しい書き方。

RSpec.configure do |config|
  # 中略
  config.include FactoryBot::Syntax::Methods
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

これがあれば忘れても大丈夫。gem 'devise' 使い方

基本機能

gem "devise"のインストール

Gemfile
# rails5系なら以下を記述
# For 5.0.x, 5.1.x and 5.2.x
gem 'rails-i18n', '~> 5.1'

gem 'devise'
gem 'devise-i18n'
gem 'devise-i18n-views'
gem 'omniauth-twitter'

deviseの初期設定

ターミナル
# gemをインストール
$ bundle install

# deviseのインストール
$ rails g devise:install

# 日本語化するためのymlファイルを作成 > devise.views.ja.ymlが生成される
$ rails g devise:views:locale ja

# ログイン、新規登録などのviewを取得 
$ rails g devise:views

# deviseのコントローラーのカスタマイズ
$ rails g devise:controllers users

# userモデルの作成
$ rails g devise user


日本語対応させる。

config/application.rb
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module app-name
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2
    config.i18n.default_locale = :ja # 日本語対応
    config.time_zone = 'Tokyo' # 日本時間
    config.active_record.default_timezone = :local # 「データベースに保存されている時刻の値がlocalの時間にする」

    # 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



新規登録のカラム追加

application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:username]) # ここにカラム名を追加する。paramsの定義は不要。
  end
end

サインインしていない場合、ログイン画面表示

〇〇_controller.rb
before_action :authenticate_user!

ログイン時とログアウト時で表示を変える

html.haml
%ul.float-right
    - if user_signed_in?
      %li 
        = link_to "マイページ", user_path(current_user.id), class:"btn login"
      %li 
        = link_to "ログアウト", destroy_user_session_path, class:"btn login"

    - else
      %li 
        = link_to "ログイン", new_user_session_path, class:"btn login"
      %li
        = link_to "新規登録", registration_users_path, class:"btn registration"

ユーザー情報を取得

html.haml
 %p current_user.name

current_userでユーザー情報を取得できます。

メール認証

のちほど追記予定

参考リンク

参考になるサイト
Railsを日本語にする
日本語にする
日本語にする参考2
メール認証

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

ActiveAdmin 画面遷移時にfilterのパラメーターをセットする

親要素から子要素に遷移する際, 親要素のidで絞りたい

これだけと何をいってるかわからないので

例)
Company : Shop = 1 : N のRelationの時,

Companyの画面から, Shopの一覧画面へ遷移する時,
そのままではデータが大きすぎるので,
最初からShopを外部キーのCompany_Idで絞っておきたい

  # カスタム アクション
  # 一度変数に入れないと, エスケープされずエラーとなる
  query = "q[company_id_equals]"
  action_item :my_button1, only: :show do
    link_to "店舗の一覧に遷移する", active_admin_shops_path({query => company.id})
  end

これだけで遷移と同時にfilterがかかります.
調べても載っておらず, 尚且つ需要があると思うので書きました.

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

Amazon Linux2でLet's EncryptするのをItamae化

はじめに

RailsアプリケーションのサーバーをItamaeで管理しているます。
環境構築時にELBを立てず、サーバー直接でアクセスさせる場合、サーバーへのSSL証明書の設定が必要になります。
サーバーはAmazon Linux2で、SSL証明書はLet's Encriptで取得するのをItamae化してみました。

nginx, Itamae, Certbotのインストール、構築は既に済んでいるものとします。

環境

  • サーバー
    EC2

  • OS
    Amazon Linux2

  • バージョン
    Rails: 5.x.x(複数プロジェクト)
    Ruby: 2.x.x

手動構築手順

参照:
https://qiita.com/MysteriousMonkey/items/4d3d857c0e68d4bfff39[https://qiita.com/MysteriousMonkey/items/4d3d857c0e68d4bfff39]

簡単に説明すると、下記コマンドを打つ

$ sudo certbot --nginx

すると、対話式でメールアドレス、対象ドメイン、規約への同意などなどの確認があったのち、SSL証明書が作成され、nginx.confに自動的に設定される.

その対話をコマンドオプション指定でなくすことができるかどうかが今回の争点!

結論

  • cookbooks/certbot/default.rb
execute 'download EPEL7' do
  user 'root'
  command "wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/"
end

execute 'install EPEL7' do
  user 'root'
  command "rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm"
end

execute 'enable EPEL7' do
  user 'root'
  command 'yum-config-manager --enable epel*'
end

package 'certbot'
package 'python2-certbot-nginx'


execute 'SSL証明書を作成' do
  user 'root'
  command "certbot --nginx -d #{node[:dns][:server_name]} --agree-tos --register-unsafely-without-email -n"
end

やったこと

  • Amazon Linux 2 でEPEL7を使用可能にする。
  • certbotとpython2-certbot-nginxをインストール
  • 証明書の作成
    • オプション
      • -d example.com # => DNSを指定。nginx.confのserver_nameと同じにする
      • --agree-tos # => 規約に同意
      • --register-unsafely-without-email # => メールを登録しない(非推奨)
      • -n #=> 対話をなくす

certbot自体のオプションはそのまま流用可能なようなので、registerする場合は

--email test@example.com

などの指定も可能。

これでめっちゃ手軽にLet's Encript生活!!

事後作業

証明書更新のCronの設定は適宜参照してください。===>> 上記URL

参考にさせていただいたサイト

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

Rails6 のちょい足しな新機能を試す 121(PostgreSQL index_exists?編)

はじめに

Rails 6 に追加された新機能を試す第121段。 今回は、PostgreSQL index_exists? 編です。
Rails 6 (と Rails 5.2.4.1) では、 index_exists? が正しく動作しないバグが fix されています。

Ruby 2.6.5, Rails 6.0.2.1, Rails 5.2.4.1, Rails 5.2.3, PostgreSQL 12.0 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)

$ rails --version
Rails 6.0.2.1

今回は、 name の属性を持つ User モデルを作り、インデックスを追加して確認してみます。

Rails プロジェクトを作る

Rails プロジェクトを新たに作成します。

$ rails new rails_sandbox
$ cd rails_sandbox

User モデルを作る

name 属性を持つ User モデルを作ります。

$ bin/rails g model User name

インデックスを追加する

今回は2つのインデックスを作成します。 lower(name) を指定したインデックスと、単純に name カラムを指定したインデックスです。

bin/rails g migration add_index_lower_name_to_users
db/migrate/20200207213234_add_index_lower_name_to_users.rb
class AddIndexLowerNameToUsers < ActiveRecord::Migration[6.0]
  def change
    add_index :users, 'lower(name)', name: 'index_lower_name'
    add_index :users, 'name', name: 'index_name'
  end
end

マイグレーションを実行する

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

bin/rails db:create db:migrate

rails console で確認する

rails c を実行します。

$ bin/rails c
Running via Spring preloader in process 344
Loading development environment (Rails 6.0.2.1)

indexes で登録されているインデックスを調べてみましょう。
(わかりやすいように表示は折り返してます。)

irb(main):001:0> User.connection.indexes(:users)
=> [
#<ActiveRecord::ConnectionAdapters::IndexDefinition:0x00005617bc6ef580 
@table=:users, 
@name="index_lower_name", 
@unique=false, 
@columns="lower((name)::text)",  # <=  ここに注目
@lengths={}, 
@orders={}, 
@opclasses={}, 
@where=nil, 
@type=nil, 
@using=:btree, 
@comment=nil>, 
#<ActiveRecord::ConnectionAdapters::IndexDefinition:0x00005617bc8c3be0 
@table=:users, 
@name="index_name", 
@unique=false, 
@columns=["name"],  # <= ここに注目
@lengths={}, 
@orders={}, 
@opclasses={}, 
@where=nil, 
@type=nil, 
@using=:btree, 
@comment=nil>
]

index_lower_name に対応する @columns'lower((name)::text)' で String であるのに対して、 index_name に対応する @columns["name"] とArray になっていることに注意してください。

index_exists? を使って index_name が存在することを確認します。

irb(main):002:0> User.connection.index_exists?(:users, "name", name: :index_name)
=> true

今度は、 index_lower_name が存在することを確認します。

irb(main):003:0> User.connection.index_exists?(:users, "lower((name)::text)", name: :index_lower_name)
=> true

Rails 5 では

Rails 5.2.4.1 では、 Rails 6 と同じ動作ですが、 Rails 5.2.3 では、 index_lower_name の存在を確認したとき false を返します。

irb(main):001:0> User.connection.index_exists?(:users, "name", name: :index_name)
=> true
irb(main):002:0> User.connection.index_exists?(:users, "lower((name)::text)", name: :index_lower_name)
=> false

試したソース

https://github.com/suketa/rails_sandbox/tree/try121_index_exists_of_postgresql

参考情報

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

自作したrakeタスクをrailsが認識してないときに疑うこと

発生した問題

rake タスクを作成して実行しようとしたら、そんなタスク知らん(Don't know how to build task 'xxxxx')と突っぱねられてしまいました。

$ bundle exec rails my_awesome_task
rails aborted!
Don't know how to build task 'my_awesome_task' (See the list of available tasks with `rails --tasks`)

確かに rails --tasks に挙がってない。

$ bundle exec rails --tasks | grep my_awesome_task
(該当なし)

なんとなく rake -T も試したけどやっぱり見つからない。

$ bundle exec rake -T | grep my_awesome_task
(該当なし)

以下がそのタスクです。

lib/my_awesome_task.rb
desc 'すごいタスク'
task :my_awesome_task do
  puts 'すごいぞー \(^o^)/'
end

チェック1:配置場所は正しいか?

(Rails.root)/lib/tasks 配下に置かないとダメでした。

修正前: lib/my_awesome_task.rb
           ↓
修正後: lib/tasks/my_awesome_task.rb

チェック2:拡張子は正しいか?

Rakeタスクの拡張子は .rake じゃないとダメでした。

修正前: lib/tasks/my_awesome_task.rb
           ↓
修正後: lib/tasks/my_awesome_task.rake

この2つを直したら無事に動きました。

ちなみに

タスクを作るときに rails generate task コマンドを使っていれば、こんな変なハマり方しなかったと思います。反省。

--

参考: カスタムRakeタスク | Railsガイド

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

ActiveRecord::PendingMigrationErrorの解決方法

1.エラーメッセージの内容

下記のようなエラーの場合の解決方法を記載します
スクリーンショット 2020-02-19 7.45.15.png

2.エラーの原因

エラーの理由はmigrateし忘れてますよというものです。
考えられる理由は下記の3つです

1.単純にrails db:createのあとrails db:migrateし忘れている
2.すでにmigrationが完了している(up)状態でマイグレーションファイルを更新しrails db:migrateしてしまった
3.マイグレーションファイルの中に入れた外部参照キーが参照するテーブルがない状態でrails db:creatしてしまった(マイグレーションファイルの記述に誤りがある)

3.エラーの解決方法

1.単純にrails db:createのあとrails db:migrateし忘れている

マイグレーション実行の下記のコマンドを打ち込みましょう

$ rails db:migrate

2.すでにmigrationが完了している(up)状態でマイグレーションファイルを更新しrails db:migrateしてしまった

方法1 既存のテーブルを全削除し、再度マイグレーションの状態をupにする下記のコマンドを打ち込みましょう

$ rails db:migrate:reset

方法2 マイグレーションの最新のものだけ実行を取りやめて編集可能(down)にするコマンドrails db:rollbackののち、マイグレーションファイルの更新コマンドrails db:migrateを打ちましょう

$ rails db:rollback
$ rails db:migrate

※方法1と方法2の違いは処理速度(工程)の量の違いです。テーブルの数が多い場合は方法2としましょう。テーブルが数十程度なら方法1でも2でも大差はありません

※マイグレーションの状態を調べるコマンドは下記の通りです

$ rails db:migrate:status

3.マイグレーションファイルの中に入れた外部参照キーが参照するテーブルがない状態でrails db:creatしてしまった

①-1外部参照されるマイグレーションファイルを作成し、app/modelの中のファイルにアソシエーションを追記(または修正)する
①-2マイグレーションファイルから不必要な外部参照キーを消す

②上記①のどちらかの修正のあとに下記コマンドを打ち込みましょう

$ rails db:migrate:reset

4.すでにデータベースにレコードを入れてしまい、それを残したまま修正する方法(補足)

新たに追加したい項目のテーブルを作成し、本来追加したかったテーブルのidを外部キーとする方法をとりましょう。

参考(railsガイド): https://railsguides.jp/active_record_migrations.html#%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B

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

RubyのMysql2の各種情報(バージョン情報やオプション)を取得する方法

背景

Ruby on Rails 等で MySQLを利用している時、低レイヤーの根深い問題や、よくわからないエラーに遭遇したことはありますでしょうか?

RubyでMySQLを利用する際によく使われるライブラリである Mysql2 では、C拡張を利用して libmysqlclient とリンクし、データベースサーバ上の mysqld に対してアクセスしている都合上、複数の概念に対してのバージョン情報やオプションを扱う必要があります。

これらのバージョン情報は、問題の原因究明に重要であるため、取得方法を知っておくことで、効率的な作業が可能になります。自分が調べる時は勿論、他の人に手伝ってもらう時にも重要なヒントになります。

TL; DR

以下のようなデータを記録しておくと、調査が捗ります。

# Railsの場合の Mysql2::Client のオブジェクトの取得方法の例
client = ActiveRecord::Base.connection.raw_connection
# 自前の場合の Mysql2::Client のオブジェクトの作成方法の例
client = Mysql2::Client.new(host: 'localhost', username: 'root')

puts JSON.pretty_generate(
  'RUBY_DESCRIPTION' =>  RUBY_DESCRIPTION,            # Rubyの詳細
  'Mysql2::VERSION' =>  Mysql2::VERSION,              # gemのバージョン
  'Mysql2::Client#info' => client.info,               # libmysqlclient のバージョン
  'Mysql2::Client#server_info' => client.server_info, # 接続先の MySQL Server のバージョン
)

# 接続に関するオプション (おまけ)
puts JSON.pretty_generate(client.query_options.reject { |k, _v| k == :password })  # 他にも重要な情報はあるが、最低限、パスワードは取り除く

出力例

{
  "RUBY_DESCRIPTION": "ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]",
  "Mysql2::VERSION": "0.4.10",
  "Mysql2::Client#info": {
    "id": 80016,
    "version": "8.0.16",
    "header_version": "8.0.16"
  },
  "Mysql2::Client#server_info": {
    "id": 50637,
    "version": "5.6.37"
  }
}

各種バージョン

Rubyの詳細情報

ruby -v の詳細が記述されます。

https://docs.ruby-lang.org/ja/latest/method/Object/c/RUBY_DESCRIPTION.html

gem のバージョン

gem のバージョンは、以下の Mysql2 自体のバージョン Mysql2::VERSION になります。

https://github.com/brianmario/mysql2

コードは以下のような形になっています。

https://github.com/brianmario/mysql2/blob/982dbdb1d521c668547cfd4ab927558c689b8bfd/lib/mysql2/version.rb#L2

module Mysql2
  VERSION = "0.5.3".freeze
end

libmysqlclient のバージョン

Mysql2 は、独自に MySQLサーバ との通信プロトコルを実装しているわけではなく、 MySQLの提供している、Cで書かれたクライアントライブラリを利用してアクセスしています。
この時のライブラリのバージョンによって、微妙な挙動の差異が発生する場合があるので、この情報も重要です。

Mysql2 では、以下のようにC拡張で Mysql2::Client#info が実装されています。

https://github.com/brianmario/mysql2/blob/f8560c551bf1999baf7df43290e8f89471e77af4/ext/mysql2/client.c#L948-L968

  version = rb_str_new2(mysql_get_client_info());

これが呼び出している mysql_get_client_info() が何を返すかは、MySQLのドキュメントに記述されています。

https://dev.mysql.com/doc/refman/5.6/en/mysql-get-client-info.html

Returns a string that represents the MySQL client library version (for example, "5.6.48").

ということで、クライアントライブラリ libmysqlclient のバージョンが返却されます。

接続先の MySQL Server のバージョン

問題が発生した時は、クライアントバージョンは勿論ですが、相手側のサーババージョンも気になるところです。

Mysql2 では、以下のようにC拡張で Mysql2::Client#server_info が実装されています。

https://github.com/brianmario/mysql2/blob/f8560c551bf1999baf7df43290e8f89471e77af4/ext/mysql2/client.c#L970-L994

  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));

これが呼び出している mysql_get_server_info() が何を返すかは、MySQLのドキュメントに記述されています。

https://dev.mysql.com/doc/refman/5.6/en/mysql-get-server-info.html

Returns a string that represents the MySQL server version (for example, "5.6.48").

ということで、接続先の MySQL サーバのバージョンが返却されます。

接続に関するオプション

MySQLのサーバ側で設定されているオプションも勿論大切ですが、各クライアント(コネクション)毎に維持されているオプションも、細かい挙動を知る上では重要になってきます。

Mysql2 では、以下のようにインスタンス変数へのアクセッサーが生やされて、 Mysql2::Client#query_options があるので、それを利用する形が良いと思われます。

※ このアクセッサーにはパスワードを含む情報が格納されているので、情報共有の際には、それをしっかり取り除くことを留意しておいてください。

https://github.com/brianmario/mysql2/blob/98304cbbc0c9a964c833474a06f55c433ec26432/lib/mysql2/client.rb#L3

参考

https://github.com/brianmario/mysql2
https://dev.mysql.com/doc/refman/5.6/en/c-api-server-client-versions.html

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

rails 短縮系コマンドについて

完全なコマンド 短縮形
$ rails server →$ rails s
$ rails console →$ rails c
$ rails generate→ $ rails g
$ rails test →$ rails t
$ bundle install→ $ bundle
表 3.1: Railsで使える短縮形の例

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

【Rails】ActiveStorageを使わずにGoogle Cloud Storage(GCS)を使う

概要

RailsでGoogle Cloud Storage(GCS)を使うとなると、ActiveStorageを使う方法が挙がりますが、ActiveRecordを使わないケース(*1)や、そもそもActiveStorageを導入するのが手間だったりすることがあると思います。
ので、ActiveStorageを使わず比較的手軽にGCSを使う方法を紹介します。

*1…2020年2月時点だと、ActiveRecordにしか対応してないみたいです。ActiveStorage and Mongo #31408

事前準備

サンプルの概要

以下のような処理を想定して、GCS使用部分のサンプルを書きます。

実装サンプル

バケットの取得

Exampleを参考にして、バケットを取得します。また、事前準備で取得したkeyファイルは、configフォルダ配下に配置しているものとします。

google_cloud_storage_util.rb
require "google/cloud/storage"

module GoogleCloudStorageUtil

  def getBucket()
    storage = Google::Cloud::Storage.new(
      project_id: "GCPのプロジェクトIDを入力",
      credentials: "config/test_key.json"
    )
    storage.bucket "事前準備で作成したバケット名を入力"
  end
end

ファイルの確認・削除

アップロードするファイルはパス(フォルダ名+ファイル名)で一意にするので、受け取ったファイル名で既に存在する場合は削除します。

google_cloud_storage_util.rb
  def deleteImageFile(fileName, bucket)
    file = bucket.file("test_folder/" + fileName)
    if !file.nil?
      file.delete
    end
  end

ファイルの追加

指定したパスにファイルを追加します。multipart/form-dataで受け取った場合は、tempfileで送信します。

google_cloud_storage_util.rb
  def addImageFile(imageFile, fileName, bucket)
    # tempfileを送る
    bucket.create_file(imageFile.tempfile, "test_folder/" + fileName)
  end

参照用URLの取得

アップロードしたファイルをパブリックで参照するためには、期限付きのURLを取得する必要があります。

google_cloud_storage_util.rb
  def getImageUrl(fileName, bucket)
    file = bucket.file("test_folder/" + fileName)
    if !file.nil?
      file.signed_url(method: "GET", expires: 60 * 60 * 24)
    else
      nil
    end
  end

参考記事など

google-cloud-storage API documentation
Google Cloud Storageのファイルを指定したファイル名でダウンロードしたい

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

画像アップロード機能実装について

目的

作成したアプリケーションに画像アップロード機能を実装するための手順や
考え方などを備忘録的に残していきます。

画像アップロードとは

アップロードとはファイルやデータをPCやスマホからインターネット上のサーバーへ転送することを指します。

画像アップロードの仕組み

  • ファイル選択ボタンを押して、画像を選択
  • 送信ボタンをおす
  • 画像ファイルを画像パスがリクエストして送信される
  • 画像ファイルはアクセス先コンピューター内に、画像のパスはDBないにあるテーブルに保存される (画像の保存先には外部のストレージを使うことが多い。AWSサービスのS3など..)

実装の流れ

ライブラリのインストール
Gemfile
gem 'carrierwave'
gem 'mini_magick'
bundle install
アプリの雛形を作成

scaffoldで必要なモデルやコントローラーなど一式を作成します。
モデル名はFeedとし、画像を保存するためにimageカラムを作成します。

$ rails g scaffold feed image:text
$ rails db:migrate
アップローダファイルを作成

アップローダファイルとは アップロードに関する設定 をするためのファイルです。
アップロードに関する設定とは以下が挙げられます。

  • 外部ストレージを連携するかどうか
  • 保存形式
  • 画像ファイルの保存先の設定
  • 画像サイズの調整
$ rails g uploader Image
app/uploader/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
   # MiniMagickをincludeすると画像サイズ調整ができるようになる
  storage :file # 保存形式の設定。他に使うものとしてはfog形式などがある。
  process :resize_to_limit => [50, 50] # 画像サイズの調整
  # 画像ファイルの保存先の設定
  # 保存先を指定するには `store_dir` というメソッドに定義します。
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

保存形式を指定しているのが storage :file という記述です。
file以外の保存形式として クラウドのストレージ が挙げられます。
クラウドのストレージとして、Amazonが提供している S3 というサービスが有名です。file形式は画像が実体として存在しますが、クラウドのストレージにおける画像は実体が存在しません。この場合は専用のgemを利用して storage :fog という記述をします。

最後に該当のモデルにカラムとアップローダを指定します。
Feedモデル内に以下を記述

app/model/feed.rb
~省略~
#アップローダ定義
 mount_uploader :image, ImageUploader
~省略~

上記の記述をすることで、feedsテーブルの中にあるimageカラムにimageUploaderという名前のアップローダ機能を追加することができます。

まとめ

アップローダ定義の意味を総括すると以下のようになります。

  • feedモデルが関係しているのでfeedsテーブルが関係している
  • mount_uploaderは、画像アップロードの宣言をしている
  • :imageはfeedsテーブル内の画像パスが入っているカラム名を指している
  • ImageUploaderは、ImageUploaderファイル内の設定を元にアップロードすることを意味している
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列で便利なメソッドを紹介するぞpart1

whereメソッド基本形

User.where(admin: true)

この場合、adminがtrueのユーザーを全て取得することができる。

whereメソッドの配列で指定

User.where(kind: [1,2])

この場合、kindが1、または2のユーザーを全て取得することができる。

where.notメソッド

User.where.not(id: 2)

この場合、idが2のユーザー以外を取得することができる。

whereメソッドで比較

User.where("kind" > "?", 1)

これでkindが1より大きいユーザーを全て取得することができる。

whereメソッドでorを使う

User.where('kind = ? or name = ?', 0, 'yamada')

この場合、kindが0かnameがyamadaのユーザーを全て取得することができる。

whereメソッドでandを使う

User.where('kind = ? and name = ?, 0, "yamada")

この場合、kindが0かつ、nameがyamadaのユーザーを全て取得することができる。

whereメソッドでメソッドチェーンを使う

User.where(kind: 0).where(name: 'yamada')

この場合、意味は先ほどのandと同じになる。

distinct uniqメソッド

これは配列の中で被っている要素をなくすメソッド。

Sample.select(:last_name,:first_name).distinct

#レコード件数を数える場合
Sample.select(:last_name).distinct.size
[1,1,2,2,3,3].distinct

=>[1,2,3]

こんな感じで使う。

uniqメソッドも使えるが、どうやら非推奨らしい。

sampleメソッド

sampleメソッドは配列の中からランダムで要素を取り出すメソッド。

配列.sample
配列.sample(2)

引数がない場合は1つだけ要素を取り出す。

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

Rubyの配列で使えるメソッド

Railsアプリで配列データを分けて出力しようとした時に色々試したので記録しとこうと思います。

students = ["Tom","Mike"],["Sam","Eric"],["Billy"],["Jorge","Nancy","Rick"]

適当に配列を用意しました。

flatten

入力
p students.flatten
出力
["Tom", "Mike", "Sam", "Eric", "Billy", "Jorge", "Nancy", "Rick"]

配列がくっつきます。

join

入力
p students.join
出力
"TomMikeSamEricBillyJorgeNancyRick"

これもくっつきます。

flattenのクラスはArray、joinの場合はstringです。

ただ作ってたアプリでは複数の配列からランダムに一つずつ出力したかったため、これらでは上手くいかなくて(なぜかflattenでくっつかなかった)

sample

入力
p students.sample(1)
出力
[["Jorge", "Nancy", "Rick"]]

sampleは配列の要素を1個(引数を指定した場合は自身の要素数を越えない範囲で n 個) ランダムに選んで返します。
この場合はランダムに配列を1つ出力しています。

入力
students.each do |student|
  p student.sample(1)
end
出力
["Mike"]
["Sam"]
["Billy"]
["Jorge"]

この場合は一度ループ処理をしているので各配列から一つずつ要素が出力されています。
これを応用してアプリの機能が実装できました。

配列面白いですよね。

参考

Ruby 2.7.0 リファレンスマニュアル

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

Rails6 シンプルなカレンダーにDBに格納されている予定を記載する

目的

環境

  • Rails version
    • 6.0.2.1
  • SQLite3
    • 1.4
  • OS
    • macOS 10.13.6

目標

  • http://localhost:3000/home/topにアクセスした時にカレンダーが表示される。
  • 表示されたカレンダーにDBのデータが予定として記載されるようにする。

公式の手順

条件

実施方法

  1. モデルの作成とテーブル作成

    1. 下記コマンドを実行してモデルを作成する。(string型のnameカラムとdatetime型のstart_timeカラムを保有したMeetingテーブル)

      $ cd アプリ名フォルダ
      $ rails g scaffold Meeting name:string start_time:datetime
      
    2. 下記コマンドを実行してテーブルを作成する。

      $ rails db:migrate
      
  2. 予定の格納

    1. 下記コマンドを実行してテスト用の予定を格納する。

      $ rails c
      irb(main):001:0> meeting = Meeting.new(name: "test", start_time: "2020-02-18 08:50:42")
      irb(main):002:0> meeting.save
      => true
      
  3. ビューの設定

    1. 下記のファイルを開く
      • アプリ名ディレクトリ/app/views/home
        • top.html.erb
    2. 下記の内容をtop.html.erbに記載する。(すでに記載されている内容は削除してから記載する。)

      <%= month_calendar events: @meetings do |date, meetings| %>
        <%= date.day %>
      
        <% meetings.each do |meeting| %>
          <div>
            <%= meeting.name %>
          </div>
        <% end %>
      <% end %>
      
  4. 表示の確認

    1. 下記コマンドを実行してアプリケーションをスタートする。

      $ cd アプリ名フォルダ
      $ rails s
      
    2. ブラウザでhttp://localhost:3000/home/topにアクセスし下記の画面が表示されるか確認する。2020年2月18日のカレンダーのセルに「test」と記載されていることを確認する。

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