woshidan's blog

あいとゆうきとITと、とっておきの話。

DNSサーバの種類とDNSサーバの受ける問い合わせの種類についてまとめました

speakerdeck.com

参考のところに載せてるんですけど、DNSについて勉強する前に読みたかった資料です*1。もし、他の人がこの記事を見てDNSについてちょっと調べてみようと思ったら3分くらいは眺めてから調べ始めるとはかどると思います。

http://2014.seccon.jp/dns/dns_basics_in_30minutes.pdf

ただ、問い合わせの特徴によって覚えて区別つけるのもいいんですけど、基本的にどのプロトコルのアプリケーションやそれらのアプリケーションたるWebサービスなんかもサーバがクライアントから受けるリクエストによって挙動を変えるものだと考えると、上位から下位へ問い合わせを続ける過程で再帰的に動いて欲しいフルサービスリゾルバに飛んでくるリクエストが再帰問い合わせということになりわかりやすいのかな、と Webエンジニアは思いました。

特徴はどっちにしろ覚えないといけないんですけどね(苦笑)

DNS周りの情報は、後でDNSキャッシュポイズニングについて再帰問い合わせ周りの分も追加してやり直そうと思っているんですが、自分がググった限りJPRSの資料が一番はっきりしています。

ネットワークエンジニアとして(個人運営サイトらしいのですが、これでネットワーク勉強してました系のブログ記事とかでよく紹介されてた) => JPRSの資料(初心者網羅的な情報は多くないですが、専門家集団が出しているので概要であったり、個別トピックがすぱっとしてめっちゃわかりやすい) => @IT(個別の質問とかの記事がめっちゃ助かる) => BINDなど実装用ソフトのノウハウ系の記事、の順で巡回すると混乱が少ないのではないでしょうか。

*1:お約束としてある程度勉強してグーグル力が上がらないと当たれないんですね

DNSの概要と耐障害性、リソースレコードの読み方(BIND式)をまとめました

再帰問い合わせとかまで行こうとしたらスライドが50枚超えてたから打ち切り。

自分のスライドの作り方どこかで見たな、と思ってたんですが、ニコ動の攻略解説動画だ、たぶん。 少しずつ説明してもらわないとわかんない残念な頭をしている...!

speakerdeck.com

それにしても、実はドメインを扱ってる会社に勤めているんですが、これを作りはじめる前はDNSのAレコードって聞くと寒気してたとかは内緒にしておいてください。

DNSキャッシュポイズニングについてまとめました

ネットワークの勉強をして色々挫折中。

とりあえず、外部連携するときにFTPとか色々クライアントかませるんだけど、そのトラブルシューティングや、サービスの中でつないでいる各種サーバへの接続設定にもこの辺の知識があると便利そうということだけわかってきた感じ。

ひとまず今日からノートに書いてあるよくわかってないことを1個ずつまとめていきますよっと。

speakerdeck.com

スライドの中で紹介した Source port randomization、最初に勉強したときの資料にはほとんど出てこなくて、それはもうベンダの方で対応してるから考えなくていいということなのか、とか、再帰的問い合わせ分からんとか壁にぶつかったりしていた。

あと、スライド中の偽造用、偽装用かもしれない...。

Support LibraryのNavigationViewでどこまでできるのか試してみる

久しぶりにサイドメニューのコードいじろうかと思ったのですが、デザインのカスタマイズ性の都合でサードパーティライブラリを使うのが辛く、ここから整理するよりSupport LibraryのNavigationViewを使って書き直した方が楽ではないか、と思ったので、どこまでできるのか試してみます。

内容

  • ライブラリのバージョンについて
  • 下記のコードを試す上で基本となるレイアウトなどのソースコード
  • HeaderViewのレイアウトを変更する
  • サイドメニューの項目を変更する場合
  • サイドメニューの項目にバッジをつけたい場合
  • 下に固定のfooterをつけたい場合

