gulpでSlimのビルド環境作る2
以前、こんなことを書きました。
現在、改めてこのへんの整備をしているのですが、もっと簡単に導入する方法があったのでメモがてら書いておきます。
今年3月くらいに、こんなものが作られていたようです。
導入方法は上のリンク先見てもらえば全部書いてるので、特に特筆すべきことはないのですが、簡単な手順をば。
gem install slim
※結局これは必要npm install --save-dev gulp-slim
- gulpfileを作成
これでビルドできる( ・`ω・´)
ただ、これで吐き出したコードは圧縮されるので、改行やインデントが軒並み死にます。
インデント欲しい時は、gulp-slimの引数にこんなふうなやついれるといい感じになります。
gulp.src("./slim/*.slim") .pipe(slim({pretty: true})) //オプション追加 .pipe(gulp.dest('./html/'));
使えるオプション一覧は
https://www.npmjs.com/package/gulp-slim#options
を参照してください。
mysql(mariadb)のテーブルサイズとインデックスを一覧で見る
小ネタ過ぎてエントリに上げるのもアレですが、備忘録兼ねて( ・`ω・´)
テーブルサイズ一覧
SELECT table_name, table_rows AS "rows_count", ROUND(data_length/1024/1024,1) AS "table size(MB)", ROUND(index_length/1024/1024,1) AS "index size(MB)" FROM information_schema.tables ORDER BY data_length DESC
table_name | rows_count | table size(MB) | index size(MB) |
---|---|---|---|
articles | 327540 | 10.5 | 5.5 |
users | 2 | 0.0 | 0.0 |
schema_migrations | 2 | 0.0 | 0.0 |
dummy | 0 | 0.0 | 0.0 |
インデックスのサイズも見れる。まあそこそこ使う。
インデックス情報一覧
SELECT * FROM information_schema.statistics WHERE table_schema = "sandbox" ORDER BY table_name, index_name
TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | NON_UNIQUE | INDEX_SCHEMA | INDEX_NAME | SEQ_IN_INDEX | COLUMN_NAME | COLLATION | CARDINALITY | SUB_PART | PACKED | NULLABLE | INDEX_TYPE | COMMENT | INDEX_COMMENT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
def | sandbox | articles | 1 | sandbox | ix_user_id | 1 | user_id | A | 2 | NULL | NULL | YES | BTREE | ||
def | sandbox | articles | 0 | sandbox | PRIMARY | 1 | id | A | 327540 | NULL | NULL | BTREE | |||
def | sandbox | dummy | 0 | sandbox | PRIMARY | 1 | id | A | 0 | NULL | NULL | BTREE | |||
def | sandbox | schema_migrations | 0 | sandbox | unique_schema_migrations | 1 | version | A | 2 | NULL | NULL | BTREE | |||
def | sandbox | users | 0 | sandbox | PRIMARY | 1 | id | A | 2 | NULL | NULL | BTREE |
このデータの見方はそのうち
mithril.jsとCORSの話
個人用のツールとして、最近話題になり始めてるmithril.js使ってるんですが、いいですよこれ。 react.jsと同じようにVirtualDomサポートしつつ、その上でAPIが16しかない(ここ重要)という素晴らしいJavascriptMVCフレームワークです。
react.js少し使っていて、JS側にテンプレート書けるJSXはそこそこ気に入っていたんですが、 素のブラウザで普通には動かせないんで大分辛みを感じてました。 このmithrilなら、素のブラウザで動作しつつ、react.jsのいいところも使えて大変便利です。 ちなみに、JSXのようなものを提供する拡張もあるようです。MSXだったかな?
さて、このmithril、こんな感じでAjaxRequestを発行することができます。
var users = m.request({method: "GET", url: "/user"}) .then(log) .then(function(users) { //add one more user to the response return users.concat({name: "Jane"}) })
まあ、これまんま公式から引っ張ってきたやつですが、いわゆるスタンダードなJqueryと比較して、callbackがchainで書けたりできるのでめちゃ使いやすいです。 素晴らしい。
そんなm.requestを使って起こった問題について書いてみます。 ここからが本題。
Access-Control-Allow-Origin
今回ターゲットにしているサービスは、完全に自分と関係ないやつなので、まあ、こいつと遭遇することになるわけです。
"No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access."
最近のブラウザでは標準搭載されているCross-Domainの制限ですね。
今回は自分の作ったJSと、サービスの公開ドメインが異なっているので、このアクセス制限を食らってしまうわけですねー。
詳しい話はこのへん https://developer.mozilla.org/ja/docs/HTTP_access_control http://qiita.com/zaru/items/6e57ab83a8e0e40f2115
当然、mithril限定の問題ではなく、他のライブラリ使っても発生する問題なので、対応方法も豊富に公開されていました。ありがたい限りです( ・`ω・´)
必要な対応としては3つ
- Jsonp使う
- サーバー側でAccess-Control-Allow-Originを返してあげるようにする
- プロキシ経由させて、プロキシ側でサービスにリクエスト送りつつ、Access-Control-Allow-Originをクライアントに返す
今回の接続先は、自分が管理していないサービスなので、1、2では対応できません。当然ですね(´・ω・`) となると必然的に3の選択を取ることになります。
なぜ3が有効かというと、Cross-Origin Resource Sharingは、あくまでクライアントから通信するときの制限であるため、 3で強制的にAccess-Control-Allow-Originをレスポンスとして返して上げることで、この問題を防ぐことができます。
ちなみにjqueryであれば、jquery.xdomainajax.js というものを使えば簡単に解決可能です。やってることも同じかんじ。
とはいえ、Yahooapiを経由してリクエストぶん投げる方法で、Yahooさんの意向一つで使えなくなる怖さはあるので、今回は自力で対応しています。
今回は、ツール開発で使ってるcoffeeScriptをugilifyしてsourcemapをはき出すためにgulpを使っており、node.jsとは切っても切れない関係になっているので、node.jsでプロキシ作成することにしました。
node.jsでの実装はこんな感じ
リクエスト発行するHTMLと、サーバーのドメインが異なると、それだけで弾かれてしまうので、HTMLやjsもnodeで配信するようにしました。
これで、クライアントとサーバーが同一ドメインであることが満たされるので、問題なく動作するようになります。
before_destroy時点でdestroyされてしまう、dependentの罠
まずはこれを見て欲しい
# == Schema Information # # Table name: users # # id :integer not null, primary key # name :string(255) # class User < ActiveRecord::Base has_many :articles, dependent: :destroy after_create do articles.import(%w"1 2 3 4 5".map{|text| Article.new(text:text)}) end before_destroy do p articles.map(&:text) end end
# == Schema Information # # Table name: articles # # id :integer not null, primary key # user_id :integer # text :string(255) # class Article < ActiveRecord::Base belongs_to :user end
よくある1対多の関連を持ったモデルがありますよ、と。
さらに、dependent指定で、userが消えた時、関連するArticlesを消すようにしています。
userの方では、生成時にarticlesに複数の初期データを同時に作成するようにしています。
after_createのところ見てもらえば分かる通り、1〜5の文字を適当に突っ込んで5レコード作っています。
ここまで前準備。
本題。。。。
さて、ここでarticlesを消す前に、他のテーブルに移すとか、ログにはき出すなりをやりたいとします。
当然dependent指定されているため、after_destroyではarticles拾えなくなるため、before_destroyで拾うとします。
user.rbみてもらうばわかる通り、before_destroyで標準出力に出してあげるようにしています。
User.create(id:13, name:1) => #<User id: 13, name: "test"> User.find(13).articles => [#<Article id: 26, user_id: 13, text: "1">, #<Article id: 27, user_id: 13, text: "2">, #<Article id: 28, user_id: 13, text: "3">, #<Article id: 29, user_id: 13, text: "4">, #<Article id: 30, user_id: 13, text: "5">] User.find(13).destroy >> []
ファッ!?
なぜか空配列が帰ってきてしまいます。 pry眺めてると先にarticlesのDeleteが走っていることがわかります。
ちなみに、こう書けば空配列にはなりません。
class User < ActiveRecord::Base before_destroy do # こちらの定義を先にした p articles.map(&:text) end has_many :articles, dependent: :destroy after_create do articles.import(%w"1 2 3 4 5".map{|text| Article.new(text:text)}) end end >> ["1", "2", "3", "4", "5"]
なぜこのような挙動をするのか。
深くまで潜っていけば、has_manyにdependent: :destroyが指定されている場合、
lib/active_record/associations/builder/association.rbで以下のように、before_destroy時に関連するモデルを削除する処理が埋め込まれます。
def self.define_callbacks(model, reflection) add_before_destroy_callbacks(model, reflection) if reflection.options[:dependent] Association.extensions.each do |extension| extension.build model, reflection end end
さて、ここで思い出してもらいたい。
modelに対するcallbackは複数回指定することができます。
さっきのuser.rbにこれ追加します。
before_destroy do p 1 end before_destroy do p 2 end >> [] 1 2
追加したbefore_destroyを逆にして再度実行すると…
[] 2 1
そうです、大体見えてみた。
先にbefore_destroyを定義した順番に実行されていくのです。
最初に記載していたコードを見てください。
has_manyの定義が先に書かれています。そのため、has_many内で追加されていたdependent: :destroyのbefore_destroyが先に実行されていたために、空配列になっていたのです(`・ω・´)
これで地味にハマりました。
でもrubyは上から順番に評価されていくので、原因が分かってさえすれば理屈はだいたいわかりますねー。
おわり。
肥大化するseeds.rbをうまい具合に管理する
seeds.rb中でif~elsifしたり、rakeタスクを環境分用意するなど、剛の者もいるようですが、それはあんまりにも辛みを予感させてしまいます。
個人的に楽でオススメなのが、環境毎のdirを切って、その下に実処理書いたファイル配置し、requireでごそっと開始させる方法。
root └ db ├ seeds.rb ├ seeds/ ├ development/ │ ├ users.rb │ └ ...~.rb ├ production/
こんな感じ。
規模が小さいなら、environmentごとのdir切らずに、直接ファイルおいても良いかも。
db/seeds.rbにはこんな感じの処理書いておく
seed_files_root="#{Rails.root}/db/seeds/#{Rails.env}" Dir.glob("#{seed_files_root}/**.rb").each{|file| require file if FileTest.file?(file) }
これによって、起動した時の環境に合わせたdirから、Seedするファイルが読み込まれて処理されるようになります。
というメモ。
Fabricインストール時に起きたDistributionNotFoundを解決する
OSX python2.7.5で起きたもの。
- pipでインストール
- インストールされていたparamikoは1.15.2
こんなエラー
Traceback (most recent call last): File "/usr/local/bin/fab", line 5, in <module> from pkg_resources import load_entry_point File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 2603, in <module> working_set.require(__requires__) File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 666, in require needed = self.resolve(parse_requirements(requirements)) File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 565, in resolve raise DistributionNotFound(req) # XXX put more info here pkg_resources.DistributionNotFound: paramiko>=1.10
最後の行に見て取れるように、単純に依存関係が解決できていない模様。 こういう時は、エラーに書かれているバージョンを入れると固い。
pip uninstall paramiko pip install paramiko==1.10
これでOK( ・`ω・´)
Google Playで、課金テスト行うまでのメモ
作業中の簡易的なメモで、割りとうろ覚えですが、ググる取っ掛かりになれば幸いです(´・ω・`)
GooglePlayの開発者アカウントを取得して、決済設定を行っていることが前提。まずはGoogleが提供しているSandbox用の決済IDで決済処理が走るかどうか見ておくとよさげ。
http://devwalker.blogspot.jp/2013/07/google-play-in-app-billing.html
APKのアップロード
以前はできていたけど、今年の5月くらいからドラフト版での課金テストサポートしなくなったらしい。α、またはβでアプリ公開の設定が必要。
https://support.google.com/googleplay/android-developer/answer/3131213?hl=ja
ドラフト版でアプリ課金のテストができないということは、一度APKをアップロードして、特定ユーザーしか見えない形で公開状態にする必要がある。
このとき、製品版、ではなくアルファ版か、ベータ版であることを注意する。
基本、APKを作成してアップロードするだけ。APK作成時に、KeyStoreで署名する必要があるのでそこだけ注意。 http://techbooster.org/android/environment/1445/
もしUnityとかでアプリ作ってる場合は、UnityのPlayetSettingsから作成できる方法があるのでそっち使うことをおすすめ。
APKのサイズが50MBを超えていると、アップロード時に弾かれる。 個人開発のアプリだとそうそうないと思うけど、その時は、拡張APKの仕組みを導入してあげることで50MB以下に落とすことができる。 https://support.google.com/googleplay/android-developer/answer/2481797?hl=ja
拡張APKを適用した場合、拡張APKとして分割したファイルも一緒にアップロードする必要がある。
ただし、初回に登録する場合は、まずAPKだけを単体でアップロードする。
成功した後、BundleVersionと、BundleVersionCodeを変更したAPKをアップロードした後、拡張ファイル分をアップロードできるようになるので、そこでアップロードしてあげる。
一旦APKアップロードすると、「公開できない理由」の表示が大体出てるはず。
クリックすると、何が問題か教えてくれるので、警告内容を見て、修正して、アップロードして…を警告なくなるまで繰り返す。
警告が全て消えれば、アプリケーションを公開できるようになるので、間違って製品版を公開しようとしていないかチェックして、公開ボタン押す。
公開すると、大体2,3時間くらいで反映される。
アプリ内アイテムの追加
決済設定が終了+APKアップロード後に設定できるようになる。
任意のIDを振り、提供価格と提供国の設定をして、確定させればゲーム内アイテム販売可能な状態になる。
IDの振り方は、会社ドメインなどのprefix.アプリ名.アイテム名のような感じが推奨されてるらしい。
このIDに本番用のアプリ名を使うのは避けた方がいい。AppStoreと違って、ここで登録したIDと同じものは永久に使えなくなるため(´・ω・`)
テストユーザーの追加
APKアップロード後がよいかも。テストユーザーの追加が必要。
公開設定したアプリを使えるようにするには、Googleグループでテストユーザーグループを作成しておく必要がある。非公開のグループでもOK。
Gmailのアカウントが必要になるので、テスト用に1つ作成し、そのユーザーでグループ作るようにすることをおすすめ。
(実機テストで個人のGmail使うと、課金テスト時に実課金されてしまう事故が起こるかもしれないので)
APKアップロードしたバージョンのタブを選択して、「テスターのリストを管理」から、作成したグループを紐付ける。
紐付けに成功した場合、しばらく経つとGoolgeグループに所属しているアカウントに、アプリ公開の通知が飛んでくるので、飛んできた通知にあるリンクからアプリをインストールできる。
APKの設定部分で、テストユーザーのグループを指定することで、そのユーザーだけにインストールさせることができる。
APKのアップロード
なにか修正してAPK上げなおすときの注意点。
BundleVersionだけ上げても弾かれてしまう。 BundleVersion以外に、Bundle VersionCodeも変更しないと更新できないので注意。
APKアップロードした後、テストユーザーに配信されるまで最低でも2、3時間かかる(2,3時間かからないとStoreに並ばない)
なので、頻繁に更新してアップロードして、検証!みたいなことはできないのが辛み。