読者です 読者をやめる 読者になる 読者になる

iOS - WebView でローカルファイルを表示する方法

ローカルファイルを Xcode プロジェクト内に配置して WebView で表示させてみます。

f:id:kzy52:20150322143745p:plain

プロジェクトの作成

適当にプロジェクトを作成してください。
ここでは 「WebViewLocalFileExample」 というプロジェクト名にします。

WebView で表示させたいファイルの準備

Assetsファイル/
├── index.html
├── javascripts
│   └── example.js
└── stylesheets
    ├── example.css

WebView で表示させたいファイルをプロジェクトに配置

  1. [WebViewLocalFileExample] を右クリックする。
  2. [New Group] を選択し、ディレクトリ名を「Resources」にする。
  3. [ユーティリティエリア] > [Identity and type]の フォルダアイコンをクリックする。
  4. [New Folder] をクリックし、ダイアログが表示されるので「Resources」ディレクトリを作成する
  5. [Choose]をクリックする。
  6. [Resources] を右クリックする。
  7. [Add Files to "WebViewLocalFileExample"...] を選択する。
  8. 用意しておいたassets配下のファイルとディレクトリを選択する。
  9. [Destination] で Copy items if needed にチェックを入れる。
  10. [Added folders] で Create folder references を選択する。
  11. [Add] をクリックする。

f:id:kzy52:20150322154419p:plain

この方法でやるとディレクトリ階層を維持したままプロジェクト内に配置することができます。

WebViewで表示

import UIKit

class ViewController: UIViewController, UIWebViewDelegate {
    
    var webView: UIWebView = UIWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // デリゲートを指定する
        self.webView.delegate = self;

        // 全画面表示にする
        self.webView.frame = self.view.bounds
        
        // サブビューを追加する
        self.view.addSubview(self.webView)
        
        // index.htmlのパスを取得する
        let path = NSBundle.mainBundle().pathForResource("index", ofType: "html")!
        let url = NSURL(string: path)!

        // リクエストを生成する
        let urlRequest = NSURLRequest(URL: url)
        
        // 指定したページを読み込む
        self.webView.loadRequest(urlRequest)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

ソースコード

github.com

iOS - Swift で CocoaPods を使う方法

Swift で CocoaPods を使う方法です。

プロジェクトの作成

適当にプロジェクトを作成します。
今回は 「CocoaPodsExample」 というプロジェクトを作成します。
プロジェクト作成後 Xcode は一旦閉じてください。

Cocoapodsのインストール

$ gem install cocoapods

Podfileの追加

$ cd [PROJECT_PATH]
$ pod init
$ vim Podfile
# Podfile

# Uncomment this line to define a global platform for your project
platform :ios, '7.0'

target 'CocoaPodsExample' do
  pod 'AFNetworking', '~> 2.0'
end

target 'CocoaPodsExampleTests' do

end
$ pod install
$ open CocoaPodsExample.xcworkspace/

Bridging Headerの作成

[CocoaPodsExample] > [CocoaPodsExample] で右クリック > [New File...] > [iOS] > [Source] > [Header FIle]を選択し、Nextボタンをクリックします。

ファイル名は CocoaPodsExample-Bridging-Header.h にします。([PRODUCT_NAME]-Bridging-Header.h)

// CocoaPodsExample-Bridging-Header.h

#import <AFNetworking/AFNetworking.h>

f:id:kzy52:20150321170804p:plain

① プロジェクトを選択する。
② [Build Settings] を選択する。
③ [All] を選択する。
④ [Swift Compiler - Code Generation] > [Objective-C Bridging Header] 「CocoaPodsExample/CocoaPodsExample-Bridging-Header.h」 と入力する。

f:id:kzy52:20150321171052p:plain

ソースコード

github.com

Type 'ViewController' doen not conform to protocol 'UITableViewDataSource'

問題

UITableViewDataSource プロトコルを指定した場合に以下のようなエラーが発生。

Type 'ViewController' doen not conform to protocol 'UITableViewDataSource'

原因

UITableViewDataSource プロトコルを使用する場合、以下2つのメソッドが実装必須になっています。

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}

