AndroidでGoogle Playにログインできない場合に疑うべきこと

AcerのA1-810が再起動したままうんともすんとも言わなくなってしまったので、ファクトリーリセットしたところ、Google Playにログインできなくなってしまいました。

アカウントにログインするときに、ちゃんと2段階認証もブラウザ経由で行ったし、なんでだろう?と疑っていたのですが、まさかこの2段階認証が原因だとは…。

Google: ユーザー名とパスワードのエラー

ここによると、

Google Play でのユーザー名とパスワードのエラーのよくある原因として考えられるのは、アカウントで 2 段階認証プロセスを使用しているが、携帯端末でのアプリケーション固有のパスワードを設定していないことです。

だそうです。

あれ?前は別にアプリケーション固有のパスワードとかいらんかったような…。普通にブラウザで2段階認証したらうまくいったと思うんだが…。とにもかくにも、とりあえず2段階認証をしていない他のアカウントでログインしてみたところ、普通にログインできたので、どうやらそれか…と思い、アプリケーション固有のパスワードを作って再度ログインしてみたら、うまくいきました。

Google: アプリケーション固有のパスワードを使用してログインする

Google Playにログインできない原因は、他にもあるようで、

  • 端末の現在時刻がずれている
  • アプリケーションのキャッシュが悪さしている

などもあり得るらしいので、ログインできない場合は、これらも疑ってみた方がよいでしょう。


MiddlemanでJavascriptをデバッグしやすくする

Middlemanで開発していて不満だったのが、毎回sprocketsでファイルが連結されてしまっていることでした。せめて開発中くらいはrailsみたいにバラバラに読み込んでほしいなぁと思ってググったところ、すごい簡単な解決策が。

set :debug_assets, true

これを書くだけでした。そうすると、javascript_include_tagで読み込んだsprockets用jsファイルがバラバラになっていました。これで多少開発しやすくなる!というか最初からこの設定であってほしかった!


Railsでキャッシュにmemcachedを使う

現在作っているプロジェクトの高速化のために、キャッシュの利用を考えたのですが、本番環境としてherokuを検討しているので、herokuでmemcachedが使える方法を模索していました。ちなみにherokuではmemcachedのadd-onが2つあります。

今回は料金の安い方のMemCachierを使う事にしてみます。
Rails3,4での導入方法は、Heroku DevCenterに書かれています。

Gemfileに以下を追加。

gem 'memcachier'
gem 'dalli'

そしてインストール。

bundle install

そしてcache_storeにdalliを設定します。

config.action_controller.perform_caching = true
config.cache_store = :dalli_store

開発環境(ローカル)で試したい場合はdevelopment.rbにproduction.rbと同様の設定を行いましょう。

なお、ローカルでmemcachedがインストールされていない場合はインストールしましょう。Mac OSXの場合、homebrewで入れられます。

brew install memcached

memcachedが動いているかどうかの確認。

ps ux | grep memcached

memcachedの自動起動・停止はこちら(OSX)

launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist # 起動
launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist # 停止

これといって特に設定しないでも動く事は確認できましたが、手動でmemcachedにアクセスしたい場合は、telnetで接続してみましょう。

telnet localhost 11211

memcachedのコマンドはこちらを参考にしましょう。
memcachedコマンド一覧(Life with IT)

memcachedに値をセットする場合、領域を確保してからデータを入れることになります。

set foo 0 0 3[Enter]
123[Enter]

=> STORED

get foo
=> VALUE foo 0 3
=> 123
=> END

とりあえずこれでmemcachedの動作は確認できました。

私の場合はフラグメントキャッシュ(ページの一部をキャッシュ)をやりたかったので、それの実験をしてみましたが、ちょっとハマってしまったのでメモ書きを残します。

フラグメントキャッシュは簡単です。例えばサイドバーに広告を出していたとして、それは別に頻繁に更新されないのだとしたら、キャッシュしておきたいでしょう。こんな感じでやります(テンプレートエンジンはslim)。

- cache :top_ad_area do
  div.sidebar
    ul
      - @ads.each do |ad|
        = link_to ad.name, ad_path(ad)

