20200316のRubyに関する記事は23件です。

【MySQL】プログラミング初心者が今まで遭遇したエラーとその解決方法のメモ

こんばんは!スージーです!
なかなか技術ブログが書けずにモヤモヤしていたので、ちょうど良いネタがあり備忘録も兼ねて。

MySQLに接続できないエラー

私は度々、MySQLに接続できずに四苦八苦しています。pidファイルないよーとかsocketないよーとか、その度に過去のメモや参考記事をあちこち探しているのでまとめてみようと思います。

参考

【遭遇率第1位】

Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

原因:
ざっくり言うとmysql.sockファイルが存在しないから作ってね
解決策:
/tmp直下にmysql.sockを作る

mysql.sockファイルがあるか確認
~ $cd /tmp
tmp $ ls
mysql.sockがない

tmp $ sudo touch mysql.sock
tmp $ cd
~ $ sudo mysql.server start
ERROR!~~~

startできずエラー発生したら
権限の書き換え(chownでファイルやディレクトリの所有者と所属グループを変更する)
~ $ sudo chown username /tmp/mysql.sock
もしくは権限付与
~ $ sudo chmod 777 /tmp/mysql.sock

もう一度mysql起動
~ $ sudo mysql.server start
...SUCCESS!

mysql.sockファイルの作り方は色々あると思いますが、私はこのエラーが起きた時はtouchコマンドで/tmp/ファイル直下にmysql.sockファイルを直接作ってしまいます。socketファイル作成しても権限がないと怒られる事もあるのでその時はchownコマンドかchmodコマンドで権限の書き換えか権限付与してあげて下さい。

【遭遇率第2位】

Can't connect to local MySQL server through socket '/tmp/mysql.sock' (38)

原因:
ざっくり言うとpidファイルが存在しないから作ってね
解決策:
/usr/local/var/mysql/直下にlocal.pidファイルを作る


~ $ sudo mysql.server start
Starting MySQL
… ERROR! server quit updating PID file(/usr/local/var/mysql/usernoMacBook-ea.local.pid)

pidファイルがあるか確認してみる
~ $ cd /usr/local/var/mysql
mysql $ ls
usernoMacBook-ea.local.pidがない

mysql $ sudo touch usernoMacBook-ea.local.pid
mysql $ cd
~ $ sudo mysql.server start
...ERROR!~~/usr/local/var/mysql/username.local.err: Permission denied
権限ないよと怒られたら先ほどのエラーと同様に権限の書き換え(chownでファイルやディレクトリの所有者と所属グループを変更する)
~ $ sudo chown /usr/local/var/mysql/usernoMacBook-ea.local.pid
もしくは権限付与
~ $ sudo chmod 777 /usr/local/var/mysql/usernoMacBook-ea.local.pid

もう一度起動してみる
~ $ sudo mysql.server start
...SUCCESS!

先ほどと同様にやり方は色々あると思いますが、pidファイルもtouchコマンドで作ってあげましょう。mysql.sockのエラーと同様に権限ないよって怒られたら権限の書き換えか権限付与してあげましょう。

ここまでのまとめ

ここまで紹介したmysql.sockないよ、~.local.pidないよ、権限ないよ、がMySQL関連エラーの大半を占めていると思います。「/tmpディレクトリってどこ!?」とか「/usrディレクトリなんて知らないよ」って最初は思いましたが、まずはファイルが無い事を確認する為にcdコマンドで該当ディレクトリまで潜って見てみましょう。

【遭遇率第3位】

mysql立ち上がった!と思ったらsafeモードのプロセスが既に存在してますよっていうエラー

原因:
既にMySQLのプロセスが動いている
解決法:
processをkillする

~ $ sudo mysql.server start
Starting MySQL
 SUCCESS! 
~ $ 200315 13:51:58 mysqld_safe A mysqld process already exists

動いているMySQLプロセスを確認してみる
~ $ ps aux| grep mysqld
user       98762  0.0 0.0 4268296  672 s004 S+  2:00PM  0:00.01 grep mysqld
user       98633  0.0 5.4 4929884 450652  ?? S   1:51PM  0:00.46 /usr/local/opt/mysql@5.6/bin/mysqld --basedir=/usr/local/opt/mysql@5.6 --datadir=/usr/local/var/mysql --plugin-dir=/usr/local/opt/mysql@5.6/lib/plugin --log-error=usernoMacBook-ea.local.err --pid-file= usernoMacBook-ea.local.pid
user       98536  0.0 0.0 4281052  1128  ?? S   1:51PM  0:00.03 /bin/sh /usr/local/opt/mysql@5.6/bin/mysqld_safe --datadir=/usr/local/var/mysql ←注目
mysqld_safeプロセスが動いている模様

プロセスをkillする
~ $ sudo kill -9 98536
Password:

一度mysqlを停止する
~ $ mysql.server stop
Shutting down MySQL
.. SUCCESS! 

もう一度MySQLを起動してみる
~ $ mysql.server start
Starting MySQL
. SUCCESS!

これはこの前初めて遭遇したエラーです。頻出エラーより遭遇確率は低いと感じますが、grepしてmysql_safeのプロセスが動いているか確認しましょう。mysql_safe以外のプロセスをkillしてもエラー解消しなかったので悪さしているmysql_safeを見つけてあげましょう。

【番外編】

過去に一度でも5.7を起動してしまうと、同じデータを使って5.6を起動すると今までの方法では解決できない場合がある。

この状況は過去に一度dockerでMySQL@5.7で起動した後に開発環境のMySQL@5.6を起動した時に起きた事があります。

この状況になったらMySQLをアンインストールする方法があります。ただし、開発環境で大切なデータがDBに残っている場合は何らかの方法で退避させてあげて下さい(退避方法は割愛します。実践した事ないです)。私は幸いにも開発環境に貴重なデータはないので躊躇無くアンインストールしました。

mysqlをアンインストールします
~ $ brew remove mysql
~ $ brew cleanup
以下のディレクトリ下にはmysql関連ファイルが作成されているので念の為、綺麗に削除しておきます
~ $ sudo rm -rf /usr/local/mysql
~ $ sudo rm -rf /Library/StartupItems/MYSQL
~ $ sudo rm -rf /Library/PreferencePanes/MySQL.prefPane
~ $ sudo rm -rf /Library/Receipts/mysql-.pkg
~ $ sudo rm -rf /usr/local/Cellar/mysql*
~ $ sudo rm -rf /usr/local/bin/mysql*
~ $ sudo rm -rf /usr/local/var/mysql*
~ $ sudo rm -rf /usr/local/etc/my.cnf
~ $ sudo rm -rf /usr/local/share/mysql*
~ $ sudo rm -rf /usr/local/opt/mysql*

最後にver5.6をインストール
~ $ brew install mysql@5.6

インストールが完了したらパス通す
$ echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.bash_profile

.bash_profileの変更を反映
$ source ~/.bash_profile

MySQLを起動
~ $ sudo mysql.server start
. SUCCESS!

アンインストールしても過去のmysqlファイルがなぜか残っている場合があるので面倒ですけど rmコマンドで該当該当ファイルを削除してあげます。完全にクリーンになったMySQLならちゃんと起動してくれます。

【番外編2】

rails new project-name -d mysql→ Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.21.dylib (LoadError)

原因:
mysqlのライブラリーがロードできませんよ
対策:
mysql2をアンインストールしてbundle install

project-name $ bundle exec gem uninstall mysql2
project-name $ bundle install
.
..
...
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/akito/git/media/vendor/bundle/gems/mysql2-0.4.4/ext/mysql2
/Users/akito/.rbenv/versions/2.3.1/bin/ruby -r ./siteconf20160929-55293-192vx35.rb extconf.rb
checking for ruby/thread.h... yes
checking for rb_thread_call_without_gvl() in ruby/thread.h... yes
checking for rb_thread_blocking_region()... no
checking for rb_wait_for_single_fd()... yes
checking for rb_hash_dup()... yes
checking for rb_intern3()... yes
-----
Using mysql_config at /usr/local/bin/mysql_config
-----
checking for mysql.h... yes
checking for errmsg.h... yes
checking for mysqld_error.h... yes
-----
Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load
-----
-----
Setting libpath to /usr/local/Cellar/mysql/5.7.14/lib
-----
creating Makefile

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /Users/akito/git/media/vendor/bundle/extensions/x86_64-darwin-15/2.3.0-static/mysql2-0.4.4/mkmf.log

current directory: /Users/akito/git/media/vendor/bundle/gems/mysql2-0.4.4/ext/mysql2
make "DESTDIR=" clean

current directory: /Users/akito/git/media/vendor/bundle/gems/mysql2-0.4.4/ext/mysql2
make "DESTDIR="
compiling client.c
compiling infile.c
compiling mysql2_ext.c
compiling result.c
compiling statement.c
linking shared-object mysql2/mysql2.bundle
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/akito/git/media/vendor/bundle/gems/mysql2-0.4.4 for inspection.
Results logged to
/Users/akito/git/media/vendor/bundle/extensions/x86_64-darwin-15/2.3.0-static/mysql2-0.4.4/gem_make.out

An error occurred while installing mysql2 (0.4.4), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.4.4'` succeeds before bundling.

盛大に怒られている...

以下の参考記事の通りにやってみます

RailsプロジェクトでMySQLがbundle installできなかった
https://qiita.com/akito19/items/e1dc54f907987e688cc0

project-name $ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include"
project-name $ bundle install 
.
..
...
Bundle complete! 18 Gemfile dependencies, 78 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
     run bundle exec spring binstub --all
* bin/rake: Spring inserted
* bin/rails: Spring inserted
正常にbundle install完了したみたい

project-name $ rails db:create
Created database ‘project-name_development’
Created database ‘ project-name_test’
project-name $ rails db:migrate
project-name $ rails s
=> Booting Puma
=> Rails 5.2.4.1 application starting in development 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.4 (ruby 2.5.1-p57), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
きたきたきたーーーー!!!!

localhost:3000にアクセスすればいつものrails初期画面が表示されます。MySQLエラーのドツボにハマってrails sでサーバ起動できた時は快感ですね。

【忘れがちなコマンド覚書き】

sudo mysql.server start

→MySQLを立ち上げます

sudo mysql.server stop

→MySQLを停止します

sudo mysql.server restart

→MySQLをstop&startします

sudo mysql.server status

→MySQLの状態を確認します

ps aux| grep mysqld

→MySQLのプロセスを探します

まとめ

以上、私が遭遇したMySQLエラーのまとめでした。自分で何度も遭遇して解決しているうちに遭遇するパターンが同じだったので一覧で見れるようにしておけば後から振り返る時に便利だなと思い久々にQiitaを書いてみました。今回は初めてipadでこの記事を全部書いてみましたが、以外と快適に書けたのでipadとsmart keybord買った甲斐がありました。

終わり

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

railsタグ検索機能(railsチュートリアル後の機能追加)

はじめに

今回は前回追加した、タグ付機能を利用してタグ検索機能を追加します。
前回の記事(タグ付機能)
https://qiita.com/E6YOteYPzmFGfOD/items/bfffe8c3b31555acd51d

作るもの

マイクロポストタグ付機能を利用したタグ検索機能。(題材は自分のポートフォリオです。)

対象読者

railsチュートリアルに機能を追加したい等自分と同じ位のレベルの人を対象としています。

作成の流れ

1.検索フォームの作成
2.コントローラーのアクション作成

1.検索フォームの作成

今回は検索フォームをroot_path上に設けます。(自分のポートフォリオがマイクロポストの一覧をroot_path(static_pages/home)に設けているため)

app/views/static_pages/home
<%= form_with url: root_path, method: :get, class: '自由に' do %>
  <%= select_tag :tag_id,
    options_from_collection_for_select(Tag.all, :id, :name, params[:tag_id]),
    {
      prompt: 'タグで絞り込み',
      class: ,'自由に'
      onchange: 'submit(this.form);'
    }%>
<% end %>

fomr_withを使って検索フォームを作ります。各指定している値を自分なりに説明します。