解決方法

上記2つのメソッドを実装すればエラーは消えます。

Jenkins で 静的コードチェックを自動化する MetricFu, RuboCop, Brakeman

ソースコードの品質向上のため静的コード解析ツールである MetricFu , RuboCop , Brakeman のドキュメント出力を Jenkins で自動化する方法をまとめました。
これらを導入することにより人力コードレビューの負担を少しでも減らすことができるかなと思います。

MetricFu

metricfu/metric_fu · GitHub

コードメトリクスを測定。 ソースコードの重複チェック、処理の複雑度などを出力してくれる。
Reek, Flay, Chrn, Stats, Flog, Cane, Roodi, Saikuro, Rails Best Practices, Hotspots

RuboCop

bbatsov/rubocop · GitHub

静的コード解析ツール。
Ruby Style Guide に沿っているかチェックしてくれる。

Brakeman

presidentbeef/brakeman · GitHub

セキュリティチェック。
脆弱性の含まれたライブラリを使っていたり、セキュリティ的にまずい実装をしていると警告してくれる。

MetricFu

gem のインストール

# Gemfile

gem 'metric_fu'

Jenkins の設定

プラグインのインストール

[Jenkinsの管理] > [プラグインの管理] から HTML Publisher Plugin プラグインをインストールする。

HTML Publisher Plugin プラグイン の設定

[ビルド後の処理の追加] > [Publish HTML reports] を選択し以下のように入力する。

f:id:kzy52:20150126224437p:plain

HTML directory to archive: tmp/metric_fu/output
Index page[s]: index.html
Report title: metric-fu

シェルスクリプトの設定

bundle exec metric_fu

f:id:kzy52:20150128221618p:plain

f:id:kzy52:20150128221447p:plain

RuboCop

gem のインストール

# Gemfile

gem 'rubocop', require: false
gem 'rubocop-checkstyle_formatter', require: false

Jenkins の設定

プラグインのインストール

[Jenkinsの管理] > [プラグインの管理] から Violations プラグインをインストールする

Violations プラグインの設定

[ビルド後の処理の追加] > [Report Violations] を選択し以下のように入力する。

f:id:kzy52:20150126225100p:plain

checkstyle: tmp/checkstyle.xml

シェルスクリプトの設定

bundle exec rubocop --require rubocop/formatter/checkstyle_formatter --format RuboCop::Formatter::CheckstyleFormatter --no-color --rails --out tmp/checkstyle.xml

f:id:kzy52:20150128222030p:plain

f:id:kzy52:20150128222104p:plain

Brakeman

gem のインストール

# Gemfile

gem 'brakeman'

Jenkins の設定

プラグインのインストール

[Jenkinsの管理] > [プラグインの管理] から Brakeman Plugin プラグインをインストールする。

Brakeman Plugin プラグインの設定

[ビルド後の処理の追加] > [Publish Brakeman warnings] を選択し以下のように入力する。

f:id:kzy52:20150126225556p:plain

Brakeman Output File: tmp/brakeman-output.tabs

シェルスクリプトの設定

bundle exec brakeman -o tmp/brakeman-output.tabs

f:id:kzy52:20150129204124p:plain

f:id:kzy52:20150129204137p:plain

f:id:kzy52:20150129204314p:plain

気に入ったものがあれば是非導入してみてください。

iOS - Swift で WebViewアプリを作ってみた

Swift WebView を使ってWebページを表示するだけのアプリを作ってみます。
初めて実装する人でもわかるよう、細かくコメント入れています。
是非、お試しください。

テンプレートは Single View Application にします。

Webページを表示する

UIWebViewクラスを使って WebView を実装していきます。
WebView を実装するには他にも WKWebView というクラスがあります。

class ViewController: UIViewController {
    
    var webView: UIWebView?
    
    var targetURL = "http://www.yahoo.co.jp/"

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // WebViewを生成する
        self.webView = self.createWebView()
        
