PeaceJetのブログ

証券会社の社内エンジニアをやりながら、マーケティングやデータ分析・UI/UX改善などをやっています。

iPhoneXの痺れる感覚


痺れるiPhoneX

iPhoneXを使っているのですが、充電している時に、iPhoneXのサイドボタンが配置してある面を持つと痺れる感覚があるんですけど、僕だけでしょうか??

充電ケーブルを指している時には、何ともないんですけどね…。

尿路結石になって救急車乗りました…尿路結石の原因と対策

f:id:PeaceJet:20180214200144j:plain

病院に担ぎ込まれたお話と、尿路結石をどうにか再発しないための方法を考えてみました。

尿路結石ってなに?

そもそも、尿路結石を知らない人のために簡単に説明しましょう。

  1. 人間が知覚できる痛みの中で、三本の指に入る激痛(他に、群発頭痛心筋梗塞と言われる)を伴う症状。
  2. 細い尿管を2ミリくらいの石が通過すると、激痛が走る。
  3. 立ち上がることが出来ないほどの痛みで、血尿が出て腎臓が腫れる。
  4. CTスキャンを通して内蔵を見ると、白い粒となって正体を現す。

自分はこれまで4回くらい尿路結石をやっているのですが、病院まで自力でたどり着けたのは、そのうちの2回です。
ほかの2回は救急車に乗っています。

その日🤕

その日の前日は、ごく普通に生活をしていてビールでも飲んで寝るかなぁなんて思って、オリンピックも始まるし下調べでもしておくかなぁなんて思いながら過ごしてベッドに入りました。
正午前、「痛てぇ!🌩」と言いながら目が覚め、脇腹の激痛と夢なのか現実なのか判別がつかない自分が、そこにいるだけでした。
なにがなんだか分からず、とっさの判断で親に電話をしていました。
「寝て起きたら、脇腹が痛くなっている病気ってある?」などと、わけのわからないことを訊いていました。
「ないよ」と、はっきり言われたので電話を切って救急車を呼んでから会社へ連絡しました。

救急隊が来て、玄関まで這って移動して鍵をあけた後は、石が通過するまで、ずっと目を閉じていたので景色を見る余裕がありませんでした。
自分がどこの病院に運ばれたのかも、分からない状態でした。
とにかく早く連れて行って欲しい一心でした。

病院に到着しても、立ち上がれないので車椅子に乗せてもらって、診察室へ。
痛み止めの座薬――ボルタレン50とか言っていた――を打たれて横になること1時間強。

その後も、鈍痛が続きながらCT撮ったり採血したりして、4時間ぐらい経ったあたりから、痛みが引いてきて、その日のうちに家に帰りました。

痛みの推移を例えて話すと、急にエベレストが現れて去っていくような感じ。
あの痛みはなんだったのかと思うくらいケロッと治るのですが、例えようのない痛みなのです。

痛みの原因はなに?

色々と調べてみたのですが、シュウ酸カルシウムとかリン酸カルシウムといった物質が結晶化して、尿管に落ちた時に、通ることが困難な石が尿管を傷つけることで、痛みが起こるようです。
ひぇ~。

尿酸値が高い?

血液検査をしてもらって判明したのは、参考値よりも少しだけ高いということでした。
ただ、お医者さんからは、これが原因だと断定できるわけではないと言われ明確な因果関係があるわけではないが…油っこいものとか肉は控えてねということでした😅

予防法

これはあくまでも自分の予防方法なので、参考程度にして頂きたいのですが、私の場合はいわゆる前兆というものがあります。
今回は、膀胱というかタマ(=^・^=)というか…が痛いなぁなんて思っていて、変だなぁというのはあったのですが、明確に脇腹ではなかったので分からなかったんですね。
でも、いつどこで来るかもわからないし、未然に防ぐ方法はないのかと思って調べました。

