AngularJSで半角数値しか入力出来ないディレクティブを作ってみた
WEBアプリのバリデーション処理はめんどくさい
とある業務系のアプリを作成していて、 数値を入力してもらう箇所に普通の文字列を入れられ挙動がおかしくなることがあった
それぐらいバリデーション入れろよ・・・とツッコミが聞こえそうだけど、仕様が変わりすぎて追いつけなかったんや・・・
バリデーションのパターンを作るのも面倒なので、「そもそも数値以外入力できない」ようにしたい
なので数値しか入力できないディレクティブを作ってみた
ちょっとこだわった点
- そもそも半角数値以外はインプットさせない
- 全角の数値を自動で変換する
- メッセージ等の見た目を良くしたいので、AngularStrapを利用
できたもの 実はこれ作ったの多分1年前
↓HTML
<div class="container" ng-app="App"> <form> <p ng-init="hoge=234"> <label for="age">強制半角フォーム : </label> <input type="text" id="age" name="age" ng-model="hoge" ng-number-only/> </p> <p> model:{{hoge}} </p> </form> </div>
↓Javascript(jQuery、AngularJS、AngularStrap)
(function(window) { 'use strict'; window.isNullOrEmpty = function(target) { if (typeof target === 'undefined' || target === null || target === '') { return true; } else { return false; } }; })(window); (function(window, angular) { 'use strict'; var module = angular.module('App', ['mgcrea.ngStrap']); module.directive('ngNumberOnly', [ '$tooltip', function($tooltip) { return { 'restrict': 'A', 'require': '?ngModel', 'link': function(scope, el, atts, ctrl) { if (el[0].localName.toLowerCase() !== 'input' || atts.type !== 'text') { throw new Error('Support Only "input[type=text]"'); } var title = '数字を入力してください'; if (!window.isNullOrEmpty(atts.title)) { title = atts.title; } var tooltipParams = { 'placement': 'right', 'trigger': 'manual', 'title': title }; var myTooltip = $tooltip(el, tooltipParams); el.on('input propertychange', function(e) { var caretPosition = this.selectionStart; //半角0-9以外は if (/[^0-9]+/g.test(this.value)) { // 全角0-9は半角0-9へ this.value = this.value.replace(/[0-9]/g, function(s) { myTooltip.hide(); caretPosition = caretPosition + 1; return String.fromCharCode(s.charCodeAt(0) - 0xFEE0); }); // 0-9以外はアラートのツールチップを出す if (/[^0-9]+/g.test(this.value)) { myTooltip.show(); } // 半角英字記号半角スペースはキャレット(I)が右に動いてしまうので一つ左へ移動 if(/[A-Za-z!"#$%&'()\*\+\-\.,\/:;<=>?@\[\\\]^_`{|}~\s]/.test(this.value)){ caretPosition = caretPosition-1; } //0-9以外を全て空("")に置き換え this.value = this.value.replace(/[^0-9]+/g, ''); //キャレットを元の位置に戻す this.selectionStart = caretPosition; this.selectionEnd = caretPosition; } else { myTooltip.hide(); } if (ctrl != null) { // jQueryやイベントハンドラ(AngularJSの管轄外)から要素のvalueを変更すると、 // modelに値が反映されない。htmlのvalueをmodelに反映するため、$setViewValueを呼び出す。 // (コメントアウトすると分かりやすい) ctrl.$setViewValue(this.value); } }); el.on('blur', function(e) { myTooltip.hide(); }); } }; } ]); })(window, window.angular);
↓CSS
.container{ margin:50px; width:300px; height:300px; border:1px solid #ddd; padding:30px; }
↓全角を半角にするのに参考にしたサイト
動作確認
作っていてハマった点
- キャレットの位置が変わる
- Angularのモデルの値が変わらない
Angularのモデルの値が変わらないについて
AngularJSの管理外から値の変更があると、AngularJSが検知できない
scopeで検知できない感じ
AngularJSの管理外から値の変更した場合は、「$setViewValue」というメソッドを呼び出す必要がある。
↓参考サイト
ngModel.NgModelController | AngularJS 1.2 日本語リファレンス | js STUDIO
わかったことまとめ
- AngularJSの管理外(scope外)から値の変更した場合は、「$setViewValue」を使用する
- 全角数値を半角数値に変換する方法
Angular2がリリースされているけど、使ったことは無い
Angular2にこういうハマりそうなポイントがなければいいなー
Pythonスクリプトをデーモン化するのにsupervisorを利用した話
簡単なスクリプトをデーモン化出来ないかと調べたところ
supervisorを使うことでスクリプトをデーモン化させることが出来そう
↓参考にしたサイト
SupervisorでPythonのスクリプトをデーモンプロセスとして動かす - Symfoware
私が大好きなCentOS6.8でやってみた
ちなみに私の環境はこんな感じ
見出し
supervisor(version 2.1)のインストール
supervisorのインストールにepelリポジトリが必要なのでyumでインストール
# yum install epel-release
supervisorのインストール
# yum install supervisor
デーモン化するスクリプトの作成
とりあえず簡単なスクリプトを
supervisorの動作確認のため、簡単なスクリプトを作成
「/usr/local/bin/」ディレクトリにファイル名「test.py」でスクリプトを作成
# cd /usr/local/bin/ # mkdir /test # vim test.py
test.pyの中身(Python3.5用)
#!/usr/bin/env python # -*- coding: utf-8 -*- from time import sleep from datetime import datetime while True: now = datetime.now() now_str = now.strftime('%Y-%m-%d %H:%M:%S') print('[%s] %s' % (now_str, 'unko')) sleep(5)
スクリプトの機能としては、単純に時刻付きで「unko」を出力するだけ
supervisorの設定
先程作成したPythonスクリプトを動作させるため、supervisorの設定ファイルを編集する
# vim /etc/supervisord.conf
設定ファイル内に、コメントで設定のテンプレートがあるので、コピペで以下の内容をファイルの最後に追記した。
[program:unko] command=/usr/local/lib/anaconda3/bin/python /usr/local/bin/test.py ;priority=999 ; the relative start priority (default 999) autostart=true ; start at supervisord start (default: true) autorestart=true ; retstart at unexpected quit (default: true) startsecs=10 ; number of secs prog must stay running (def. 10) startretries=3 ; max # of serial start failures (default 3) ;exitcodes=0,2 ; 'expected' exit codes for process (default 0,2) ;stopsignal=QUIT ; signal used to kill process (default TERM) ;stopwaitsecs=10 ; max num secs to wait before SIGKILL (default 10) ;user=chrism ; setuid to this UNIX account to run the program log_stdout=true ; if true, log program stdout (default true) log_stderr=true ; if true, log program stderr (def false) logfile=/var/log/supervisor/unko.log ; child log path, use NONE for none; default AUTO ;logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) ;logfile_backups=10 ; # of logfile backups (default 10)
commmandのキーに、実行するコマンドを記載
自分の環境ではAnaconda3を利用しているので、Anaconda3のpythonコマンドのフルパスを記載している
supervisorハマった点
インターネットで調べると3.x系の設定方法が結構見つかるけど、それを書いてしまうともちろん動作しない
supervisorで使用できる設定は、2.x系・3.x系両方ともコメントでテンプレート化されている感じ
別の言い方をすると「/etc/supervisord.conf」のコメントに無い設定は、多分使えない
自分は以下の点で結構ハマった
- [include]セクションが使えない
- 環境変数がほとんど設定されていない状態
- カレントを設定する「directory」キーが使えない
- 変数(?)が使えない
1. [include]セクションが使えない
色んな所で書かれている設定だけど
[include] files = /etc/supervisord.d/*.ini
これはsupervisor2では使えない・・・
supervisor3ではコメント化されてるけど、supervisor2はコメントに無いじゃないですかー
2. 環境変数がほとんど設定されていない状態
自分が使用したsupervisor2では、環境変数が設定されていなかった
正確に言うとPATHの中身が「/sbin:/bin:/usr/sbin」って感じ
なので自分のようにデフォルトではないpythonを使わないときにうまくいかなかった
「/etc/profile」、「/etc/profile.d/*.sh」の設定を使うためには、commandの設定を変更する
↓設定例
command=bash -c 'python /usr/local/bin/test.py;'
3. カレントを設定する「directory」キーが使えない
supervisor2では「directory」キーが使えない(supervisor3では使えるし、コメントにも書いてある)
カレントを変更する場合は、commandの設定を工夫する
↓例
command=bash -c 'cd /usr/local/bin; python /usr/local/bin/test.py;'
「bash -c」便利やな
4. 変数(?)が使えない
supervisorの設定を調べると、時々以下のような設定を見ることがあった。
[program:sample] logfile=/var/log/supervisor/%(program_name)s.log ; child log path, use NONE for none; default AUTO ```
supervisor3.xでは%(program_name)sに[program:sample]のsampleが自動置換されるのだろうが、supervisor2.xでは置換されなかった。
supervisorでスクリプトを起動
supervisorを起動する
# service supervisord start supervisord を起動中: [ OK ]
起動状態の確認
# supervisorctl status unko RUNNING pid 6647, uptime 00:00:56
ここで何も表示されていないときは、(ほぼ)設定ファイルに問題がある
スクリプトがすぐ落ちる場合は、別のメッセージが表示される
ログファイル(/var/log/supervisor/unko.log)の中身の確認
[2017-01-28 23:15:17] unko [2017-01-28 23:15:22] unko [2017-01-28 23:15:27] unko [2017-01-28 23:15:32] unko [2017-01-28 23:15:37] unko
supervisorでpythonをデーモン化することができた。
今は別のスクリプトを起動させているが、1日経って特に今のところ問題なし
ログファイル周りの設定をデフォルトのまま使っているので、次はsupervisorのログファイル周りを調査しないといけませんな
PythonでSlackにサーバの状態を送信してみた
はじめに
はてなブログのTOPページを見ていたらたまたま見つけたブログ
このサイトを見るまでSlackを知らなかった
Slackについて、失礼ながら以下のサイトから転記
Slackとは、チャットができるコミュニケーションツールです。シンプルなUIデザインで、ショートカットキーも豊富、プログラミングコードを綺麗に投稿できるスニペットが用意されていて、高性能な検索機能など、情報共有のしやすさ情報の蓄積として使いやすいコミュニケーションサービスです。機能は他にもまだまだあります。Web版、スマホアプリ版、タブレット版があり、いつでもどこでも情報共有ができます。「ショートカット」が豊富なのと、「プログラムコード投稿オプション」がついているので、特にエンジニアがコミュニケーションとるのに便利です。
社内のチャットツールとして結構使われているらしい
いくつか料金プランがあり、無料でも使える
↓料金プランについてはコチラのサイトがわかりやすい
すごく個人的な問題点
実は1年前から気象庁XMLを受信しているけど、全然活用できていない・・・
XMLが3~4GBたまっているんですけど
どこかのAPIを利用したいと思いつつも、万が一間違った情報が人の目に付くところに出るのも困る
でも何かに利用したいと考えていたところ、Slackで一人チャットなら問題ないでしょ!
というわけで、SlackのAPIを利用してみた
Slackのユーザ登録はおいていて、個人的にハマったアプリの登録から手順をφ(..)メモメモ
アプリの登録・OAuthの認証
アプリの登録・OAuthの認証方法は以下のサイトと同じ手順でやった
一応、公式サイトと同じ手順ではある
アプリの登録は、Slack APIのサイトの右上の方にの「Your Apps」から登録した
あとアプリのOAuth認証は、ブラウザを使った手動で行った。(本当は、OAuthの認証まで内部処理を作らないとイケないでしょうが・・・)
また、OAuthをする前に「Redirect URL(s)」の設定をしないと「Step1」でうまく行かなかった
Step1(認証)
以下のようなURLにアクセスする
https://slack.com/oauth/authorize?client_id=xxx&scope=channels:read chat:write:bot&
client_idとscopeは必須
- client_id - アプリと登録されたときに発行されたID
- scope - アプリの権限。ココでアプリが出来ること(やること)を設定する
scopeの詳細は以下のサイトを見るべし!
アクセスすると「なんかアプリが許可求めているけどどうする?」的な画面が表示されるのでOKする。
すると「Redirect URL(s)」で設定したページへGETクエリ付きでリダイレクトする。
GETクエリに「code=???」と付いているけど、次のstepで使用する
Step2(アクセストークンの取得)
次にアクセストークンを取得する
https://slack.com/api/oauth.access?client_id=xxx&client_secret=yyy&code=zzz
client_idとclient_secretとcodeは必須
- client_id - アプリと登録されたときに発行されたID
- client_secret - アプリと登録されたときに発行されたモノ
- code - Step1のリダイレクトで送られた(取得した)コード
アクセスするとjsonが帰ってくる
{ "access_token": "xxx", "ok": true, "scope": "xxx", "team_id": "xxx", "team_name": "xxx", "user_id": "xxx" }
Pythonでサーバの状態を通知する
取り敢えず動作確認なので、Pythonのスクリプトでtopコマンドを実行した結果を1時間の間隔で通知することにした。
#!/usr/bin/env python # -*- coding: utf-8 -*- import subprocess import requests def res_cmd_lfeed(cmd): return subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True).stdout.readlines() def res_cmd_no_lfeed(cmd): return [x.decode('utf-8').rstrip("\n") for x in res_cmd_lfeed(cmd)] def main(): cmd = ("top -b -n 1 | sed -n '1,5p'") cmd_res = res_cmd_no_lfeed(cmd) msg = '\n'.join(cmd_res) post_message(msg) def post_message(msg): postUrl = 'https://slack.com/api/chat.postMessage' token = 'xxxxxx' channel = 'xxxxxx' username = 'xxxxxx' pyload = { 'token': token, 'channel': channel, 'username': username, 'text': msg } res = requests.post(postUrl, data=pyload) if __name__ == '__main__': main()
参考にしたサイト
作ったPythonのスクリプトを「/usr/local/bin/cron/chat_top.py」に保存し、cronで1時間おきに実行するようにした。
以下の様なテキストファイルを「crontab.conf」に保存
0 */1 * * * export PATH="/usr/local/lib/anaconda3/bin:$PATH"; python /usr/local/bin/cron/chat_top.py
以下のコマンドでcronを設定
crontab crontab.conf
一時間おきにサーバからチャットが送られるようになった
サーバからSlackへチャットを送ることが出来るようになった。