edy hub

プログラミングやライフスタイルについて書き綴っています

シェルスクリプトのtrap

trapは シェルスクリプトの実行結果に関わらず、 trapコマンドを後処理として実行させることができるようになります。

trap 'コマンド' シグナルリスト

※実行中のシェルスクリプトに対して送出されたシグナルは、trap コマンドを使用することで捕捉することが可能です。

シグナルについては、こちらを参考に

shellscript.sunone.me

さらに終了時のシグナルを分けて指定することで、エラー時・正常終了時のパターンに応じた処理の分担もできるようになります! スクリプトが途中終了した場合があったとしても、飛ぶ鳥跡を濁さずということで、綺麗に保ってくれるのです。

参考

qiita.com

RailsのCarrierwaveをコンソールで試してみる

はじめに

Railsで画像をアップロードする際に、gem 'carrierwave' を使用しています。

github.com

コンソールで画像をアップロードする

例の如く、コンソールを立ち上げます。

rails c
uploder = HogeUploader.new

image_file = Pathname.new(Rails.root.join("db/images/mcdonalds_logo.png")).open

uploader.store!(imge_file)

これで、画像の保存を試すことができます。 自分はバリデーションがワークしているかのチェックに使いました。

railsで任意のURLにJSONをPOSTする方法

特定のURLをSlackのWebhookとします。

def post_man
  url = 'https://hooks.slack.com/services/xxxxxx/yyyyy/zzzzz'
 
 content = { text: 'This post is from Ruby on Rails', icon_emoji: ':ghost:' }
 uri  = URI.parse(WEBHOOK_URL)
 http = Net::HTTP.new(uri.host, uri.port)
 http.use_ssl = true
 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
 http.start do
   request = Net::HTTP::Post.new(uri.path)
   request.set_form_data(payload: content.to_json)
   http.request(request)
 end
end

参考記事

masawada.hatenablog.jp

便利Gemを発見

なんとかPOSTできましたが、そんなときに gem 'rest-client' という便利Gemの存在を知りました。

github.com

こやつのお蔭で冗長だった、記述がここまで簡潔に!!

def post_man
  url = 'https://hooks.slack.com/services/xxxxxx/yyyyy/zzzzz'
  body = params.to_json
  content_type = :json
  RestClient.post(url, body, content_type: content_type)
end

Railsでモジュールを作ってインクルードして使うまで

特定の処理をモジュール化したい

Railsで独自実装などを記しておく、自前のモジュールを使いたい場合が誰にでもあると思います。 その際の準備など書き記しておきます。

方法

下記に則って進めましょう

  • 命名規則に沿ってモジュールを作る
  • app/libにそのモジュールを置く
  • 使用したいコントローラでincludeする

具体例

1. 命名規則に沿ってモジュールを作る

以下の命名規則に従ってモジュールを作ります。

  • ファイル名は小文字で書く
  • ファイル名の単語を_(アンダーバー)で区切る
  • クラス名(モジュール名)はアッパーキャメルケースで記述する

例えば、モジュール名がFooBarの場合、ファイル名はfoo_bar.rbとなります。

# lib/foo_bar.rb
class FooBar
  def hello
    puts 'Hello, World'
  end
end

2. app/libにそのモジュールを置く

上述の例で出したファイルのディレクトリにlibが登場しましたね。 作成したファイルはapp/lib配下に配置しましょう。

もしlib配下にさらにもう一段ディレクトリが存在した場合は、下記のパターンがあります。 例えば、lib/foo配下にファイルを設置するとします。

モジュールを用いる場合

# lib/foo/bar.rb
module Foo
  class Bar
    def hello
      puts "Foo::Bar: Hello, World"
    end
  end
end

moduleを用いない場合

# lib/foo/bar.rb
class Foo::Bar
  def hello
    puts "Foo::Bar: Hello, World"
  end
end

3. 使用したいコントローラでincludeする

例えば、controllers/sample_controller.rbでfoo_bar.rbのモジュールを使用する場合は以下のようにincludeします。これで当該モジュールが使えるようになりました。

class SampleController < ApplicationController
  include FooBar

  def index
    hello
    # => 'Hello, World'
  end

  ...
end

4.config/application.rbに一行追加