2013年の尿路結石の診療ガイドラインは尿路結石の再発予防の基本を以下3点としている。
- 水分を多くとる 尿量が増加し結石の成分を問わない。
- 肥満防止
- 食生活改善
水分では、2000ミリリットル以上で低下する。
プリン体を含むアルコール、特にビールを控えることが重要である[1]。茶、紅茶もシュウ酸を含むが、緑茶でもほうじ茶は最も少量である。

動物性蛋白質、脂肪の過剰摂取は避ける。
動物性たんぱく質と食塩は、尿中にカルシウムを排出させるため、制限することの効果は、従来のカルシウム制限より勝ると考えられる。
脂肪分は、カルシウムがシュウ酸の吸収を影響を邪魔するという良い反応を邪魔する。

尿管結石 | 予防法 - Wikipedia
水をたくさん飲む💧

これに尽きるような気がしています。
食生活を工夫して減らすことは出来ると思いますが、ベースとしておしっこをいっぱい出さないと溜まっちゃいますから…。
最近は、冬で寒いので水分取らなくても行ける感じというか、夏ほど喉も渇かなかったので水分補給をサボっていました。
お酒はよく飲んでいるんですけどね(笑い)

プリン体をとらない🍮

ビールは大好きなのですが、最近は気を使って「プリン体ゼロ・糖質ゼロ」なお酒ばかり飲んでいました。
それでも、石ができるということがわかったので、水を飲みながらお酒を飲むことにしました。
なぜなら、肝臓で分解される時に尿酸が生成されるらしいんですよね。
つまり、お酒を飲んでいると潜在的に石ができやすい体になっているということ。

シュウ酸を多く含む野菜を採らない

つまり、ほうれん草とか小松菜となシュウ酸が含まれるものは、湯引きしてからおひたしにして食べようということ。
小松菜のおひたしってあるの?

お肉を控える

結構、お肉食べておりましたから控えます。

コレステロールを下げる

生活習慣病に位置づけられるとのご指摘もあり、つまりは痩せて健康的な生活をしないと行けないということですね😂

まとめ

  1. 痩せて健康的な食生活をする。
  2. なかでも、お肉の摂取は抑える。
  3. 水を多めに飲むようにする。
  4. お酒も控えめに(汗)

【Amazon.co.jp 限定】 キリン アルカリイオンの水 PET (2L×9本)

【Amazon.co.jp 限定】 キリン アルカリイオンの水 PET (2L×9本)

【はてブアプリ】あると便利な3つの機能

こんにちは、PeaceJetです。

今回は、iPhoneはてブアプリにあると便利な3つの機能を紹介させていただきます。

あると便利な機能…ということなので、実際にはありません(笑)

どうぞ、ご注意ください。

また、これから先を読み進める方は、以下の注意・免責事項をお読みください。

注意事項および免責事項

  • 改善要望を出させていただいた上で、エントリーを書いております。
  • これら提案された機能については、自身の考え方に基づいたものであり、必ずしも私以外の個人及び団体について最適なものではない可能性があることにご留意ください。
  • 私はiPhoneユーザーであり、iPhoneアプリしか触ったことがないということ。
  • はてブ歴は2年くらいです。実は以下に示す機能がデフォルトで備わっていて、自分が知らなかっただけだった言うこともあるかもしれません。そんな時は、そっと教えていただけると嬉しいです。

はてブって、最高の集合知だと思いませんか?

すみません、誇大な言い方をしました。

もちろん、人の考えには偏りがあるという前提の元で、さらに言えば万人に受けるものが必ずしも存在しないという事実や互助会なるものも含めた上でお話をします。

私の場合、GoogleChromeにアドオンを入れて、検索結果の脇に表示されるはてブの数を気にしながら検索することも多いです。

そこで、はてブアプリでこんな機能が備わっていたら便利だなぁと感じたことを書いてみたいと思い今回のエントリーを書きました。

① 関心ワードをピン止めする機能

問題