        // サブビューを追加する
        self.view.addSubview(self.webView!)
        
        // リクエストを生成する
        var url = NSURL(string: targetURL)
        var request = NSURLRequest(URL: url!)
        
        // 指定したページを読み込む
        self.webView?.loadRequest(request)
    }
    
    // WebView を生成する
    func createWebView() -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // 全画面表示にする
        _webView.frame = self.view.bounds
        
        return _webView
    }

f:id:kzy52:20150218211310p:plain

ステータスバーとビューが被らないようにする

ステータスバーと追加したビューが被ってしまっているのでステータスバーの高さに合わせてビューのサイズを変更します。

class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // 横幅、高さ、ステータスバーの高さを取得する
        let width: CGFloat! = self.view.bounds.width
        let height: CGFloat! = self.view.bounds.height
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        
        // WebViewを生成する
        self.webView = self.createWebView(frame: CGRectMake(0, statusBarHeight, width, height - statusBarHeight))
        ...
    }
    
    // WebView を生成する
    func createWebView(#frame: CGRect) -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // 画面サイズを設定する
        _webView.frame = frame
        
        return _webView
    }
    ...
}

UIApplication.sharedApplication().statusBarFrame.height

これでステータスバーの高さを取得できます。

CGRectMake

対象オブジェクトのサイズと位置を指定したい場合に使用します。

CGRect rect = CGRectMake(位置x, 位置y, 幅, 高さ)

このように生成します。

f:id:kzy52:20150218212526p:plain

ランドスケープモード(横)で表示が崩れないようにする

class ViewController: UIViewController {
    ...

    func createWebView(#frame: CGRect) -> UIWebView {
        ...
        _webView.frame = frame
        
        // ビューサイズの自動調整
        _webView.autoresizingMask = UIViewAutoresizing.FlexibleRightMargin |
            UIViewAutoresizing.FlexibleTopMargin |
            UIViewAutoresizing.FlexibleLeftMargin |
            UIViewAutoresizing.FlexibleBottomMargin |
            UIViewAutoresizing.FlexibleWidth |
            UIViewAutoresizing.FlexibleHeight
        
        return _webView
    }

    // ビューが再レイアウトされるときに呼び出される
    override func viewWillLayoutSubviews() {
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        self.webView?.frame = CGRectMake(0, statusBarHeight, self.view.bounds.width, self.view.bounds.height)
    }
}

autoresizingMask

子要素の自動調整をしてくれます。
値は以下を指定することができます。

設定値 説明
UIViewAutoresizingFlexibleTopMargin 上を自動調整
UIViewAutoresizingFlexibleBottomMargin 下を自動調整
UIViewAutoresizingFlexibleRightMargin 右を自動調整
UIViewAutoresizingFlexibleLeftMargin 左を自動調整
UIViewAutoresizingFlexibleWidth 幅を自動調整
UIViewAutoresizingFlexibleHeight 高さを自動調整
UIViewAutoresizingNone 自動調整なし

viewWillLayoutSubviews()

ビューが再レイアウトされるときに呼び出されます。
ランドスケープモードだとステータスバーの部分が空いてしまうので再度ビューのサイズを設定しています。

f:id:kzy52:20150218222531p:plain

インジケータを表示する

class ViewController: UIViewController, UIWebViewDelegate {
    ...
    override func viewDidLoad() {
        ...
        self.webView?.loadRequest(request)
        
        // インジケータを表示する
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    }
    
    // WebView を生成する
    func createWebView(#frame: CGRect) -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // デリゲートを指定する
        _webView.delegate = self;
        ...
    }
    ...
    // WebView がコンテンツの読み込みを完了した後に呼ばれる
    func webViewDidFinishLoad(webView: UIWebView) {
        // インジケータを非表示にする
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    }
    ...
}

UIWebViewDelegate

ウェブコンテンツを読み込んだ時の処理を追加したいので UIWebViewDelegate プロトコルを追加します。
WebView がコンテンツの読み込みを開始した場合に呼ばれる webViewDidStartLoad と
完了した場合に呼ばれる webViewDidFinishLoad が使用できるようになります。

