20200630のRubyに関する記事は15件です。

ruby 定数 変数

小文字は変数

大文字は定数

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

アプリのテストについて RSpec(単体テスト)

RSpec

Rubyを元に作成されたテストに特化した言語です

テストコードの基本

スクリーンショット 2020-06-30 22.09.20.png

describe

1行目のdescribeは、直後のdo ~ endまでのテストのまとまりを作ります。describeの後に続く""の中にはそのまとまりの説明を書きます。

itとexample

2行目のitはexampleと呼ばれる実際に動作するテストコードのまとまりを表します。itの後に続く""の中にはそのexampleの説明を書きます。

エクスペクテーション

実際に評価される式のことです。it do ~ endの間に書きます。上記の式ではexpect(1 + 1).to eq 2の部分がエクスペクテーションです。

expect(X).to eq Y

エクスペクテーションの文法です。xの部分に入れた式の値がYの部分の値と等しければ、テストが成功します。eqの部分を、マッチャと言います。

マッチャ

エクスペクテーションの中で、テストが成功する条件を示します。例えばeqは「等しければ」という意味になります。他にも
include(含んでいれば)、valid(バリデーションされれば)など複数のマッチャが存在します。

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

【Rails】lightbox2を用いて画像拡大機能の実装

目標

ezgif.com-video-to-gif.gif

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

下記実装済み。

Slim導入
投稿機能実装

実装

1.application.html.slimを編集

application.html.slim
doctype html
html
  head
    title
      | Bookers2
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    / 追記
    link href='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/css/lightbox.css' rel='stylesheet'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
    / 追記
    script src='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/js/lightbox.min.js' type='text/javascript'

2.image_tagを編集

books/show.html.slim
= link_to @book.image.url, 'data-lightbox': @book.image do
  = image_tag @book.image.to_s
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

APIについて噛み砕く

近況報告

 実務経験を積みたい!

 これにつきます。ツイッターでテックの先輩のつぶやきをみても,独学と実務では圧倒的に差があることが伺えます。どのくらい差があるのかは日を追うごとに興味が増します。私の周りにwebエンジニアがいないため,現状を聞くとなるとやはり面接先のPM・CTOが多くなります。ただ,「実務って独学で得られる経験となにが違いますか」なんて質問落とされそうだから言えないよねw 私が考える限りでは,納品スピード,瞬時にメソッドなど使用するものを決める能力,ggr力なのかな。ただ,やはり現場で肩を並べて経験しないと実感はできないでしょう。

 本日のお題  

「API」
 テックキャンプに参加していた時さっぱりわからずスルーしていた単語。ただ,就活をしていると多々現れる単語。「API」について噛み砕く

API

参考
API・・・ Application Programing Interface

 アプリケーションのプログラミングの表面。。。直訳しても意味が分からない略語である。アプリケーションをアプリケーション外から操作する窓口って感じで私は理解しました。

具体例

 youtubeのAPIを用いて再生回数が1億回の動画をrubyで抽出し,出力しました。

 この場合「youtube」というアプリケーションの中の情報を,アプリケーション外から「ruby」を用いて抽出した,と考えられます。このように,外側からアクセスしてアプリケーションの情報を処理,抽出できるカラクリがAPIです。ここでのAPIは,youtubeの情報を得るためのAPIと考えられます。このようにAPIは目的によって動きが変わっていくものです。

データを抽出するAPI,アプリケーションを操作するAPI,同期するAPI,ソートを行うAPIなど

データとか情報 |壁←API←壁| 外側

 のような内側にアクセスするためのカラクリという認識で私は捉えています。つまりAPIがあれば誰でも中の情報が操れる訳で,流石にセキュリティの観点でそれはマズイので対策としてyoutubeのAPIはAPIキーが発行されそれを利用してアクセスできるようになります。

実用例(天気予報)

require 'net/http'
require 'uri'
require 'json'
require 'time'
require 'date'

uri = URI.parse('http://weather.livedoor.com/forecast/webservice/json/v1?city=170010')
json = Net::HTTP.get(uri)
result = JSON.parse(json)
today_tel = result['forecasts'][0]['telop']
tomor_tel = result['forecasts'][1]['telop']
min_tem = result['forecasts'][1]['temperature']['min']['celsius']
max_tem = result['forecasts'][1]['temperature']['max']['celsius']
des = result['description']['text']
des1 = result['description']['publicTime']
des2 = DateTime.parse(des1)

#puts result
puts ' 天気予報 '.center(60, '-')
puts ''
puts '今日の天気は、' + today_tel + ' でしょう '
puts '最低気温は、' + min_tem + '℃' + ' でしょう '
puts '最高気温は、' + max_tem + '℃' + ' でしょう '
puts '明日の天気は、' + tomor_tel + ' でしょう '
puts ''
puts ' 天気予報 '.center(60, '-')
puts ''
puts ''
puts ' 天気概況文 '.center(60, '-')
puts
puts des
puts ''
print ' 天気概況文の発表時刻  '
puts des2.strftime('%Y年%m月%d日 %H時%M分%S秒')
puts ''
puts ' 天気概況文 '.center(60, '-')