関心ワードのページでは、関心ワードがカテゴリに分けられてランダムに列挙される仕様となっています。従って、仮に「アイデア」というワードを追いかけていた場合には、スクロールして目的のワードにたどり着くしか方法がなく、自分が見たいと思っている関心ワードを探すのが手間になってしまいます。

提案
  1. そこで、関心ワードをピンで止める機能があれば良いと感じました。以下の画像は、iPhoneアプリである「Wunderlist」を参考にしたものです。このようなピン機能があれば、見たい関心ワードの情報にすぐアクセスすることができるようになります。


名前順にすることが出来る

ちなみに、下記のような機能があり並べ変えることもできますが、ピン機能があればもっと便利になると思っています。

② 関心ワードのはてブ数ソート機能

問題

毎日、自分の最も関心のあるワードについてのフィードを見ています。しかし、はてブの数が1桁台の記事ばかりがズラッと並ぶこともあります。そこで、期間やはてブの数を指定して、1年以内の記事ではてブ数が10個以上などと絞り込むことが出来れば、すごくありがたいし右上の虫眼鏡ボタンを押してから検索をかけることもなくなると感じました。
※ ちなみに、虫眼鏡ボタンを押して検索をかけた場合のみ、上記のようなソート機能を使うことが出来るのです。

提案
  1. まず、下記イメージのようなソート機能実装を考えてみてはいかがでしょうか。


  1. あるいは別の道として、関心ワードにも「新着エントリー」「人気エントリー」「ランキング」機能をつけることも一つかもしれません。このようにすれば、上記と似たような機能を実現できます。

③ 戻るボタンの最適化

これは賛否が別れるかもしれないのですが、左上のボタンの「戻る機能」について考えてみたいと思います。
私個人としては「戻る」という行為は「見ていたページから、一つ前に見ていたページへ戻る」という認識になっています。
これは、ウェブブラウザやエクスプローラー、ファインダーなどでも左上の戻るボタンが、上記のような機能となっているためかも知れません。
しかし、昨今のアプリでは「戻るボタン」が「一番最初の画面に戻る」と言った機能として実装されていることが多いと感じています。
私見で恐縮ですが、楽天市場のアプリではこれが改善されていて、一番最初に戻らなくなりました。*1
はてブアプリでは、その機能が左上ではなく左下にあります。
そのため、何度も誤クリックをしてしまって、一番最初に戻ってしまい、離脱・・・なんてことを繰り返していた時期があります。
現在は、仕様に慣れてきたため減ってはいますが、完全になくなったわけではありません。

提案

賛否が別れるところだと思いますが、単純に逆にしてみるというのは、いかがでしょうか。

あとがき

  • 個人的に「はてなブックマーク」って、やっぱり便利なWebアプリケーションだなと再度実感することができました。なので、もっと成長してほしいと思っています!
  • 最後に免責事項などの責任限定情報や注意喚起情報が多いのは金融という仕事柄です。ご容赦くださいませ。

*1:アプリのバージョン履歴を追うことが出来なかったので、証拠はありません。たしか、4.0.0へのバージョンアップで改善されたと記憶しています

チェックボックスを最低でも一つ以上チェックさせたい。

f:id:PeaceJet:20180206204602p:plain

最低でも一つ以上チェックさせる

チェックボックスが複数設置されているグループに対して、最低でも一つ以上チェックボックスにレ点を入れさせたい場合があります。
今回は、その方法をご紹介できればと思います。

HTML5のバリデーション機能を活かす

フロントエンドでは、さまざまなやり方が存在しますがHTML5に標準で備わっているバリデーション機能を活かす方法として、以下の実装がしっくりきたので紹介いたします。
結論から申し上げれば、あらかじめinputタグにrequired="required"を入れておいて、一つでもチェックされていればrequired="required"をはずすといった動作を行っています。

Bootstrap4

今回、コードにはBootstrap4を使用しています。
Bootstrap4では、required="required"が入っている場合、あらかじめ赤色で表示され、チェックを入れると緑色に変わります。
視覚的にもわかりやすく、UXの向上が期待できると考えています。

