動かざることバグの如し

近づきたいよ 君の理想に

mysqlcheckで壊れたDBの修復を試みる

変なタイミングでサーバーがクラッシュすると、MySQLのデータもおかしくなる時がある。

Table 'xxxx' is marked as crashed and should be repaired
Incorrect key file for table '/tmp/#sql_******'; try to repair it

とかがそれ。そんな時にmysqlcheckコマンドで治る場合がある。

とりあえずチェックだけ行いたい

mysqlcheck --check  -uUSER -p DB TABLE

チェックして壊れてたら直してほしい

mysqlcheck --auto-repair  -uUSER -p DB TABLE

クイックモードで調査してほしい

大体かかる時間はつけてないのに比べて1/3程度になる。

mysqlcheck --check --quick -uUSER -p DB TABLE

全データベースをチェックしてほしい

mysqlcheck --check --quick -uUSER -p --all-databases

あとはなんだかんだで公式ドキュメントが一番充実している

Rubyの高速JSONパーサーojは本当に高速なのか

rubyJSONをパースするときはJSON.parseが一般的だがojライブラリを使ったほうが速いらしい。

ohler55/oj: Optimized JSON

A fast JSON parser and Object marshaller as a Ruby gem.

が、このライブラリ自体は結構昔からあるし、Ruby 2.5時代でも本当に有能なのか実際にベンチマークしてみた

やりかた

いいソースコードがあったのでこれを使った。

github.com

ただしGemfileが不足しているので

gem "yajl-ruby"                                                                                                                             

Gemfileに追記する必要がある。

結果

$bundle exec ruby benchmark_json.rb 
=== dumping ===
                      user     system      total        real
OJ:               1.036272   0.148676   1.184948 (  1.192646)
OJc:              1.290457   0.166344   1.456801 (  1.467624)
OJr:              1.341350   0.129613   1.470963 (  1.482770)
Yajl:             4.224447   0.130227   4.354674 (  4.383445)
JSON:             6.937912   0.196359   7.134271 (  7.349190)
to_json:          6.871794   0.243478   7.115272 (  7.507943)
JSON (mimic):     1.444729   0.131286   1.576015 (  1.612629)
to_json (mimic):  7.461746   0.250444   7.712190 (  8.516543)
to_json (Oj):     6.879305   0.213003   7.092308 (  7.569498)

あ、普通にOJ速かったわ。疑ってすまねえ

結論

でかいJSONをパースする、レスポンス重視の場合はoj使っていこう

vuexメモ

自分にはNuxtJSは早すぎた(てきとう

以下のようなsrc/App.vue

<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: 'nyaa',
    };
  },
};
</script>

これは今まで。これをstoreで管理できるように

yarn add vuex

でsrc/main.jsはデフォルトだと以下のようになっているはず。

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})

import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    msg: 'nyaa',
  },
})

new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

に変更

でApp.vueを以下に変更

<template>
  <div>
    <h1>{{ this.$store.state.msg }}</h1>
    {{ msg }}
  </div>
</template>

<script>
export default {
  /*data() {
    return {
      msg: 'nyaa',
    }
  },
  }*/
}
</script>

これでstoreから参照できるようになる

事実、今までのようにthis.msgのようにアクセスしようとしても

Property or method "msg" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

とエラーになる。

取得

this.$store.state.msg

変更

this.$store.state.msg = 'changed'

はダメで必ずmutation(操作)を経由しなければならない

さっきのmain.jsのstoreにmutationを追加して

const store = new Vuex.Store({
  state: {
    msg: 'nyaa',
  },
  mutations: {
    setMessage(state, payload) {
      state.msg = payload
    }
  }
})

変更するときはコミット

this.$store.commit('setMessage', this.$store.state.msg+'!')

全体で言うと

<template>
  <div>
    <h1>{{ this.$store.state.msg }}</h1>
    <button v-on:click="increaseMsg">click</button>
  </div>
</template>

<script>
export default {
  methods: {
    increaseMsg: function() {
      // NG
      /*this.$store.state.msg += '!'*/
      this.$store.commit('setMessage', this.$store.state.msg+'!')
    }
  }
}
</script>

になる。が、実際にはthis.$store.commit()を呼び出すことは殆ど無い。(らしい)

なぜか。それはmutations関数の中は同期的でなければならいから。つまり非同期処理は絶対にかけない。

じゃあ非同期にstoreの処理変更できないやんけ、ってなる。そこでactionsという別の仕組みがある。

