├── lib └── tasks │ └── .gitkeep ├── public ├── favicon.ico ├── stylesheets │ ├── .gitkeep │ └── scaffold.css ├── .DS_Store ├── images │ ├── rails.png │ └── connect_facebook.png ├── javascripts │ ├── application.js │ ├── icanhaz.min.js │ ├── underscore.min.js │ ├── backbone.min.js │ ├── icanhaz.js │ ├── jquery_ujs.js │ ├── json2.js │ ├── underscore.js │ └── backbone.js ├── robots.txt ├── 422.html ├── 404.html └── 500.html ├── vendor └── plugins │ └── .gitkeep ├── .rvmrc ├── app ├── helpers │ ├── home_helper.rb │ ├── users_helper.rb │ ├── sessions_helper.rb │ └── application_helper.rb ├── views │ ├── users │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── show.html.erb │ │ ├── index.html.erb │ │ └── _form.html.erb │ ├── home │ │ ├── authorize.html.erb │ │ └── index.html.erb │ └── layouts │ │ └── application.html.erb ├── controllers │ ├── home_controller.rb │ ├── application_controller.rb │ ├── sessions_controller.rb │ └── users_controller.rb └── models │ └── user.rb ├── .gitignore ├── config ├── initializers │ ├── backbone.rb │ ├── mime_types.rb │ ├── inflections.rb │ ├── backtrace_silencers.rb │ ├── session_store.rb │ ├── secret_token.rb │ └── omniauth.rb ├── environment.rb ├── boot.rb ├── locales │ └── en.yml ├── database.yml ├── routes.rb ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb └── application.rb ├── .DS_Store ├── test ├── unit │ ├── helpers │ │ ├── home_helper_test.rb │ │ ├── users_helper_test.rb │ │ └── sessions_helper_test.rb │ └── user_test.rb ├── functional │ ├── sessions_controller_test.rb │ ├── home_controller_test.rb │ └── users_controller_test.rb ├── performance │ └── browsing_test.rb ├── fixtures │ └── users.yml └── test_helper.rb ├── config.ru ├── db ├── migrate │ ├── 20110626022944_change_column_type_users.rb │ ├── 20110625201310_rename_columns_in_users.rb │ └── 20110625194437_create_users.rb ├── seeds.rb └── schema.rb ├── doc └── README_FOR_APP ├── script └── rails ├── Rakefile ├── Gemfile ├── Gemfile.lock └── README /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm use 1.9.2@fb-demo1 --create 2 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | tmp/ 5 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /config/initializers/backbone.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Base.include_root_in_json = false -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gertig/facebook-invitations-demo/HEAD/.DS_Store -------------------------------------------------------------------------------- /public/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gertig/facebook-invitations-demo/HEAD/public/.DS_Store -------------------------------------------------------------------------------- /public/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gertig/facebook-invitations-demo/HEAD/public/images/rails.png -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 |

New user

2 | 3 | <%= render 'form' %> 4 | 5 | <%= link_to 'Back', users_path %> 6 | -------------------------------------------------------------------------------- /test/unit/helpers/home_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/helpers/users_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/helpers/sessions_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /public/images/connect_facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gertig/facebook-invitations-demo/HEAD/public/images/connect_facebook.png -------------------------------------------------------------------------------- /app/views/home/authorize.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing user

2 | 3 | <%= render 'form' %> 4 | 5 | <%= link_to 'Show', @user %> | 6 | <%= link_to 'Back', users_path %> 7 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run FbDemo1::Application 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | FbDemo1::Application.initialize! 6 | -------------------------------------------------------------------------------- /public/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // Place your application-specific JavaScript functions and classes here 2 | // This file is automatically included by javascript_include_tag :defaults 3 | -------------------------------------------------------------------------------- /test/unit/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # Replace this with your real tests. 5 | test "the truth" do 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /db/migrate/20110626022944_change_column_type_users.rb: -------------------------------------------------------------------------------- 1 | class ChangeColumnTypeUsers < ActiveRecord::Migration 2 | def self.up 3 | change_column(:users, :fb_uid, :string) 4 | end 5 | 6 | def self.down 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/functional/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionController::TestCase 4 | # Replace this with your real tests. 5 | test "the truth" do 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/functional/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeControllerTest < ActionController::TestCase 4 | test "should get index" do 5 | get :index 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | 3 | def index 4 | @users = User.all 5 | end 6 | 7 | def authorize 8 | respond_to do |format| 9 | format.html # authorize.html.erb 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20110625201310_rename_columns_in_users.rb: -------------------------------------------------------------------------------- 1 | class RenameColumnsInUsers < ActiveRecord::Migration 2 | def self.up 3 | #rename_column(:users, :fb_id, :fb_uid) 4 | #rename_column(:users, :fb_access_token, :fb_token) 5 | end 6 | 7 | def self.down 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/performance/browsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'rails/performance_test_help' 3 | 4 | # Profiling results for each test method are written to tmp/performance. 5 | class BrowsingTest < ActionDispatch::PerformanceTest 6 | def test_homepage 7 | get '/' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Awesomeness 5 | <%= stylesheet_link_tag :all %> 6 | <%= javascript_include_tag :defaults %> 7 | <%= csrf_meta_tag %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake/dsl_definition' 6 | require 'rake' 7 | 8 | FbDemo1::Application.load_tasks 9 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | helper_method :current_user #Makes it available to the views 4 | 5 | private 6 | 7 | def current_user 8 | @current_user ||= User.find(session[:user_id]) if session[:user_id] 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) 7 | # Mayor.create(:name => 'Daley', :city => cities.first) 8 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | 3 | one: 4 | first_name: MyString 5 | last_name: MyString 6 | email: MyString 7 | number_of_friends: 1 8 | fb_uid: 1 9 | fb_token: MyString 10 | 11 | two: 12 | first_name: MyString 13 | last_name: MyString 14 | email: MyString 15 | number_of_friends: 1 16 | fb_uid: 1 17 | fb_token: MyString 18 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | 3 | def self.create_with_omniauth(auth) 4 | create! do |user| 5 | user.first_name = auth["user_info"]["first_name"] 6 | user.last_name = auth["user_info"]["last_name"] 7 | user.email = auth["user_info"]["email"] 8 | user.fb_uid = auth["uid"] 9 | user.fb_token = auth["credentials"]["token"] 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /db/migrate/20110625194437_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def self.up 3 | create_table :users do |t| 4 | t.string :first_name 5 | t.string :last_name 6 | t.string :email 7 | t.integer :number_of_friends, :default => 0 8 | t.integer :fb_uid 9 | t.string :fb_token 10 | 11 | t.timestamps 12 | end 13 | end 14 | 15 | def self.down 16 | drop_table :users 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'rails', '3.0.9' 4 | 5 | # Bundle edge Rails instead: 6 | # gem 'rails', :git => 'git://github.com/rails/rails.git' 7 | 8 | 9 | gem 'sqlite3' 10 | gem 'jquery-rails' 11 | gem 'backbonejs-rails', "~> 0.0.3" 12 | gem 'omniauth' 13 | #gem 'koala' 14 | 15 | group :development do 16 | #gem "rake", "0.9.2" 17 | gem "mongrel", "~> 1.2.0.pre2" 18 | end 19 | 20 | group :production do 21 | #gem 'rake', '~> 0.8.7' 22 | end -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | FbDemo1::Application.config.session_store :cookie_store, :key => '_fb-demo1_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # FbDemo1::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. 7 | # 8 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 9 | # -- they do not yet inherit this setting 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | 4 | def create 5 | auth = request.env["omniauth.auth"] 6 | user = User.find_by_fb_uid_and_fb_token(auth["uid"], auth["credentials"]["token"]) || User.create_with_omniauth(auth) 7 | session[:user_id] = user.id 8 | 9 | redirect_to root_url, :notice => "Signed In" 10 | end 11 | 12 | def destroy 13 | session[:user_id] = nil 14 | 15 | redirect_to root_url, :notice => "Signed Out" 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | FbDemo1::Application.config.secret_token = 'b22c3b54f6c0d8e6017ed33755e58ae46370236ae59ccaaf5b7b0d35a49e016e0bc19c446e0828d7491c1f1c1ee875732835346c09abd67f118d08f7061f9e7a' 8 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | development: 4 | adapter: sqlite3 5 | database: db/development.sqlite3 6 | pool: 5 7 | timeout: 5000 8 | 9 | # Warning: The database defined as "test" will be erased and 10 | # re-generated from your development database when you run "rake". 11 | # Do not set this db to the same as development or production. 12 | test: 13 | adapter: sqlite3 14 | database: db/test.sqlite3 15 | pool: 5 16 | timeout: 5000 17 | 18 | production: 19 | adapter: sqlite3 20 | database: db/production.sqlite3 21 | pool: 5 22 | timeout: 5000 23 | -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= notice %>

