ハッキングバカ

プログラミングに関するメモ

Rails で Font-awesome 5 系を使う

RailsFont-awesome を使う場合 font-awesome-rails が一般的だったけれど、(今のところ) 5系に対応していない。
使いたい icon が5 系であることが多かったため、それらが使える font-awesome-sassに乗り換えることにした。
まずは gem に追加。font-awesome-rails があったらそちらは消しておく。

# Gemfile
gem 'font-awesome-sass', '5.3.1'

反映。

$ bundle update

アセットからインポートする。

/* app/assets/stylesheets/application.scss */
@import "font-awesome-sprockets";
@import "font-awesome";

これで view 中で

<%= icon("fas", "link") %>

のような形で使えるようになる。
font-awesome-rails からの移行も楽だろう。

公式: GitHub - FortAwesome/font-awesome-sass: Font-Awesome Sass gem for use in Ruby/Rails projects, Icons | Font Awesome
taka-no-MacBook-Air:~

Mariadb の文字コードを utf8mb4 に変更する[macOS]

環境: macOS 10.13.6 Mariadb 10.3.10

MariadbRails から絵文字を流し込むとエラーになった。
文字コードを確認。

MariaDB [(none)]>  show variables like 'char%';
+--------------------------+--------------------------------------------------------+
| Variable_name            | Value                                                  |
+--------------------------+--------------------------------------------------------+
| character_set_client     | utf8                                                   |
| character_set_connection | utf8                                                   |
| character_set_database   | utf8mb4                                                |
| character_set_filesystem | binary                                                 |
| character_set_results    | utf8                                                   |
| character_set_server     | utf8mb4                                                |
| character_set_system     | utf8                                                   |
| character_sets_dir       | /usr/local/Cellar/mariadb/10.3.9/share/mysql/charsets/ |
+--------------------------+--------------------------------------------------------+
8 rows in set (0.012 sec)

絵文字を扱うには utf8mb4 にする必要がある。
設定ファイルの include を有効化。

# /usr/local/etc/my.cnf
!includedir /usr/local/etc/my.cnf.d

my.cnf.d 下に設定ファイルを作成。

# /usr/local/etc/my.cnf.d/utf8mb4.cnf
[client-mariadb]
default-character-set = utf8mb4
[mariadb]
character-set-server = utf8mb4

mariadb を再起動して、確認。

MariaDB [(none)]> show variables like "chara%";
+--------------------------+---------------------------------------------------------+
| Variable_name            | Value                                                   |
+--------------------------+---------------------------------------------------------+
| character_set_client     | utf8mb4                                                 |
| character_set_connection | utf8mb4                                                 |
| character_set_database   | utf8mb4                                                 |
| character_set_filesystem | binary                                                  |
| character_set_results    | utf8mb4                                                 |
| character_set_server     | utf8mb4                                                 |
| character_set_system     | utf8                                                    |
| character_sets_dir       | /usr/local/Cellar/mariadb/10.3.10/share/mysql/charsets/ |
+--------------------------+---------------------------------------------------------+
8 rows in set (0.001 sec)

これでよし。
system だけちょっと気分が悪いがこれは変えられないらしい。
無事絵文字を使えるようになった。
ただしすでに作ったデータベースは utf8 のままなので再作成か変換が必要。

参考: Migrating UTF8 to UTF8-MB4 on MariaDB, MariaDB で 4バイトの utf を扱う設定 | Balun Software (Info)

Ruby インストール時の rdoc エラー[Ubuntu]

環境: ubuntu 16.04

新しく建てた ubuntu サーバに ruby を rbenv を用いてインストールしようとしたらエラーが発生した。

$ rbenv install 2.5.1
...
Generating RI format into /tmp/ruby-build.20180913055306.29926/ruby-2.5.1/.ext/rdoc...
Killed
uncommon.mk:491: recipe for target 'rdoc' failed
make: *** [rdoc] Error 137

インストール時のオプションを設定する。

$ RUBY_CONFIGURE_OPTS=--disable-install-doc rbenv install 2.5.1
...
Installed ruby-2.5.1 to /home/name/.rbenv/versions/2.5.1

参考: ruby 2.2.2 installation fails on Ubuntu 15.04 · Issue #223 · postmodern/ruby-install · GitHub

Clojure で チャットアプリを作る [Websocket]

環境: macOS 10.13.6, Leiningen 2.8.1 on Java 1.8.0_112

Clojure の Web フレームワークである Luminus を使えば Websocket を用いたチャットアプリが手軽に実現でき、パフォーマンスもすぐれているらしい。
まずは

$ lein new luminus chat

でプロジェクトを作成。
サーバサイドの Websocket コードを配置。

; src/clj/chat/routes/websocket.clj

(ns chat.routes.websocket
  (:require [compojure.core :refer [GET defroutes wrap-routes]]
            [clojure.tools.logging :as log]
            [immutant.web.async :as async]))