これで、初回のアクセスでキャッシュを作成して、次回からはキャッシュが使われます。しかし、@adsという変数はコントローラーから引き継がれています。@adsを作るためにDBにアクセスしていたら、キャッシュの旨味がないので、これを抑止します。

before_action do
  unless fragment_exist? :top_ad_area
    @ads = Ad.all
  end
end

これで大丈夫だと思ったら、毎回毎回@adsが生成されていました…。
ログを見てみると、作られたキャッシュ側にはダイジェスト文字列が割り当てられていました。
controllerのfragment_exists?での確認対象: views/top_ad_area
viewのcache :top_ad_area での確認対象: views/top_ad_area/f6da12ef………

この影響でfragment_exists?は全然ヒットしてなかったのです。
この場合、view側のcacheのところで、ダイジェスト文字列を作らないように抑制します。

- cache :top_ad_area, skip_digest: true do
  div.sidebar
    ul
      - @ads.each do |ad|
        = link_to ad.name, ad_path(ad)

これでめでたくキャッシュにヒットして、クエリの抑制ができるようになりました!
参考ページ
RailsのFragment Cache(元祖ワシ的日記 4.0)

また、デプロイしたときにキャッシュをクリアするようにしておきましょう。
アプリケーションサーバとしてunicornを使っている場合は、以下を追記します。

after_fork do |server, worker|
  if defined?(ActiveSupport::Cache::DalliStore) && Rails.cache.is_a?(ActiveSupport::Cache::DalliStore)
    Rails.cache.reset
    # SessionStoreにDalliを使っていたらこちらも有効にする
    #ObjectSpace.each_object(ActionDispatch::Session::DalliStore) { |obj| obj.reset }
  end
end

herokuにデプロイしてみましたが、エラーにならずに表示できているので大丈夫だと思います。


AngularJSのテストをJasmineで書く。

先のmiddleman-jasmineを使って、テストを書こうとしたのだけれど、色々とハマってしまった。
どうも世間で書かれているテストコードが自分の環境では動かない。
とりあえず動いたコードをここに書いておく。

まず、spec.coffeeに、angular-mock.jsを登録する。
angular-mocksの置き場所は自分で考えてください!ちなみに私の場合はbower_components以下にあります。

#= require application
#= require angular-mocks/angular-mocks

次にテストを書く。

'use strict';
describe('RssReader controllers', function() {
  describe('TopCtrl', function(){
    var $scope, createController;
    beforeEach(function(){
        module('RssReader.controllers');
        inject(function($rootScope, $controller) {
          $scope = $rootScope.$new();
          createController = function(){
            return $controller('TopCtrl', {
              $scope: $scope,
              $rootScope: $rootScope
            });
          }
        });
      });
    it('タイトルがIonicRSSReaderであること', function(){
      var controller = createController();
      expect($scope.title).toBe('IonicRSSReader');
    });
  });
});

よくみたサンプルコードだと、beforeEach(module(“RssReader.controllers”));
というコードだったのですが、これだとmoduleがundefinedだと言われてテストが落ちるだけでした。私もそもそもなんでこういうコードなのか謎だなと思っていたので、ここを無名関数にしたら普通に動いたので、beforeEach(function(){});がよさそうです。それとも、なにか前提条件が間違っているのだろうか…。


MiddlemanでJavaScriptのテストを行う

Middlemanでアプリを作成する場合、ビジネスロジックはJavaScript内に書かれる事も多くなるので、JavaScriptのテスト方法について勉強しようとしていました。twitter上だと、jasmine,mochaとかexpress.jsとかchaiとかkarmaなど色々あるような感じで言われて何が何やらという感じだったので、整理してみようと思います。

Jasmine
BDDスタイルで記述するテスティングフレームワーク。TLの人たち曰く、ちょっとオワコン気味らしい…。
mocha
mocha自体はテスティングフレームだけれど、検証するための方法は他のライブラリ(assert, shuld, expect.js, chai)などを使う。いろんな組み合わせができる自由度が高いのが特徴っぽい。TLの人たちにはこれを薦められた。
karma
JavaScriptテストランナー。テストフレームワークの選択、テスト対象のソースディレクトリの選択、テストするブラウザの選択などを設定していく。レポートの出力形式も設定できる。