jQuery

jQueryを使用して、イベントごとにrequired="required"を変更しています。

先に、触ってみて下さい。

See the Pen Bootstrap4でチェックボックスのバリデーション by OneWorld (@OneWorld) on CodePen.


HTML

例として、3カ国のうち一つ以上を選択させたい場合を考えてみます。

<form method="post" action="" class="was-validated mt-3 mb-3" id="form">
  <section class="card mb-3">
    <div class="card-header">
      <section class="col" id="country">
        <h5>選択してください</h5>
        <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" value="日本" name="checkbox[]" id="a" required="required">
          <label for="a" class="custom-control-label">
            <span class="label">日本</span>
          </label>
        </div>
        <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" value="アメリカ" name="checkbox[]" id="b" required="required">
          <label for="b" class="custom-control-label">
            <span class="label">アメリカ</span>
          </label>
        </div>
        <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" value="中国" name="checkbox[]" id="c" required="required">
          <label for="c" class="custom-control-label">
            <span class="label">中国</span>
          </label>
        </div>
      </section>
    </div>
  </section>
</form>
JavaScript
$(function(){
    var requiredCheckboxes = $('#country :checkbox[required]');
    requiredCheckboxes.change(function(){
        if(requiredCheckboxes.is(':checked')) {
            requiredCheckboxes.removeAttr('required');
        } else {
            requiredCheckboxes.attr('required', 'required');
        }
    });
});

さいごに

皆さんのお役にたてば、幸いです。

【Python3】出会い系サイトをテキスト解析して見える化してみました。

こんにちは、PeaceJetです。

はじめに

以前から、興味があったデータ分析に挑戦しようと思います。
最近の流行といいましょうか、コンピューターによるデータ分析が盛り上がっているように感じますから、テンションを上げて行きたいと思います。

目的

  • 後学のため

目標

  • データを可視化し、そのデータから見られる傾向によって仮説を立てて検証する。

今回、この目標の決着点として、対象となるデータを「テキストマイニングすることで出現頻度を計測してWordCloudで可視化をする」こととしました。
※WordCloudとはAndreas Muellerさんが開発した熱盛なテキストが大きく表示されるプログラムです。
github.com

イメージとしては以下のようなものになります。

f:id:PeaceJet:20170907040001p:plain
出典:Andreas Mueller

データ収集

とにもかくにも、データがないと話になりません。
イトクローリングを行いHTMLファイルをダウンロードしながら、スクレイピングを行いデータを集めることにしました。

どのデータを使用するのか?

FX? 株?

馴染み深いデータではあるのですが・・・もう少し角度を変えたいという思いがあり、さらにデータを探すこととしました。

Twitter

次に思い浮かんだのは、Twitterでした。
しかし、タイムラインの分析では自分のフォローしている属性によって偏りが生じてしまいます。
特に、どんな言葉が頻繁に使用されているのかが容易に予想できてしまうことが問題だなとおもったので辞めました。

馬?

次に、競馬サイトとかも良いなと思ったのですが、こちらは言葉というより数字が主体ですので諦めました。

いろいろと探してみた結果・・・。

出会い系サイトはどうだろうと思うに至りました。

早速、Google先生に質問を投げかけることに。

「出会い系」
「出会い 掲示板」

上記のような検索ワードで、検索しました。

広告のほか、様々なサイトが運営されていることがわかります。

多くの候補がある

候補はたくさんありました。
探していくうちに、世界では単純に男性×女性が出会いを求めているわけではないという事実に気が付きました。

同性同士も出会いを求めている

つまり、男性から男性や女性から女性も出会いを求めているという事実です。
データとしては、あまり取られたことのないものではないかと思い興味を持ちました。
そこで、今回は「男性から男性」というフィールドを扱ったサイトを対象としました。

好みによってカテゴリが別れる

しかし、ここへ来てカテゴリが別れることに気が付きます。
男性だからといって男性の姿をした男性との出会いを欲している人もいる一方で、女性の姿をした男性との出会いを求めている人もいるということです。