ここではhttp://weather.livedoor.com/forecast/webservice/json/v1?city=170010 にアクセスして気象情報を抽出して表示しています。

おわりに

 イメージが湧きましたでしょうか?もっといい表現があればおしえて!

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

【Ruby】Hash#has_key? がまた一歩、公式のdeprecatedに近づいた?

Hash#has_key? は、Matz さんから、

"has_key" has already been deprecated by "key?"

宣告されて早8年、いろんな Style guide で、 deprecated だと指摘されています。例えば、

しかし、公式には、Hash#has_key? は今なお、Hash#include?Hash#key?Hash#member? を抑えて、唯一サンプルコードで用いられているメソッドであり、全く deprecated さを感じさせない活躍ぶりです。

日本語版はこんな感じで、

日本語版
p({1 => "one"}.has_key?(1)) #=> true
p({1 => "one"}.has_key?(2)) #=> false

英語版は、こんな感じです。

英語版
h = { "a" => 100, "b" => 200 }
h.has_key?("a")   #=> true
h.has_key?("z")   #=> false

しかし、これも Ruby 2.7 までのことで、Ruby 2.8 からは、サンプルコードでの活躍をほかのメソッドに譲り渡すことになりそうです。

GitHub に上がっている最新のコメントはこうなっています。

/*
 *  call-seq:
 *    hash.include?(key) -> true or false
 *    hash.has_key?(key) -> true or false
 *    hash.key?(key) -> true or false
 *    hash.member?(key) -> true or false

 *  Methods #has_key?, #key?, and #member? are aliases for \#include?.
 *
 *  Returns +true+ if +key+ is a key in +self+, otherwise +false+:
 *    h = {foo: 0, bar: 1, baz: 2}
 *    h.include?(:bar) # => true
 *    h.include?(:nosuch) # => false
 *

Hash#include? 一押しって感じですね。とはいえ、Hash#has_key? はまだ2番手につけています。

個人的には、Hash#has_key? に愛着を感じており、書き捨てのコードなどでは今でも Hash#has_key? を使うことがあります。

いまだ公式には deprecated 宣言されていない(ですよね?)Hash#has_key? を使い続けていいものやら、それとも、心を鬼にして Hash#key? など他のメソッドを使うべきなのか? 心が揺れ動きます。

本当に deprecated にする気があるのなら、早々に引導を渡してほしいと思う今日この頃です。

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

MySQLの稼働が安定しない…Railsアプリが起動していない。

発生した背景

MySQLサービスがまたもやエラー。何回も発生すると、流石に時間の浪費に怯えてしまいます。以下エラー内容になります。

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

環境

項目 内容
OS.Catalina v10.15.4
Ruby v2.5.1
Ruby On Rails v5.2.4.3
MySQL v5.6

対応手順

【調査1】MySQLが起動しているか。
僕は「SequelPro」を使っているので、接続してみる。→接続エラー

プロセスの稼働状況を確認
CMD>ps -ef | grep mysql
  501  1287     1   0 11:45AM ??         0:00.06 /bin/sh /usr/local/opt/mysql@5.6/bin/mysqld_safe --datadir=/usr/local/var/mysql
  501  6400  1287   0 11:55AM ??         0:00.51 /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=ichikawadaisukenoMacBook-Air.local.err --pid-file=ichikawadaisukenoMacBook-Air.local.pid
  501  6402   804   0 11:55AM ttys000    0:00.01 grep mysql

!?稼働しているようには見える!?

【調査2】まずはエラー内容から。ソケットファイルがないと言われているので、以下のコマンドで対応してみました。

ソケットファイルを試しに作成してみる。
CMD>cd アプリケーションフォルダ
CMD>touch /tmp/mysql.sock

→結果、改善せず。

【調査3】ログを確認してみる。

MySQLのログを確認
CMD>more /usr/local/var/mysql/ichikawadaisukenoMacBook-Air.local.err
(一部省略)
mysqld_safe A mysqld process already exists

※この後再起動するも、改善ならず。

対応

プロセスが多重起動している様子。既存の稼働中のプロセスを停止する。

プロセスの停止
CMD>kill -9 対象のサービスプロセス

今回はこれだけで改善しました。しかし、まだ以下のエラーメッセージが出力されたままです。

200616 16:04:42 mysqld_safe A mysqld process already exists

対応は後日にしようと思います。

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

[Ruby] CSVファイルをYaml(Yml)に変換する方法

手順

1、requireメソッドでRubyの標準ライブラリのcsvyml を読み込む。

require 'csv'
require 'yaml'

2、CSV.read(path_to_csv, :headers => true)を実行すると、CSV::tableクラスのインスタンスが返ってくる。
mapメソッドで要素(CSV::Rowクラスのインスタンス)を一つずつ取り出し、その要素をto_hashメソッドでハッシュ化する。mapメソッドの返り値は配列となるため、ハッシュを要素とした配列が作成される。今回:header => trueオプションを付け加えたことでハッシュのkey名がCSVのカラム名になっているところがポイント。

hash_arr = CSV.read(path_to_csv, :headers => true).map(&:to_hash)
=>[{"region_id"=>"1", "country_id"=>"1", "region_name"=>"北海道"}, {"region_id"=>"2", "country_id"=>"1", "region_name"=>"青森県"},...]

3、pathで指定されたファイルを書き込みモードでオープンし、2のハッシュを要素とする配列をYamlに変換して保存。

File.open(path_to_yaml, 'w') { |f| f.write(hash_arr.to_yaml) }

P.S

ここからは余談ですが、DBで基本的に値が変更されないテーブル(国や地域)を管理していたので、それをActiveYamlで管理するために今回の変換しました。ActiveYamlで管理してもActiveRecordのようにテーブルを操作すること(allやfindなどのクラスメソッドが使える)ができるので便利です。

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

ブラウザが認識できる言語に変換する仕組み

高級言語と低級言語

・高級言語
高級言語は、機械よりも人間が理解しやすいように設計されたプログラミング言語のことです。
また、「高級」とは優れた言語という意味ではなく、機種やOSなどに固有の要素を抽象度の高い記述が可能であることを表しており、アプリケーションソフトの開発などに用いられる言語の多くは高級言語に分類されるから「高級」という表現を用いています。

・低級言語
機械が認識しやすい言語は低級言語と呼ばれます。
「低級」とは劣った言語であることを意味するのではなく、ハードウェアに近いことを意味
することから「低級」という表現を用いています。

ファイルをブラウザに表示させるには、高級言語で開発したあとに、それを機械が認識
できる低級言語に翻訳する必要があり、その翻訳の事をコンパイルと呼びます。

コンパイル

プログラミング言語を、動作する機械が理解できるように翻訳する作業のことです。
コンパイルは、コンパイラ(機会にやさしいプログラミング言語のこと)と呼ばれる
プログラムによって行われます。
もしコンパイラで認識できない言語があれば、あらかじめ認識できる言語にプリコンパイルしておく必要があります。

プリコンパイル

コンパイラが翻訳できない言語を翻訳できるようにする事前コンパイルのことです。あるいは、メインとなる処理に対して必要な前処理のことを指します。
つまり、コンパイルやメインの処理の前に行われるべきコンパイルが、プリコンパイルです。

たとえば、Webアプリケーションでは、Webブラウザが静的ファイルを読み込んで表示するというメインの処理ができるように、高級言語で書かれたファイルをプリコンパイルしてからブラウザに渡しています。

このようなプリコンパイルを行うための手法として、アセットパイプラインという仕組みがあります。

アセットパイプライン

JavaScriptやCSSなどのアセットと呼ばれる静的ファイルを小さくまとめてくれる機能です。
アセットパイプラインには、高級言語のプリコンパイルを行う機能も追加できるため、これを応用してSCSSやTypeScriptなどを使用した開発もできるようになります。

アセットパイプラインの処理は、「①プリコンパイル」「②連結」「③圧縮」「④配置」の流れで行われます。
複数の静的ファイルをプリコンパイルして連結したのち、圧縮して軽量化したものをpublicディレクトリに配置して、ブラウザへ渡せるようにします。

これまでRailsでは、CSSやJavaScript等をプリコンパイルするために、SprocketsというGemによるアセットパイプラインをデフォルトで用いていました。

しかし、近年JavaScriptのライブラリ機能を合わせて開発することが大体となってきたため、Railsではバージョン6以降から、モジュールバンドラを使用したプリコンパイルを主流とするようになりました。

モジュールバンドラ

モジュールバンドラは、JavaScriptのモジュールを依存関係を考慮しながら管理するツールです。

モジュールとは、機能を1つずつ分けて他のファイルから読み込めるようにした処理のまとまりのことです。
JavaScriptでは複数の機能をファイルごとに分割すると、1つのファイルへまとめる際に不具合が生じるため、Node.js環境では機能をモジュールという単位で扱います。

モジュールによって、大量の機能を複数のファイルに分けることができ、分けられた機能を1つのファイルにまとめても問題なく動作できます。
ただし、複数に分かれるため依存関係が生まれ、読み込みができてないモジュールがあると正しく動作できない問題があります。

これを解消するのが、モジュールを一括で束ねて管理してくれるモジュールバンドラです。
近年では、モジュールバンドラの中でもwebpackが主流となりつつあります。

webpack

webpackとは、Webアプリケーションを作成する際に必要な、さまざまなJavaScriptをひとまとめに管理するためのツールです。
webpackが行うことは、大きく分けて4つあります。

基本要素 役割
Entry 依存関係を解決するために、どのファイルを基準(エントリーポイント)とするかを決める
Output エントリーポイントにされ、webpackによってまとめられたファイルを、どこへどのような名前で出力(アウトプット)するのか指定する
Loaders JavaScript以外のCSSやHTMLなどのファイルをモジュールに変換する方法を読み込み(ロード)、指定した処理を行う
Plugins 圧縮などの、ファイルをまとめる以外でローダーが実行できないタスクを導入し、拡張(プラグイン)する

つまり、webpackを用いることで、JavaScriptのライブラリとJavaScript以外のさまざまな言語を変換・圧縮した上で、好きな場所に配置することが可能となります。

Railsにもwebpackを導入してコマンド操作が可能ですが、設定ファイルの記述がやや難しいことから、設定を簡易化してくれるWebpackerというGemがあります。

Webpacker

webpackをRails仕様にし、専用の設定ファイルやヘルパーメソッドを用意してくれるGemです。Railsバージョン6系以降からは、デフォルトでWebpackerが導入されます。
Webpackerによって、Sprocketsのアセットパイプラインと同じような静的ファイルのプリコンパイルに加え、JavaScriptのパッケージが利用できるようになります。

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

レコードの新規追加時に登録データにMAX+1のIDを付与する方法

Railsでレコードの新規追加をする際に、自動的に独自IDの付与をしたいケースがあると思います。
例えば、my_idフィールドの最大値+1を付与するなど。

部屋(room)の名前を登録したときに、独自id = my_id を付与するサンプルを書いてみました。

成功例

  def assign
    my_id = -1
    room = Room.find_by(room: params[:room])
    if !room.nil?
      my_id = room.my_id
    else
      Room.connection.execute(
        "INSERT INTO rooms (my_id, room, created_at, updated_at) SELECT COALESCE(max(my_id), 0)+1, '#{params[:room]}', '#{Time.now}', '#{Time.now}' from rooms"
      )
      my_id = Room.find_by(roomr: params[:room]).my_id
    end
    redirect_to("/rooms/#{my_id}")
  end

ポイントは

  • my_idの最大値検索をINSERT文のサブクエリで行い、一回のクエリ発行になるようにすること (排他)
  • created_at, update_atに現在時刻を設定すること
  • COALESCEでnull対策をすること

失敗例

  def assign
    my_id = -1
    room = Room.find_by(room: params[:room])
    if !room.nil?
      my_id = room.my_id
    else
      new_room = Room.create(
        my_id: Room.max(my_id) + 1,
        room: params[:room]
      )
      my_id = new_room.my_id
    end
    redirect_to("/rooms/#{my_id}")
  end

失敗例のほうでは、begin transaction ~ commit transaction の間に

  1. SELECT文 --> Room.max(my_id)の検索のため
  2. INSERT文 --> Room.createのため

が発行されるのですが、
複数のクライアントから同時にassignを呼ばれた際に排他が十分でなく
my_idが重複してしまいます

本当は

本当は生SQLは発行せずにActiveRecordで記述したいのですが、
いい方法ないですかね?

参考URL

INSERT時にカラムの最大値+1を持ってくる
INSERT文と同時にMAX+1を行いたいです。
INSERT時にデータ登録とmaxの発番がしたい
ActiveRecord の find_or_create_by を確実に実行するには

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

【整理】render&redirect_toが、ごっちゃになってしまうあなたへ

対象者

なんかredirect_toとかrenderとかそれぞれどんな違いあるんだっけ?使い方は?と、知ってるけど頭の中で整理できていない方

前提知識

Railsのコントローラにおけるアクションでは、最終的にrenderされてビューを返す仕組みになっている。
つまり、1アクションにつき、1renderが必ず起こる。

def index
end

これは、render 'index'が発動していることと同じことである。

エラー例

renderが2回処理されてしまう時

def show
  ~~
    if ~~
      ~~
      render 'index'
    end
  render 'show'
end

解決策としては、returnをしてあげる

def show
  ~~
    if ~~
      ~~
      render 'index'
      return
    end
  render 'show'
end

render

アクション内で、呼び出すビューを指定するメソッド。
つまり、デフォルトで呼び出されるビュー以外のものを呼び出すことができる。
呼び出すビューの形式は、RHTML形式(.html / html.erb等)

  • 同じコントローラの別アクションビューを呼び出す時
microposts_controller.rb
def index
  render 'edit'
  render :edit
end
  • 別コントローラのアクションビューを呼び出す時
microposts_controller.rb
def index
  render "users/show"
  render template: "users/show"
end
  • 別ディレクトリや別アプリのアクションビューを呼び出す時
microposts_controller.rb
def index
  render "/warehouse_app/app/views/products/show"
  render file: "/warehouse_app/app/views/products/show"
end

redirect_to

指定したURLへGETリクエストを送信するメソッド。
つまり、アクション内で発動すれば、再度RCVを呼び出すことになる。

  • 指定のページに飛ばす
microposts_controller.rb
def index
  redirect_to root_url
end
  • 直前のページに戻す
microposts_controller.rb
def index
  redirect_back(fallback_location: root_path)
end

参考にさせていただきました

Railsガイドライン
https://railsguides.jp/layouts_and_rendering.html#render%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B

@1ulce様
https://qiita.com/1ulce/items/282cccba1e44158489c8

@morikuma709様
https://qiita.com/morikuma709/items/e9146465df2d8a094d78

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

【Ruby+Nokogiri】でyahooニュースをスクレイピング→CSV保存

以前PythonのBeautifulSoupを使ってスクレイピングをしたことがあったのですが、RubyでもNokogiriというライブラリで実現できるということで試してみました。

でまずは完成コード、完成品から

scraping.rb
require 'nokogiri'
require 'open-uri'
require "csv"

require "byebug"


url_base = "https://news.yahoo.co.jp/"

def get_categories(url)
  html = open(url)
  doc = Nokogiri::HTML.parse(html)
  categories = doc.css(".yjnHeader_sub_cat li a")
  categories.map do |category|
    cat_name = category.text
    cat = category[:href]
  end
end

@cat_list = get_categories(url_base)
@infos = []


@cat_list.each do |cat|
  url = "#{url_base + cat}"
  html = open(url)
  doc = Nokogiri::HTML.parse(html)
  titles = doc.css(".topicsListItem a")
  i = 1
  titles.each do |title|
    @infos << [i,title.text]
    i += 1
  end
end

CSV.open("result.csv", "w") do |csv|
  @infos.each do |info|
    csv << info
    puts "-------------------------------"
    puts info
  end
end

スクリーンショット (94).png

それぞれ解説していきます。

ファイルの読み込み

require 'nokogiri'
require 'open-uri'
require "csv"

require "byebug"

今回使うのはNokogiriとopen-uri、そしてCSV保存のためのcsvです。

NokogiriはHTMLやXMLコードを解析し、セレクターによって抜き出してくれるRubyのライブラリです。セレクターはcssの他にxpathでも指定できるので、複雑な構造をしたページでもスクレイピングがスムーズに行えます。

スクレイピング先のページ構造

スクリーンショット (88).png
スクリーンショット (89).png

今回は各トピックのタイトルを取得していき、最終的にCSVファイルにまとめます。

トピックのページはyjnHeader_subというクラスのliの中にあるリンク(aタグ)から繋がっているようです。

カテゴリーごとのリンクを取得

url_base = "https://news.yahoo.co.jp/"

def get_categories(url)
  html = open(url)
  #parseで読み込んだURLのHTMLコードを取得
  doc = Nokogiri::HTML.parse(html)
  #cssセレクターを使い、先ほどのカテゴリーに繋がっているaタグをすべて取得
  categories = doc.css(".yjnHeader_sub_cat li a")
  categories.map do |category|
    #取得したaタグからhrefの中身(リンク先のURL)をひとつづつ取り出して返します
    cat = category[:href]
  end
end

#@cat_listとして取得したリンクをまとめておきます
@cat_list = get_categories(url_base)

トピックのタイトルを取得

先ほど取得したリンクを用いてトピックごとのタイトルを取得していきます。

@infos = []

@cat_list.each do |cat|
 #トピックページのURLは元のURL + 取得したURLのため
  url = "#{url_base + cat}"
  html = open(url)
  doc = Nokogiri::HTML.parse(html)
  titles = doc.css(".topicsListItem a")
  i = 1
  titles.each do |title|
  #CSVにまとめるためにトピックのナンバーとタイトルをセットで格納します
    @infos << [i,title.text]
    i += 1
  end
end

取得したタイトルをCSVにまとめる

最後にまとめたタイトルをCSVに保存します。

#CSVライブラリを使い"result.csv"を新規作成
CSV.open("result.csv", "w") do |csv|
  @infos.each do |info|
  #csvに追加するとともにログとして使いした項目を出力しています。
    csv << info
    puts "-------------------------------"
    puts info
  end
end

スクリーンショット (90).png

文字化け対策

しかしこのままではおそらく文字化けしてしまうためBOMを付けて保存し直します。
(本来であればCSV保存中に行うのが正しいのですが、上手くいかなかったためいったんこちらで対応しました)

"result.csv"をメモ帳でopenして、上書き保存を選択。

スクリーンショット (92).png

スクリーンショット (93).png

この時UTF-8(BOM付)を選択して保存し直してください。

スクリーンショット (94).png

もう一度csvを開くと文字化けが解消されています。

最後に

まだまだ至らない点が多いと思うのでご指摘があればコメントいただけると幸いです。

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

Rubyスクレイピング-VPSでSeleniumをHeadlessに動かす。

開発環境

ruby2.7.1
centos7

手順

①Gemを追加しよう

gem install gem install selenium-webdriver

②GoogleChromeをインストール

インストールする
yum install google-chrome-stable
バージョン確認しておく
google-chrome -version

③GoogleChromeをインストール

http://chromedriver.chromium.org/downloads からgoogle-chromeと同じバージョンのChromeDriverをダウンロードする

下記はGoogleChromeがバージョン83.0.4103.39のコマンド
wget https://chromedriver.storage.googleapis.com/index.html?path=83.0.4103.39/

展開
unzip chromedriver_linux64.zip
移動
sudo mv chromedriver ~/usr/local/bin/
権限変更
sudo chmod 755 /usr/local/bin/chromedriver

準備完了

④Seleniumをつかったサンプルコード

scraping.rb
def scraping
  url = 'https://google.com/' #開きたいURL
  options = Selenium::WebDriver::Chrome::Options.new #オプションをNew
  options.add_argument('--headless') #ヘッドレスオプション追加
  options.add_argument('--disable-gpu') #GPUを向こう
  options.add_argument('--window-size=4000,4000') #画面を最大化
  driver = Selenium::WebDriver.for :chrome, options: options #オプションを反映してdriverをNew
#お好きにスクレイピングコードを
.
.
.
.
end

--headless--disable-gpuはLinuxのためヘッドレスで処理するために追加
--window-size=4000,4000のオプションの追加は画面に表示されている部分でしかスクレイピングができないので書いてます。

linuxでGUIを表示したい場合は別途バーチャルディスプレイをインストールする必要あり

※ビットコインの自動売買ツールを制作する人に向けて(最近多いので)

SeleniumはAPIがないサイトを自動化するのに向いてます。
ビットコインの自動売買とかでしたらSeleniumは必要ないです。下記のようにBitflyerのAPIを使って作った方が良いと思います。
http://benzenetarou.hatenablog.com/entry/bitcoin/automatic_trade/1

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

消せいないプロセスID(PID)の消去方法!(Vim、Viでの操作不能の対処)

$ sudo visudoのコマンド入力できなくなり凄く困ったので解決方法を記載します!

 何かの不具合で、.ssh %の状態もしくは[ec2-user@ip-xx-x-x-xxx] $から強制終了されるとターミナルでのコマンド入力ができなくなる時があります。

 私も、過去に似たような経験をしたことがあり、多分プロセスIDが正常に切られなかったため、ec2-userにログインした際、$ sudo visudoが入力できないと予測しました。
発生したエラーは下記です

エラー文
visudo: /etc/sudoers がビジー状態です。後で再試行してください

 この問題を可決するのに凄く時間を費やしたので、プロセスID(PID)の一般的な消去方法プロセスID(PID)が無限に増殖する際の消去方法について記載したいと思います。

1. 基本のプロセスID(PID)の消去方法!

1. ターミナルでルートディレクトリーに移動する。または、.ssh %の状態もしくは[ec2-user@ip-xx-x-x-xxx] $にする。

ターミナルの初期画面の状態
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ % (例です)

2.psを入力する

ルートディレクトリー
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ % ps
.ssh
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ .ssh % ps
ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ ps

3.PID(プロセスID)が表示されるのを確認する

psを入力すると下記の図のような画面が表示されると思います。
スクリーンショット 2020-06-30 1.07.29.png

4.PID(プロセスID)をkillorkill -9 で削除する

(私はec2-userの状態でエラーが発生したため、ec2-user状態での記載例を載せる)

[ec2-user@ip-xx-xx-xx-xxx ~]$ kill PIDの数値 or kill -9 PIDの数値
[           入力例  ~] $ kill *** or kill -9 *** (*** = PID)

kill -9 (PID番号) で入力すると強制的に終了させる事ができる。

5.PID(プロセスID)が消去できれば、問題なく動作可能

私の場合、この手順ではPID(プロセスID)の消去ができませんでした。実際に起きていた問題は、PID(プロセスID)を消去しても無限に再生される状態でした。

2. PID(プロセスID)の無限出現の消去方法

ここからが本題です!
私を苦しめたPID(プロセスID)の無限出現です。消し方が分かればすごく簡単に直せます!

1. ターミナルでルートディレクトリーに移動する。または、.ssh %の状態もしくは[ec2-user@ip-xx-x-x-xxx] $にする。

ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ (例です)

私は、ec2-user内で発生したので、上記の状態で説明を続ける

2.ps l を入力する

ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ ps l

3.プロセスごとの実行状態を確認する

  • 下記のような画面が表示されると思います スクリーンショット 2020-06-30 0.29.24.png

4.WCHANの列にあるwait_wのPID(プロセスID)をkillで消去する

詳細画面を載せておきます スクリーンショット 2020-06-30 0.29.35.png

killやり方
[ec2-user@ip-xx-xx-xx-xxx ~]$ kill PIDの数値 or kill -9 PIDの数値
[           入力例  ~] $ kill *** or kill -9 *** (*** = PID)

5.無限増殖するプロセスID(PID)の消去ができたため、無事に入力可能となる!

参考資料

psのコマンドの種類は下記のps コマンド集に記載されてます。
もし、ご興味がありましたら見て下さい!
ps コマンド集

最後に

以上で、消せいないプロセスID(PID)の消去方法(プロセスID無限増殖)の説明を終わりたいと思います。間違っているてん、不明点があればご指摘頂けると助かります。

最後までご覧いただき、ありがとうございました。

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

消せないプロセスID(PID)の消去方法!(Vim、Viでの操作不能の対処)

$ sudo visudoのコマンド入力できなくなり凄く困ったので解決方法を記載します!

 何かの不具合で、.ssh %の状態もしくは[ec2-user@ip-xx-x-x-xxx] $から強制終了されるとターミナルでのコマンド入力ができなくなる時があります。

 私も、過去に似たような経験をしたことがあり、多分プロセスIDが正常に切られなかったため、ec2-userにログインした際、$ sudo visudoが入力できないと予測しました。
発生したエラーは下記です

エラー文
visudo: /etc/sudoers がビジー状態です。後で再試行してください

 この問題を可決するのに凄く時間を費やしたので、プロセスID(PID)の一般的な消去方法プロセスID(PID)が無限に増殖する際の消去方法について記載したいと思います。

1. 基本のプロセスID(PID)の消去方法!

1. ターミナルでルートディレクトリーに移動する。または、.ssh %の状態もしくは[ec2-user@ip-xx-x-x-xxx] $にする。

ターミナルの初期画面の状態
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ % (例です)

2.psを入力する

ルートディレクトリー
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ % ps
.ssh
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ .ssh % ps
ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ ps

3.PID(プロセスID)が表示されるのを確認する

psを入力すると下記の図のような画面が表示されると思います。
スクリーンショット 2020-06-30 1.07.29.png

4.PID(プロセスID)をkillorkill -9 で削除する
(私はec2-userの状態でエラーが発生したため、ec2-user状態での記載例を記載します)

[ec2-user@ip-xx-xx-xx-xxx ~]$ kill PIDの数値 or kill -9 PIDの数値
[           入力例  ~] $ kill *** or kill -9 *** (*** = PID)

kill -9 (PID番号) で入力すると強制的に終了させる事ができる。

5.PID(プロセスID)が消去できれば、問題なく動作可能

私の場合、この手順ではPID(プロセスID)の消去ができませんでした。実際に起きていた問題は、PID(プロセスID)を消去しても無限に再生される状態でした。

2. PID(プロセスID)の無限増殖の消去方法!

ここからが本題です!
私を苦しめたPID(プロセスID)の無限増殖です。消し方が分かればすごく簡単に直せます!

1. ターミナルでルートディレクトリーに移動する。または、.ssh %の状態もしくは[ec2-user@ip-xx-x-x-xxx] $にする。

ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ (例です)

私は、ec2-user内で発生したので、上記の状態で説明を続けます。

2.ps l を入力する

ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ ps l

3.プロセスごとの実行状態を確認する
下記のような画面が表示されると思います
スクリーンショット 2020-06-30 0.29.24.png

4.WCHANの列にあるwait_wのPID(プロセスID)をkillで消去する

詳細画面を載せておきます 
スクリーンショット 2020-06-30 0.29.35.png

killやり方
[ec2-user@ip-xx-xx-xx-xxx ~]$ kill PIDの数値 or kill -9 PIDの数値
[           入力例  ~] $ kill *** or kill -9 *** (*** = PID)

5.無限増殖するプロセスID(PID)の消去ができたため、無事に入力可能となる!

参考資料

psのコマンドの種類は下記のps コマンド集に記載されてます。
もし、ご興味がありましたら見て下さい!
ps コマンド集

最後に

以上で、消せないプロセスID(PID)の消去方法(プロセスID無限増殖)の説明を終わりたいと思います。間違っている点がありましたらご指摘頂けると助かります。

最後までご覧いただき、ありがとうございました。

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

[RubyOnRails]テーブルデータを基にform_withにプルダウンを実装する方法

内容

form_withにプルダウン選択を導入しようと思い実装していく中で、何個かトライアンドエラーを繰り返した所があったので、備忘として残しておくもの。

環境

・Ruby 2.6.5
・Rails 6.0.3

前提の状況とやりたいこと

状況

飲食店の情報を登録するshopテーブルがある。
山手線の駅データをリストとして入れたstationテーブルがある。
二つのテーブルは多対多の関係にあり、中間テーブル(shop_stationテーブル)を介してアソシエーションを組んでいる。

やりたいこと

以下の機能を実装したい。
①shopデータ作成時に、最寄り駅をプルダウンで選択し登録したい。
②最寄り駅の登録は任意で登録しなくても良い。登録できるのは最大で2駅まで。
③当然、一回のフォーム送信で一度に全ての最寄り駅を登録したい。

上記の実装手順を備忘として残していく。

※ちなみに、静的なデータを登録する場合はテーブルをいちいち作るのではなくアクティブハッシュでリストを生成することがベターという記事をいくつも読んだのですが、今回の様に多対多の関係となる場合(そしてshopが複数のstationを持つ場合)の取り扱いがあまり見えてこず、テーブルを用いて作っています。
例えばshopが一つのstationしか持たないのであれば、アクティブハッシュのidをstation_idとして登録すれば良いと思うのですが、複数持つ場合はテーブルの正規化の観点から別テーブルが好ましいよなと思い、その場合にアクティブハッシュをどう活かすのかが見えてきませんでした。
どなたかわかる方がいらっしゃったら教えていただけるとありがたいです。

さて以下で手順を書いていきます。

①テーブルデータを基にプルダウンの選択肢を作成、実装する

今回はshopコントローラのnewアクション内にフォームを実装していきます。
先にコードを抜粋し書いていきます。

Controller

shops_controller.rb
before_action :set_select_lists, only: [:new]

def new
    @shop = Shop.new
  end

private
  def set_select_lists
    @stations = Station.all.map {|station| [station.name, station.id] }
  end

View

new.html.haml
.shop-wrapper
  = form_with model: [@owner, @shop], html: {class: "shopform"}, local: true do |f|

-# 中略(syntaxハイライトを当てるためインデントは適当です。すいません。hamlの記法に従ってインデントしてください)

= f.select :station_ids, @stations,{},{class: "select"}
= f.select :station_ids, @stations,{},{class: "select"}
*最寄り駅を登録することで駅指定検索に表示される様になります

それぞれの説明は以下の通りです。

Controller

@stationsで今回の選択肢の基になるデータを作成しています。
ざっくり説明していくと以下の通りです。

Station.all
Stationテーブルの全データ

Station.all.map {~}
Stationテーブル全データの中の各データ(レコード)に関し、{}の通りに処理していく

Station.all.map {|station| [station.name, station.id]}
各レコードをstationとし、station.name(各レコードのnameカラムの値)をキー、station.id(idカラムの値)をバリューとするハッシュを生成する。

View

= f.select :station_ids, @stations,{},{class: "select"}

form_withでデータを送る。
f.selectでプルダウンを作成。データを送る際に:station_idsというキーで送る。選択肢は@stationsを基に作る。クラス名はselect。

コントローラーで定義したキーが「プルダウンに表示される選択肢」、バリューが「paramsで送る値」になります。
人が選ぶときは駅名で選んで、データ登録時はidで紐付けたいですよね。だからこの様にキーとバリューを設定した訳です。

※ちなみにクラス属性は第四引数で設定しなければならないため、空の第三引数を存在させるため間に{}が入っています。

これらを実装した図が以下の通りです。

スクリーンショット 2020-06-29 23.56.04.png

同じプルダウンが二つ並んでいて、一つを開いている図です。確かにstation.nameが選択肢として表示されていますね。

②最寄り駅の登録を任意登録にする

先ほどの画像だと、テーブルデータでリストを作っているため、「駅を選択しない」という「選択肢」が存在しないです。
よくある「以下から選んでください」を作るため、以下の通りコントローラを書き換えました。

shops_controller.rb
def set_select_lists
    @stations = Station.all.map {|station| [station.name, station.id] }.unshift(["以下から選んでください", nil])
end

先ほどの@stationsに、.unshift(["以下から選んでください", nil])と追記しています。
unshiftは配列の一番最初に要素を追加するメソッドです。キーとして「以下から選んでください」という文章を、バリューにnilを渡す選択肢を追加したのです。

テーブルのデータ自体に「最寄り駅がない」というようなレコードを追加するのは中間テーブルも無駄にレコードが増えるし好ましくないと判断し、バリューをnilとする選択肢を自分で追加した形ですね。

スクリーンショット 2020-06-30 0.01.57.png

これで無事、思い通りのプルダウンが実装できました。

③一回のフォーム送信で一度に全ての最寄り駅を登録する

ここまできたらもう登録できるだろと思ったんですけど、実際にparamsからデータを飛ばした時、以下の問題が生じました。
プルダウンが二つともstation_idsという同名のキーでデータを送っているため、2個目の値が1個目の値を上書きし、1つのみ値を送るという結果になっている。

binding.pryで見ると一目瞭然でした。
しかしこちらのやりたいこととしては複数のstation_idsを送り登録したいのです。すなわち、station_idsを配列で送りたいのです

やり方は何個かあると思うのですが、僕はviewを以下の通り書き換えました(これ多分力技なんですが、結構その後もフォームで活用できました)

new.html.haml
= f.select :station_ids, @stations,{},{name: 'shop[station_ids][]', class: "select"}
= f.select :station_ids, @stations,{},{name: 'shop[station_ids][]', class: "select"}

先ほどの状態にname属性を追加しています。
form_withでname属性をつけると、「どの様な形でそのparamsを送るのか?」を指定できます。

実際にbinding.pryでparamsを見るとどの様な形でデータが送られているのかがわかります。
station_idsは params[:shop][:station_ids]という形で送られます。

これを配列にしたかったので、name属性で最後に[]をふしてあげることで無事配列の形で両方のデータを送ることができる様になりました。

shops_controller.rb
def shop_params
    params.require(:shop).permit(:name, :address, :capacity, :owner_id, :genre_id, :mark_ids,introduces_attributes: [:content, :image, :number],station_ids: [])
end

ちなみに先ほどparams[:shop][:station_ids]の形でデータが送られていると行ったのですが、だから上記の様にストロングパラメータではまず:shopをrequireして、その中のキーをpermitすることで無事データが入るわけです。
これはbinding.pryしまくる中でよく理解できました。面白かった。

終わりに

name属性で指定するやり方、結構力技感あるのでもっとスマートなやり方会ったら教えて欲しいです。
が、結構この後のフォームの各値もゴリゴリこれで実装していきました。

binding.pryでparamsがどんな形で送られているか見るのが一番勉強になりましたね。

*初学者ゆえ何かあればご指摘いただけると嬉しいです。

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