どれがいいのかわからない上に、数日かけて検証したものの俺のやり方が合ってないんだろうけれどどれもまともに動いてくれなかったので、もうエンドツーエンドテストだけにしようかな…と考えてしまっていたのですが、なんかないか!?とググったところ、middleman-jasmineというgemがありました。

TLではmochaがいいよーと言われてたけれど、テスト自体ができなければ意味がないので、とりあえずなんでもいいからテストができる環境を一旦構築したい!と思っていたので、middleman-jasmineを動かしてみる事に。

まず、Gemfileにmiddleman-jasmineを追記します。

gem 'middleman-jasmine'

次に、middleman-jasmineをインストールします。
そしてjasmineの初期化処理を行います。自動生成されるファイルがpublicにできるので、それをsourceに移動させた後、publicディレクトリを削除します。

bundle install
bundle exec jasmine init
cp public/javascripts/* source/javascripts/
rm -rf public

middleman-jasmineを有効化するために、config.rbに以下を追記します。

activate :jasmine

この状態でmiddlemanを起動してみます。起動は以下のコマンドを打つだけです。

middleman

そうすると、 http://localhost:4567/ にアクセスすると、以下のような画面が出ます。
スクリーンショット 2014-02-24 15.11.16

その後、 http://localhost:4567/jasmine にアクセスしてみると、Jasmineが起動しています。しかし、結果は以下のようになっています。
スクリーンショット 2014-02-24 15.13.58

source/javascripts/以下のファイルがちゃんと読めてません。
ここで、 spec/javascripts/support/jasmine.yml を編集します。

# src_filesをコメントアウトする
#src_files:
#    - public/javascripts/**/*.js

なぜコメントアウトするのかというと、src_filesで指定したファイルを読みにいこうとするからです。ん?読みにいってもいいんじゃないの?と思うじゃないですか。しかし、ここで指定した場合、middleman経由でアクセスするとパスが変わって動きませんでした。

bundle exec rake jasmine の場合、
DocumentRootが ./ になる。
http://localhost:8888 にアクセス
jasmine単体でのファイルパス: /source/javascript/**/*.js ◯動く

middleman-jasmineの場合、
DocumentRootが ./source/ になる。
http://localhost:4567/jasmine にアクセス
jasmine単体でのファイルパス: /source/javascript/**/*.js ×動かない

まあこうなってしまったので、じゃあsrc_files:をjavascripts/**/*.jsにすればいいかと思って

# なぜか動かない。JavaScriptファイルが読み込まれない
src_files:
    - javascripts/**/*.js

このようにしてみたけれど動かないのです。

テスト対象のファイルが読み込めないんだったらテストできないじゃないか!と思うのですが、sproketsからのファイルパスだけは生きているのです。なぜか。sproketsとは、JavaScriptのファイルを連結する役割を担うgemです。middlemanには最初から導入されています。

source/javascripts/application.jsを作ります。以下のように書きましょう。もしくはall.jsが同じ内容かもしれないので、その場合はall.js => application.js にリネームしましょう。

//= require_tree .

これで、application.jsの中身は、source/javascripts/にあるPlayer.jsとSong.jsの内容が連結されたものになります。

その後、spec/javascrpts/ 以下にファイルを作ります。

//= require application

この状態で http://localhost:4567/jasmine にアクセスするとテストが通っています。
スクリーンショット 2014-02-24 15.41.41

以上で、MiddlemanでJasmineを使う前提が整ったかと思います。

ちなみに、spec側をcoffeeファイルで記述することもできるので、その設定を最後に書いておきます。

# spec_filesの対象拡張子をjsとcoffeeに設定
spec_files:
  - '**/*[sS]pec.{js,coffee}'

以上です。

ただ、今のやつだとJasmineのテストが毎回毎回全部のテストが走るような状態なので、単体でテストを通す方法がまだよくわかりません。また調べていかねばならんかなと思います。