2 | 3 |

4 | First name: 5 | <%= @user.first_name %> 6 |

7 | 8 |

9 | Last name: 10 | <%= @user.last_name %> 11 |

12 | 13 |

14 | Email: 15 | <%= @user.email %> 16 |

17 | 18 |

19 | Number of friends: 20 | <%= @user.number_of_friends %> 21 |

22 | 23 |

24 | Fb: 25 | <%= @user.fb_uid %> 26 |

27 | 28 |

29 | Fb access token: 30 | <%= @user.fb_token %> 31 |

32 | 33 | 34 | <%= link_to 'Edit', edit_user_path(@user) %> | 35 | <%= link_to 'Back', users_path %> 36 | -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |

Listing users

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <% @users.each do |user| %> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | <% end %> 25 |
First nameLast nameEmailFb uid
<%= user.first_name %><%= user.last_name %><%= user.email %><%= user.fb_uid %><%= link_to 'Show', user %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %>
26 | 27 |
28 | 29 | <%= link_to 'New User', new_user_path %> 30 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | FbDemo1::Application.routes.draw do 2 | get "home/index" 3 | post "home/authorize" #The route that Facebook will post to 4 | 5 | resources :users 6 | 7 | match "/auth/:provider/callback" => "sessions#create" 8 | match "/signout" => "sessions#destroy", :as => :signout #:as => :signout means you can use signout_path to access this 9 | 10 | # You can have the root of your site routed with "root" 11 | # just remember to delete public/index.html. 12 | root :to => "home#index" 13 | 14 | # See how all your routes lay out with "rake routes" 15 | 16 | # This is a legacy wild controller route that's not recommended for RESTful applications. 17 | # Note: This route will make all actions in every controller accessible via GET requests. 18 | # match ':controller(/:action(/:id(.:format)))' 19 | end 20 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | FbDemo1::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the webserver when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_view.debug_rjs = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Don't care if the mailer can't send 18 | config.action_mailer.raise_delivery_errors = false 19 | 20 | # Print deprecation notices to the Rails logger 21 | config.active_support.deprecation = :log 22 | 23 | # Only use best-standards-support built into browsers 24 | config.action_dispatch.best_standards_support = :builtin 25 | end 26 | 27 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended to check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(:version => 20110626022944) do 14 | 15 | create_table "users", :force => true do |t| 16 | t.string "first_name" 17 | t.string "last_name" 18 | t.string "email" 19 | t.integer "number_of_friends", :default => 0 20 | t.string "fb_uid" 21 | t.string "fb_token" 22 | t.datetime "created_at" 23 | t.datetime "updated_at" 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /app/views/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(@user) do |f| %> 2 | <% if @user.errors.any? %> 3 |
4 |

<%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:

5 | 6 | 11 |
12 | <% end %> 13 | 14 |
15 | <%= f.label :first_name %>
16 | <%= f.text_field :first_name %> 17 |
18 |
19 | <%= f.label :last_name %>
20 | <%= f.text_field :last_name %> 21 |
22 |
23 | <%= f.label :email %>
24 | <%= f.text_field :email %> 25 |
26 |
27 | <%= f.label :number_of_friends %>
28 | <%= f.text_field :number_of_friends %> 29 |
30 |
31 | <%= f.label :fb_uid %>
32 | <%= f.text_field :fb_uid %> 33 |
34 |
35 | <%= f.label :fb_token %>
36 | <%= f.text_field :fb_token %> 37 |
38 |
39 | <%= f.submit %> 40 |
41 | <% end %> 42 | -------------------------------------------------------------------------------- /test/functional/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersControllerTest < ActionController::TestCase 4 | setup do 5 | @user = users(:one) 6 | end 7 | 8 | test "should get index" do 9 | get :index 10 | assert_response :success 11 | assert_not_nil assigns(:users) 12 | end 13 | 14 | test "should get new" do 15 | get :new 16 | assert_response :success 17 | end 18 | 19 | test "should create user" do 20 | assert_difference('User.count') do 21 | post :create, :user => @user.attributes 22 | end 23 | 24 | assert_redirected_to user_path(assigns(:user)) 25 | end 26 | 27 | test "should show user" do 28 | get :show, :id => @user.to_param 29 | assert_response :success 30 | end 31 | 32 | test "should get edit" do 33 | get :edit, :id => @user.to_param 34 | assert_response :success 35 | end 36 | 37 | test "should update user" do 38 | put :update, :id => @user.to_param, :user => @user.attributes 39 | assert_redirected_to user_path(assigns(:user)) 40 | end 41 | 42 | test "should destroy user" do 43 | assert_difference('User.count', -1) do 44 | delete :destroy, :id => @user.to_param 45 | end 46 | 47 | assert_redirected_to users_path 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Awesome People

3 | 11 | 12 | <% if current_user %> 13 |
14 |

<%= link_to "Invite Your Facebook Friends", "#", :id => "invite_fb_friends" %>