注意事項

※これより先は、下記のことを十分に念頭においてから、読み進めてください。

免責事項

  1. ここからは、刺激の強い内容になります。
  2. 対象のサイトは伏せさせていただきます。
  3. 同性同士の話題を取り扱っています。
  4. 著者は、あくまでもデータによる可視化を目的としています。
  5. この記事で発生したいかなる損失について、筆者は責任を負いません。
  6. 筆者の判断により、記事を削除または修正する可能性があります。
  7. ソースコードは動作チェックをしておりますが、記事に誤りが含まれる可能性があります。

スクレイピングをする際には注意事項があります。
qiita.com

参照してされると良いかと思います。

データ収集の方法

今回は、GoogleAppsScriptでサイトをスクレイピングして、定期的にクローリングすることとしました。
なぜ、データベースを使わなかったかといいますと、常時、起動しておけるコンピューターがなかったことが背景にあります。
Raspberry Piを使って、Crontabで定期実行しながらデータを蓄積しようとも考えましたが、GoogleAppsScriptとGoogleスプレッドシートの方が早そうだったので、そちらにしました。

クローラーの動作

  1. 12時間毎にデータを収集し、時系列として欠損のないようにデータを収集する。
  2. 一度のクローリングで、5ページまでのHTMLファイルをダウンロードする。
  3. ダウンロードしたHTMLファイルをスクレイピングしてデータを抜き出す。
  4. データをGoogleスプレッドシートに格納する。

GoogleAppsScriptのプログラム

実際に使用しているソースコードではなく、以下のようなメソッドを使用して取得しているというふうに捉えて頂ければと思います。

//URL
function getSource() {
  
  //シート名はランダムに設定する。
  //var rand = 's9mxeb9ccf3s';
  //var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(rand);
  
  for (var p = 1; p <=5; p++) {
    
    var url = "WEBページのURL" + p;
    var response = UrlFetchApp.fetch(url);
    var split = response.getContentText().split('<hr>');
    
    for ( var i = 1; i < split.length; i++ ) {
      
      sheet.appendRow([split[i]]);
      
    }
    
    Utilities.sleep(3000);
    
  }
  
}

解説

  1. UrlFetchApp#fetchメソッドにて、WEBページのソースを取得します。
  2. hrから次のhrタグまでが一つのブロックになっているので、splitメソッドを使用して要素を配列に格納します。
  3. 最後にSheet#appendRowメソッドでシートの1列目・最終行に追加していきます。

※サーバーへの負荷を考えて、3秒位に一回WEBページを取得するようにしています。1秒以上の間隔を設けるのが常識のようです。

スクレイピング

取得したテキストデータとHTMLソースを切り分けます。
何度か工程を踏んでテキスト部分を抽出します。

ここでは、一部紹介いたします。

< br />や< br >の表記の揺れを統一する。

function breakOptimization () {

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var LastRow = sheet.getLastRow();
  
  //<br>を最適化する
  for (var i = 0; i < LastRow; i++) {
    //一列目のデータを編集する。
    var getString = sheet.getRange(i + 1, 1).getValue();
    var setString = getString.replace(/<br \/>|<br>/g,"<br>");//g(Global)を指定することで、全ての<br>をreplaceする。
    sheet.getRange(i + 1, 1).setValue(setString);
  }
  
}

抽出したテキスト

抽出したテキストの一部を以下に掲載します。
※リンク先はモザイク加工がされておりません。
※全画面で表示されるので、誰も見ていないところで開けるのが懸命です。
ポップアップウインドウで別の画像を表示する方法を知っている方がいらっしゃいましたら教えてください。



MeCabを使用して単語の出現頻度を計測する

ここからは取得したデータに、どのような単語が出現するのか、出現頻度を計測してみたいと思います。

文章を一行にする

データはフィールド(セル)に入っているのですが、それらを一度全て繋げて一行にするという処理を行います。
プログラムはPython3で書きます。