url: 検索した後に移動するページ。(getメソッド後の移動するページ)
method::get  検索時はゲットメソッドを使用する。(root_urlにゲットアクションを起こすのでstatic_page_contorollerのhomeアクションに飛びます。

select_tag: フォームの中でセレクトタグを表示するためのメゾット
select_tag:オブジェクト名(tag_id)URLのクエリ(検索するとURLの?以降に表示されてます)
options_from_collection_for_select(
第1引数:オブジェクトのリスト(Tag.all全件表示)
第2,3引数:取得したリストの表示。順番が大切でid,nameの順にすることによりタグnameを表示する。(idを基準に表示する。逆の順番にするとidの数字だけが表示される。)
第4引数:検索後に表示する値。params[:tag_id]とすることによりURLのクエリからidを取得して検索後もフォームにタグ名を表示します。(第2、第3の引数の値が重要です。)

{promt:は何も選択していないときに表示される値です。
onchange:submit(this.form)はJavaScriptを実行できるものでセレクトボックスが選択されたときにこの検索がスタートするようにしています。
}

続いてコントローラーです。

app/controllers/static_pages_controller.rb
@microposts = params[:tag_id].present? ? Tag.find(params[:tag_id]).microposts : Micropost.all
※ページネーション等に渡してください

タグ検索が実行されるとparamsでURLのクエリ(tag_id)を取得してfindでテーブルの中を探し関連付けしているのでmicropostsで関連するマイクロポストを取得します。検索されていない場合は全てのマイクロポストを取得するようにしています。

おまけ

URLのクエリとはURL上に表示されている?以降のやつです。
なんちゃら.com/?utf8=✓&tag_id=1
paramsメゾットではgetメゾット時にURLのクエリを取得できます!
※ログを見るとこんな記述もあります。
Parameters: {"utf8"=>"✓", "tag_id"=>"2"}

以上でタグ検索機能は完成となります。最後までお読みいただきありがとうございました。
アドバイス等いただけるととても喜びます。次はマイクロポストの検索機能を記事にしたいと思います。
ありがとうございました。

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

二次元配列の格納方法

こんちは!
今日はrails上で二次元配列の格納方法を記載します。

理由

この内容が容易に見当たらなかったため、他の方の助けになればと思います。

一次元配列の格納方法

イメージとしては、[1,2,3,4,5,6]
という風に格納したい場合、railsではコントローラーに以下のように記載するとできます。

tests_contoroller.rb
def idex
  @test = [] #配列の宣言

  6.times do |i = 0| #6回読み込ませる、そしてiには0を格納している。iは初期化式
    @test[i] = i + 1 #初期化式で宣言したiに1足した数字を各配列に代入している。
    i += 1 #iに1を足してはじめに戻る。何もしないままはじめに戻ると同じ配列[0]を六回繰り返すだけだから
  end
  # 配列[i]になぜ1を足さないかは、配列の初期値は[0]だから

二次元配列の格納方法

次は二次元配列です。
イメージとしては[[1,1],[2,1],[3,1]]
と格納したい場合は以下の通りになります。

tests_contoroller.rb
def idex
  @test = [] #配列の宣言

  3.times do |i = 0| #3回読み込ませる、そしてiには0を格納している。iは初期化式
    @test[i] = [i + 1,1] 
    i += 1 
  end

@test[i] ←の部分は、一緒です。
その配列の中に、さらに配列を入れるイメージです。
|1|1|
|2|1|
|3|1|

これを使った応用で、以下のようなコードが表示が実現できます。

<select name="exp_year" id="exp_year"><option value="">--</option>
<option value="2020">20</option>
<option value="2021">21</option>
<option value="2022">22</option>
<option value="2023">23</option>
<option value="2024">24</option>
<option value="2025">25</option>
<option value="2026">26</option>
<option value="2027">27</option>
<option value="2028">28</option>
<option value="2029">29</option>
<option value="2030">30</option></select>

HTML上表示したいものは、1つめの配列に格納されています。
valueの値は、2つめに格納したものがです。

格納例としては、以下の通り
|20|2020|
|21|2021|
|22|2022|
|23|2023|

これらは以下のコードをコントローラー側に入力すると、vew側で表示でます。

tests_controller.rb
  def index
    year = 2020
    @year = []
    11.times do |i = 0|
      @year[i] = [year - 2000,year]
      year += 1
      i += 1
    end
  end

vewには以下の一文追加してね。

tests/test.html.haml
= select_tag 'exp_year',options_for_select((@year),class: "exp_year"),prompt: "--"

追加したら以下のように見れるはずです。(CSSは適当に付けてみるとよいでしょう)
Screenshot from Gyazo

以上です!
またこう言った、見当たらなかったものをあり次第、アップしていきます!

ほなねー。

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

Progate無料版をやってみる【Ruby on Rails5 II】

前回の続きになります。

Ruby on Rails5 IIになります。
railsの無料分は今回のレッスンまでになります。

Ruby on Rails5 II

公式レッスン

投稿一覧ページを作成しよう

・前レッスンと同様に

rails g controller posts index

posts → コントローラー
index → アクション

posts.html.erb
posts.scss
posts_controller

が自動で作成され

routes.rbにget 'posts/index'が記述される。

変数を使って表示しよう

・拡張子.erbは<% %>で囲むことで、サーバーサイド処理のように書ける。

変数定義、代入

<% post = "test" %>

変数の中身を表示

<%= post %>

each文で表示しよう

・配列を定義して、each文で要素を一つずつ表示することが可能
eachの初めに<% %>を使用して終わりにも<% %>使用する。煩雑化しそうですね。

アクションで変数を定義しよう

・アクション内で定義することが普通。
・@を先頭に付けることで、アクションで定義した変数がViewで使用できる。

データベースを用意しよう・テーブルを用意しよう・モデルを確認しよう

・ターミナルでテーブルも作成できちゃう・・・。
rails g model Post content:textrails db:migrate
app/models/post.rbが自動生成(モデルクラス)

rails consoleを使ってみよう

・ん、ターミナル上にコンソール!?

テーブルに投稿データを保存しよう

・上のコンソールはdbに保存する際に必要となる模様。

post = Post.new(content: "テスト")`
post.save

んーかなり独特ですね。

テーブルから1つのデータを取り出す

・んー独特すぎてやばい。
https://shuheitakada.com/rails-database-check
のように確認したい・・・。

テーブルから全てのデータを取り出そう

posts = Post.all(0)

データベースのデータを表示しよう

・Post.allをアクションの@postsに代入する
 View側はpost.contentにする
 すると画面表示でDBから持ってきた値を表示できる。

共通のレイアウトをまとめよう

・共通のHTMLはviews/layouts/application.html.erbに記載する。

link_toメソッド

・aタグを使用せずとも<%= link_to('文字', 'パス')の形でリンクを実現できる。

クリアしました
image.png

感想

・railsならではのターミナルからのコマンド
・rails consoleのところはいまいちピンときていない・・・。
 大規模なDBを作成する際もこれでやるのだろうか・・・。

Railsの無料版はここまでなので、次回はPHPをやっていこうと思います。

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

Twitterログイン機能を搭載したRailsアプリを作る

Twitterログイン機能について学習するため、簡素なテストアプリを作成してみることにしたので、その一連の流れを記しておきます。

大きな流れとしては以下のようになります。

  • アプリを作成する
  • gem「Devise」を導入
  • Twitterにアプリを登録し、key情報を取得
  • gem「dotenv-rails」を導入して、セキュリティ強化
  • Twitterログイン機能の実装

それでは順番に解説していきます!

アプリを作成する

まずはrails newでアプリを作成します。

$ rails new twitter-login-app

作成したアプリのディレクトリへ移動します。

$ cd twitter-login-app

「Devise」を追加

ログイン機能を使用するため、Deviseというgemを使用します。

Gemfile
gem 'devise'
$ bundle install

Deviseをインストールします。

$ rails g devise:install
Running via Spring preloader in process 807
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml

Deviseで使用するViewファイルを作成します。

$ rails g devise:views
Running via Spring preloader in process 843
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/devise/shared
      create    app/views/devise/shared/_error_messages.html.erb
      create    app/views/devise/shared/_links.html.erb
      invoke  form_for
      create    app/views/devise/confirmations
      create    app/views/devise/confirmations/new.html.erb
      create    app/views/devise/passwords
      create    app/views/devise/passwords/edit.html.erb
      create    app/views/devise/passwords/new.html.erb
      create    app/views/devise/registrations
      create    app/views/devise/registrations/edit.html.erb
      create    app/views/devise/registrations/new.html.erb
      create    app/views/devise/sessions
      create    app/views/devise/sessions/new.html.erb
      create    app/views/devise/unlocks
      create    app/views/devise/unlocks/new.html.erb
      invoke  erb
      create    app/views/devise/mailer
      create    app/views/devise/mailer/confirmation_instructions.html.erb
      create    app/views/devise/mailer/email_changed.html.erb
      create    app/views/devise/mailer/password_change.html.erb
      create    app/views/devise/mailer/reset_password_instructions.html.erb
      create    app/views/devise/mailer/unlock_instructions.html.erb

Deviseを使用するモデルを作成します。

$ rails g devise user
Running via Spring preloader in process 886
      invoke  active_record
      create    db/migrate/20200316050323_devise_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert    app/models/user.rb
       route  devise_for :users

$ rails db:migrate

これでDeviseに対応したuserモデルが作成されました!

Twitterにアプリを登録し、key情報を取得

Twitterログインを実装するため、Twitter Developersへアプリケーション情報を登録します。
未承認の方は申請の手続きからになります。

以下の記事が参考になるかと思います。

Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報

登録の際、webサイトURLを指定する箇所を「localhost:3000」などローカル環境のアドレスにするとエラーが表示され登録することができないので、適当なアドレスを入力しておきます。

callback urlには、http://localhost:3000/users/auth/twitter/callback と追加し、「Allow this application to be used to Sign in with Twitter」のチェックボックスにチェックを入れておきます。

スクショ.png

アプリケーション作成後、API keyとAPI secret keyの情報を取得しておきます。

「dotenv」を導入して、セキュリティ強化

Twitetrから取得したAPI keyやAPI secret keyの情報をGitなどに公開してしまうと、悪意のある第三者に利用され、損害が発生してしまうリスクがあります。

そのためこうした知られたくない情報は、非公開な情報として扱う必要があります。

そうした時に便利なのが、「dotenv」というgemです。

Gemfile
gem 'dotenv-rails'
$bundle install

その後、Gemfileと同じ位置に「.env」を作成し、先ほどのkey情報を記述します。

.env
# Using Twitter API
TWITTER_API_KEY=API key
TWITTER_SECRET_KEY=API secret key

続いて「.gitignore」を編集します。
ここにファイル名を追加することで、特定のファイルをGitの管理対象から除外することができます。

.gitignore
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore uploaded files in development
/storage/*
!/storage/.keep

/node_modules
/yarn-error.log

/public/assets
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key
.env  #ここを追記

Twitterログインの実装

Twitterログインは「Oauth」という認証機能を使用します。

まずは必要なgemをインストールします!

Gemfile
gem 'omniauth'
gem 'omniauth-twitter'

gemをインストールします。

$ bundle install

先ほど追加したuserモデルにカラムを追加します。

$ rails g migration AddColumnsToUsers uid:string provider:string
Running via Spring preloader in process 91573
      invoke  active_record
      create    db/migrate/20200316073244_add_columns_to_users.rb

$ rails db:migrate

config/initializers/devise.rbを編集します。

config/initializers/devise.rb
Devise.setup do |config|

  #<省略>

  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'

  config.omniauth :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_SECRET_KEY']

 #<省略>
end

userモデルに:omniauthableを追加します。

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

続けて以下のメソッドを追加します。
ログインしたアカウントを確認し、初めてログインする場合は新しくアカウントを作成するメソッドになります。

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

  def self.find_for_oauth(auth)
    user = User.where(uid: auth.uid, provider: auth.provider).first

    unless user
      user = User.create(
        uid:      auth.uid,
        provider: auth.provider,
        email:    User.dummy_email(auth),
        password: Devise.friendly_token[0, 20]
      )
      user.save!
    end

    current_user = user
  end

    private

      def self.dummy_email(auth)
        "#{auth.uid}-#{auth.provider}@example.com"
      end
end

コールバック処理を行うため、app/controllers/users/omniauth_callbacks_controller.rbというコントローラーを作成します。

app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def twitter
    callback_from :twitter
  end

  private

  def callback_from(provider)
    provider = provider.to_s

    @user = User.find_for_oauth(request.env['omniauth.auth'])

    if @user.persisted?
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user, event: :authentication
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
end

ルーティングを追加します。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end

ログインページ作成

Twitterログイン元のページを作成します。

$rails g controller login index

app/views/login/index.html.erbに、Twitterログインのリンクなどを追加します。

ログイン時はログアウトのリンクを、ログアウト時はログインのリンクを表示できるようにするため、sessionコントローラーを作成します。

$rails g controller session destroy

sessionコントローラーを以下のように変更します。
ログイン時に保存されるsession情報を削除し、rootにリダイレクトする処理です。

sessions_controller.rb
class SessionsController < ApplicationController
  def destroy
    reset_session
    redirect_to root_path
  end
end

ログイン成功時は「Hello,world!」の文字が表示されるようにします。

views/login/index.html.erb
<% if user_signed_in? %>
  <%= link_to 'Twitter Logout', sessions_destroy_path %>
<% else %>
  <%= link_to 'Twitter Login', user_twitter_omniauth_authorize_path %>
<% end %>

<% if user_signed_in? %>
  Hello, world!
<% end %>

ルーティングを追加します。

config/routes.rb
Rails.application.routes.draw do
  root 'login#index'
  get 'sessions/destroy'

  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

実際にログインしてみる

まずはサーバーを起動します。

$cd twitter-login-app
$rails s

localhost:3000にアクセス・・・

スクリーンショット 2020-03-16 17.42.41.png

スクリーンショット 2020-03-16 17.43.01.png

無事ログインでき、リンク表示もログアウトになっています!

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

ここから始めます。目標をぶち上げる。

はじめに

えー、どうも、圧倒的初心者。NSパンダまんです。
今回はProgateにて基本的な勉強を終了した完全未経験くそごみの私が、今年の8月か9月までに本気でモダンなイケてるナウいポートフォリオを作り、それを引っさげて渋谷のWEB系自社開発企業に内定をもらうことを目標としたサクセスストーリーブログです。
あと、学んだ言語ですが、一応、、、

学習教材が多い
短い期間でアプリケーションが作れる。(らしい。実際作ってないから知らん。)
日本人が創り上げた由緒正しき言語である(日本語での問題解決のテキストも多い)
これらの点からファーストチョイスはRubyを選択しました。
Rubyで基礎を深め、そこから他の言語も学んでいこうって寸法です。

あと,,,もしこれをみている変わり者なあなた!!

もしあなたが現在プログラミングを学習していて私と同じようなナウい目標を掲げているのなら、、なんかコメントください。
なんでもいいんです。独学でやってるから寂しいんです。寂しがりやなんです。

ここでは私とおんなじような目標をたてて頑張っている方と共に目標に向かって頑張って行きたい。そんな願いも込めて書いているのです。

*なお初心者でありインターネットのイの字もわかっておりませんので訳のわからんことを書いていると思ったらばしばしと叩いていただければと思います。コメント欄にベギラゴン唱えても可です。

progateを一通り学び終え、さあ次なにするかとひたすら考えておりました。
progateをやっているうちはどんどんと湧き出てくる新しい知識をひたすらインプットするだけで良かったのでかなり思考停止していたと思います。

それが終わったとき、ふと考えてしまったのです。
「あれっこれ次どうすりゃいいの?」ってね。

部活の県大会とかで負けた高校生みたいなもんです。完全に方向性を失いました。

それで色々調べたんですけどなんか、、、よくわかんないんですけど
「RAILS チュートリアル」
ってのを発見したのです。

最初なんかの本なのかなって思ってAmazonで調べまくりました。でもなんかサイト?電子書籍?みたいな感じみたい。

んで必死の思いで探して。。。(ググったら一発ででました)
見てみたんですけど。。。。。

「えっなにこれくっそ読みづらくね??しかも今まで散々インプットしてきたのにまたインプット?お兄さん疲れちゃったよ。もう簡単なアプリ作りたいよ!!」

と正直思いました。やる気になりませんでしたね。あと、なんか外国の方が作っているのかな?それを翻訳した感じでなんか読みづらい。文系のくせに読解能力に疎い私は簡単に2,3分パラパラとページをみて諦めました。笑

とりあえずチュートリアルをすっ飛ばして簡単なアプリ作る方針に決めました。
そこでわからないことあったらまた戻ってインプットすりゃあええと。

というわけで次回は環境構築について書いていきたいと思います!!
それでは!!!!

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

【Ruby】Nokogiriでカクヨムの作品をダウンロード

前置き

カクヨムというサイトがあります。これは小説家になろうとかノベルアップ+とかハーメルンのような1所謂「小説投稿サイト」の一つです。
私は今月にコミカライズが連載開始予定の名作・超世界転生エグゾドライブ -激闘!異世界全日本大会編-をオフライン保存して読もうと思ったのですが、カクヨムの公式は小説家になろうのように第三者向けにダウンロードの手段を用意してません。
そこで、作品ページを直接スクレイピングして保存することでダウンロードを実現したので、それに使ったプログラムをメモついでに紹介します。

注意

  • Rubyは殆ど初めて触るので、至らない点も多々あると思われます
  • まずないと思いますがこのプログラムを使う場合、自己責任でお願いします。
  • 保存したデータは私的利用に留めましょう。他人への譲渡・販売は違法です。

準備

この記事等を参考に、Nokogiriをインストールしておいてください。

作る

入力を受け取る

特段延べるべきこともなく、普通に受け取ります。

Scrape.rb
require 'nokogiri'
require 'open-uri'

puts "目次のURLを下さい"
get = gets.chomp
if ! /https:\/\/kakuyomu.jp\/works\/\d{19}/ === get #入力された文字列がURLの型に合うかを正規表現でチェック
    while ! /https:\/\/kakuyomu.jp\/works\/\d{19}/ === get
        puts "目次のURLを下さい"
    end
end

もうちょっと短縮できたと思うんですが、どうもエラーが連続しまして…

目次を取得する

カクヨムのURLに関するシステムは、こういう局面においては少しばかり面倒臭いものです。
仮にこれが小説家になろうとかハーメルンとかだったら、ユニークIDは各作品に1つずつ割り振られる他には存在しないため、URLから直接ダウンロードを開始できたでしょう。

小説家になろうにおける話数の増え方(例:「シャングリラ・フロンティア~クソゲーハンター、神ゲーに挑まんとす~」)
第一話:https://ncode.syosetu.com/n6169dz/1/
第二話:https://ncode.syosetu.com/n6169dz/2/
第三話:https://ncode.syosetu.com/n6169dz/3/
第四話:https://ncode.syosetu.com/n6169dz/4/
第五話:https://ncode.syosetu.com/n6169dz/5/

ですが、カクヨムやノベルアップ+だとそうはいきません。恐らくエピソードの挿入との兼ね合いだと思われるんですが、こいつらは「各作品」とはまた別に「各エピソード」にもユニークIDを設定しているんです。

カクヨムにおける話数の増え方(例:「超世界転生エグゾドライブ -激闘!異世界全日本大会編-」)
第一話:https://kakuyomu.jp/works/1177354054884850859/episodes/1177354054884851015
第二話:https://kakuyomu.jp/works/1177354054884850859/episodes/1177354054884853763
第三話:https://kakuyomu.jp/works/1177354054884850859/episodes/1177354054884862727
第四話:https://kakuyomu.jp/works/1177354054884850859/episodes/1177354054884878794
第五話:https://kakuyomu.jp/works/1177354054884850859/episodes/1177354054884886288

そういうわけで、カクヨムでは小説家になろうみたいに作品のユニークIDの後ろにつける数字を1ずつ増やしていくだけではダメでして、先に目次を取得する必要があります。
ということで、次のようなコードでぱぱっと取得します。

Scrape.rb
require 'nokogiri'
require 'open-uri'
#(略)
doc = Nokogiri::HTML(URI.open(get))
doc.xpath("//a[@class='widget-toc-episode-episodeTitle']").each do |url|
    urls.push(url[:href]) #配列「urls」に目次を格納
end

ディレクトリ周りのあれこれ

プログラムと同じ階層にテキストデータを保存するのはぐちゃぐちゃになる未来が目に見えている選択肢なので、フォルダを新しく作ってその中にダウンロードしたデータを格納しましょう。

Scrape.rb
require 'nokogiri'
#(略)
if ! Dir::exist?("Kakuyomu_DL_Data") #既にフォルダを作成済みか判定
    Dir::mkdir("Kakuyomu_DL_Data")
end

そして、作ったフォルダ「Kakuyomu_DL_Data」の下に更にフォルダを作ります。カクヨムのタイトルは被ることもあったと思うので、万が一のフォルダ名の重複を避けるために変化をつけるようにしています。

Scrape.rb
#(略)
Dir_Path = "Kakuyomu_DL_Data/" + doc.xpath("//h1[@id='workTitle']").inner_text #先ほど読み込んだ目次から作品タイトルを取得
if Dir.exist?(Dir_Path)
    count = 1
    while Dir.exist?("#{Dir_Path}(#{count})") do
        count += 1
    end
    Dir_Path << "(#{count})"
end
Dir::mkdir(Dir_Path)

各エピソードにアクセスしてダウンロード

いよいよ本文のダウンロードのフェーズです。先ほど作った作品専用フォルダの下に、テキストデータとして一話ずつ作品を保存します。

Scrape.rb
#(略)
count = 0
size = urls.size
urls.each do |url|
    count += 1
    doc = Nokogiri::HTML(URI.open("https://kakuyomu.jp" + url))
    File.open("#{Dir_Path}/#{count}.txt","w") do |text|
        text.puts doc.xpath("//header[@id='contentMain-header']").inner_text #章名とエピソード名
        text.puts doc.xpath("//div[@class='widget-episodeBody js-episode-body']").inner_text#本文
    end
    puts "保存中…(#{count}/#{size})"
    sleep 1 #スクレイピングをするときは、しっかり間隔を置こう!
end
puts "完了!"

ここまでに挙げたコードを連結すれば、カクヨムの目次を投げると全話保存するプログラムができます。

感想

やっぱりRubyはScratchではとてもできないことが色々できますね……
ところでもうちょっとコードが短くできるのでは?(診断メーカー脳)

参考サイト等


  1. 例として挙げるサイト群の選定について疑問をお持ちの方もいらっしゃるでしょうが、適当に挙げただけですので見逃していただけますと幸いです 

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

refileのattachment_image_tagで画像がうまく表示されない場合に

概要

現在スクールにてRailsの学習中で、画像表示にrefileを使用しましたが、正しく記述しているはずなのに上手く表示されず少し苦労したので備忘録も兼ねて対処法をメモします。(スクールのカリキュラムには後半にちゃんと対処法が書いてありましたが、自分が読み飛ばしてしまい、対処に時間がかかってしまいました。)同じ症状の方の助けになれば幸いです。

環境

OS: macOS Catalina バージョン10.15.3
Ruby: 2.5.7p206
Rails: 5.2.4.1
開発環境:VirtualBox + vagrant

前提

こちらの記事にある1〜5のインストール・設定・記述は実施しました。
refileの基本的な使い方が良く纏まっていると拝見いたしましたので、引用させていただきます。
refileの基本と複数画像のアップロード

ちなみにgemのバージョンは
refile 0.6.2
refile-mini_magick 0.2.0
です。

症状

正しく設定・記述されているはずなのにブラウザ上で以下の様になってしまい、画像がうまく表示されませんでした。

  • Chrome
chrome_image_error.png
  • Safari
safari_image_error.png

確認してみたこと、試してみたこと

  • データベースの中身の確認

そもそもデータベースのimage_idカラムに保存がされていないのではないかと思い確認。
自分の場合はlistsテーブルにimage_idカラムを設置していたので、

rails dbconsole

から

SELECT * FROM lists

で全レコードを表示。
image_idカラムを確認しましたが、きちんと文字列が記載されていたのでデータベースへの保存自体はうまくいっている様でした。

  • ブラウザの変更

あまり関係ないかもと思いつつ、上記の通りChromeとSafari両方のブラウザで表示状況を確認しましたがどちらも上手く表示されていませんでした。

  • 別の拡張子の画像ファイルをアップ

最初に投稿していた画像が.jpgだったので、試しにと思い.pngの画像もアップしてみる。しかしやはり上手く表示されていませんでした。

原因・解決法

結論から言うとattachment_image_tagの書き方が原因でした。

上記のリンク先にもある通り、自分が見た多くの記事ではviewの記述としては

<%= attachment_image_tag モデル名, :カラム名(末尾の_idは除く),format: '拡張子名(例: jpeg)', :fill, 横幅の数値(例: 100), 高さの数値 %> 

が紹介されていましたが、これの:fill, 横幅, 高さの部分が問題だった様です。
自分の場合は以下の記法に変更して画像が表示される様になりました。

<%= attachment_image_tag モデル名, :カラム名(末尾の_idは除く),format: '拡張子名', size: "横幅数値x高さ数値" %>

⚠️最後の横幅数値、高さ数値の間の記号は小文字のエックスです。

例:

<%= attachment_image_tag list, :image, format: 'jpeg', size: "100x100" %>

これで以下の様に、きちんと表示されるようになりました!

success_image.png

後書き

なぜ:fillでの指定はダメでsize:ならいけるのかという原理的な理由は分かりませんでしたが、自分の場合はこれでとりあえず画像がちゃんと表示される様になりました。今後これで進めて行った場合に不具合が出てきたら都度追記していこうと思います。
また、冒頭でも述べた通りスクールのカリキュラム内で、後半にではありましたがきちんと対処法が書かれていました。(自分はそこまでカリキュラムを進めていたので読んでいたはず。)今後は1文字も読み飛ばさず、しっかりと確認しつつ取り組んでいこうと思います!

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

SpecTest - ビヘイビア駆動開発(BDD)用テスティング・フレームワーク

はじめに

この記事 の続き。SpecTest の内容を少し紹介。まだすぐに使える形ではないですが、要望があれば何とかしたい。...無いか?

気にせずに進みます。こういうのは勢いと思い一気に書いてみました。

ビヘイビア駆動開発では、振る舞いに対するテストを書くことによって動作仕様を明確にしていく。いわゆる Tests as DocumentationSpecification by Example というもの。

しかし既存の BDD フレームワークは「テストを書く=それがドキュメント」という図式は成り立つものの、ユーザー向けの説明文書になるかというと、決してそうはなっていない、という問題がある、と私は思う、ような気がする。TDD から派生して、あくまで仕様を理解できる、という点にフォーカスしたモノ。

そこで SpecTest だ。

  • SpecTest
    • Writing a specification means writing a test, and examples are becoming test codes as is.
    • 訳「仕様を書くことはテストを書くこと、で、例はそのままテストになってるんだよ」

Specification by Example はまさにそんな感じ、Tests as Documentation はなんか逆転して Documentation as Tests みたいな感じだが、BDD というくくりの本質である「振る舞い駆動」という意味では間違ってないはず。一応、「今までの BDD テスティング・フレームワークが〇〇な形だから、これは違うよ!」という意見は聞かないことにします...。いや、間違ってないはず。たぶん間違っていないと思う。間違ってないんじゃないかな。ちょっとだけ覚悟しておきます。イメージ的には doctest 系に近いと思うが、よりプログラミングの世界から距離を取っています。書くのはユーザー向けのドキュメント。

恐らく厳密な意味で BDD のこれまでの経緯や定義と照らし合わせると違うとは思うがしかし、理想と現実の間の中で私が欲しいと思うスタイルはこうだった、それは概念として BDD と言って差し支えないだろう、ということは表明しても良いかなー、と思ってます。

まーあまり深く考えないでくださいませませ。

どんな感じ?

  • プログラミング言語は問わない(なんでも行ける)
  • Markdown で記述
  • .spectest に解釈ルールを記載(だが沢山書く必要はない)
  • (Markdown を解釈して自動的にコードを抽出して)テストしてレポート

そう、書くのは仕様。

プログラミング言語は問わない

作るのはユーザー説明用の Markdown ドキュメントなので、特にプログラミング言語は問わない。あなたの素敵な Ruby で書かれたプロダクトに対しても機能するようにデザインしましたよ。

というか何だったらプログラムじゃなくても Okay だ。curl コマンド使って REST API で取ってきた JSON の内容が妥当か、みたいな。

Markdown で記述

以下がテンプレート。

# TestSuite Name

最初の `#` で示される表題は自動的にテストスイート名として認識される。

## なんでも

なんでも書ける。

## なんでも

なんでも書ける。

## Examples

`## Examples` はテストコード記述エリア開始のサイン。
テキスト部分はなんでも書ける。

### Example 1. TestCase Name

`### Example [0-9]+\\.` はテストケース開始のサイン。
上記に続く名前がテストケース名になって、1つのテストケースが作られる。

#### Code

`#### Code` はテストコード開始のサイン。
でも以下のコードブロックがテスト本体。それ以外は何でも書ける。

```language
Test Code
```

テストコードの結果は標準出力に出すようにすること。

#### Result

`#### Result` はテスト期待値開始のサイン。
でも以下のコードブロックが期待値本体。それ以外は何でも書ける。

```
Expected Result
```

### Example 2. TestCase Name 2

2 つ目のテストケース。以下略。

予め決まったキーワードにさえ気を付けて書けば、テストという意識を持つことなく文書を書ける。キーワードは .spectest ファイルで指定も可能。

.spectest に解釈ルールを記載

.spectest ファイルのサンプルは以下。詳細は長くなるのでひとまずリンクで... SpecTest の表あたりを参照。

{
    "root": "doc/spec",

    "testfile": "test.kx",
    "resultfile": "result.txt",
    "interpreter": "kinx",

    "ignoreFiles": [
        "doc/spec/../benchmark/README.md",
        "doc/spec/spectest/README.md"
    ]
}

root はドキュメントのあるフォルダ(ディレクトリ)。ここにある README.md または CONTENTS.md をスタート地点として、そこからリンクされている .md ファイルを全てリストアップして実行する。

testfileresultfile はテストするときに使用する一時ファイル。毎回上書きして最後に消すので既にあるファイルを指定しないこと(要注意)。interpreter はテストコードを実行するインタプリタ名。コンパイル言語にはまだ対応していない(そんなに難しくないのでできそうだが)。ignoreFiles はリストアップされてしまうファイルのうち、テスト実行対象外にするファイル名を配列で指定しておく。

テストしてレポート

現在(2020/3/16)の Kinx のテスト状況をサンプルにすると、こんな感じになっている。

Test Cout = 69
[<>[<*********>][<****>][<***>][<*>][<*>][<***>][<****>][<******>][<**>][<**>][<***>]
[<****>][<*****>[W]][<*>][<*>][<*>][<*>][W][W][W][W][W][W][W][W][W][W][W][W][W][W][W]
[W][W]]

<Test Result Detail>

Entry: doc/spec/README.md
    Kinx Specification with SpecTest (0.00s)
    Entry: doc/spec/statement/declaration.md
        Declaration statement (0.55s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (With initializer) ...................... successful (  0.06s)
            Case[2] (With initializer of expression) ........ successful (  0.06s)
            Case[3] (Multiple variable declaration) ......... successful (  0.07s)
            Case[4] (Constant value (1)) .................... successful (  0.04s)
            Case[5] (Constant value (2)) .................... successful (  0.06s)
            Case[6] (Constant value (3)) .................... successful (  0.04s)
            Case[7] (Constant value (4)) .................... successful (  0.04s)
            Case[8] (Constant value (5)) .................... successful (  0.08s)
    Entry: doc/spec/statement/enum.md
        Enum statement (0.29s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (With initializer (1)) .................. successful (  0.07s)
            Case[2] (With initializer (2)) .................. successful (  0.07s)
            Case[3] (The scope) ............................. successful (  0.07s)
    Entry: doc/spec/statement/expression.md
        Expression statement (0.23s)
            Case[0] (Assignment) ............................ successful (  0.08s)
            Case[1] (Exponent Evaluation) ................... successful (  0.07s)
            Case[2] (Logical Undefined Operator) ............ successful (  0.06s)
    Entry: doc/spec/statement/mixin.md
        Mixin statement (0.07s)
            Case[0] (Normal case) ........................... successful (  0.07s)
    Entry: doc/spec/statement/block.md
        Block statement (0.08s)
            Case[0] (Scope) ................................. successful (  0.08s)
    Entry: doc/spec/statement/if_else.md
        If-Else statement (0.21s)
            Case[0] (Normal case) ........................... successful (  0.06s)
            Case[1] (No else clause) ........................ successful (  0.06s)
            Case[2] (If-else combination) ................... successful (  0.07s)
    Entry: doc/spec/statement/switch_case.md
        Switch-Case statement (0.31s)
            Case[0] (Normal case) ........................... successful (  0.08s)
            Case[1] (With do-while) ......................... successful (  0.07s)
            Case[2] (Non-integer value) ..................... successful (  0.08s)
            Case[3] (Complex switch-case pattern) ........... successful (  0.07s)
    Entry: doc/spec/statement/try_catch_finally.md
        Try-Catch-Finally statement (0.43s)
            Case[0] (Normal catch) .......................... successful (  0.07s)
            Case[1] (Finally (1)) ........................... successful (  0.07s)
            Case[2] (Finally (2)) ........................... successful (  0.06s)
            Case[3] (Finally (3)) ........................... successful (  0.07s)
            Case[4] (Define own exception) .................. successful (  0.06s)
            Case[5] (Complex example) ....................... successful (  0.07s)
    Entry: doc/spec/statement/while.md
        While statement (0.14s)
            Case[0] (Normal case) ........................... successful (  0.06s)
            Case[1] (Infinaite loop) ........................ successful (  0.07s)
    Entry: doc/spec/statement/do_while.md
        Do-While statement (0.15s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Infinaite loop) ........................ successful (  0.07s)
    Entry: doc/spec/statement/for.md
        For statement (0.21s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Infinaite loop) ........................ successful (  0.07s)
            Case[2] (Declation variable in scope) ........... successful (  0.06s)
    Entry: doc/spec/statement/return.md
        Return statement (0.32s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Without expression) .................... successful (  0.07s)
            Case[2] (if-modifier (1)) ....................... successful (  0.09s)
            Case[3] (if-modifier (2)) ....................... successful (  0.08s)
    Entry: doc/spec/statement/yield.md
        Return statement (0.36s)
            Case[0] (Normal case) ........................... successful (  0.07s)
            Case[1] (Without expression) .................... successful (  0.06s)
            Case[2] (if-modifier (1)) ....................... successful (  0.08s)
            Case[3] (if-modifier (2)) ....................... successful (  0.07s)
            Case[4] (`yield` returns array.) ................ successful (  0.07s)
        Entry(nolink): doc/spec/statement/statement/fiber.md
    Entry: doc/spec/algorithm/qsort.md
        Quicksort (0.08s)
            Case[0] (Quicksort Algorithm) ................... successful (  0.08s)
    Entry: doc/spec/algorithm/heapsort.md
        Heapsort (0.08s)
            Case[0] (Heapsort Algorithm) .................... successful (  0.08s)
    Entry: doc/spec/algorithm/mergesort.md
        Merge Sort (0.08s)
            Case[0] (Merge Sort Algorithm) .................. successful (  0.08s)
    Entry: doc/spec/algorithm/crc32.md
        CRC32 (0.08s)
            Case[0] (CRC32 Algorithm) ....................... successful (  0.08s)
    Entry(nolink): doc/spec/statement/throw.md
    Entry(nolink): doc/spec/statement/function.md
    Entry(nolink): doc/spec/statement/class.md
    Entry(nolink): doc/spec/statement/module.md
    Entry(nolink): doc/spec/statement/lambda.md
    Entry(nolink): doc/spec/statement/closure.md
    Entry(nolink): doc/spec/statement/fiber.md
    Entry(nolink): doc/spec/lib/primitive/integer.md
    Entry(nolink): doc/spec/lib/primitive/double.md
    Entry(nolink): doc/spec/lib/primitive/string.md
    Entry(nolink): doc/spec/lib/primitive/array.md
    Entry(nolink): doc/spec/lib/basic/file.md
    Entry(nolink): doc/spec/lib/basic/directory.md
    Entry(nolink): doc/spec/lib/basic/regex.md
    Entry(nolink): doc/spec/lib/basic/xml.md
    Entry(nolink): doc/spec/lib/basic/zip.md
    Entry(nolink): doc/spec/lib/net/http.md

<Test Result>
    Total Test Cases:       69
        Successful  :       51
        Failed      :        0
        Warning     :       18

Entry(nolink) はまだドキュメントが書けていないものです。書きます。もちろんテストではなく、仕様(=例)を。

おわりに

まだ以下に対応していないので、以下に対応することでもうちょっと実用になると思う。

  • Todo
    • JUnit 形式の XML で出力する。多分簡単。
    • Kinx をまだ簡単にインストールできないので、簡単にインストールできるようにする。それができないと実質使えない。もしくは完全に Kinx から独立させてしまう。
    • 履歴を保存しておき、テスト結果の推移を可視化できるようにする。
    • CircleCI とかで結果に対するバッチを作れるようにする。
    • コンパイル型のプログラミング言語に対応する。

コンセプトに応援してくださる方は(いつもの通り)★をお願いします。やる気出ると思うので。Kinx のほうに。どうしても分離して独立して使えたほうが良いよ、という方(誰に言ってるんだろう...? まぁいいか)は SpecTest のほうに★してくれればそういう意思表示と認識しましょう。

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

クラスメソッドとインスタンスメソッド

メソッドとは

Rubyにおけるメソッドとは、標準で組み込まれているメソッド(関数)ではなく、ユーザーが独自に定義できる関数のことをメソッドと呼びます。

qiita.rb
def メソッド名
    実行する処理
end
  • クラスメソッド クラスが使用できるメソッドです。 クラスメソッドを定義したクラス自身が使用できます。クラスで共通の情報を使った処理に使用します。

『クラスメソッド』

qiita.rb
class Qiita
  def self.page
      print("こんにちは")
  end
end
Qiita.page

『実行結果』

qiita.rb
こんにちは
  • インスタンスメソッド インスタンスが使用できるメソッドです。インスタンスメソッドを定義したクラスのインスタンスに使用できます。インスタンスごとの個別の情報(属性値)を使った処理に使用します。

『インスタンスメソッド』

qiita.rb
class Qiita
  def page
    print("Ryo Katsuno")
  end
end

instance = Qiita.new
instance.page

『実行結果』
ruby:qiita.rb
Ryo Katsuno

まとめ

クラスメソッドとインスタンスメソッドの違い
覚えるの大変ですね。。。

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

部分テンプレートとは?

部分テンプレートってなんなの??

部分テンプレートとは・・・
複数のビューファイルの中で使われている部分を一つのビューファイルとして管理すること。

つまりどういうことかというと複数回使うビューをテンプレート化してしまうことで冗長な記述が必要なくなるというとっても便利かつ簡単なテクニックです。

部分テンプレートを作成する

部分テンプレートのビューを作成するときの注意点としてビューファイルのファイル名の先頭の部分に_ (アンダーバー)をつける必要があります。
例えば、呼び出したい部分テンプレートファイルがheader.html.erbだとすると、
_header.html.erbと書き換えましょう。

部分テンプレートの呼び出し方

当たり前ですが、実際に部分テンプレートを呼び出すためのメソッドがあります。それが
renderメソッド です.

_header.html.erb
render 'ファイル名'  #'ファイル名'で部分テンプレートの呼び出し

render partial: 'ファイル名' #partialはつけてもつけなくても可

このように記述していくことで部分テンプレートが使用できて、同じようなコード再度記述していく必要がなくなります!便利ですよね。

hamlで書くとどうなるのかというと

_header.html.haml
= render 'ファイル名' #=でくくってあげちゃうだけ

renderメソッドにはもっと活用できるオプションがあるみたいなので詳しく知りたい方は検索してみましょう(優秀なエンジニアに丸投げ)

以上です。至らない点があればコメントお願いします。

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

RubyとPythonにおける、外部ソースコードを読み込む書き方の違い

これは何?

RubyとPythonの両方でプログラムを書いていると、外部ソースコードを読み込む書き方が「どっちがどっちだっけ?」と混乱することがあります。そうした場合に向けての備忘録です。

Rubyの場合

require './foobar'
  • モジュール名を含むパス全体をrequireの後に書く
  • モジュール名を引用符で囲う必要がある

Pythonの場合

from . import foobar
  • パス指定でモジュールをインポートする場合、モジュール名を除くパスを、fromの後・importの前に書く
  • モジュール名はimportの後に書く
  • モジュール名を引用符で囲う必要はない

Pythonの場合、「パッケージ」やらなんやらの関連概念があるが、今回はそうした項目には触れない。

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

1行追加のみ! Net::HTTPで発生するcertificate verify failedを回避

外部APIを叩くのに、Docker使っていたらエラーが発生

SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate in certificate chain)

原因を調べるのに疲れた。
ローカル環境のみで発生するので真面目に対応するのも面倒。
本番はDocker使ってないし。
楽できる方法、探した。

解決策1

config/initailizersのファイルに以下を追記する。

OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if Rails.env.development?

参照:Rails5 devise twitterソーシャルログイン ローカル環境でのエラー解消

ただ、これだと、
・サーバー再起動が必要
・ローカル環境のログでalready initialized constant OpenSSL::SSL::VERIFY_PEER的な警告がうざい
というデメリットがある。

もっと楽、したい。
テキトーに生きたい。

解決策2

  def api(url)
    uri = URI.parse(url)
    req = Net::HTTP::Get.new(uri)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = (uri.scheme == 'https')
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE # これ追加。
    res = http.start { |h| h.request(req) }

    JSON.parse(res.body)
  end

1行追加で動いた。
テキトーにif分岐追加しても1行で済む。

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

VSCodeにおいて、インストール済みのlive shareができない場合の対処法(MAC)

先生から送られてきた、liveshareのリンクを踏んでも、collaboration することができませんでした、、

解決策の結論として「GitHub で再認証を行うこと」が必要でした。

VSCodeをメニューバーから強制終了し、再起動を行なった場合は、再度Githubの認証を得る必要があり、以下に手順を示します。


1.VSCodeの下にある、紫色のliveshareと書いてあるバーをクリックする

2.sign in with githubと表示されるのでそれをクリック

3.safariが開き、表示された認証ボタンを押すことで、認証が完了し、liveshareが使える様になる


下のバーに自分のユーザー名が表示されていれば成功です。

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

外部キー制約のついたカラムを削除したい

外部キー制約の付いたカラムを削除するのにプチハマりしたのでまとめます

参考

こちらの記事でマイグレーションについてかなり詳しくまとめられています
マイグレーションの操作をするときは、こちらの記事で該当箇所をざっと読んでから公式ドキュメントに目を通すのが良さそう
https://pikawaka.com/rails/migration

APIdoc
https://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/remove_index

例)ベストアンサー機能

  • questionモデル内に、ベストアンサーに選ばれた回答を保存するためのbestカラムがある
  • bestカラムには外部キーが設定してある(answersテーブル)

という状況

db/schema.rb
create_table "questions", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
  t.bigint "best"
  t.index ["best"], name: "fk_rails_dd35f91b0c"
end

add_foreign_key "questions", "answers", column: "best"

実践

まずはカラム削除用のマイグレーションファイルを作成する

$rails g migration RemoveBestFromQuestions

マイグレーションファイルが生成

db/migrate/xxxx_remove_best_from_questions.rb
class RemoveBestFromQuestions < ActiveRecord::Migration[5.1]
  def change
  end
end

こちらにカラムを削除するための記述を追加していく
外部キーとインデックスを貼っているので、カラムを削除する記述だけではエラーが発生してしまう
それらを削除する記述も必要っぽい

db/migrate/xxxx_remove_best_from_questions.rb
class RemoveBestFromQuestions < ActiveRecord::Migration[5.1]
  def change
    remove_foreign_key :questions, :answers
    remove_index :questions, :best
    remove_column :questions, :best, :bigint
  end
end

これでrails db:migrateを実行すればOK

最後に

間違っている部分があれば遠慮なくご指摘ください

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

[Rails] Mechanizeがファイルディスクリプタを大量に消費してしまう

内容はタイトル通り。
Railsにて、Mysql2のConnectionError出てる場合とか
ファイルディスクリプタを異常に使いまくってる場合はもしかしたら関係あるかも。

経緯

お客様に提供しているサービス(Rails,puma)にて

Mysql2::Error::ConnectionError

が多発してサービスが利用できなくなる障害が発生しました。

詳しくログを追ってみると他にも

Errno::EMFILE (Too many open files @ rb_sysopen ...(略)

のようなエラーも発生している様子。

さらに、、、

# ファイルディスクリプタ上限確認
ulimit -n
# pumaプロセスのファイルディスクリプタ確認
for i in $(ps aux | grep "[p]uma" | awk '{print $2}'); do sudo ls /proc/$i/fd | wc -l; done

上記コマンドでプロセスの開いてるファイル数を確認してみたら
どうやらファイルディスクリプタがいっぱいになっているようだった。

原因

実は、ファイルを開いているわけではなくてアプリケーションで使用していた Mechanize というgemにて
外部からデータを取得する際にコネクションを作成していた様子。
(スクレイピングとかするときに使われてるgemらしい。)

ファイルディスクリプタでは、ファイルだけでなくコネクションもカウントされるようで
大量のコネクションが削除されずに作成され続けたことで問題が発生した。
(Mysqlも多分コネクション関係?? 詳しい人コメントお願いします。)

対応

単純に、コネクションが開いたままになっていることが問題。
閉じてあげる必要がある。

Mechanizeのコネクションは以下のメソッドを呼び出すと閉じてくれるらしいので
使用中のコネクションの最後の処理が終わったあたりで呼び出してあげる。

Mechanize.shutdown

あとは、サーバー再起動などで既存コネクションを消してあげましょう。

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

クラスとインスタンス

はじめに

rubyの復習をしている時に、そもそもクラスとインスタンスとは何なのかわからなくなったので記事にしました

クラスとは

とある種類のオブジェクトの属性とメソッドをまとめて定義しておく型のようなものです。

インスタンスとは

オブジェクト同士で共通しそうな部分をクラスという型で定義します。
そこで、クラスから生まれたオブジェクトのことをインスタンスといいます。

イメージ

言葉だけの説明ではいまいち理解できなかったので、スマートフォンでイメージを整理してみました。

image.png

終わりに

言語化がうまくできないことが多いので、もっと図や表でイメージできるようにしたいです。

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

rspec-railsで使うコマンドを羅列する

使うコマンドを雑多にメモる。

導入

Gemfile
group :development, :test do
  gem 'rspec-rails', '~> 3.6.0'
  gem 'factory_bot_rails'
end
spec/support/factory_bot.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end
$ rails g rspec:install
.....
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gemとbundlerまとめ

まえおき

PJで運用保守にあたる中でgemのメモリ効率改善アップデートを取り入れたく


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

Vultr VPSにCentOS8+MySQL8+nginx

はじめに

Vultrの一番安い10GB SSDプランだとCentOS8ではディスクが足りなくなるので25G以上をお勧めします。

MySQL 8 installation

$ sudo su

Unistall MariaDB
# yum -y remove mariadb-libs
# rm -rf /var/lib/mysql/

Install MySQL 8
# yum -y install @mysql
# systemctl start mysqld
# systemctl enable --now mysqld
# systemctl status mysqld
# mysql_secure_installation

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: y

There are three levels of password validation policy:

LOW    Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary                  file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
Please set the password for root here.

New password: 

Re-enter new password: 

Estimated strength of the password: 100 
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done! 

Ruby V2.5.3 install

# yum -y install glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel bzip2
# yum install gcc-c++
# yum -y install git
# cd /usr/local
# git clone git://github.com/sstephenson/rbenv.git rbenv
# git clone git://github.com/sstephenson/ruby-build.git rbenv/plugins/ruby-build
# yum groupinstall "Development Tools"
# yum install -y openssl-devel readline-devel zlib-devel

# vi /etc/profile.d/rbenv.sh

> 以下を記述してパスを通しておく。
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init --no-rehash -)"

# source /etc/profile.d/rbenv.sh
# rbenv install 2.5.3  <=これが時間がかかる。
# rbenv global 2.5.3
# rbenv rehash

bundler 2.1.2 install

# gem install bundler -v 2.1.2

node.js

# yum install -y nodejs
# node --version
v10.19.0

yarn install

# npm install -g yarn

Nginx stable最新版をインストール

# yum install yum-utils
/etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key

Install

# cat /etc/redhat-release
CentOS Linux release 8.1.1911 (Core) 

# yum --disablerepo=AppStream install -y nginx

動作確認

nginx起動
# systemctl start nginx

nginx自動起動設定
# systemctl enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.

動作状態確認
# systemctl status nginx
● nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-03-15 17:14:15 UTC; 5s ago
     Docs: http://nginx.org/en/docs/
  Process: 3413 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
 Main PID: 3414 (nginx)
    Tasks: 2 (limit: 5066)
   Memory: 2.0M
   CGroup: /system.slice/nginx.service
           ├─3414 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
           └─3415 nginx: worker process

Mar 15 17:14:14 vultrguest systemd[1]: Starting nginx - high performance web server...
Mar 15 17:14:15 vultrguest systemd[1]: nginx.service: Can't open PID file /var/run/nginx.pid (yet?) after start: No such file or directory
Mar 15 17:14:15 vultrguest systemd[1]: Started nginx - high performance web server.

ブラウザでアクセス

スクリーンショット 2020-03-15 午後1.17.53.png

snapshot

ここまででクリーンインストールが終わったので、他の作業前にスナップショットを取得しておく。

スクリーンショット 2020-03-15 午後1.21.40.png

参考記事

How to Install MySQL 8.0 on CentOS 8 / RHEL 8
CentOSに最新版のGitをインストール・アップデートする方法
CentOS 8にNginx stable最新版をインストール(公式repository)

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

Vultr VPSにCentOS8+MySQL8+nginxをインストール

はじめに

CentOS7での記事の後、CentOS8も試したので、書き留めておきます。

Vultrの一番安い10GB SSDプランだとCentOS8ではディスクが足りなくなるので25G以上をお勧めします。以下の手順はRubyだけ古いバージョンですが、これはローカルのアプリが ruby 2.5.3 で動いているためです。2.7でも手順は同じでOKと思います。(未検証)

激安VPSのVultr紹介者からのリンク経由でアカウント登録すると$100のクレジットをもらえるので、CentOS7, 8, MySQL 5.7, 8などの組み合わせをいろいろ試しました。このキャンペーンはいつ終わるか分からないので、興味のある方は以下のリンクからアカウント登録してください。あなたはクレジットをもらえるし、私にも多少のクレジットが入るらしいので、Win-Winです。

このリンクで$100もらえます

スクリーンショット 2020-03-09 午後7.03.25.png

0. OS基本設定

前回のCentOS7での記事の1〜6までを行って、ユーザー作成やSwap領域の設定などをやっておきます。

1. MySQL 8 をインストール

$ sudo su

Unistall MariaDB
# yum -y remove mariadb-libs
# rm -rf /var/lib/mysql/

Install MySQL 8
# yum -y install @mysql
# systemctl start mysqld
# systemctl enable --now mysqld
# systemctl status mysqld
# mysql_secure_installation

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: y

There are three levels of password validation policy:

LOW    Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary                  file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
Please set the password for root here.

New password: 

Re-enter new password: 

Estimated strength of the password: 100 
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done! 

2. Ruby V2.5.3 をインストール

# yum groupinstall "Development Tools"
# yum -y install libyaml-devel
# yum install -y openssl-devel readline-devel zlib-devel
# cd /usr/local
# git clone git://github.com/sstephenson/rbenv.git rbenv
# git clone git://github.com/sstephenson/ruby-build.git rbenv/plugins/ruby-build


# vi /etc/profile.d/rbenv.sh

> 以下を記述してパスを通しておく。
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init --no-rehash -)"

# source /etc/profile.d/rbenv.sh
# rbenv install 2.5.3  <=これが時間がかかる。
# rbenv global 2.5.3
# rbenv rehash

3. bundler 2.1.2 をインストール

自分の開発環境のBundlerが2.1.2なのでそれに合わせたが、バージョンは開発環境に合わせる。

# gem install bundler -v 2.1.2

4. node.js をインストール

# yum install -y nodejs
# node --version
v10.19.0

5. yarn をインストール

# npm install -g yarn

6. Nginx stable最新版をインストール

(1)yum-utils をインストール

# yum install yum-utils
/etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key

(2) インストール

# cat /etc/redhat-release
CentOS Linux release 8.1.1911 (Core) 

# yum --disablerepo=AppStream install -y nginx

(3) 動作確認

nginx起動
# systemctl start nginx

nginx自動起動設定
# systemctl enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.

動作状態確認
# systemctl status nginx
● nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-03-15 17:14:15 UTC; 5s ago
     Docs: http://nginx.org/en/docs/
  Process: 3413 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
 Main PID: 3414 (nginx)
    Tasks: 2 (limit: 5066)
   Memory: 2.0M
   CGroup: /system.slice/nginx.service
           ├─3414 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
           └─3415 nginx: worker process

Mar 15 17:14:14 vultrguest systemd[1]: Starting nginx - high performance web server...
Mar 15 17:14:15 vultrguest systemd[1]: nginx.service: Can't open PID file /var/run/nginx.pid (yet?) after start: No such file or directory
Mar 15 17:14:15 vultrguest systemd[1]: Started nginx - high performance web server.

(4) ブラウザでアクセス

スクリーンショット 2020-03-15 午後1.17.53.png

7. snapshot

ここまででクリーンインストールが終わったので、他の作業前にスナップショットを取得しておく。

スクリーンショット 2020-03-15 午後1.21.40.png

8. Rails 6 をインストール

(1) Rails6のための事前準備

# yum install mysql-devel
# gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'
# gem install msgpack

(2) Rails 6 をインストール

# gem install rails -v 6.0.2.1
...
Done installing documentation for concurrent-ruby, i18n, thread_safe, tzinfo, zeitwerk, activesupport, rack, rack-test, mini_portile2, nokogiri, crass, loofah, rails-html-sanitizer, rails-dom-testing, builder, erubi, actionview, actionpack, activemodel, activerecord, globalid, activejob, mini_mime, mail, actionmailer, nio4r, websocket-extensions, websocket-driver, actioncable, mimemagic, marcel, activestorage, actionmailbox, actiontext, thor, method_source, railties, sprockets, sprockets-rails, rails after 87 seconds
40 gems installed

9. Test rails app

動作検証用に適当なアプリを作る。Capistranoでローカル環境からデプロイする場合はこの動作検証はスキップして構わない。

(1) directory preparation

$ sudo su
[root@vultrguest var]# mkdir /var/www
[root@vultrguest var]# mkdir /var/www/myapp
[root@vultrguest var]# chown -R deploy.deploy /var/www

(2) Gemfile preparation

$ bundle init
Writing new Gemfile to /var/www/myapp/Gemfile

$ vi Gemfile
/var/www/myapp/Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails"  <= remove "#" here

(3) rails new

$ bundle install --path vendor/bundle
$ bundle exec rails new . -B -d mysql --skip-test
$ bundle install --path vendor/bundle
$ rails webpacker:install
...
Webpacker successfully installed ? ?

$ rails s
=> Booting Puma
=> Rails 6.0.2.1 application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.3 (ruby 2.5.3-p105), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop

(4) telnetで確認

前提条件 :Port 3000が開いている事

$ yum list installed | grep telnet

もしtelnetがインストールされていなければインストール
$ sudo yum -y install telnet telnet-server
$ telnet 127.0.0.1 3000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
GET / HTTP/1.1   <=Enterを2回押す事。

HTTP/1.1 403 Forbidden
Content-Type: text/html; charset=UTF-8
Content-Length: 3102

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Action Controller: Exception caught</title>
  <style>
    body {
      background-color: #FAFAFA;
....
</body>
</html>
Connection closed by foreign host.

これでPumaが動いていることは確認できました。外部からブラウザでアクセスするにはnginxとの連携の設定が必要です。

(5) Snapshot

一旦、ここまででスナップショットを取っておきます。

10. Capistrano3でデプロイ

今度はローカルの開発環境で作ったアプリをCapistrano3でデプロイしてみます。

前提条件

  • GitHubにSSH公開鍵でローカルからPushできていること。

(1) Vultr VPSにアプリのデプロイ先ディレクトリを準備

$ sudo su
# mkdir /var/www
# mkdir /var/www/myapp
# mkdir /var/www/myapp/shared
# mkdir /var/www/myapp/shared/config

# adduser www
# chown -R www:www /var/www
# chmod -R 770 /var/www/
# gpasswd -a deploy www
# gpasswd -a nginx www

(2) デプロイ対象外指定したファイルをコピー

$ scp -i ~/.ssh/vultr config/master.key deploy@SERVER_IP_ADDR:/var/www/myapp/shared/config/
$ scp -i ~/.ssh/vultr config/database.yml deploy@SERVER_IP_ADDR:/var/www/myapp/shared/config/

(3) Capistrano関連Config

Gemfileに以下を追加
# Use Capistrano for deployment
group :development do
  gem 'capistrano'
  gem 'ed25519'
  gem 'bcrypt_pbkdf'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano3-puma'
end

Capfile, config/deploy.rbを生成

ローカル環境のRails_rootで実行
$ bundle exec cap install STAGES=production

Capfile, config/deploy.rbを設定

Capfile
config/deploy.rb

(4) nginx-puma連携

VPS事前準備

Vultrで作業
# mkdir /etc/nginx/sites-available
# mkdir /etc/nginx/sites-enabled
# chgrp www sites-available
# chgrp www sites-enabled

capistrano-pumaプラグインで設定ファイル自動生成

これらコマンドで、サーバ上の /etc/nginx/sites-availableshared/puma.rb に設定ファイルが作られる。

ローカル環境のRails_rootで実行
$ bundle exec cap production puma:nginx_config
00:00 puma:nginx_config
      Uploading /tmp/nginx_myapp_production 100.0%
      01 sudo mv /tmp/nginx_myapp_production /etc/nginx/sites-available/myapp_production
     01 deploy@Vultr_IP_ADDR 0.200s
      02 sudo ln -fs /etc/nginx/sites-available/myapp_production /etc/nginx/sites-enabled/myapp_production
     02 deploy@Vultr_IP_ADDR 0.223s
$ bundle exec cap production puma:config
00:00 puma:config
      Uploading /var/www/myapp/shared/puma.rb 100.0%

デプロイ実行

ローカル環境のRails_rootで実行

参考記事

How to Install MySQL 8.0 on CentOS 8 / RHEL 8
CentOSに最新版のGitをインストール・アップデートする方法
CentOS 8にNginx stable最新版をインストール(公式repository)
新規Railsプロジェクトの作成手順まとめ

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

Ruby, Rails たまに使うけどすぐ忘れるやつ

RailsとかRubyの過去に学んだものを適当に残しておく(最近はRailsもRubyも触ってない・・)
思い出したら随時追加する。

rakeタスクをrails consoleから実行する

require 'rake'
Rails.application.load_tasks
Rake::Task['hoge:huga'].execute
# 引数つけて実行
Rake::Task["hoge:huga"].execute({ hogege: 'hugaga' })

bundle でバージョン指定してインストール

# bundle installするときはGemfile.lockのバージョンを確認してから実行
bundle  _1.16.1_ install

rakeタスク内のメソッドはObjectクラス直下に作られちゃう

https://blog.freedom-man.com/rake-definemethod-namespace

namespace配下にメソッド書いていると思っていてもObjectクラス以下に作られるのでバグの原因になるかもね

SQLをrails consoleから実行

sql="SELECT * from hoge);"
result=ActiveRecord::Base.connection.select_all(sql).to_hash

rspecの書き方で参考になるページ

http://www.betterspecs.org/jp/

ruby引数処理

わかりやすい記事
キーワード引数をhashとして受け取る方法とか
https://qiita.com/metheglin/items/306e81c95f8a5cdea296

privateメソッドをrails consoleから呼び出す

# User.find_by(id: 1)は引数
# Hugaはクラス, hoge_private_methodはHugaクラス内のプライベートメソッド
Huga.send(:hoge_private_method, User.find_by(id: 1) )

CSV書き出し

使い捨てコード書くときによく使う

CSV.open('/tmp/hoge.csv','wb') do |csv|
   csv << [1,2]
end

テキスト読み込み

File.foreach("tmp/hoge.txt"){|f| s << f.chomp.to_i}

rakeタスクの確認

bundle exec rake -vT

rails consoleでexplainを実行

https://railsguides.jp/active_record_querying.html#explain%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B

User.where(id: 1).joins(:articles).explain

ActiveSupportのString拡張(活用形)まとめ

https://qiita.com/hana-da/items/ec9ac3e1c8803f5fa1fc

"Invoice".tableize     # => "invoices"

メール送信

手元のメール送信確認はmailcacherを使ったことがある
https://qiita.com/pocari/items/de0436c39ffc65647cf0

ActionMailer::Base.mail(to: "to@example.com", from: "from@example.jp", subject: "題名", body: "本文").deliver_now

メソッドの定義場所を探す

https://docs.ruby-lang.org/ja/latest/method/Method/i/source_location.html

Hoge.method(:huga).source_location

sqlファイルをrails dbに食わせる

bundle exec rails db < my_db.sql

日付と時刻を扱う

https://qiita.com/prgseek/items/c0fc2ffc8e1736348486

t = Time.parse("2017/04/25 19:23:55"); p t  # 2017-04-25 19:23:55 +0900

クエリ高速化

わかりやすい, Rails6はあまり知らない
https://texta.pixta.jp/entry/2016/02/10/180244
https://qiita.com/leon-joel/items/f26556c9e56833983856

rails consoleの再読み込み

reload!

find_each

わかりやすかった
https://blog.toshimaru.net/rails-find_each/#order%E4%BB%98%E3%81%8D-find_each

スクリプト実行

Rails機能を使いつつ単独のスクリプトを実行する。

bundle exec rails runner hoge.rb 'huga'

pry

わかりやすい
https://qiita.com/k0kubun/items/b118e9ccaef8707c4d9f
https://www.slideshare.net/cuzic/pry-repl

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

AtCoderの入力例を一度に試すための1行(Ruby)

AtCoder等のプログラミング問題を解く際、提出前に手元の環境で動作確認すると思う。私の場合は便利な環境を用意できていなく1、代わりに paiza.io を利用している2

問題には入出力の例が複数与えられているのでなるべく全て試したいが、入力例をいちいち変えるのが辛い。自分で例を追加する場合は尚更である。

環境を整えてさえいれば、テストの自動生成なども可能なようだが…

RubyでAtcoderからテストを自動生成するライブラリを作りました - Qiita

何とかならないか考えた結果、Rubyにおいて1行で解決する方法を思いついた。

方法

Rubyスクリプトの最後に以下の1行を追加する。解答のメソッド化などは必要ない。

load __FILE__ unless $stdin.eof?

あとは入力例を結合して読み込ませれば、全ての出力がまとめて表示される。

制限

解答のスクリプト内では「入力終端まで読み込む」ことをしてはいけない。問題に与えられた数字をもとに #gets などで正確な行数だけ取得しなければいけない。

動作原理

単に同じスクリプトを何度も実行している。

Kernel.#loadKernel.#require と異なり、常に指定ファイルを読み込む。指定ファイルとして自分自身を与えれば、スクリプトを再び先頭から実行する効果が得られる。あとは無限ループ防止のために「入力終端でなければ」という条件を追加すればいい。

AtCoder Beginner Contest 154 の A問題 に適用してみる。この問題は入力が3行で与えられる。

abc154_a.rb
s, t = gets.split
a, b = gets.split.map(&:to_i)
u    = gets.chomp

if u == s
  a -= 1
else
  b -= 1
end

puts "#{a} #{b}"

load __FILE__ unless $stdin.eof?

入力例が2つあるので、それらを結合して与える。

入力例1,2
red blue
3 4
red
red blue
5 5
blue

すると、それぞれの結果がまとめて表示される。

出力
2 4
5 4

制限に違反した場合

解答のスクリプト内で「入力終端まで読み込む」ことをしてしまうと、一度に試すことはできなくなる。提出する分には問題ない。

abc154_a.rb
s, t, *ab, u = $stdin.read.split
ab.map!(&:to_i)
ab[u == s ? 0 : 1] -= 1
puts ab * " "

load __FILE__ unless $stdin.eof?
出力
3 3 0 0 0 5 5

  1. 電車の中からタブレットで参加することもあった 

  2. 言語のバージョンを合わせられないのでお勧めはしない 

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