ライブラリのバージョンについて

gradleで指定していたバージョンは下記。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:design:23.4.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha3'
}

下記のコードを試す上で基本となるレイアウトなどのソースコード

Android Studioに用意されているNavigation Drawer Activityのプロジェクトのものをほとんどそのまま利用しています。

一応見たいという方はこちらよりご確認ください。

HeaderViewのレイアウトを変更する

NavigationView.removeHeaderView(View view)NavigationView.addHeaderView(View view)を使えば特に難しいことを考えずサクッと入れ替えられました。

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // HeaderViewを変更できるテストとしてHeaderViewが1個以上あれば
                // HeaderViewを消すテスト
                if(navigationView.getHeaderCount() >= 1) {
                    navigationView.removeHeaderView(navigationView.getHeaderView(0));
                } else {
                    navigationView.inflateHeaderView(R.layout.nav_header_main);
                }
            }
        });

f:id:woshidan:20160907222153g:plain

サイドメニューの項目を変更する場合

一部の項目の表示/非表示にする場合。

    boolean isMenuGroup1Visible = true;


        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 一部のmenuの項目の表示/非表示を切り替えるテスト
                navigationView.getMenu().setGroupVisible(R.id.menu_group_1, !isMenuGroup1Visible);
                isMenuGroup1Visible = !isMenuGroup1Visible;
            }
        });

f:id:woshidan:20160907223531g:plain

サイドメニューの項目をがらっと変えたい場合。

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // サイドメニューの項目をmenuリソースから生成し直す場合
                navigationView.getMenu().clear();
                navigationView.inflateMenu(R.menu.test);
            }
        });
<!-- test.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/nav_gallery"
        android:icon="@drawable/ic_menu_gallery"
        android:title="Gallery" />
    <item
        android:id="@+id/nav_slideshow"
        android:icon="@drawable/ic_menu_slideshow"
        android:title="Slideshow" />
    <item
        android:id="@+id/nav_manage"
        android:icon="@drawable/ic_menu_manage"
        android:title="Tools" />
</menu>

f:id:woshidan:20160907224022g:plain

サイドメニューの項目にバッジをつけたい場合

stackoverflow.com

menuリソースのitem要素の属性に、バッジの元となるWidgetのクラスとしてTextViewを指定して...とありましたが、この方法だと、どうも追加した要素の高さをうまく調整できなかったので、MenuItemCompat.setActionView(MenuItem menuItem, int layoutRes)メソッドで、バッジ用に用意したレイアウトを与えて、そのレイアウトを変更してやる方がいじりやすそうでした。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/nav_gallery"
        android:icon="@drawable/ic_menu_gallery"
        android:title="Gallery" />
    <item
        android:id="@+id/nav_slideshow"
        android:icon="@drawable/ic_menu_slideshow"
        android:title="Slideshow" />
    <item
        android:id="@+id/nav_manage"
        android:icon="@drawable/ic_menu_manage"
        android:title="Tools" />
</menu>
<!-- nav_menu_badge.xml  -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:textColor="@android:color/white"
        android:id="@+id/badge_text"
        android:background="@color/colorAccent"
        android:padding="4dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
        MenuItem galleryItem = MenuItemCompat.setActionView(navigationView.getMenu().findItem(R.id.nav_gallery), R.layout.nav_menu_badge);
        TextView badgeText = (TextView) galleryItem.getActionView().findViewById(R.id.badge_text);
        badgeText.setText("99");

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // バッジを消したり、内容を変更したりするテスト
                LinearLayout galleryBadge = (LinearLayout) MenuItemCompat.getActionView(navigationView.getMenu().findItem(R.id.nav_gallery));
                TextView badgeText = (TextView) galleryBadge.findViewById(R.id.badge_text);

                if(badgeText.getText() == "99+") {
                    MenuItemCompat.setActionView(navigationView.getMenu().findItem(R.id.nav_gallery), null);
                } else {
                    badgeText.setText("99+");
                }

            }
        });

f:id:woshidan:20160908001443g:plain

下に固定のfooterをつけたい場合

NavigationViewの中にandroid:layout_gravity="bottom"の指定をつけて要素を追加したらよさそうでした。

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" >

        <TextView
            android:text="Footerのテスト"
            android:gravity="center"
            android:layout_gravity="bottom"
            android:background="@color/colorAccent"
            android:padding="16dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();
    }

f:id:woshidan:20160908075544p:plain

追加したFooterとかぶった部分についてスクロールさせたいときはどうするかについては、対処が若干微妙で、アイコン透明なものを用意した選択不可能なメニューをその部分に追加するのが自分が探した中で綺麗な方の解決方法でした....^^;

Mac OSにmecabをインストールしてnattoを利用してrubyからmecabを叩いてみる

ちょっとある程度の量の短文から単語の出現頻度を抽出する必要があり、さわったのでメモ。

Mac OSにHomebrewでmecabをインストールする

Homebrewを通して入れます*1

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

$ brew install mecab
$ brew install mecab-ipadic

mecabrubyから叩きやすいようにnattoのgemを入れる

割と薄い感じのラッパーっぽいやつです。mecab-rubyでもよさそう。

参考:

$ gem install natto

ユーザー辞書を作る

普通の辞書だけだとインターネット業界の人間にとっては、単語が足りないので*2はてなキーワードからユーザー辞書を作ってみます。

手順としては、

となります。

参考:

ユーザー辞書の元となるデータの取得

curl -L http://d.hatena.ne.jp/images/keyword/keywordlist_furigana.csv | iconv -f euc-jp -t utf-8 > keywordlist_furigana.csv

ユーザー辞書の元となるデータをCSVにする

表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音という行が並んだCSVを作っていきます。

例: 工藤,1223,1223,6058,名詞,固有名詞,人名,名,*,*,くどう,クドウ,クドウ

# http://qiita.com/ynakayama/items/388c82cbe14c65827769 の引用に近いですが...
require 'csv'