Vuex.Store内に以下を追記

  actions: {
    saveMessage(context, payload) {
      context.commit('setMessage', payload)
    },
  }

で呼び出すときは以下

this.$store.dispatch('saveMessage', this.$store.state.msg+'!')

全体図

import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    msg: 'nyaa',
  },
  mutations: {
    setMessage(state, payload) {
      state.msg = payload
    }
  },
  actions: {
    saveMessage(context, payload) {
      context.commit('setMessage', payload)
    },
  }
})

new Vue({
  el: '#app',
  store,
  render: h => h(App)
})
<template>
  <div>
    <h1>{{ this.$store.state.msg }}</h1>
    <button v-on:click="increaseMsg">click</button>
  </div>
</template>

<script>
export default {
  methods: {
    increaseMsg: function() {
      this.$store.dispatch('saveMessage', this.$store.state.msg+'!')
    }
  }
}
</script>

Chrome拡張機能作ったけど審査通らなかった話

てかお前審査してたんかよ。。。(

経緯

意外に知られていないが、Kindleはアプリで見る以外にKindle Cloud Readerというブラウザ版が存在する。

つまり https://read.amazon.co.jp/ にアクセスするとブラウザ上からKindleの本/漫画が読める。

が、タブで開くとブックマークバーとかアドレスバーとか邪魔。そこでChrome拡張機能でフルスクリーン表示する。そこで「Open Kindle Cloud Reader」という拡張機能を作った。アイコンクリックするとKindleのウィンドウがフルスクリーンが開くだけ。

こんなシンプルな拡張機能他にあるだろうかと思いつつZipをアップしたのだが後日こんなメールが

Dear Developer,

Your Google Chrome item, "Open Kindle Cloud Reader," with ID: ********* did not comply with our policies and was removed from the Google Chrome Web Store.

Your item did not comply with the following section of our policy:

"Do not post repetitive content.
Do not use irrelevant, misleading, or excessive keywords in app descriptions, titles, or metadata.
Do not attempt to change the placement of any Product in the store or manipulate any Product ratings or reviews by unauthorized means, such as fraudulent installs, paid or fake reviews or ratings, or offering incentives to rate Products.
Do not post an app where the primary functionality is to link to a website not owned by the developer.
Do not post an app where the primary functionality is to install or launch another app, theme, or extension. For example, if your app’s primary function is to launch a desktop app that the user has already installed, that is not allowed. Another example is a packaged app that just launches a website.
Your app must comply with Google's Webmaster Quality Guidelines."

If you'd like to re-submit your item, please make the appropriate changes to the item so that it complies with our policies, then re-publish it in your developer dashboard. Please reply to this email for issues regarding this item removal.

*Please keep in mind that your re-submitted item will not be immediately published live in the store. All re-submitted items undergo a strict compliance review and will be re-published if the item passes review.

*Important Note
Repeated or egregious violations in the store may result in your developer account being banned from the store. This may also result in the suspension of related Google services associated with your Google account. All re-submitted items will continue to be subject to Chrome Web Store policies and terms of service.

Thank you for your cooperation,
Google Chrome Web Store team

どうもシンプルすぎたのが逆に駄目だったぽい。Kindleの初秋者じゃない人間がKindle語ったアプリケーション作るなよ、と。

てか審査してたことに驚き。あんなにスパイウェアまみれだったからバリデーションチェック程度だと思ってたは

ということでAmazonさんお願いだから早く公式Chrome拡張機能 for kindle作って(懇願

ActionMailerのメール送信ログを出力する

久々のRailsネタ

今回やりたいのはActionMailer経由でのメール送信時に送信先のログを吐きたい。

ActionMailerにはObserverというトリガー?的な機能があるのでそれにhookする感じで

まずはログ出力先の設定 config/application.rbに追記

config.mail_logger = Logger.new("log/mail_#{Rails.env}.log")

次にObserverクラスを適当に作る 最終的にはmailクラスを引数に取るdelivered_emailが走るのでここでログを吐く

# config/initializers/email_log_observer.rb
class EmailLogObserver

  def delivered_email(email)
    msg = "from: #{email.from.join(',')} to: #{email.to.join(',')} subject: #{email.subject}"
    Rails.application.config.mail_logger.debug(msg)
  end
end

で、最後にapp/mailers/application_mailer.rbのApplicationMailer宣言後に一行追記

class ApplicationMailer < ActionMailer::Base
(略)
end

ActionMailer::Base.register_observer(EmailLogObserver.new)

これでメール送信時にlog/mail_development.logにログが流れるはず

参考リンク