config.autoload_paths += %W(#{config.root}/lib)

これにて、autoloadの設定が完了し、命名の規約に従った場合に、lib ディレクトリ以下のファイルが自動的に読み込まれるようになります。

ただし、この記事だけを読んでも初歩の初歩でしか無いので、実務で使うとしたらこちらの記事が優れていると思うので、目を通すことをオススメします。

参考記事

Ruby on Rails で lib ディレクトリの自作クラスを使用する - Qiita

docker-compose upしたときに「A server is already running.」でサーバーが立ち上がらない問題

何が起きたか?

すでに動いているプロセスの影響でサーバの立ち上げがうまくいかない。

A server is already running. Check /myapp/tmp/pids/server.pid.

f:id:yuki-eda0629:20190609201411p:plain

解決方法

docker-compose up --buildする前にプロセスを消すようにした。

$ rm -f tmp/pids/server.pid

としてから、

$ docker-compose up --build を実行する。

補足

毎回プロセスを消すコマンドを打つのは面倒だし、忘れがちなのでdocker-compose.yml内に実行処理を書いておく。

rails serverの直前に実行したいので、下記のように記述した。

ポイントは、commandで複数実行することになるので、bash -cに変更したこと。

command: rails s -b 0.0.0.0 -p 3000 # 変更前
command: bash -c "rm -f tmp/pids/server.pid && rails s -b 0.0.0.0 -p 3000" # 変更後

Rspecを書く時に念頭においておきたいことのメモ

この記事の役割

  • 全体像をメインにメモをしていきます。
  • 各論も含むかもしれませんが、ボリュームが増大した場合は別記事に切り分けます。

体系的なまとめ

参考: RSpecを綺麗に書くための基本Rule - Qiita

この記事からの学び

命名規則

「AAメソッドは、BBの時、CCをDDします。」

AA : Describe → コントローラーのメソッド名を表記

describe 'GET #show'
describe 'GET /api/v1/users'

BB : Context → 状況を表記

when/withで始める

context "when book is present"
context "with valid params"

CC : Subject → テスト対象を表記

# eqの場合
subject{ assings(:book) }
subject{ Book.find(1)}

# change/render/redirect/http statusの場合
subject {Proc.new { get: index }}
subject {Proc.new { get :show, id: 1 }}
subject {Proc.new { get :new, id: 1 }}
subject {Proc.new { get :edit, id: 1 }}
subject {Proc.new { post :create, id: 1, book: @valid_params }}
subject {Proc.new { patch :update, id: 1, book: @valid_params }}
subject {Proc.new { destroy :delete, id: 1 }}

DD : it → テスト対象に対する動詞を表記

# eqの場合
it "主語 is 目的語"
it "主語 has 目的語"

# changeの場合

it "create 目的語"
it "update 目的語"
it "delete 目的語"

# renderの場合
it "render the template名"

# redirectの場合
it "redirect to path名"

# http statusの場合
it "returns http ok"
it "returns http success"
it "returns http 204"

Puppeteerでスクレイピングをする

はじめに

const puppeteer = require('puppeteer');

ファイル内でpuppeteerを読み込む

ファイル読み込み

ファイル操作を行うライブラリ

const fs = require('fs');

node.jsはShift-jisをサポートしていないので、通常の方法で読み込むと、文字化けしてしまう。 iconv-liteは様々な文字コードを変換するモジュール。

const iconv = require('iconv-lite');

Puppeteerクラスを使う

Puppeteer module provides a method to launch a Chromium instance. The following is a typical example of using Puppeteer to drive automation:

puppeteer/api.md at master · GoogleChrome/puppeteer · GitHub より

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  await page.goto('https://www.google.com');
  // other actions...
  await browser.close();
});
const browser = await puppeteer.launch();

JSの実行

JS実行は page.evaluate を使用します。 https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageevaluatepagefunction-args

例えば、submit要素をクリックする。

await page.evaluate(({}) => {
              $('input[type="submit"]').click();
        },{});

CSVのダウンロード

await page.goto(DLURL);        
        await page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: './store' });
        await page.evaluate(({}) => {
              $('img[src="/master/common/btn_dl_01.png"]').click();
        },{});
        await page.waitFor(4000)

await page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: './store' }); がよくわからない・・・

Githubで会話されていた

Question: How do I get puppeteer to download a file? · Issue #299 · GoogleChrome/puppeteer · GitHub