UIApplication.sharedApplication().networkActivityIndicatorVisible

ネットワークにアクセスしていることを示すインジケータの表示、非表示を設定することができます。

f:id:kzy52:20150218220334p:plain

戻る、進む、リフレッシュ、Safariで開くボタンを表示する

class ViewController: UIViewController, UIWebViewDelegate {
    ...    
    var toolBar: UIToolbar?
    var rewindButton = UIBarButtonItem()
    var fastForwardButton = UIBarButtonItem()
    var refreshButton = UIBarButtonItem()
    var openInSafari = UIBarButtonItem()

    override func viewDidLoad() {
        ...
        self.view.addSubview(self.webView!)
        
        // ツールバーを生成する
        self.toolBar = self.createToolBar(frame: CGRectMake(0, height-44, width, 40.0), position: CGPointMake(width/2, height-20.0))
        
        // サブビューを追加する
        self.view.addSubview(self.toolBar!)
        
        // リクエストを生成する
        ...
        
        // 前のページに戻れるかどうか
        self.rewindButton.enabled = self.webView!.canGoBack
        // 次のページに進めるかどうか
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = false
        self.openInSafari.enabled = false
    }
    ...
    
    // ツールバーを生成する
    func createToolBar(#frame: CGRect, position: CGPoint) -> UIToolbar {
        // UIWebViewのインスタンスを生成
        let _toolBar = UIToolbar()
        
        // ツールバーのサイズを決める.
        _toolBar.frame = frame
        
        // ツールバーの位置を決める.
        _toolBar.layer.position = position
        
        // 文字色を設定する
        _toolBar.tintColor = UIColor.blueColor()
        // 背景色を設定する
        _toolBar.backgroundColor = UIColor.whiteColor()
        
        // 各ボタンを生成する
        // UIBarButtonItem(style, デリゲートのターゲットを指定, ボタンが押されたときに呼ばれるメソッドを指定)
        let spacer: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
        self.rewindButton = UIBarButtonItem(barButtonSystemItem: .Rewind, target: self, action: "back:")
        self.fastForwardButton = UIBarButtonItem(barButtonSystemItem: .FastForward, target: self, action: "forward:")
        self.refreshButton = UIBarButtonItem(barButtonSystemItem: .Refresh, target: self, action: "refresh:")
        self.openInSafari = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "safari:")
        
        // ボタンをツールバーに入れる.
        _toolBar.items = [rewindButton, fastForwardButton, refreshButton, spacer, openInSafari]
        
        return _toolBar
    }
    ...
    
    // WebViewがコンテンツの読み込みを開始した時に呼ばれる
    func webViewDidStartLoad(webView: UIWebView) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = true
        self.openInSafari.enabled = true
    }
    
    // WebView がコンテンツの読み込みを完了した後に呼ばれる
    func webViewDidFinishLoad(webView: UIWebView) {
        // インジケータを非表示にする
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
    }
    
    // 戻るボタンの処理
    @IBAction func back(AnyObject) {
        self.webView?.goBack()
    }
    
    // 進むボタンの処理
    @IBAction func forward(AnyObject) {
        self.webView?.goForward()
    }
    
    // 再読み込みボタンの処理
    @IBAction func refresh(AnyObject) {
        self.webView?.reload()
    }
    
    // safari で開く
    @IBAction func safari(AnyObject) {
        let url = self.webView?.request?.URL
        UIApplication.sharedApplication().openURL(url!)
    }
    ...
}

CGPointMake

対象オブジェクトの位置を指定したい場合に使用します。

CGPoint point = CGPointMake(位置x, 位置y);

このように生成します。

@IBAction

クリックイベントの定義には IBAction を使用します。

f:id:kzy52:20150218221905p:plain

最終的には以下のようになります。

import UIKit

class ViewController: UIViewController, UIWebViewDelegate {
    
    var webView: UIWebView?
    
    var targetURL = "http://www.yahoo.co.jp/"
    
    var toolBar: UIToolbar?
    var rewindButton = UIBarButtonItem()
    var fastForwardButton = UIBarButtonItem()
    var refreshButton = UIBarButtonItem()
    var openInSafari = UIBarButtonItem()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // 横幅、高さ、ステータスバーの高さを取得する
        let width: CGFloat! = self.view.bounds.width
        let height: CGFloat! = self.view.bounds.height
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        
        // WebViewを生成する
        self.webView = self.createWebView(frame: CGRectMake(0, statusBarHeight, width, height - statusBarHeight))
        
        // サブビューを追加する
        self.view.addSubview(self.webView!)
        
        // ツールバーを生成する
        self.toolBar = self.createToolBar(frame: CGRectMake(0, height-44, width, 40.0), position: CGPointMake(width/2, height-20.0))
        
        // サブビューを追加する
        self.view.addSubview(self.toolBar!)
        
        // リクエストを生成する
        var url = NSURL(string: targetURL)
        var request = NSURLRequest(URL: url!)
        
        // 指定したページを読み込む
        self.webView?.loadRequest(request)
        
        // インジケータを表示する
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        // 前のページに戻れるかどうか
        self.rewindButton.enabled = self.webView!.canGoBack
        // 次のページに進めるかどうか
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = false
        self.openInSafari.enabled = false
    }
    
    // WebView を生成する
    func createWebView(#frame: CGRect) -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // デリゲートを指定する
        _webView.delegate = self;
        
        // 画面サイズを設定する
        _webView.frame = frame
        
        // ビューサイズの自動調整
        _webView.autoresizingMask = UIViewAutoresizing.FlexibleRightMargin |
            UIViewAutoresizing.FlexibleTopMargin |
            UIViewAutoresizing.FlexibleLeftMargin |
            UIViewAutoresizing.FlexibleBottomMargin |
            UIViewAutoresizing.FlexibleWidth |
            UIViewAutoresizing.FlexibleHeight
        
        return _webView
    }
    
    // ツールバーを生成する
    func createToolBar(#frame: CGRect, position: CGPoint) -> UIToolbar {
        // UIWebViewのインスタンスを生成
        let _toolBar = UIToolbar()
        
        // ツールバーのサイズを決める.
        _toolBar.frame = frame
        
        // ツールバーの位置を決める.
        _toolBar.layer.position = position
        
        // 文字色を設定する
        _toolBar.tintColor = UIColor.blueColor()
        // 背景色を設定する
        _toolBar.backgroundColor = UIColor.whiteColor()
        
        // 各ボタンを生成する
        // UIBarButtonItem(style, デリゲートのターゲットを指定, ボタンが押されたときに呼ばれるメソッドを指定)
        let spacer: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
        self.rewindButton = UIBarButtonItem(barButtonSystemItem: .Rewind, target: self, action: "back:")
        self.fastForwardButton = UIBarButtonItem(barButtonSystemItem: .FastForward, target: self, action: "forward:")
        self.refreshButton = UIBarButtonItem(barButtonSystemItem: .Refresh, target: self, action: "refresh:")
        self.openInSafari = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "safari:")
        
        // ボタンをツールバーに入れる.
        _toolBar.items = [rewindButton, fastForwardButton, refreshButton, spacer, openInSafari]
        
        return _toolBar
    }
    
    // ビューが再レイアウトされるときに呼び出される
    override func viewWillLayoutSubviews() {
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        self.webView?.frame = CGRectMake(0, statusBarHeight, self.view.bounds.width, self.view.bounds.height)
    }
    
    // WebViewがコンテンツの読み込みを開始した時に呼ばれる
    func webViewDidStartLoad(webView: UIWebView) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = true
        self.openInSafari.enabled = true
    }
    
    // WebView がコンテンツの読み込みを完了した後に呼ばれる
    func webViewDidFinishLoad(webView: UIWebView) {
        // インジケータを非表示にする
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
    }
    
    // 戻るボタンの処理
    @IBAction func back(AnyObject) {
        self.webView?.goBack()
    }
    
    // 進むボタンの処理
    @IBAction func forward(AnyObject) {
        self.webView?.goForward()
    }
    
    // 再読み込みボタンの処理
    @IBAction func refresh(AnyObject) {
        self.webView?.reload()
    }
    
    // safari で開く
    @IBAction func safari(AnyObject) {
        let url = self.webView?.request?.URL
        UIApplication.sharedApplication().openURL(url!)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

ソースコード

kzy52/ios-swift-webview-example · GitHub

参考

http://iphone-tora.sakura.ne.jp/uikit_size.html

http://blog.impl.co.jp/post/60930914933/ios-autoresizingmask

Rails でオートコンプリート機能を実装する

Rails で typeahead ライブラリを使ってオートコンプリート検索を実装する方法です。

twitter/typeahead.js · GitHub

こんな感じ。

f:id:kzy52:20150118101819p:plain

環境

rails-assets-typehead.js 0.10.5

typehead gem を追加する

# Gemfile

# 追加
source 'https://rails-assets.org'
gem 'rails-assets-typehead.js'
$ bundle install

typehead ライブラリを assets に追加する

# app/assets/javascripts/application.js

...
//= require typehead
...

typehead 用のスタイルシートを assets に追加する

https://github.com/bassjobsen/typeahead.js-bootstrap-css

これを使わせてもらう。
ダウンロードしたファイルは vendor/assets/stylesheets/typeaheadjs.css として配置する。

# app/assets/stylesheets/application.css

...
 *= require typeaheadjs
...

絞込結果を返す API を作成する

# app/controllers/movies_controller.rb

class MoviesController < ApplicationController
  def index
    render json: Movie.where(Movie.arel_table[:title].matches("%#{params[:term]}%"))
  end
end

フォームを作成する

# app/views/home/index.html.erb

<%= form_tag('/watchlist') do %>
  <div class="field">
    <%= text_field_tag :movie_id, nil, autofocus: true, class: 'typeahead' %>
    <%= hidden_field_tag :movie_id, nil, class: 'hidden-movie-id' %>
  </div>
<% end %>

オートコンプリート処理を追加する

# app/assets/javascripts/typehead_sample.coffee

$ ->
  engine = new Bloodhound(
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value')
    queryTokenizer: Bloodhound.tokenizers.whitespace
    remote:
      url: '/movies?term=%QUERY' # %QUERY で入力された値を取得できる。
      filter: (movies) ->
        $.map movies, (movie) ->
          id: movie.id
          value: movie.title

  )

  engine.initialize()

  $('.typeahead').typeahead(
    {
      highlight: true
      hint: true
      minLength: 2 # 2文字以上入力されたら絞込開始。
    },
    {
      name: "movies"
      displayKey: 'value'
      source: engine.ttAdapter()
    }
  ).on "typeahead:selected typeahead:autocomplete", (e, item) ->
    $(this).closest(".field").find(".hidden-movie-id").val item.id # 選択後に id を hidden field に設定する。

これで完了。テキストフィールドに「アイ」と入力すると...

f:id:kzy52:20150125225449p:plain

こうなります。いい感じ!

ソースコード

https://github.com/kzy52/typehead-example

参考

http://twitter.github.io/typeahead.js/examples/

GitLab - push 時に 「Permission denied (publickey,gssapi-keyex,gssapi-with-mic).」

エラー内容

GitLab で push 時に以下のようなエラーが発生した。

$ git push -u origin master

Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

公開鍵もSSHの設定も間違っていないはず...。
ログを見てみると以下のメッセージが出ていた。

# /var/log/secure

User git not allowed because account is locked
input_userauth_request: invalid user git

パスワードを設定していないユーザーで SSH ログインしようとするとこのようなエラーが出るみたい。

解決方法

パスワードを設定するか以下のようにすれば解決する。

# /etc/shadow

git:!!:16569::::::
↓ 変更する
git:*:16569::::::