Created
April 7, 2013 18:55
-
-
Save Lordnibbler/5331943 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$:.unshift File.expand_path('../../../lib', __FILE__) | |
require 'sinatra/base' | |
require 'json' | |
require 'data_mapper' | |
require 'geocoder' | |
# DataMapper.setup(:default, 'sqlite::memory:') | |
DataMapper.setup(:default, ENV['DATABASE_URL'] || 'postgres://localhost/uber') | |
class Location | |
include DataMapper::Resource | |
property :id, Serial # An auto-increment integer key | |
property :latitude, Float | |
property :longitude, Float | |
property :address, String | |
property :name, String | |
def to_hash | |
{ :id => id, | |
:latitude => latitude, | |
:longitude => longitude, | |
:address => address, | |
:name => name } | |
end | |
end | |
DataMapper.finalize | |
# run this to migrate the db (add tables) | |
# DataMapper.auto_migrate! | |
class App < Sinatra::Base | |
enable :raise_errors, :logging | |
enable :show_exceptions if development? | |
set :root, File.expand_path('../', __FILE__) | |
set :views, File.expand_path('../', __FILE__) | |
set :public_folder, File.expand_path('../public', __FILE__) | |
get '/' do | |
erb :home | |
end | |
get '/dashboard' do | |
# erb :dashboard | |
send_file "dashboard.html" | |
end | |
get '/locations/:id' do | |
# get a single location | |
location = Location.get(params[:id]) | |
# params = JSON.parse(request.body.read.to_s) | |
location.to_json | |
end | |
put '/locations/:id' do | |
# PUT (update) an existing location | |
# update this to check if params are different from location | |
location = Location.get(params[:id]) | |
params = JSON.parse(request.body.read.to_s) | |
location.update(params) | |
end | |
get '/locations' do | |
# GET all locations | |
Location.all.to_json | |
end | |
delete '/locations/:id' do | |
# DELETE a location | |
location = Location.get(params[:id]) | |
location.destroy | |
end | |
post '/locations' do | |
# CREATE a location | |
# grab POST data | |
params = JSON.parse(request.body.read.to_s) | |
# geocode the address | |
params = geo_code(params) | |
# create a new location record in db | |
location = Location.create(params) | |
end | |
# get '/create_sample_location' do | |
# # Location.auto_migrate! | |
# Location.create(:name => "test", :address => "test") | |
# end | |
def geo_code(params) | |
if params | |
geo_data = Geocoder.search(params["address"]).first | |
params["address"] = geo_data.formatted_address | |
params["longitude"] = geo_data.longitude | |
params["latitude"] = geo_data.latitude | |
params | |
end | |
end | |
# alternatively, run rackup -p 4567 in terminal | |
run! if app_file == $0 | |
end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>Favorite Location Manager</title> | |
<script src='//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'></script> | |
<script src='http://underscorejs.org/underscore-min.js'></script> | |
<script src='http://backbonejs.org/backbone-min.js'></script> | |
<script src="/dashboard.js"></script> | |
<link rel="stylesheet" type="text/css" href="http://twitter.github.io/bootstrap/assets/css/bootstrap.css"> | |
<link rel="stylesheet" type="text/css" href="http://twitter.github.io/bootstrap/assets/css/bootstrap-responsive.css"> | |
<link rel="stylesheet" type="text/css" href="main.css"> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="row"> | |
<h1 style='display:none;'>Favorite Location Manager</h1> | |
<div class="span6"> | |
<h3>Add New Location</h3> | |
<form id="new-location"> | |
<label for="new-location-name">New Location Name</label> | |
<input type="text" id="new-location-name" /> | |
<label for="new-location-address">New Location Address</label> | |
<input type="text" id="new-location-address" /> | |
<br/> | |
<a class="new-location btn primary">Submit</a> | |
</form> | |
</div> | |
<div class="span6"> | |
<h3>Favorite Locations</h3> | |
<ul id="favorite-locations"></ul> | |
</div> | |
</div> | |
</div> | |
<!-- Templates --> | |
<script type="text/template" id="location-template"> | |
<span class="location-name"><%- name %></span> | |
<input class="edit location-name" type="text" value="<%- name %>" /> | |
<span class="lbl location-address"><%- address %></span> | |
<input class="edit location-address" type="text" value="<%- address %>" /> | |
<a class="btn btn-mini done" href="#"><i class="icon-ok"></i> Done</a> | |
<a class="btn btn-mini edit" href="#"><i class="icon-edit"></i> Edit</a> | |
<a class="btn btn-mini destroy" href="#"><i class="icon-remove"></i> Delete</a> | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Load the application once the DOM is ready, using `jQuery.ready`: | |
$(function(){ | |
// Location Model | |
// ---------- | |
// Our basic **Location** model has `long`, `lat`, `address`, and `name` attributes. | |
var Location = Backbone.Model.extend({ | |
// Default attributes for the todo item. | |
defaults: function() { | |
return { | |
longitude: 0.0, | |
latitude: 0.0, | |
address: "123 Pleasant St", | |
name: "Pleasantville" | |
}; | |
}, | |
// Toggle the `done` state of this todo item. | |
// toggle: function() { | |
// this.save({done: !this.get("done")}); | |
// } | |
}); | |
// Location Collection | |
// --------------- | |
// The collection of loctions is backed by postgres | |
var LocationList = Backbone.Collection.extend({ | |
// Reference to this collection's model. | |
model: Location, | |
// Save all of the locations under the `"locations"` namespace. | |
// localStorage: new Backbone.LocalStorage("todos-backbone"), | |
url: '/locations' | |
// Filter down the list of all todo items that are finished. | |
// done: function() { | |
// return this.where({done: true}); | |
// }, | |
// Filter down the list to only todo items that are still not finished. | |
// remaining: function() { | |
// return this.without.apply(this, this.done()); | |
// }, | |
// We keep the Todos in sequential order, despite being saved by unordered | |
// GUID in the database. This generates the next order number for new items. | |
// nextOrder: function() { | |
// if (!this.length) return 1; | |
// return this.last().get('order') + 1; | |
// }, | |
// Todos are sorted by their original insertion order. | |
// comparator: 'order' | |
}); | |
// Create our global collection of **Locations**. | |
var Locations = new LocationList; | |
// Location Item View | |
// -------------- | |
// The DOM element for a location... | |
var LocationView = Backbone.View.extend({ | |
//... is a list tag. | |
tagName: "li", | |
// Cache the template function for a single location. | |
template: _.template($('#location-template').html()), | |
// The DOM events specific to a location. | |
events: { | |
"dblclick span" : "edit", | |
"click a.edit" : "edit", | |
"click a.destroy" : "clear", | |
"keypress .edit" : "updateOnEnter", | |
"click a.btn.done" : "close", | |
// "blur .edit" : "close", | |
}, | |
// The LocationView listens for changes to its model, re-rendering. Since there's | |
// a one-to-one correspondence between a **Location** and a **LocationView** in this | |
// app, we set a direct reference on the model for convenience. | |
initialize: function() { | |
this.listenTo(this.model, 'change', this.render); | |
this.listenTo(this.model, 'destroy', this.remove); | |
}, | |
// Re-render the titles of the todo item. | |
render: function() { | |
console.log("rendering locationView") | |
this.$el.html(this.template(this.model.toJSON())); | |
// this.$el.toggleClass('done', this.model.get('done')); | |
// this.input = this.$('.edit'); | |
this.location_name = this.$('.edit.location-name'); | |
this.location_address = this.$('.edit.location-address'); | |
return this; | |
}, | |
// Toggle the `"done"` state of the model. | |
// toggleDone: function() { | |
// this.model.toggle(); | |
// }, | |
// Switch this view into `"editing"` mode, displaying the input field. | |
edit: function() { | |
this.$el.addClass("editing"); | |
this.$("a.btn.done").css('display', 'inline-block'); | |
this.$("a.btn.edit").css('display', 'none'); | |
// this.location_name.focus(); | |
}, | |
// Close the `"editing"` mode, saving changes to the todo. | |
close: function() { | |
var location_name = this.location_name.val(); | |
var location_address = this.location_address.val(); | |
if (!location_name || !location_address) { | |
this.clear(); | |
} else { | |
this.model.save({name: location_name, address: location_address}); | |
this.$el.removeClass("editing"); | |
this.$("a.btn.done").css('display', 'none'); | |
this.$("a.btn.edit").css('display', 'inline-block'); | |
} | |
}, | |
// If you hit `enter`, we're through editing the item. | |
updateOnEnter: function(e) { | |
if (e.keyCode == 13) this.close(); | |
}, | |
// Remove the item, destroy the model. | |
clear: function() { | |
this.model.destroy(); | |
}, | |
}); | |
// The Application | |
// --------------- | |
// Our overall **AppView** is the top-level piece of UI. | |
var AppView = Backbone.View.extend({ | |
// Instead of generating a new element, bind to the existing skeleton of | |
// the App already present in the HTML. | |
el: $("div.container"), | |
// Our template for the line of statistics at the bottom of the app. | |
// statsTemplate: _.template($('#stats-template').html()), | |
// Delegated events for creating new items, and clearing completed ones. | |
events: { | |
// "keypress #new-todo": "createOnEnter", | |
// "click #clear-completed": "clearCompleted", | |
// "click #toggle-all": "toggleAllComplete", | |
"click a.new-location": "create", | |
"keypress input" : "createOnEnter", | |
}, | |
// At initialization we bind to the relevant events on the `Todos` | |
// collection, when items are added or changed. Kick things off by | |
// loading any preexisting todos that might be saved in *localStorage*. | |
initialize: function() { | |
// this.input = this.$("#new-todo"); | |
this.location_address = this.$('#new-location-address'); | |
this.location_name = this.$('#new-location-name'); | |
this.allCheckbox = this.$("#toggle-all")[0]; | |
this.listenTo(Locations, 'add', this.addOne); | |
this.listenTo(Locations, 'reset', this.addAll); | |
this.listenTo(Locations, 'all', this.render); | |
// this.footer = this.$('footer'); | |
// this.main = $('#main'); | |
Locations.fetch(); | |
}, | |
// Re-rendering the App just means refreshing the statistics -- the rest | |
// of the app doesn't change. | |
render: function() { | |
console.log("rendering appView"); | |
// var done = Locations.done().length; | |
// var remaining = Locations.remaining().length; | |
// if (Locations.length) { | |
// this.main.show(); | |
// this.footer.show(); | |
//this.footer.html(this.statsTemplate({done: done, remaining: remaining})); | |
// } else { | |
// this.main.hide(); | |
// this.footer.hide(); | |
// } | |
// this.allCheckbox.checked = !remaining; | |
}, | |
// Add a single todo item to the list by creating a view for it, and | |
// appending its element to the `<ul>`. | |
addOne: function(location) { | |
var view = new LocationView({model: location}); | |
this.$("#favorite-locations").append(view.render().el); | |
}, | |
// Add all items in the **Todos** collection at once. | |
addAll: function() { | |
Locations.each(this.addOne, this); | |
}, | |
// If you hit return in the main input field, create new **Todo** model, | |
// persisting it to *localStorage*. | |
createOnEnter: function(e) { | |
if (e.keyCode != 13) return; | |
if (!this.location_address.val() || !this.location_name.val()) return; | |
Locations.create({name: this.location_name.val(), address: this.location_address.val()}); | |
// clear the inputs | |
this.location_name.val(''); | |
this.location_address.val(''); | |
}, | |
create: function() { | |
// get name and address data | |
var location_address = this.location_address.val(); | |
var location_name = this.location_name.val(); | |
// validate that data exists | |
if (!location_address || !location_name) { | |
// no data | |
} else { | |
// create a new model | |
Locations.create({ name: location_name, address: location_address }) | |
// clear the inputs | |
this.location_name.val(''); | |
this.location_address.val(''); | |
} | |
}, | |
// Clear all done todo items, destroying their models. | |
clearCompleted: function() { | |
_.invoke(Locations.done(), 'destroy'); | |
return false; | |
}, | |
toggleAllComplete: function () { | |
var done = this.allCheckbox.checked; | |
Locations.each(function (todo) { todo.save({'done': done}); }); | |
} | |
}); | |
// Finally, we kick things off by creating the **App**. | |
var App = new AppView; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment