Gemfile

# Gemfile
...
source 'https://rails-assets.org' do
  gem 'rails-assets-mapbox.js'
end
Javascript Manifest

# app/assets/angulars/mapbox_app.js
...
//= require mapbox.js # 또는 mapbox.js/mapbox.uncompressed.js
//= require_tree './mapboxes/basic'
Asset Precompile

# app/config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( mapbox_app.js )
Stylesheet

# app/assets/stylesheets/application.scss
...
@import "mapbox.js";
Markup

<div class="row" ng-controller="MapBoxDemoController as mapBoxDemoCtrl">
  <div class="input-group">
    <input type="text" class="form-control" ng-model="mapBoxDemoCtrl.terms">
      <span class="input-group-btn">
        <button class="btn btn-default" ng-click="mapBoxDemoCtrl.updatePlaces(mapBoxDemoCtrl.terms)" type="button">Find</button>
      </span>
    </div>
  </div>
  <place-map places="mapBoxDemoCtrl.places" geocode="<%= geocode_url %>"></place-map>
</div>

Angular

# app/assets/angulars/mapboxes/basic.coffee
app = angular.module('app', ['ui.router', 'templates'])

app.controller 'MapBoxDemoController', [ '$scope', ($scope) ->
  @places = []

  @updatePlaces = (terms) ->
    if terms?
      tokens = terms.split(/,/)
      tokens.splice(0,1) if tokens[0] == ""
      tokens = tokens.reduce (a, b) ->
        b = b.trim() if b?
        a.push(b) if( a.indexOf(b) < 0 and b != "")
        a
      ,[]
      @places = tokens
    else
      @places = []
  @
]

app.directive 'placeMap', [ () ->
  restrict: 'E'
  replace: true
  scope:
    places: "="
    geocode: "@"

  template: '<div id="place_map" class="map" style="height: 300px"></div>'
  controller: [ '$scope', '$http', ($scope, $http) ->
    currentPlacesAndMarkers = {}
    L.mapbox.accessToken = '[YOUR MAPBOX TOKEN]'

    map = L.mapbox.map('place_map', '[YOUR MAPBOX ID]',
      scrollWheelZoom: false
    )

    @updatePlaces = () =>
      currentPlaces = _.keys(currentPlacesAndMarkers)
      deletedPlaces = _.difference(currentPlaces, $scope.places)
      addedPlaces = _.difference($scope.places, currentPlaces)

      for place in deletedPlaces
        marker = currentPlacesAndMarkers[place]
        map.removeLayer(marker)
        delete currentPlacesAndMarkers[place]
        if _.keys(currentPlacesAndMarkers).length > 0
          bounds = _.values(currentPlacesAndMarkers).map (marker, i) ->
            [marker._latlng.lat, marker._latlng.lng]
          map.fitBounds(bounds)
          map.setZoom(4) if _.keys(currentPlacesAndMarkers).length == 1

      for place in addedPlaces
        $http.get($scope.geocode + "?address=#{place}").success (data) =>
          lat = parseFloat(data.latitude)
          lng = parseFloat(data.longitude)
          marker = L.marker([lat, lng], title: data.address).addTo(map)
          currentPlacesAndMarkers[data.address] = marker
          bounds = _.values(currentPlacesAndMarkers).map (marker, i) ->
            [marker._latlng.lat, marker._latlng.lng]
          map.fitBounds(bounds)
          map.setZoom(4) if _.keys(currentPlacesAndMarkers).length == 1
    @
  ]
  controllerAs: 'mapBoxCtrl'
  link: (scope, element, attrs, ctrl) ->
    scope.$watchCollection 'places', () ->
      ctrl.updatePlaces()
]
레일즈 API
위 Angular 코드는 지명에 대한 경위도 정보를 API를 통해 얻게 되어있다. 레일즈로 이 API 서비스를 구현하는 방법은 이글의 Location API 파트에 기술되어 있다.