15 | 16 |
17 | 18 | 19 | 20 | 34 |
35 | <% end %> 36 | 37 |
38 | 39 | 40 | <% @users.each do |user| %> 41 | 42 | 43 | 44 | <% end %> 45 |
<%= user.first_name %> <%= user.last_name %> is Awesome!
46 | 47 | 48 |
49 |
-------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | FbDemo1::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Log error messages when you accidentally call methods on nil. 11 | config.whiny_nils = true 12 | 13 | # Show full error reports and disable caching 14 | config.consider_all_requests_local = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Raise exceptions instead of rendering exception templates 18 | config.action_dispatch.show_exceptions = false 19 | 20 | # Disable request forgery protection in test environment 21 | config.action_controller.allow_forgery_protection = false 22 | 23 | # Tell Action Mailer not to deliver emails to the real world. 24 | # The :test delivery method accumulates sent emails in the 25 | # ActionMailer::Base.deliveries array. 26 | config.action_mailer.delivery_method = :test 27 | 28 | # Use SQL instead of Active Record's schema dumper when creating the test database. 29 | # This is necessary if your schema can't be completely dumped by the schema dumper, 30 | # like if you have constraints or database-specific column types 31 | # config.active_record.schema_format = :sql 32 | 33 | # Print deprecation notices to the stderr 34 | config.active_support.deprecation = :stderr 35 | end 36 | -------------------------------------------------------------------------------- /config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | #APP_ID, APP_SECRET 2 | Rails.application.config.middleware.use OmniAuth::Builder do 3 | provider :facebook , '138781652866563', '5825f4b91f3ecce46efebca3df717aa2', :iframe => true, 4 | :scope => 'email, user_about_me,user_activities,user_birthday,user_education_history, 5 | user_events,user_groups,user_hometown,user_interests,user_likes, user_location, 6 | user_religion_politics, 7 | user_status,user_work_history, 8 | publish_stream, offline_access, 9 | friends_about_me' 10 | 11 | # friends_activities,friends_birthday,friends_education_history,friends_events, 12 | # friends_groups,friends_hometown,friends_interests,friends_likes,friends_location, 13 | # friends_photo_video_tags,friends_photos,friends_relationships, 14 | # friends_relationship_details,friends_religion_politics,friends_status, 15 | # friends_work_history 16 | 17 | # user_relationships, user_relationship_details, 18 | # user_photo_video_tags,user_photos, 19 | # publish_checkins,manage_pages, 20 | # create_event,rsvp_event,sms,read_friendlists,read_insights,read_mailbox, 21 | # read_requests,read_stream,xmpp_login,ads_management, 22 | # user_checkins,user_videos,user_website, user_notes,user_online_presence,user_address,user_mobile_phone 23 | # friends_notes,friends_online_presence, friends_videos,friends_website, manage_friendlists,friends_checkins 24 | 25 | #Adding, :iframe => true allows a Facebook App to redirect out of Facebook for authentication 26 | end -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | FbDemo1::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The production environment is meant for finished, "live" apps. 5 | # Code is not reloaded between requests 6 | config.cache_classes = true 7 | 8 | # Full error reports are disabled and caching is turned on 9 | config.consider_all_requests_local = false 10 | config.action_controller.perform_caching = true 11 | 12 | # Specifies the header that your server uses for sending files 13 | config.action_dispatch.x_sendfile_header = "X-Sendfile" 14 | 15 | # For nginx: 16 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' 17 | 18 | # If you have no front-end server that supports something like X-Sendfile, 19 | # just comment this out and Rails will serve the files 20 | 21 | # See everything in the log (default is :info) 22 | # config.log_level = :debug 23 | 24 | # Use a different logger for distributed setups 25 | # config.logger = SyslogLogger.new 26 | 27 | # Use a different cache store in production 28 | # config.cache_store = :mem_cache_store 29 | 30 | # Disable Rails's static asset server 31 | # In production, Apache or nginx will already do this 32 | config.serve_static_assets = false 33 | 34 | # Enable serving of images, stylesheets, and javascripts from an asset server 35 | # config.action_controller.asset_host = "http://assets.example.com" 36 | 37 | # Disable delivery errors, bad email addresses will be ignored 38 | # config.action_mailer.raise_delivery_errors = false 39 | 40 | # Enable threaded mode 41 | # config.threadsafe! 42 | 43 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 44 | # the I18n.default_locale when a translation can not be found) 45 | config.i18n.fallbacks = true 46 | 47 | # Send deprecation notices to registered listeners 48 | config.active_support.deprecation = :notify 49 | end 50 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # If you have a Gemfile, require the gems listed there, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(:default, Rails.env) if defined?(Bundler) 8 | 9 | module FbDemo1 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Custom directories with classes and modules you want to be autoloadable. 16 | # config.autoload_paths += %W(#{config.root}/extras) 17 | 18 | # Only load the plugins named here, in the order given (default is alphabetical). 19 | # :all can be used as a placeholder for all plugins not explicitly named. 20 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 21 | 22 | # Activate observers that should always be running. 23 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 24 | 25 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 26 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 27 | # config.time_zone = 'Central Time (US & Canada)' 28 | 29 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 30 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 31 | # config.i18n.default_locale = :de 32 | 33 | # JavaScript files you want as :defaults (application.js is always included). 34 | # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) 35 | 36 | # Configure the default encoding used in templates for Ruby 1.9. 37 | config.encoding = "utf-8" 38 | 39 | # Configure sensitive parameters which will be filtered from the log file. 40 | config.filter_parameters += [:password] 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /public/stylesheets/scaffold.css: -------------------------------------------------------------------------------- 1 | /* FACEBOOK BUTTON */ 2 | a { padding: 0; magin: 0; } 3 | a.connect_with_facebook_button{ 4 | display:block; width:250px; height:50px; margin: 10px auto 10px; 5 | text-indent:-10000px; 6 | background:transparent url("/images/connect_facebook.png") top left no-repeat; 7 | } 8 | 9 | /* * { outline: 1px solid #336699;} */ 10 | 11 | a.connect_with_facebook_button:hover{background-position:0 -50px} 12 | 13 | div#facebook_app { 14 | width: 480px; 15 | height: 300px; 16 | background: #CCDDEE; 17 | border: 1px solid #336699; 18 | border-top: 0px; 19 | margin: 0px auto; 20 | padding: 20px; 21 | } 22 | 23 | div#nav, div#friends { 24 | margin: 20px 0px; 25 | width: 100%; 26 | text-align: center; 27 | } 28 | 29 | h2, h3 { 30 | color: #336699; 31 | width: 100%; 32 | text-align: center; 33 | margin-top: 10px; 34 | } 35 | 36 | body { background-color: #fff; color: #333; } 37 | 38 | body, p, ol, ul, td { 39 | font-family: verdana, arial, helvetica, sans-serif; 40 | font-size: 13px; 41 | line-height: 18px; 42 | } 43 | 44 | pre { 45 | background-color: #eee; 46 | padding: 10px; 47 | font-size: 11px; 48 | } 49 | 50 | a { color: #000; } 51 | a:visited { color: #666; } 52 | /* a:hover { color: #fff; background-color:#000; } */ 53 | 54 | div.field, div.actions { 55 | margin-bottom: 10px; 56 | } 57 | 58 | #notice { 59 | color: green; 60 | } 61 | 62 | .field_with_errors { 63 | padding: 2px; 64 | background-color: red; 65 | display: table; 66 | } 67 | 68 | #error_explanation { 69 | width: 450px; 70 | border: 2px solid red; 71 | padding: 7px; 72 | padding-bottom: 0; 73 | margin-bottom: 20px; 74 | background-color: #f0f0f0; 75 | } 76 | 77 | #error_explanation h2 { 78 | text-align: left; 79 | font-weight: bold; 80 | padding: 5px 5px 5px 15px; 81 | font-size: 12px; 82 | margin: -7px; 83 | margin-bottom: 0px; 84 | background-color: #c00; 85 | color: #fff; 86 | } 87 | 88 | #error_explanation ul li { 89 | font-size: 12px; 90 | list-style: square; 91 | } 92 | 93 | body, html { padding: 0px; margin: 0px;} 94 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | # GET /users 3 | # GET /users.xml 4 | def index 5 | @users = User.all 6 | 7 | respond_to do |format| 8 | format.html # index.html.erb 9 | format.xml { render :xml => @users } 10 | end 11 | end 12 | 13 | # GET /users/1 14 | # GET /users/1.xml 15 | def show 16 | @user = User.find(params[:id]) 17 | 18 | respond_to do |format| 19 | format.html # show.html.erb 20 | format.xml { render :xml => @user } 21 | end 22 | end 23 | 24 | # GET /users/new 25 | # GET /users/new.xml 26 | def new 27 | @user = User.new 28 | 29 | respond_to do |format| 30 | format.html # new.html.erb 31 | format.xml { render :xml => @user } 32 | end 33 | end 34 | 35 | # GET /users/1/edit 36 | def edit 37 | @user = User.find(params[:id]) 38 | end 39 | 40 | # POST /users 41 | # POST /users.xml 42 | def create 43 | @user = User.new(params[:user]) 44 | 45 | respond_to do |format| 46 | if @user.save 47 | format.html { redirect_to(@user, :notice => 'User was successfully created.') } 48 | format.xml { render :xml => @user, :status => :created, :location => @user } 49 | else 50 | format.html { render :action => "new" } 51 | format.xml { render :xml => @user.errors, :status => :unprocessable_entity } 52 | end 53 | end 54 | end 55 | 56 | # PUT /users/1 57 | # PUT /users/1.xml 58 | def update 59 | @user = User.find(params[:id]) 60 | 61 | respond_to do |format| 62 | if @user.update_attributes(params[:user]) 63 | format.html { redirect_to(@user, :notice => 'User was successfully updated.') } 64 | format.xml { head :ok } 65 | else 66 | format.html { render :action => "edit" } 67 | format.xml { render :xml => @user.errors, :status => :unprocessable_entity } 68 | end 69 | end 70 | end 71 | 72 | # DELETE /users/1 73 | # DELETE /users/1.xml 74 | def destroy 75 | @user = User.find(params[:id]) 76 | @user.destroy 77 | 78 | respond_to do |format| 79 | format.html { redirect_to(users_url) } 80 | format.xml { head :ok } 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | abstract (1.0.0) 5 | actionmailer (3.0.9) 6 | actionpack (= 3.0.9) 7 | mail (~> 2.2.19) 8 | actionpack (3.0.9) 9 | activemodel (= 3.0.9) 10 | activesupport (= 3.0.9) 11 | builder (~> 2.1.2) 12 | erubis (~> 2.6.6) 13 | i18n (~> 0.5.0) 14 | rack (~> 1.2.1) 15 | rack-mount (~> 0.6.14) 16 | rack-test (~> 0.5.7) 17 | tzinfo (~> 0.3.23) 18 | activemodel (3.0.9) 19 | activesupport (= 3.0.9) 20 | builder (~> 2.1.2) 21 | i18n (~> 0.5.0) 22 | activerecord (3.0.9) 23 | activemodel (= 3.0.9) 24 | activesupport (= 3.0.9) 25 | arel (~> 2.0.10) 26 | tzinfo (~> 0.3.23) 27 | activeresource (3.0.9) 28 | activemodel (= 3.0.9) 29 | activesupport (= 3.0.9) 30 | activesupport (3.0.9) 31 | addressable (2.2.4) 32 | arel (2.0.10) 33 | backbonejs-rails (0.0.3) 34 | curb (~> 0.7.15) 35 | railties (~> 3.0) 36 | thor (~> 0.14) 37 | builder (2.1.2) 38 | curb (0.7.15) 39 | daemons (1.0.10) 40 | erubis (2.6.6) 41 | abstract (>= 1.0.0) 42 | faraday (0.6.1) 43 | addressable (~> 2.2.4) 44 | multipart-post (~> 1.1.0) 45 | rack (>= 1.1.0, < 2) 46 | gem_plugin (0.2.3) 47 | i18n (0.5.0) 48 | jquery-rails (1.0.12) 49 | railties (~> 3.0) 50 | thor (~> 0.14) 51 | mail (2.2.19) 52 | activesupport (>= 2.3.6) 53 | i18n (>= 0.4.0) 54 | mime-types (~> 1.16) 55 | treetop (~> 1.4.8) 56 | mime-types (1.16) 57 | mongrel (1.2.0.pre2) 58 | daemons (~> 1.0.10) 59 | gem_plugin (~> 0.2.3) 60 | multi_json (1.0.3) 61 | multi_xml (0.2.2) 62 | multipart-post (1.1.2) 63 | net-ldap (0.2.2) 64 | nokogiri (1.4.6) 65 | oa-basic (0.2.6) 66 | oa-core (= 0.2.6) 67 | rest-client (~> 1.6.0) 68 | oa-core (0.2.6) 69 | oa-enterprise (0.2.6) 70 | addressable (= 2.2.4) 71 | net-ldap (~> 0.2.2) 72 | nokogiri (~> 1.4.2) 73 | oa-core (= 0.2.6) 74 | pyu-ruby-sasl (~> 0.0.3.1) 75 | rubyntlm (~> 0.1.1) 76 | oa-more (0.2.6) 77 | multi_json (~> 1.0.0) 78 | oa-core (= 0.2.6) 79 | rest-client (~> 1.6.0) 80 | oa-oauth (0.2.6) 81 | faraday (~> 0.6.1) 82 | multi_json (~> 1.0.0) 83 | multi_xml (~> 0.2.2) 84 | oa-core (= 0.2.6) 85 | oauth (~> 0.4.0) 86 | oauth2 (~> 0.4.1) 87 | oa-openid (0.2.6) 88 | oa-core (= 0.2.6) 89 | rack-openid (~> 1.3.1) 90 | ruby-openid-apps-discovery (~> 1.2.0) 91 | oauth (0.4.4) 92 | oauth2 (0.4.1) 93 | faraday (~> 0.6.1) 94 | multi_json (>= 0.0.5) 95 | omniauth (0.2.6) 96 | oa-basic (= 0.2.6) 97 | oa-core (= 0.2.6) 98 | oa-enterprise (= 0.2.6) 99 | oa-more (= 0.2.6) 100 | oa-oauth (= 0.2.6) 101 | oa-openid (= 0.2.6) 102 | polyglot (0.3.1) 103 | pyu-ruby-sasl (0.0.3.3) 104 | rack (1.2.3) 105 | rack-mount (0.6.14) 106 | rack (>= 1.0.0) 107 | rack-openid (1.3.1) 108 | rack (>= 1.1.0) 109 | ruby-openid (>= 2.1.8) 110 | rack-test (0.5.7) 111 | rack (>= 1.0) 112 | rails (3.0.9) 113 | actionmailer (= 3.0.9) 114 | actionpack (= 3.0.9) 115 | activerecord (= 3.0.9) 116 | activeresource (= 3.0.9) 117 | activesupport (= 3.0.9) 118 | bundler (~> 1.0) 119 | railties (= 3.0.9) 120 | railties (3.0.9) 121 | actionpack (= 3.0.9) 122 | activesupport (= 3.0.9) 123 | rake (>= 0.8.7) 124 | rdoc (~> 3.4) 125 | thor (~> 0.14.4) 126 | rake (0.9.2) 127 | rdoc (3.6.1) 128 | rest-client (1.6.3) 129 | mime-types (>= 1.16) 130 | ruby-openid (2.1.8) 131 | ruby-openid-apps-discovery (1.2.0) 132 | ruby-openid (>= 2.1.7) 133 | rubyntlm (0.1.1) 134 | sqlite3 (1.3.3) 135 | thor (0.14.6) 136 | treetop (1.4.9) 137 | polyglot (>= 0.3.1) 138 | tzinfo (0.3.28) 139 | 140 | PLATFORMS 141 | ruby 142 | 143 | DEPENDENCIES 144 | backbonejs-rails (~> 0.0.3) 145 | jquery-rails 146 | mongrel (~> 1.2.0.pre2) 147 | omniauth 148 | rails (= 3.0.9) 149 | sqlite3 150 | -------------------------------------------------------------------------------- /public/javascripts/icanhaz.min.js: -------------------------------------------------------------------------------- 1 | (function(i){var n=function(){var f=function(){};f.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":true},context:{},render:function(a,b,c,d){if(!d){this.context=b;this.buffer=[]}if(!this.includes("",a))if(d)return a;else{this.send(a);return}a=this.render_pragmas(a);a=this.render_section(a,b,c);if(d)return this.render_tags(a,b,c,d);this.render_tags(a,b,c,d)},send:function(a){a!=""&&this.buffer.push(a)},render_pragmas:function(a){if(!this.includes("%",a))return a; 2 | var b=this;return a.replace(RegExp(this.otag+"%([\\w-]+) ?([\\w]+=[\\w]+)?"+this.ctag),function(c,d,e){if(!b.pragmas_implemented[d])throw{message:"This implementation of mustache doesn't understand the '"+d+"' pragma"};b.pragmas[d]={};if(e){c=e.split("=");b.pragmas[d][c[0]]=c[1]}return""})},render_partial:function(a,b,c){a=this.trim(a);if(!c||c[a]===undefined)throw{message:"unknown_partial '"+a+"'"};if(typeof b[a]!="object")return this.render(c[a],b,c,true);return this.render(c[a],b[a],c,true)},render_section:function(a, 3 | b,c){if(!this.includes("#",a)&&!this.includes("^",a))return a;var d=this;return a.replace(RegExp(this.otag+"(\\^|\\#)\\s*(.+)\\s*"+this.ctag+"\n*([\\s\\S]+?)"+this.otag+"\\/\\s*\\2\\s*"+this.ctag+"\\s*","mg"),function(e,j,k,h){e=d.find(k,b);if(j=="^")return!e||d.is_array(e)&&e.length===0?d.render(h,b,c,true):"";else if(j=="#")return d.is_array(e)?d.map(e,function(g){return d.render(h,d.create_context(g),c,true)}).join(""):d.is_object(e)?d.render(h,d.create_context(e),c,true):typeof e==="function"? 4 | e.call(b,h,function(g){return d.render(g,b,c,true)}):e?d.render(h,b,c,true):""})},render_tags:function(a,b,c,d){var e=this,j=function(){return RegExp(e.otag+"(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?"+e.ctag+"+","g")},k=j(),h=function(o,m,l){switch(m){case "!":return"";case "=":e.set_delimiters(l);k=j();return"";case ">":return e.render_partial(l,b,c);case "{":return e.find(l,b);default:return e.escape(e.find(l,b))}};a=a.split("\n");for(var g=0;g\\]/g,function(b){switch(b){case "&":return"&";case "\\":return"\\\\";case '"':return'"';case "<":return"<";case ">":return">";default:return b}})},create_context:function(a){if(this.is_object(a))return a;else{var b=".";if(this.pragmas["IMPLICIT-ITERATOR"])b=this.pragmas["IMPLICIT-ITERATOR"].iterator;var c={};c[b]=a;return c}}, 7 | is_object:function(a){return a&&typeof a=="object"},is_array:function(a){return Object.prototype.toString.call(a)==="[object Array]"},trim:function(a){return a.replace(/^\s*|\s*$/g,"")},map:function(a,b){if(typeof a.map=="function")return a.map(b);else{for(var c=[],d=a.length,e=0;e=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a, 13 | c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;bd?1:0}),"value")};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c), 16 | e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return function(){if(--a<1)return b.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a, 20 | b.identity)};b.functions=b.methods=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){h(f.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){h(f.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!= 21 | typeof c)return!1;if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1; 22 | for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)}; 23 | b.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset||!a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e= 24 | 0;e/g,interpolate:/<%=([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate|| 25 | null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);h(["pop","push","reverse","shift","sort", 26 | "splice","unshift"],function(a){var b=i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});h(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})(); -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | == Welcome to Rails 2 | 3 | Rails is a web-application framework that includes everything needed to create 4 | database-backed web applications according to the Model-View-Control pattern. 5 | 6 | This pattern splits the view (also called the presentation) into "dumb" 7 | templates that are primarily responsible for inserting pre-built data in between 8 | HTML tags. The model contains the "smart" domain objects (such as Account, 9 | Product, Person, Post) that holds all the business logic and knows how to 10 | persist themselves to a database. The controller handles the incoming requests 11 | (such as Save New Account, Update Product, Show Post) by manipulating the model 12 | and directing data to the view. 13 | 14 | In Rails, the model is handled by what's called an object-relational mapping 15 | layer entitled Active Record. This layer allows you to present the data from 16 | database rows as objects and embellish these data objects with business logic 17 | methods. You can read more about Active Record in 18 | link:files/vendor/rails/activerecord/README.html. 19 | 20 | The controller and view are handled by the Action Pack, which handles both 21 | layers by its two parts: Action View and Action Controller. These two layers 22 | are bundled in a single package due to their heavy interdependence. This is 23 | unlike the relationship between the Active Record and Action Pack that is much 24 | more separate. Each of these packages can be used independently outside of 25 | Rails. You can read more about Action Pack in 26 | link:files/vendor/rails/actionpack/README.html. 27 | 28 | 29 | == Getting Started 30 | 31 | 1. At the command prompt, create a new Rails application: 32 | rails new myapp (where myapp is the application name) 33 | 34 | 2. Change directory to myapp and start the web server: 35 | cd myapp; rails server (run with --help for options) 36 | 37 | 3. Go to http://localhost:3000/ and you'll see: 38 | "Welcome aboard: You're riding Ruby on Rails!" 39 | 40 | 4. Follow the guidelines to start developing your application. You can find 41 | the following resources handy: 42 | 43 | * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html 44 | * Ruby on Rails Tutorial Book: http://www.railstutorial.org/ 45 | 46 | 47 | == Debugging Rails 48 | 49 | Sometimes your application goes wrong. Fortunately there are a lot of tools that 50 | will help you debug it and get it back on the rails. 51 | 52 | First area to check is the application log files. Have "tail -f" commands 53 | running on the server.log and development.log. Rails will automatically display 54 | debugging and runtime information to these files. Debugging info will also be 55 | shown in the browser on requests from 127.0.0.1. 56 | 57 | You can also log your own messages directly into the log file from your code 58 | using the Ruby logger class from inside your controllers. Example: 59 | 60 | class WeblogController < ActionController::Base 61 | def destroy 62 | @weblog = Weblog.find(params[:id]) 63 | @weblog.destroy 64 | logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") 65 | end 66 | end 67 | 68 | The result will be a message in your log file along the lines of: 69 | 70 | Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! 71 | 72 | More information on how to use the logger is at http://www.ruby-doc.org/core/ 73 | 74 | Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are 75 | several books available online as well: 76 | 77 | * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) 78 | * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) 79 | 80 | These two books will bring you up to speed on the Ruby language and also on 81 | programming in general. 82 | 83 | 84 | == Debugger 85 | 86 | Debugger support is available through the debugger command when you start your 87 | Mongrel or WEBrick server with --debugger. This means that you can break out of 88 | execution at any point in the code, investigate and change the model, and then, 89 | resume execution! You need to install ruby-debug to run the server in debugging 90 | mode. With gems, use sudo gem install ruby-debug. Example: 91 | 92 | class WeblogController < ActionController::Base 93 | def index 94 | @posts = Post.find(:all) 95 | debugger 96 | end 97 | end 98 | 99 | So the controller will accept the action, run the first line, then present you 100 | with a IRB prompt in the server window. Here you can do things like: 101 | 102 | >> @posts.inspect 103 | => "[#nil, "body"=>nil, "id"=>"1"}>, 105 | #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" 107 | >> @posts.first.title = "hello from a debugger" 108 | => "hello from a debugger" 109 | 110 | ...and even better, you can examine how your runtime objects actually work: 111 | 112 | >> f = @posts.first 113 | => #nil, "body"=>nil, "id"=>"1"}> 114 | >> f. 115 | Display all 152 possibilities? (y or n) 116 | 117 | Finally, when you're ready to resume execution, you can enter "cont". 118 | 119 | 120 | == Console 121 | 122 | The console is a Ruby shell, which allows you to interact with your 123 | application's domain model. Here you'll have all parts of the application 124 | configured, just like it is when the application is running. You can inspect 125 | domain models, change values, and save to the database. Starting the script 126 | without arguments will launch it in the development environment. 127 | 128 | To start the console, run rails console from the application 129 | directory. 130 | 131 | Options: 132 | 133 | * Passing the -s, --sandbox argument will rollback any modifications 134 | made to the database. 135 | * Passing an environment name as an argument will load the corresponding 136 | environment. Example: rails console production. 137 | 138 | To reload your controllers and models after launching the console run 139 | reload! 140 | 141 | More information about irb can be found at: 142 | link:http://www.rubycentral.com/pickaxe/irb.html 143 | 144 | 145 | == dbconsole 146 | 147 | You can go to the command line of your database directly through rails 148 | dbconsole. You would be connected to the database with the credentials 149 | defined in database.yml. Starting the script without arguments will connect you 150 | to the development database. Passing an argument will connect you to a different 151 | database, like rails dbconsole production. Currently works for MySQL, 152 | PostgreSQL and SQLite 3. 153 | 154 | == Description of Contents 155 | 156 | The default directory structure of a generated Ruby on Rails application: 157 | 158 | |-- app 159 | | |-- controllers 160 | | |-- helpers 161 | | |-- mailers 162 | | |-- models 163 | | `-- views 164 | | `-- layouts 165 | |-- config 166 | | |-- environments 167 | | |-- initializers 168 | | `-- locales 169 | |-- db 170 | |-- doc 171 | |-- lib 172 | | `-- tasks 173 | |-- log 174 | |-- public 175 | | |-- images 176 | | |-- javascripts 177 | | `-- stylesheets 178 | |-- script 179 | |-- test 180 | | |-- fixtures 181 | | |-- functional 182 | | |-- integration 183 | | |-- performance 184 | | `-- unit 185 | |-- tmp 186 | | |-- cache 187 | | |-- pids 188 | | |-- sessions 189 | | `-- sockets 190 | `-- vendor 191 | `-- plugins 192 | 193 | app 194 | Holds all the code that's specific to this particular application. 195 | 196 | app/controllers 197 | Holds controllers that should be named like weblogs_controller.rb for 198 | automated URL mapping. All controllers should descend from 199 | ApplicationController which itself descends from ActionController::Base. 200 | 201 | app/models 202 | Holds models that should be named like post.rb. Models descend from 203 | ActiveRecord::Base by default. 204 | 205 | app/views 206 | Holds the template files for the view that should be named like 207 | weblogs/index.html.erb for the WeblogsController#index action. All views use 208 | eRuby syntax by default. 209 | 210 | app/views/layouts 211 | Holds the template files for layouts to be used with views. This models the 212 | common header/footer method of wrapping views. In your views, define a layout 213 | using the layout :default and create a file named default.html.erb. 214 | Inside default.html.erb, call <% yield %> to render the view using this 215 | layout. 216 | 217 | app/helpers 218 | Holds view helpers that should be named like weblogs_helper.rb. These are 219 | generated for you automatically when using generators for controllers. 220 | Helpers can be used to wrap functionality for your views into methods. 221 | 222 | config 223 | Configuration files for the Rails environment, the routing map, the database, 224 | and other dependencies. 225 | 226 | db 227 | Contains the database schema in schema.rb. db/migrate contains all the 228 | sequence of Migrations for your schema. 229 | 230 | doc 231 | This directory is where your application documentation will be stored when 232 | generated using rake doc:app 233 | 234 | lib 235 | Application specific libraries. Basically, any kind of custom code that 236 | doesn't belong under controllers, models, or helpers. This directory is in 237 | the load path. 238 | 239 | public 240 | The directory available for the web server. Contains subdirectories for 241 | images, stylesheets, and javascripts. Also contains the dispatchers and the 242 | default HTML files. This should be set as the DOCUMENT_ROOT of your web 243 | server. 244 | 245 | script 246 | Helper scripts for automation and generation. 247 | 248 | test 249 | Unit and functional tests along with fixtures. When using the rails generate 250 | command, template test files will be generated for you and placed in this 251 | directory. 252 | 253 | vendor 254 | External libraries that the application depends on. Also includes the plugins 255 | subdirectory. If the app has frozen rails, those gems also go here, under 256 | vendor/rails/. This directory is in the load path. 257 | -------------------------------------------------------------------------------- /public/javascripts/backbone.min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.3.3 2 | // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Backbone may be freely distributed under the MIT license. 4 | // For all details and documentation: 5 | // http://documentcloud.github.com/backbone 6 | (function(){var e;e=typeof exports!=="undefined"?exports:this.Backbone={};e.VERSION="0.3.3";var f=this._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var h=this.jQuery||this.Zepto;e.emulateHTTP=false;e.emulateJSON=false;e.Events={bind:function(a,b){this._callbacks||(this._callbacks={});(this._callbacks[a]||(this._callbacks[a]=[])).push(b);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d=0,g=c.length;d/g,">").replace(/"/g, 9 | """)},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return false;if("id"in a)this.id=a.id;for(var g in a){var i=a[g];if(!f.isEqual(c[g],i)){c[g]=i;delete d[g];if(!b.silent){this._changed=true;this.trigger("change:"+g,this,i,b)}}}!b.silent&&this._changed&&this.change(b);return this},unset:function(a,b){b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&& 10 | !this._performValidation(c,b))return false;delete this.attributes[a];delete this._escapedAttributes[a];if(!b.silent){this._changed=true;this.trigger("change:"+a,this,void 0,b);this.change(b)}return this},clear:function(a){a||(a={});var b=this.attributes,c={};for(attr in b)c[attr]=void 0;if(!a.silent&&this.validate&&!this._performValidation(c,a))return false;this.attributes={};this._escapedAttributes={};if(!a.silent){this._changed=true;for(attr in b)this.trigger("change:"+attr,this,void 0,a);this.change(a)}return this}, 11 | fetch:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("read",this,function(d){if(!b.set(b.parse(d),a))return false;a.success&&a.success(b,d)},c);return this},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return false;var c=this,d=j(b.error,c,b),g=this.isNew()?"create":"update";(this.sync||e.sync)(g,this,function(i){if(!c.set(c.parse(i),b))return false;b.success&&b.success(c,i)},d);return this},destroy:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("delete", 12 | this,function(d){b.collection&&b.collection.remove(b);a.success&&a.success(b,d)},c);return this},url:function(){var a=k(this.collection);if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+this.id},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return!this.id},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=false},hasChanged:function(a){if(a)return this._previousAttributes[a]!= 13 | this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=false,d;for(d in a)if(!f.isEqual(b[d],a[d])){c=c||{};c[d]=a[d]}return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c){b.error?b.error(this,c):this.trigger("error",this,c,b);return false}return true}}); 14 | e.Collection=function(a,b){b||(b={});if(b.comparator){this.comparator=b.comparator;delete b.comparator}this._boundOnModelEvent=f.bind(this._onModelEvent,this);this._reset();a&&this.refresh(a,{silent:true});this.initialize(a,b)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow; 22 | "onhashchange"in window&&!a?h(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);return this.loadUrl()},route:function(a,b){this.handlers.push({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();if(a==this.fragment&&this.iframe)a=this.getFragment(this.iframe.location);if(a==this.fragment||a==decodeURIComponent(this.fragment))return false;if(this.iframe)window.location.hash=this.iframe.location.hash=a;this.loadUrl()},loadUrl:function(){var a=this.fragment= 23 | this.getFragment();return f.any(this.handlers,function(b){if(b.route.test(a)){b.callback(a);return true}})},saveLocation:function(a){a=(a||"").replace(l,"");if(this.fragment!=a){window.location.hash=this.fragment=a;if(this.iframe&&a!=this.getFragment(this.iframe.location)){this.iframe.document.open().close();this.iframe.location.hash=a}}}});e.View=function(a){this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize(a)};var q=/^(\w+)\s*(.*)$/;f.extend(e.View.prototype,e.Events, 24 | {tagName:"div",$:function(a){return h(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){h(this.el).remove();return this},make:function(a,b,c){a=document.createElement(a);b&&h(a).attr(b);c&&h(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events)){h(this.el).unbind();for(var b in a){var c=a[b],d=b.match(q),g=d[1];d=d[2];c=f.bind(this[c],this);d===""?h(this.el).bind(g,c):h(this.el).delegate(d,g,c)}}},_configure:function(a){if(this.options)a=f.extend({}, 25 | this.options,a);if(a.model)this.model=a.model;if(a.collection)this.collection=a.collection;if(a.el)this.el=a.el;if(a.id)this.id=a.id;if(a.className)this.className=a.className;if(a.tagName)this.tagName=a.tagName;this.options=a},_ensureElement:function(){if(!this.el){var a={};if(this.id)a.id=this.id;if(this.className)a["class"]=this.className;this.el=this.make(this.tagName,a)}}});var m=function(a,b){var c=r(this,a,b);c.extend=m;return c};e.Model.extend=e.Collection.extend=e.Controller.extend=e.View.extend= 26 | m;var s={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};e.sync=function(a,b,c,d){var g=s[a];a=a==="create"||a==="update"?JSON.stringify(b.toJSON()):null;b={url:k(b),type:g,contentType:"application/json",data:a,dataType:"json",processData:false,success:c,error:d};if(e.emulateJSON){b.contentType="application/x-www-form-urlencoded";b.processData=true;b.data=a?{model:a}:{}}if(e.emulateHTTP)if(g==="PUT"||g==="DELETE"){if(e.emulateJSON)b.data._method=g;b.type="POST";b.beforeSend=function(i){i.setRequestHeader("X-HTTP-Method-Override", 27 | g)}}h.ajax(b)};var n=function(){},r=function(a,b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){return a.apply(this,arguments)};n.prototype=a.prototype;d.prototype=new n;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},k=function(a){if(!(a&&a.url))throw Error("A 'url' property or function must be specified");return f.isFunction(a.url)?a.url():a.url},j=function(a,b,c){return function(d){a?a(b,d):b.trigger("error",b,d,c)}}})(); -------------------------------------------------------------------------------- /public/javascripts/icanhaz.js: -------------------------------------------------------------------------------- 1 | /*! 2 | ICanHaz.js version 0.9 -- by @HenrikJoreteg 3 | More info at: http://icanhazjs.com 4 | */ 5 | (function ($) { 6 | /*! 7 | mustache.js -- Logic-less templates in JavaScript 8 | 9 | by @janl (MIT Licensed, https://github.com/janl/mustache.js/blob/master/LICENSE). 10 | 11 | See http://mustache.github.com/ for more info. 12 | */ 13 | 14 | var Mustache = function() { 15 | var Renderer = function() {}; 16 | 17 | Renderer.prototype = { 18 | otag: "{{", 19 | ctag: "}}", 20 | pragmas: {}, 21 | buffer: [], 22 | pragmas_implemented: { 23 | "IMPLICIT-ITERATOR": true 24 | }, 25 | context: {}, 26 | 27 | render: function(template, context, partials, in_recursion) { 28 | // reset buffer & set context 29 | if(!in_recursion) { 30 | this.context = context; 31 | this.buffer = []; // TODO: make this non-lazy 32 | } 33 | 34 | // fail fast 35 | if(!this.includes("", template)) { 36 | if(in_recursion) { 37 | return template; 38 | } else { 39 | this.send(template); 40 | return; 41 | } 42 | } 43 | 44 | template = this.render_pragmas(template); 45 | var html = this.render_section(template, context, partials); 46 | if(in_recursion) { 47 | return this.render_tags(html, context, partials, in_recursion); 48 | } 49 | 50 | this.render_tags(html, context, partials, in_recursion); 51 | }, 52 | 53 | /* 54 | Sends parsed lines 55 | */ 56 | send: function(line) { 57 | if(line != "") { 58 | this.buffer.push(line); 59 | } 60 | }, 61 | 62 | /* 63 | Looks for %PRAGMAS 64 | */ 65 | render_pragmas: function(template) { 66 | // no pragmas 67 | if(!this.includes("%", template)) { 68 | return template; 69 | } 70 | 71 | var that = this; 72 | var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + 73 | this.ctag); 74 | return template.replace(regex, function(match, pragma, options) { 75 | if(!that.pragmas_implemented[pragma]) { 76 | throw({message: 77 | "This implementation of mustache doesn't understand the '" + 78 | pragma + "' pragma"}); 79 | } 80 | that.pragmas[pragma] = {}; 81 | if(options) { 82 | var opts = options.split("="); 83 | that.pragmas[pragma][opts[0]] = opts[1]; 84 | } 85 | return ""; 86 | // ignore unknown pragmas silently 87 | }); 88 | }, 89 | 90 | /* 91 | Tries to find a partial in the curent scope and render it 92 | */ 93 | render_partial: function(name, context, partials) { 94 | name = this.trim(name); 95 | if(!partials || partials[name] === undefined) { 96 | throw({message: "unknown_partial '" + name + "'"}); 97 | } 98 | if(typeof(context[name]) != "object") { 99 | return this.render(partials[name], context, partials, true); 100 | } 101 | return this.render(partials[name], context[name], partials, true); 102 | }, 103 | 104 | /* 105 | Renders inverted (^) and normal (#) sections 106 | */ 107 | render_section: function(template, context, partials) { 108 | if(!this.includes("#", template) && !this.includes("^", template)) { 109 | return template; 110 | } 111 | 112 | var that = this; 113 | // CSW - Added "+?" so it finds the tighest bound, not the widest 114 | var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag + 115 | "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag + 116 | "\\s*", "mg"); 117 | 118 | // for each {{#foo}}{{/foo}} section do... 119 | return template.replace(regex, function(match, type, name, content) { 120 | var value = that.find(name, context); 121 | if(type == "^") { // inverted section 122 | if(!value || that.is_array(value) && value.length === 0) { 123 | // false or empty list, render it 124 | return that.render(content, context, partials, true); 125 | } else { 126 | return ""; 127 | } 128 | } else if(type == "#") { // normal section 129 | if(that.is_array(value)) { // Enumerable, Let's loop! 130 | return that.map(value, function(row) { 131 | return that.render(content, that.create_context(row), 132 | partials, true); 133 | }).join(""); 134 | } else if(that.is_object(value)) { // Object, Use it as subcontext! 135 | return that.render(content, that.create_context(value), 136 | partials, true); 137 | } else if(typeof value === "function") { 138 | // higher order section 139 | return value.call(context, content, function(text) { 140 | return that.render(text, context, partials, true); 141 | }); 142 | } else if(value) { // boolean section 143 | return that.render(content, context, partials, true); 144 | } else { 145 | return ""; 146 | } 147 | } 148 | }); 149 | }, 150 | 151 | /* 152 | Replace {{foo}} and friends with values from our view 153 | */ 154 | render_tags: function(template, context, partials, in_recursion) { 155 | // tit for tat 156 | var that = this; 157 | 158 | var new_regex = function() { 159 | return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + 160 | that.ctag + "+", "g"); 161 | }; 162 | 163 | var regex = new_regex(); 164 | var tag_replace_callback = function(match, operator, name) { 165 | switch(operator) { 166 | case "!": // ignore comments 167 | return ""; 168 | case "=": // set new delimiters, rebuild the replace regexp 169 | that.set_delimiters(name); 170 | regex = new_regex(); 171 | return ""; 172 | case ">": // render partial 173 | return that.render_partial(name, context, partials); 174 | case "{": // the triple mustache is unescaped 175 | return that.find(name, context); 176 | default: // escape the value 177 | return that.escape(that.find(name, context)); 178 | } 179 | }; 180 | var lines = template.split("\n"); 181 | for(var i = 0; i < lines.length; i++) { 182 | lines[i] = lines[i].replace(regex, tag_replace_callback, this); 183 | if(!in_recursion) { 184 | this.send(lines[i]); 185 | } 186 | } 187 | 188 | if(in_recursion) { 189 | return lines.join("\n"); 190 | } 191 | }, 192 | 193 | set_delimiters: function(delimiters) { 194 | var dels = delimiters.split(" "); 195 | this.otag = this.escape_regex(dels[0]); 196 | this.ctag = this.escape_regex(dels[1]); 197 | }, 198 | 199 | escape_regex: function(text) { 200 | // thank you Simon Willison 201 | if(!arguments.callee.sRE) { 202 | var specials = [ 203 | '/', '.', '*', '+', '?', '|', 204 | '(', ')', '[', ']', '{', '}', '\\' 205 | ]; 206 | arguments.callee.sRE = new RegExp( 207 | '(\\' + specials.join('|\\') + ')', 'g' 208 | ); 209 | } 210 | return text.replace(arguments.callee.sRE, '\\$1'); 211 | }, 212 | 213 | /* 214 | find `name` in current `context`. That is find me a value 215 | from the view object 216 | */ 217 | find: function(name, context) { 218 | name = this.trim(name); 219 | 220 | // Checks whether a value is thruthy or false or 0 221 | function is_kinda_truthy(bool) { 222 | return bool === false || bool === 0 || bool; 223 | } 224 | 225 | var value; 226 | if(is_kinda_truthy(context[name])) { 227 | value = context[name]; 228 | } else if(is_kinda_truthy(this.context[name])) { 229 | value = this.context[name]; 230 | } 231 | 232 | if(typeof value === "function") { 233 | return value.apply(context); 234 | } 235 | if(value !== undefined) { 236 | return value; 237 | } 238 | // silently ignore unkown variables 239 | return ""; 240 | }, 241 | 242 | // Utility methods 243 | 244 | /* includes tag */ 245 | includes: function(needle, haystack) { 246 | return haystack.indexOf(this.otag + needle) != -1; 247 | }, 248 | 249 | /* 250 | Does away with nasty characters 251 | */ 252 | escape: function(s) { 253 | s = String(s === null ? "" : s); 254 | return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) { 255 | switch(s) { 256 | case "&": return "&"; 257 | case "\\": return "\\\\"; 258 | case '"': return '\"'; 259 | case "<": return "<"; 260 | case ">": return ">"; 261 | default: return s; 262 | } 263 | }); 264 | }, 265 | 266 | // by @langalex, support for arrays of strings 267 | create_context: function(_context) { 268 | if(this.is_object(_context)) { 269 | return _context; 270 | } else { 271 | var iterator = "."; 272 | if(this.pragmas["IMPLICIT-ITERATOR"]) { 273 | iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; 274 | } 275 | var ctx = {}; 276 | ctx[iterator] = _context; 277 | return ctx; 278 | } 279 | }, 280 | 281 | is_object: function(a) { 282 | return a && typeof a == "object"; 283 | }, 284 | 285 | is_array: function(a) { 286 | return Object.prototype.toString.call(a) === '[object Array]'; 287 | }, 288 | 289 | /* 290 | Gets rid of leading and trailing whitespace 291 | */ 292 | trim: function(s) { 293 | return s.replace(/^\s*|\s*$/g, ""); 294 | }, 295 | 296 | /* 297 | Why, why, why? Because IE. Cry, cry cry. 298 | */ 299 | map: function(array, fn) { 300 | if (typeof array.map == "function") { 301 | return array.map(fn); 302 | } else { 303 | var r = []; 304 | var l = array.length; 305 | for(var i = 0; i < l; i++) { 306 | r.push(fn(array[i])); 307 | } 308 | return r; 309 | } 310 | } 311 | }; 312 | 313 | return({ 314 | name: "mustache.js", 315 | version: "0.3.0", 316 | 317 | /* 318 | Turns a template and view into HTML 319 | */ 320 | to_html: function(template, view, partials, send_fun) { 321 | var renderer = new Renderer(); 322 | if(send_fun) { 323 | renderer.send = send_fun; 324 | } 325 | renderer.render(template, view, partials); 326 | if(!send_fun) { 327 | return renderer.buffer.join("\n"); 328 | } 329 | } 330 | }); 331 | }();/*! 332 | ICanHaz.js -- by @HenrikJoreteg 333 | */ 334 | /*global jQuery */ 335 | function ICanHaz() { 336 | var self = this; 337 | self.VERSION = "0.9"; 338 | self.templates = {}; 339 | self.partials = {}; 340 | 341 | // public function for adding templates 342 | // We're enforcing uniqueness to avoid accidental template overwrites. 343 | // If you want a different template, it should have a different name. 344 | self.addTemplate = function (name, templateString) { 345 | if (self[name]) throw "Invalid name: " + name + "."; 346 | if (self.templates[name]) throw "Template \" + name + \" exists"; 347 | 348 | self.templates[name] = templateString; 349 | self[name] = function (data, raw) { 350 | data = data || {}; 351 | var result = Mustache.to_html(self.templates[name], data, self.partials); 352 | return raw ? result : $(result); 353 | }; 354 | }; 355 | 356 | // public function for adding partials 357 | self.addPartial = function (name, templateString) { 358 | if (self.partials[name]) { 359 | throw "Partial \" + name + \" exists"; 360 | } else { 361 | self.partials[name] = templateString; 362 | } 363 | }; 364 | 365 | // grabs templates from the DOM and caches them. 366 | // Loop through and add templates. 367 | // Whitespace at beginning and end of all templates inside