- 投稿日:2020-06-30T22:16:50+09:00
アプリのテストについて RSpec(単体テスト)
RSpec
Rubyを元に作成されたテストに特化した言語です
テストコードの基本
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(バリデーションされれば)など複数のマッチャが存在します。
- 投稿日:2020-06-30T20:02:33+09:00
【Rails】lightbox2を用いて画像拡大機能の実装
目標
開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina前提
下記実装済み。
実装
1.
application.html.slim
を編集application.html.slimdoctype 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
- 投稿日:2020-06-30T18:01:40+09:00
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 にアクセスして気象情報を抽出して表示しています。
おわりに
イメージが湧きましたでしょうか?もっといい表現があればおしえて!
- 投稿日:2020-06-30T17:56:19+09:00
【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
にする気があるのなら、早々に引導を渡してほしいと思う今日この頃です。
- 投稿日:2020-06-30T16:55:13+09:00
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対応は後日にしようと思います。
- 投稿日:2020-06-30T14:06:20+09:00
[Ruby] CSVファイルをYaml(Yml)に変換する方法
手順
1、requireメソッドでRubyの標準ライブラリのcsvとyml を読み込む。
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などのクラスメソッドが使える)ができるので便利です。
- 投稿日:2020-06-30T12:26:22+09:00
ブラウザが認識できる言語に変換する仕組み
高級言語と低級言語
・高級言語
高級言語は、機械よりも人間が理解しやすいように設計されたプログラミング言語のことです。
また、「高級」とは優れた言語という意味ではなく、機種や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のパッケージが利用できるようになります。
- 投稿日:2020-06-30T11:30:41+09:00
レコードの新規追加時に登録データに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 の間に
- SELECT文 --> Room.max(my_id)の検索のため
- INSERT文 --> Room.createのため
が発行されるのですが、
複数のクライアントから同時にassignを呼ばれた際に排他が十分でなく
my_idが重複してしまいます本当は
本当は生SQLは発行せずにActiveRecordで記述したいのですが、
いい方法ないですかね?参考URL
INSERT時にカラムの最大値+1を持ってくる
INSERT文と同時にMAX+1を行いたいです。
INSERT時にデータ登録とmaxの発番がしたい
ActiveRecord の find_or_create_by を確実に実行するには
- 投稿日:2020-06-30T09:55:48+09:00
【整理】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' endrender
アクション内で、呼び出すビューを指定するメソッド。
つまり、デフォルトで呼び出されるビュー以外のものを呼び出すことができる。
呼び出すビューの形式は、RHTML形式(.html / html.erb等)
- 同じコントローラの別アクションビューを呼び出す時
microposts_controller.rbdef index render 'edit' render :edit end
- 別コントローラのアクションビューを呼び出す時
microposts_controller.rbdef index render "users/show" render template: "users/show" end
- 別ディレクトリや別アプリのアクションビューを呼び出す時
microposts_controller.rbdef index render "/warehouse_app/app/views/products/show" render file: "/warehouse_app/app/views/products/show" endredirect_to
指定したURLへGETリクエストを送信するメソッド。
つまり、アクション内で発動すれば、再度RCVを呼び出すことになる。
- 指定のページに飛ばす
microposts_controller.rbdef index redirect_to root_url end
- 直前のページに戻す
microposts_controller.rbdef 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
- 投稿日:2020-06-30T09:22:39+09:00
【Ruby+Nokogiri】でyahooニュースをスクレイピング→CSV保存
以前PythonのBeautifulSoupを使ってスクレイピングをしたことがあったのですが、RubyでもNokogiriというライブラリで実現できるということで試してみました。
でまずは完成コード、完成品から
scraping.rbrequire '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それぞれ解説していきます。
ファイルの読み込み
require 'nokogiri' require 'open-uri' require "csv" require "byebug"今回使うのはNokogiriとopen-uri、そしてCSV保存のためのcsvです。
NokogiriはHTMLやXMLコードを解析し、セレクターによって抜き出してくれるRubyのライブラリです。セレクターはcssの他にxpathでも指定できるので、複雑な構造をしたページでもスクレイピングがスムーズに行えます。
スクレイピング先のページ構造
今回は各トピックのタイトルを取得していき、最終的に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文字化け対策
しかしこのままではおそらく文字化けしてしまうためBOMを付けて保存し直します。
(本来であればCSV保存中に行うのが正しいのですが、上手くいかなかったためいったんこちらで対応しました)"result.csv"をメモ帳でopenして、上書き保存を選択。
この時UTF-8(BOM付)を選択して保存し直してください。
もう一度csvを開くと文字化けが解消されています。
最後に
まだまだ至らない点が多いと思うのでご指摘があればコメントいただけると幸いです。
- 投稿日:2020-06-30T02:07:45+09:00
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.rbdef 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
- 投稿日:2020-06-30T02:06:19+09:00
消せいないプロセス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.sshxxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ .ssh % psec2-use[ec2-user@ip-xx-xx-xx-xxx ~]$ ps3.PID(プロセスID)が表示されるのを確認する
4.PID(プロセスID)を
kill
orkill -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 l3.プロセスごとの実行状態を確認する
4.
WCHAN
の列にあるwait_w
のPID(プロセスID)をkill
で消去する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無限増殖)の説明を終わりたいと思います。間違っているてん、不明点があればご指摘頂けると助かります。
最後までご覧いただき、ありがとうございました。
- 投稿日:2020-06-30T02:06:19+09:00
消せないプロセス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.sshxxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ .ssh % psec2-use[ec2-user@ip-xx-xx-xx-xxx ~]$ ps3.PID(プロセスID)が表示されるのを確認する
4.PID(プロセスID)を
kill
orkill -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 l3.プロセスごとの実行状態を確認する
下記のような画面が表示されると思います
4.
WCHAN
の列にあるwait_w
のPID(プロセスID)をkill
で消去する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無限増殖)の説明を終わりたいと思います。間違っている点がありましたらご指摘頂けると助かります。
最後までご覧いただき、ありがとうございました。
- 投稿日:2020-06-30T00:14:29+09:00
[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.rbbefore_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] } endView
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で紐付けたいですよね。だからこの様にキーとバリューを設定した訳です。※ちなみにクラス属性は第四引数で設定しなければならないため、空の第三引数を存在させるため間に{}が入っています。
これらを実装した図が以下の通りです。
同じプルダウンが二つ並んでいて、一つを開いている図です。確かにstation.nameが選択肢として表示されていますね。
②最寄り駅の登録を任意登録にする
先ほどの画像だと、テーブルデータでリストを作っているため、「駅を選択しない」という「選択肢」が存在しないです。
よくある「以下から選んでください」を作るため、以下の通りコントローラを書き換えました。shops_controller.rbdef set_select_lists @stations = Station.all.map {|station| [station.name, station.id] }.unshift(["以下から選んでください", nil]) end先ほどの@stationsに、.unshift(["以下から選んでください", nil])と追記しています。
unshiftは配列の一番最初に要素を追加するメソッドです。キーとして「以下から選んでください」という文章を、バリューにnilを渡す選択肢を追加したのです。テーブルのデータ自体に「最寄り駅がない」というようなレコードを追加するのは中間テーブルも無駄にレコードが増えるし好ましくないと判断し、バリューをnilとする選択肢を自分で追加した形ですね。
これで無事、思い通りのプルダウンが実装できました。
③一回のフォーム送信で一度に全ての最寄り駅を登録する
ここまできたらもう登録できるだろと思ったんですけど、実際に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.rbdef 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がどんな形で送られているか見るのが一番勉強になりましたね。
*初学者ゆえ何かあればご指摘いただけると嬉しいです。