#!/usr/bin/python
#-*- coding: utf-8 -*-

import re
import MeCab
import random

from collections import Counter

def optimizing_data():
    
    myArray = []
    
    '''
       データベースにあるデータをtext.txtというファイルに出力してあると仮定します。
    '''
    file = open("text.txt", "r", encoding="utf-8")
    
    for line in file:
        '''
            性別・年齢・地域・スペース・改行を削除する。
        '''
        pattern = re.compile( r'\W*:' )
        reg = pattern.search( line )
        
        if line in { "\n" }:
            continue    
        elif reg:
            continue
        else:
            xA = line.rstrip('\r\n\r\n')
            yA = xA.rstrip('\r\n')
            myArray.append(yA.rstrip('"'))
    
    file.close

    return "".join(myArray)

def output_textfile(txt):
    OutputFile = open('words.txt', 'w') # 解析結果を書き出すファイルを開く
    OutputFile.writelines(txt)
    OutputFile.close
    
#ここから、形態素解析 MeCab
#tagger = MeCab.Tagger('mecabrc')
#mecab_result = tagger.parse("".join(myArray))

#OutputFile = open('mecab_result.txt', 'w') # 解析結果を書き出すファイルを開く
#OutputFile.write(mecab_result) # 読み込んで解析して書き出し
#OutputFile.close
def extract_word(text):

    tagger = MeCab.Tagger('-Ochasen')
    node = tagger.parseToNode(text)
    word_list = []
    
    while node:
       #if node.feature.split(",")[0] in ("名詞", "動詞", "形容詞"):
       if node.feature.split(",")[0] in ("名詞"):
           word_list.append(node.surface)
       node = node.next
    return word_list

def make_histogram(word_list):
    return Counter(word_list)

def print_map_to_csv(m):
    myArray = []
    for key, value in m.items():
        myArray.append((str(key)  + "\n") * value)
        #myArray.append(str(key) + "\t" + str(value) + "\n")
    return myArray

def output_file(arr):
    OutputFile = open('mecab.txt', 'w') # 解析結果を書き出すファイルを開く
    OutputFile.writelines(arr)
    '''
    with OutputFile as f:
        for v in arr:
            f.writelines(v)# 読み込んで解析して書き出し
    '''
    OutputFile.close

def shuffle(text):
    return random.shuffle(text)   
    
if __name__ == "__main__":
    
    targetText = optimizing_data()
    word_list = extract_word(targetText)
    histogram = make_histogram(word_list)
    arr = print_map_to_csv(histogram)
    #shuf = shuffle(arr)
    output_file(arr)
    
    '''output_textfile(targetText)
    with open("lyrics.txt", "r") as f:
            text = f.readline()
    '''

以下が、出力されたテキストデータになります。

内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
内容
変態
変態
変態
変態
変態
変態
変態
変態
変態
変態
・・・・・

頻出する単語(名詞)をカウントし、そのカウントした数だけ繰り返してテキストデータに出力しています。
この後、テキストファイルに書かれた書かれた単語群をシャッフルします。

WordCloudの前処理

# -*- coding: utf-8 -*-

import random

def shuffle():

    myArray = []
   
    file = open("mecab.txt", "r", encoding="utf-8")

    for line in file:
        myArray.append(line)
    file.close

    random.shuffle(myArray) # ここで、シャッフルします。

    return myArray

def output_file(text):

    out = open("shuffled.txt", "w")
    out.writelines(text)
    out.close

if __name__ == '__main__':

    #print(shuffle())
    output_file(shuffle())
    print("finished")

これを行って、出てきたデータが以下のものになります。


趣旨
お礼
射精


童顔
アナル

新宿
ノンオペ
ハメ

むっちり


Sex

以下、57178行分のテキストが抽出されました。
しかしながら、このままだとWordCloudで上手く表示されませんでした。