(defonce channels (atom #{}))

(defn connect! [channel]
  (log/info "channel open")
  (swap! channels conj channel))

(defn disconnect! [channel {:keys [code reason]}]
  (log/info "close code:" code "reason:" reason)
  (swap! channels #(remove #{channel} %)))

(defn notify-clients! [channel msg]
  (doseq [channel @channels]
    (async/send! channel msg)))

(def websocket-callbacks
  "WebSocket callback functions"
  {:on-open connect!
   :on-close disconnect!
   :on-message notify-clients!})

(defn ws-handler [request]
  (async/as-channel request websocket-callbacks))

(defroutes websocket-routes
  (GET "/ws" [] ws-handler))

これを handler から呼び出す。

; src/clj/chat/handler.clj
(ns chat.handler
  (:require [chat.middleware :as middleware]
            [chat.layout :refer [error-page]]
            [chat.routes.home :refer [home-routes]]
            [chat.routes.websocket :refer [websocket-routes]] ; ここと
            [compojure.core :refer [routes wrap-routes]]
            [ring.util.http-response :as response]
            [compojure.route :as route]
            [chat.env :refer [defaults]]
            [mount.core :as mount]))

(mount/defstate init-app
  :start ((or (:init defaults) identity))
  :stop  ((or (:stop defaults) identity)))

(mount/defstate app
  :start
  (middleware/wrap-base
    (routes
      websocket-routes ; ここを追加
      (-> #'home-routes
          (wrap-routes middleware/wrap-csrf)
          (wrap-routes middleware/wrap-formats))
          (route/not-found
             (:body
               (error-page {:status 404
                            :title "page not found"}))))))

html ファイル。デフォの home を変更。

<!-- resource/templates/home.html -->
<input type="text" placeholder="メッセージ" id="chat_form">
<input  type="button" value="送信" id="chat_send">
<div id="messages"></div>
<script src="/js/ws.js"></script>

クライアントサイドの js ファイルを作成。公式では clojurescript を使っているがここではプレーン js を使う。
また、直接 public/js 配下においているがきちんとしたものを作るときは別のディレクトリでやるべきだろう。

// resource/public/js/ws.js
let socket = new WebSocket("ws://" + location.host + "/ws");

socket.onmessage = function(event) {
	let chat = JSON.parse(event.data)["chat"];
	let messages = document.getElementById("messages");
	let first_child = messages.firstChild;
	let msg = document.createElement("div");
	msg.innerHTML = chat
	messages.insertBefore(msg, first_child);
}

window.onload = function () {
	document.getElementById('chat_send').addEventListener('click',
		(function () { socket.send(JSON.stringify({chat: document.getElementById('chat_form').value}));
			document.getElementById('chat_form').value=""}), false);
}

これで完成。

$ lein run

でサーバを起動。
http://localhost:3000/ にアクセスする。
複数のブラウザを立ち上げてメッセージが同期していることを確認。
f:id:hackbaka:20180803171851p:plain
参考: Luminus - a Clojure web framework

Clojure で論理プログラミング

環境 macOS 10.13.6
ClojureProlog 的な論理プログラミングをする場合は、GitHub - clojure/core.logic を使う。
とりあえず

$ lein new logic

project ファイルに書き加える。

; project.clj

(defproject logic "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/core.logic "0.8.11"]]
  :main logic.core)

依存関係を更新

$ lein deps

wikiっぽく例の一家でやってみる

; src/logic/core.clj

(ns logic.core
  (:require [clojure.core.logic.pldb :as pldb]
            [clojure.core.logic :as l]))

; 関係を定義
(pldb/db-rel parent p c)

; 事実を挙列
(def facts
  (pldb/db
    [parent 'namihei 'sazae]
    [parent 'fune 'sazae]
    [parent 'namihei 'katsuo]
    [parent 'fune 'katsuo]
    [parent 'namihei 'wakame]
    [parent 'fune 'wakame]
    [parent 'sazae 'tara]
    [parent 'masuo 'tara]))

(defn -main []
  (pldb/with-db facts
    (println "波平の子供:")
    (println (l/run* [q]
                     (parent 'namihei q)))
    (println "タラの親")
    (println (l/run* [q]
                     (parent q 'tara)))
    (println "フネの孫")
    (println (l/run* [q]
              (l/fresh [x]
                       (parent 'fune x)
                       (parent x q))))))

動かしてみる

$ lein run
波平の子供:
(sazae katsuo wakame)
タラの親
(masuo sazae)
フネの孫
(tara)

面白い。
もっといじってみよう。

参考:
core.logic/tests.clj at master · clojure/core.logic · GitHub
clojure - From defrel and facts to core.logic.pldb - Stack Overflow

Ubuntu の日本語化

環境: 16.04

デフォルトのままだと日本語が文字化けすることがある。

$ sudo apt install  language-pack-ja-base

メッセージなども自動的に日本語になる。
参考: Ubuntuの言語設定を日本語に変更する。

whenever で cron を設定する[Rails]

環境 Ubuntu 16.04, Rails 5.2.0, Ruby 2.5.1

cron で定期的なバッチ処理を行う場合の設定。
まずは Gemfile に whenever を追加。

# Gemfile
gem 'whenever', '0.10.0', :require => false

インストール

$ bundle update

設定ファイルの生成

$ wheneverize .

config/schedule.rb が生成されるので、そこにスケジュールを記入する。
ここでは本番環境で毎日0:00にヘルパーメソッドを実行することにする。

# config/schedule.rb
env :PATH, ENV['PATH']
set :environment,  "production"
every 1.day, :at => '0:00 am' do
  runner "ExamplesHelper.example_batch"
end

以下のコマンドで反映。

$ whenever --update-crontab

以下のコマンドで設定を確認できるが、元の cron の見方を知っていないと難しい。

$ whenever

参考: Railsで定期的にバッチ回す「Whenever」