CSV.open("custom.csv", 'w') do |csv|
  original_data.each do |type, filename|
    next unless File.file? filename
    open('keywordlist_furigana.csv').each do |title|
      title.strip!

      next if title =~ %r(^[+-.$()?*/&%!"'_,]+)
      next if title =~ /^[-.0-9]+$/
      next if title =~ /曖昧さ回避/
      next if title =~ /_\(/
      next if title =~ /^PJ:/
      next if title =~ /の登場人物/
      next if title =~ /一覧/

      title_length = title.length

      if title_length > 3
        score = [-36000.0, -400 * (title_length ** 1.5)].max.to_i
        csv << [title, nil, nil, score, '名詞', '一般', '*', '*', '*', '*', title, '*', '*', type]
      end
    end
  end
end

CSVユーザー辞書ファイルとしてコンパイルする

環境によって若干パスが違うので、注意。

/usr/local/Cellar/mecab/0.996/libexec/mecab/mecab-dict-index -d /usr/local/Cellar/mecab-ipadic/2.7.0-20070801/lib/mecab/dic/ipadic -u custom.dic -f utf-8 -t utf-8 custom.csv

ユーザー辞書ファイルをmecabの設定ファイルに追加する

nattoのドキュメント見てもユーザー辞書をその場で登録する感じの説明が見当たらなかったのでmecabの設定ファイルにユーザー辞書のパスを追加します。

#  /usr/local/etc/mecabrc に下記の行を追加
userdic = /Users/woshidan/path/to/custom.dic

nattoを使ってmeacbを叩いてみる

require 'natto'

irb(main):001:0> require 'natto'
=> true
irb(main):002:0> nm = Natto::MeCab.new
=> #<Natto::MeCab:0x007fd4431c0948 @model=#<FFI::Pointer address=0x007fd4442c90e0>, @tagger=#<FFI::Pointer address=0x007fd444300e10>, @lattice=#<FFI::Pointer address=0x007fd444300ea0>, @libpath="/usr/local/Cellar/mecab/0.996/lib/libmecab.dylib", @options={}, @dicts=[#<Natto::DictionaryInfo:0x007fd4431f3cf8 @filepath="/usr/local/lib/mecab/dic/ipadic/sys.dic", charset=utf8, type=0>], @version=0.996>
irb(main):003:0> nm.parse("はじめてのMecab")
=> "はじめて\t副詞,一般,*,*,*,*,はじめて,ハジメテ,ハジメテ\n\t助詞,連体化,*,*,*,*,の,ノ,ノ\nMecab\t名詞,固有名詞,組織,*,*,*,*\nEOS\n"
irb(main):004:0> puts nm.parse("はじめてのMecab")
はじめて  副詞,一般,*,*,*,*,はじめて,ハジメテ,ハジメテ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
Mecab 名詞,固有名詞,組織,*,*,*,*
EOS
=> nil

natto, あるいは、mecab-rubyについて、出力形式は他にも良さげなオプションがあった気がしますが、ひとまずこういう感じで。

*1:Yosemite以上にバージョンアップしていたので、自宅のPCのbrewが壊れており http://qiita.com/sue738/items/7ab03ecc9f6fcf37408d http://qiita.com/riocampos/items/b27952faa24524e2919f http://himenaotaro.hatenablog.com/entry/2016/02/14/105251の記事にお世話になりました

*2:プリキュアとか刀剣乱舞とかいろいろ

Google App Scriptでセルを扱う

はてなが俺たちのはてなじゃない...。

諸々の集計業務を担当することになって、コピペを効率化しようと思ってGoogle App Scriptを調べたのでまとめます。

内容

  • 特定のセルの値を取得
  • 特定のセルに値を貼り付け
  • 他のファイルのソースコードを参照するときの手順
  • 確認用ダイアローグを出す
  • シートを開いたときに行う処理
  • 参考

特定のセルの値を取得

// 単一の値を取得
SpreadsheetApp.getActive().getSheetByName("SHEET_NAME").getRange("A1").getValue();

// 現在アクティブなシートを取得する場合
SpreadsheetApp.getActiveSpreadsheet()).getRange("A1").getValue();

// 特定の範囲の中の値を取得する場合
// 2次元配列が帰ってきます
SpreadsheetApp.getActive().getSheetByName("SHEET_NAME").getRange("A1:B6").getValues();

特定のセルに値を貼り付け

// 単一のセルに貼り付けというのはなく、範囲から範囲への貼り付けとなる
var rangeToCopy = sheet.getRange('A:A');
var targetToCopy = sheet.getRange('B1'); // 貼り付け範囲か、一番左上のセルを指定するようにしておく
rangeToCopy.copyTo(targetToCopy);

// 形式のみ, 値のみのオプションについて
rangeToCopy.copyTo(targetToCopy, {formatOnly:true}); // 形式のみ
rangeToCopy.copyTo(targetToCopy, {contentsOnly:true}); // 値のみ

// 2次元配列を特定の範囲へ貼り付け
var copyValues = [[1, 2], [3, 4]];
SpreadsheetApp.getActive().getSheetByName("SHEET_NAME").getRange("A1:B2").setValues(copyValues);

他のファイルのソースコードを参照するときの手順

  1. 新しいプロジェクトを作成して分割したいソースコードを貼る
  2. 1のプロジェクトに対して、 ファイル > 版で管理 から、他のプロジェクトに公開するバージョンのコメントを入れて保存
  3. 2のプロジェクトのソースコードを参照したいプロジェクトを開いて、リソース > ライブラリ を選択
  4. 3で開いたメニューの下の方に、2のプロジェクトのURLの一部 https://script.google.com/macros/d/SomeLooooongRandomString/edit?ui...SomeLooooongRandomStringの部分を入れる
  5. 入力した文字列が正しければ、2のプロジェクト名が3のメニューに現れるので、2のプロジェクトを利用する側で使うバージョンと、2のプロジェクトの関数などの前につく識別子を入力して、「保存」を押す
  6. 2のプロジェクトを利用する側で、5で決めた識別子を頭につけて2のプロジェクト内の関数を利用する

確認用ダイアローグを出す

// メッセージだけでる
Browser.msgBox("メッセージが出ます");

// OK/キャンセルのボタンがついたダイアローグが出る
Browser.msgBox("これからシートを編集しますか?", Browser.Buttons.OK_CANCEL);

// 選択されたボタンを取得
var selections = Browser.msgBox("これからシートを編集しますか?", 
                        Browser.Buttons.OK_CANCEL);
// キャンセルボタンをクリックした場合は"cancel"
Browser.msgBox(selections);

シートを開いたときに行う処理

// シートを開いたときにダイアローグを出す
function onOpen(){
  Browser.msgBox("これからシートを編集しますか?", Browser.Buttons.OK_CANCEL);
}

// シートを開いたときに上部にメニューを追加する
function onOpen() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var entries = [
    {
      name : "シートの上部にに追加するメニュー内の項目の名前",
      functionName : "その項目を選択したとき起動する関数"
    }
  ];
  sheet.addMenu("シートの上部に追加するメニューの名前", entries);
}

参考

結局なのですが、普通にGoogle Spread Sheetの関数を使ってCSVインポートからのコピペ前の整形作業用の単純なシートを何枚か作ったらそれだけで大幅に時間が短縮されてしまい、Google App Scriptの動作が遅かったため、2日程度で0からほとんど動くところまで書いたのに導入までできなかったのが残念です...。

暇なときにもう少し手直ししてリベンジしたい...。

Spinnerのレイアウトをコードで変更する

スタイルを利用した指定がうまく動かなかったり見つけるまでにちょっと時間がかかったりしたので、まあ適当にメモ。

  1. android:id="@android:id/text1"@android:id/text1がidに指定されたTextViewだけのレイアウトリソースを用意
  2. Spinnerの要素をセットする ArrayAdapter の引数に渡す
  3. ドロップダウン用のレイアウトは、ArrayAdapter.setDropDownViewResourceメソッドの引数に渡す

Adapterに手動で要素を追加しているのは、利用する時にスピナーの要素が動的に変わっていたからです。

また、Spinnerの要素の先頭に画像などをセットしたい場合は、BaseAdapterを継承してgetViewメソッドをオーバーライドして用います。

setContentView(R.layout.activity_main);

ArrayAdapter<String> adapter = new ArrayAdapter(this, R.layout.item_in_list);
adapter.add("One");
adapter.add("Two");
adapter.add("Three");
adapter.setDropDownViewResource(R.layout.item_in_drop_down_list);
AppCompatSpinner spinner = (AppCompatSpinner) findViewById(R.id.imageFolderNameSpinner);
spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }
});
<!-- res/layout/activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.woshidan.myapplication.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay">
            <android.support.v7.widget.AppCompatSpinner
                android:id="@+id/imageFolderNameSpinner"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>
<!-- item_in_list.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@android:id/text1"
    android:textSize="18dp"
    android:padding="8dp"
    android:textColor="#0f0"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:background="#fff"
    android:textAlignment="inherit"
    xmlns:android="http://schemas.android.com/apk/res/android" />
<!-- item_in_drop_down_list.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@android:id/text1"
    android:textSize="18dp"
    android:padding="8dp"
    android:textColor="#f00"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textAlignment="inherit"
    xmlns:android="http://schemas.android.com/apk/res/android" />

f:id:woshidan:20160809000455g:plain

スタイルで指定できるはずなんだけどなぁ。