github.com
なぜ、うまく行かなかったのかは上記に書かれていました。
解決策は、この単語群をシャッフルしてからでないといけないようです。

#cloud.generate_from_frequencies((("hi", 3),("seven", 7)))のようにしても良かったのですが、上手くいかなかった。

#!/usr/bin/env 
# -*- coding: utf-8 -*-
from wordcloud import WordCloud

def wordcloud():
    
    f = open('shuffled.txt', 'r')
    v = f.read()
    wordcloud = WordCloud(background_color="white", font_path="/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", width=1024, height=674).generate(v)
    wordcloud.to_file("./result.png")
    f.close()
    print("終了しました。")

if __name__ == '__main__':
    wordcloud()

抽出した結果としては、以下になります。
刺激が強すぎる可能性があるので、モザイク加工を行っております。
※リンク先はモザイク加工がされておりません。
※全画面で表示されるので、誰も見ていないところで開けるのが懸命です。
ポップアップウインドウで別の画像を表示する方法を知っている方がいらっしゃいましたら教えてください。



まとめ

※この記事は10月時点で執筆したものでして、ブログにどのようにして発表すればよいものか。
そもそも、必要なのか必要でないのかすら悩みに悩んで、ひっそりと掲載します。
なにかあったら、消すかもしれないです。
「メール」や「女装」といった言葉の出現頻度が非常に多かったです。
ここで、LINEやカカオといったSNSは一般的ではないということが分かりました。
やっぱり、匿名性が比較して高いメールが選ばれるということでしょうか。

【Ruby on Rails】MaterializeのChips(Tags)で結構ハマったはなし

Materialize

Materialize Chips

タグ機能の実装について、いろいろと試行錯誤しています。
その中で、Materializeを使用させていただいているのですが、標準のタグ機能だと結構ややこしい処理をしないと値を取ってこれなかったのでメモします。

※注意:返却される値が、配列に格納されたハッシュですので、そのような機能を実装しているのでしたら、Materializeのタグ機能を余すところなく使えると思います。
例としては以下のようなものです。

[
  {tag: "テスト"},
  {tag: "テスト"}
]

Chips

htmlを書いていきます。

<div class="chips chips-initial chips-placeholder">
</div>
<%= f.input_field :tag_list, id: :tag_list %>

javascriptも、必要に応じて取捨選択し書いていきます。

$('.chips').material_chip();
$('.chips-initial').material_chip({
  data: [{
    tag: 'Apple',
  }, {
    tag: 'Microsoft',
  }, {
    tag: 'Google',
  }],
});
$('.chips-placeholder').material_chip({
  placeholder: 'Enter a tag',
  secondaryPlaceholder: '+Tag',
});
$('.chips-autocomplete').material_chip({
  autocompleteOptions: {
    data: {
      'Apple': null,
      'Microsoft': null,
      'Google': null
    },
    limit: Infinity,
    minLength: 1
  }
});

以下のように記述すると、カンマ区切りの値を得ることができます。
「テスト,テスト」のような感じです。

$('.chips').on('chip.add chip.delete', function(e, chip){
  var x = $('.chips').material_chip('data');
  var z = [];
  $.each(x, function(index, elem) { z.push(elem.tag) });
  $("#tag_list").val(z)
})

もっと、高レイヤーでのやり方があれば良いのですが見当たらず・・・。

The Site of Materialize

materializecss.com

【Ruby on Rails】Acts_as_Taggable_onで結構ハマったはなし

最近、Ruby on Railsを使用したWebアプリケーションの開発を行っております。

その中で、タグ機能を実装するためにActs_as_Taggable_onというGemパッケージを使用したのですが、結構ハマってしまったので共有します。
もし、的外れなことを言っているなと思った方がいれば指摘していただけると助かります。

環境は以下のとおりです。

名称 バージョン
Windows 10
Ruby 2.2.6
Rails 5.1.4

そのいち

マイグレーションが通らない。

StandardError: An error has occurred, this and all later migrations canceled:

Directly inheriting from ActiveRecord::Migration is not supported. Please specify the Rails release the migration was written for:

class RolifyCreateRoles < ActiveRecord::Migration[4.2]
/Users/brandoncordell/Code/dsk_group/db/migrate/20170307143104_rolify_create_roles.rb:1:in `'
/Users/brandoncordell/Code/dsk_group/bin/rails:9:in `require'
/Users/brandoncordell/Code/dsk_group/bin/rails:9:in `'
/Users/brandoncordell/Code/dsk_group/bin/spring:15:in `'
bin/rails:4:in `load'
bin/rails:4:in `

'
StandardError: Directly inheriting from ActiveRecord::Migration is not supported. Please specify the Rails release the migration was written for:

class RolifyCreateRoles < ActiveRecord::Migration[4.2]
/Users/brandoncordell/Code/dsk_group/db/migrate/20170307143104_rolify_create_roles.rb:1:in `'
/Users/brandoncordell/Code/dsk_group/bin/rails:9:in `require'
/Users/brandoncordell/Code/dsk_group/bin/rails:9:in `'
/Users/brandoncordell/Code/dsk_group/bin/spring:15:in `'
bin/rails:4:in `load'
bin/rails:4:in `

'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

上記のようなエラーです。
意味的には、ActiveRecord::Migrationから直接継承することはサポートされていませんので、Railsバージョンが指定されたマイグレーションファイルを書いてくおくんさい。
ということだと思います。

したがって、以下のように修正しました。

#修正前
Class RolifyCreateRoles < ActiveRecord::Migration
end
#修正後
Class RolifyCreateRoles < ActiveRecord::Migration[5.0]
end

以下のサイトを参考にしました。
github.com

そのに

これもまた、マイグレーション作業時のエラーです。

igrating to AddMissingUniqueIndices (**************)
== AddMissingUniqueIndices: migrating ========================================

    • add_index(:tags, :name, {:unique=>true})

PG::UniqueViolation: ERROR: could not create unique index "index_tags_on_name"
DETAIL: Key (name)=(???) is duplicated.
: CREATE UNIQUE INDEX "index_tags_on_name" ON "tags" ("name")
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

上記のようなエラーです。
私のログが残っていないので、引用させていただきました。
マイグレーションスクリプトの中に、2度、同じカラムを追加をする項目があるようで、それが悪さをしているようです。
「すでにカラムが存在していますので作ることができません」というエラー内容なので、マイグレーションスクリプトの該当部分をコメントアウトすることで解消できました。

そのさん

これが、どうにもならないエラーでした。

NoMethodError (super: no superclass method `quote_value' for #
Did you mean? quote_bound_value):

tackeyy.com
このコミュニティーの会話の中で、gemパッケージの最新版を使ってみては?という流れなのですが、Rubyのバージョンをイジれないので良い案がないかといった会話がありました。
しかし、その後に続く人がおらず、おそらく解決策がないんだろうなぁということで完全にお手上げ状態になっていまいました。

-gem 'acts-as-taggable-on', '~> 4.0'
 +gem 'acts-as-taggable-on', github: 'mbleigh/acts-as-taggable-on', branch: 'master'

以下の記事を参考にさせていただきました。
tackeyy.com

ここで注意なのは、最新版のgemパッケージは、Rubyバージョンが2.2.7以上でないと動作しないということです。

> bundle install

上記コマンドを実行すると、Rubyのバージョンを変更してくださいと警告が出て落ちます。

結果的に、Rubyのバージョンを上げることにしました。

Ruby 2.2.6 から 2.3.3 へバージョン変更。
現状では 2.5 まであるのですが、大幅なバージョンアップによってもたらされるリスクを避けたかったので、2.3にしましたといったら怒られると思いますが、スピードを優先したいという思いがあり苦肉の策として採用しました。
開発環境を作り直すハメになるとは思わなかったのですが、いい勉強になりました。

結論

Rubyのバージョンは、2.2.7以上を使うようにしましょう。