├── lib ├── tasks │ └── .gitkeep └── authentication.rb ├── log └── .gitignore ├── tmp └── .gitignore ├── vendor ├── .gitignore └── plugins │ ├── .gitkeep │ ├── table_builder │ ├── VERSION │ ├── init.rb │ ├── lib │ │ ├── tasks │ │ │ └── table_builder_tasks.rake │ │ ├── table_builder.rb │ │ └── table_builder │ │ │ ├── table_builder.rb │ │ │ └── calendar_helper.rb │ ├── .autotest │ ├── test │ │ ├── test_helper.rb │ │ ├── table_builder_test.rb │ │ └── calendar_helper_test.rb │ ├── MIT-LICENSE │ ├── Rakefile │ ├── table_builder.gemspec │ └── README.rdoc │ └── open_id_authentication │ ├── init.rb │ ├── CHANGELOG │ ├── lib │ └── open_id_authentication.rb │ └── README ├── .rspec ├── .rvmrc ├── public ├── stylesheets │ ├── .gitkeep │ ├── generic.css │ ├── edit_goal.css │ ├── edit_stamp.css │ ├── favorites.css │ ├── home.css │ ├── application.css │ └── stamp.css ├── favicon.ico ├── images │ ├── mark.png │ ├── skip.png │ ├── greenbar.png │ ├── spacer.gif │ ├── screencast.jpg │ ├── question_mark.png │ ├── dailystamp_big.gif │ ├── stamper │ │ ├── current.png │ │ ├── red │ │ │ ├── ink.png │ │ │ ├── ready.png │ │ │ ├── small.png │ │ │ ├── tiny.png │ │ │ ├── holding.png │ │ │ └── stamping.png │ │ ├── black │ │ │ ├── ink.png │ │ │ ├── tiny.png │ │ │ ├── ready.png │ │ │ ├── small.png │ │ │ ├── holding.png │ │ │ └── stamping.png │ │ ├── blue │ │ │ ├── ink.png │ │ │ ├── ready.png │ │ │ ├── small.png │ │ │ ├── tiny.png │ │ │ ├── holding.png │ │ │ └── stamping.png │ │ ├── green │ │ │ ├── ink.png │ │ │ ├── tiny.png │ │ │ ├── ready.png │ │ │ ├── small.png │ │ │ ├── holding.png │ │ │ └── stamping.png │ │ └── purple │ │ │ ├── ink.png │ │ │ ├── ready.png │ │ │ ├── small.png │ │ │ ├── tiny.png │ │ │ ├── holding.png │ │ │ └── stamping.png │ ├── dailystamp_corner.gif │ ├── dailystamp_small.png │ ├── stamp_image_overlay.png │ └── instructions │ │ ├── instruction1.gif │ │ ├── instruction2.gif │ │ └── instruction3.gif ├── javascripts │ ├── application.js │ ├── home.js │ ├── edit_stamp.js │ ├── stamp.js │ └── rails.js ├── robots.txt ├── 422.html ├── 404.html └── 500.html ├── spec ├── rcov.opts ├── spec.opts ├── factories.rb ├── fixtures │ ├── stamp_images.yml │ ├── favorites.yml │ ├── stamps.yml │ ├── marks.yml │ ├── month_caches.yml │ └── users.yml ├── models │ ├── favorite_spec.rb │ ├── stamp_image_spec.rb │ ├── month_cache_spec.rb │ ├── user_spec.rb │ ├── mark_spec.rb │ ├── score_tracker_spec.rb │ └── stamp_spec.rb ├── controllers │ ├── user_sessions_controller_spec.rb │ ├── favorites_controller_spec.rb │ ├── users_controller_spec.rb │ ├── marks_controller_spec.rb │ ├── stamp_images_controller_spec.rb │ └── stamps_controller_spec.rb └── spec_helper.rb ├── app ├── helpers │ ├── marks_helper.rb │ ├── users_helper.rb │ ├── stamps_helper.rb │ ├── favorites_helper.rb │ ├── stamp_images_helper.rb │ ├── user_sessions_helper.rb │ ├── layout_helper.rb │ ├── error_messages_helper.rb │ └── application_helper.rb ├── views │ ├── stamp_images │ │ ├── create.js.erb │ │ ├── destroy.js.erb │ │ ├── new.js.erb │ │ ├── new.html.erb │ │ └── _form.html.erb │ ├── stamps │ │ ├── show.js.erb │ │ ├── _score.html.erb │ │ ├── edit_goal.html.erb │ │ ├── _calendar.html.erb │ │ ├── edit.html.erb │ │ ├── show.html.erb │ │ └── index.html.erb │ ├── users │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ └── _form.html.erb │ ├── marks │ │ ├── destroy.js.erb │ │ ├── create.js.erb │ │ └── _reset_points.js.erb │ ├── user_sessions │ │ └── new.html.erb │ ├── favorites │ │ └── index.html.erb │ └── layouts │ │ └── application.html.erb ├── models │ ├── user_session.rb │ ├── favorite.rb │ ├── mark.rb │ ├── month_cache.rb │ ├── score_tracker.rb │ ├── stamp_image.rb │ ├── user.rb │ └── stamp.rb └── controllers │ ├── application_controller.rb │ ├── user_sessions_controller.rb │ ├── favorites_controller.rb │ ├── marks_controller.rb │ ├── stamp_images_controller.rb │ ├── users_controller.rb │ └── stamps_controller.rb ├── config ├── initializers │ ├── openid_store.rb │ ├── stamp_colors.rb │ ├── mime_types.rb │ ├── backtrace_silencers.rb │ ├── secret_token.rb │ ├── session_store.rb │ ├── inflections.rb │ ├── new_rails_defaults.rb │ └── authlogic_fixes.rb ├── preinitializer.rb ├── session_secret.example.txt ├── environment.rb ├── locales │ └── en.yml ├── boot.rb ├── routes.rb ├── database.example.yml ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb ├── application.rb └── deploy.rb ├── autotest └── discover.rb ├── .gitignore ├── Capfile ├── config.ru ├── db ├── migrate │ ├── 20090823022359_add_color_to_stamps.rb │ ├── 20091031172658_add_time_zone_to_users.rb │ ├── 20090822161359_add_score_cache_to_stamps.rb │ ├── 20090823183047_add_guest_to_users.rb │ ├── 20090823043842_add_stamp_image_id_to_stamps.rb │ ├── 20090823171102_add_current_stamp_id_to_users.rb │ ├── 20090823182059_add_openid_identifier_to_users.rb │ ├── 20090912015155_create_favorites.rb │ ├── 20090822005303_create_stamps.rb │ ├── 20090822040759_add_position_to_marks.rb │ ├── 20090822031324_create_marks.rb │ ├── 20090905213233_add_goal_to_stamps.rb │ ├── 20090822004632_create_users.rb │ ├── 20090823070608_create_month_caches.rb │ ├── 20090823033051_create_stamp_images.rb │ ├── 20090823182048_add_open_id_authentication_tables.rb │ └── 20090823035809_create_delayed_jobs.rb ├── seeds.rb └── schema.rb ├── Rakefile ├── script └── rails ├── Gemfile ├── README └── Gemfile.lock /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /log/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm use 1.9.2-p0 2 | -------------------------------------------------------------------------------- /public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/VERSION: -------------------------------------------------------------------------------- 1 | 0.2.0 2 | -------------------------------------------------------------------------------- /spec/rcov.opts: -------------------------------------------------------------------------------- 1 | --exclude "spec/*,gems/*" 2 | --rails -------------------------------------------------------------------------------- /app/helpers/marks_helper.rb: -------------------------------------------------------------------------------- 1 | module MarksHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/views/stamp_images/create.js.erb: -------------------------------------------------------------------------------- 1 | modal_hide(); 2 | -------------------------------------------------------------------------------- /app/helpers/stamps_helper.rb: -------------------------------------------------------------------------------- 1 | module StampsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/favorites_helper.rb: -------------------------------------------------------------------------------- 1 | module FavoritesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/stamp_images_helper.rb: -------------------------------------------------------------------------------- 1 | module StampImagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/user_sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module UserSessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/init.rb: -------------------------------------------------------------------------------- 1 | require 'table_builder' 2 | 3 | -------------------------------------------------------------------------------- /config/initializers/openid_store.rb: -------------------------------------------------------------------------------- 1 | OpenIdAuthentication.store = :file 2 | -------------------------------------------------------------------------------- /app/models/user_session.rb: -------------------------------------------------------------------------------- 1 | class UserSession < Authlogic::Session::Base 2 | end 3 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --colour 2 | --format progress 3 | --loadby mtime 4 | --reverse 5 | -------------------------------------------------------------------------------- /spec/factories.rb: -------------------------------------------------------------------------------- 1 | Factory.define :stamp do |f| 2 | f.name 'Inbox Zero' 3 | end 4 | -------------------------------------------------------------------------------- /spec/fixtures/stamp_images.yml: -------------------------------------------------------------------------------- 1 | one: 2 | user: one 3 | 4 | two: 5 | user: two 6 | -------------------------------------------------------------------------------- /app/views/stamp_images/destroy.js.erb: -------------------------------------------------------------------------------- 1 | $("#stamp_image_<%= @stamp_image.id %>").remove(); 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/favicon.ico -------------------------------------------------------------------------------- /app/views/stamp_images/new.js.erb: -------------------------------------------------------------------------------- 1 | modal_dialog("<%= escape_javascript render('form') %>"); 2 | -------------------------------------------------------------------------------- /app/views/stamps/show.js.erb: -------------------------------------------------------------------------------- 1 | $("#calendar").html("<%= escape_javascript render('calendar') %>") -------------------------------------------------------------------------------- /config/initializers/stamp_colors.rb: -------------------------------------------------------------------------------- 1 | STAMP_COLORS = ["red", "green", "blue", "purple", "black"] 2 | -------------------------------------------------------------------------------- /autotest/discover.rb: -------------------------------------------------------------------------------- 1 | Autotest.add_discovery { "rails" } 2 | Autotest.add_discovery { "rspec2" } 3 | -------------------------------------------------------------------------------- /public/images/mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/mark.png -------------------------------------------------------------------------------- /public/images/skip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/skip.png -------------------------------------------------------------------------------- /public/images/greenbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/greenbar.png -------------------------------------------------------------------------------- /public/images/spacer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/spacer.gif -------------------------------------------------------------------------------- /public/images/screencast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/screencast.jpg -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Sign up" %> 2 | <% stylesheet "generic" %> 3 | 4 | <%= render 'form' %> 5 | -------------------------------------------------------------------------------- /public/images/question_mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/question_mark.png -------------------------------------------------------------------------------- /spec/fixtures/favorites.yml: -------------------------------------------------------------------------------- 1 | one: 2 | user: one 3 | stamp: one 4 | 5 | two: 6 | user: two 7 | stamp: two 8 | -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Update Profile" %> 2 | <% stylesheet "generic" %> 3 | 4 | <%= render 'form' %> 5 | -------------------------------------------------------------------------------- /public/images/dailystamp_big.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/dailystamp_big.gif -------------------------------------------------------------------------------- /public/images/stamper/current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/current.png -------------------------------------------------------------------------------- /public/images/stamper/red/ink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/red/ink.png -------------------------------------------------------------------------------- /spec/models/favorite_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe Favorite do 4 | end 5 | -------------------------------------------------------------------------------- /app/views/stamp_images/new.html.erb: -------------------------------------------------------------------------------- 1 | <% title "New Stamp Image" %> 2 | <% stylesheet "generic" %> 3 | 4 | <%= render 'form' %> -------------------------------------------------------------------------------- /public/images/dailystamp_corner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/dailystamp_corner.gif -------------------------------------------------------------------------------- /public/images/dailystamp_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/dailystamp_small.png -------------------------------------------------------------------------------- /public/images/stamper/black/ink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/black/ink.png -------------------------------------------------------------------------------- /public/images/stamper/black/tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/black/tiny.png -------------------------------------------------------------------------------- /public/images/stamper/blue/ink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/blue/ink.png -------------------------------------------------------------------------------- /public/images/stamper/blue/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/blue/ready.png -------------------------------------------------------------------------------- /public/images/stamper/blue/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/blue/small.png -------------------------------------------------------------------------------- /public/images/stamper/blue/tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/blue/tiny.png -------------------------------------------------------------------------------- /public/images/stamper/green/ink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/green/ink.png -------------------------------------------------------------------------------- /public/images/stamper/green/tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/green/tiny.png -------------------------------------------------------------------------------- /public/images/stamper/purple/ink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/purple/ink.png -------------------------------------------------------------------------------- /public/images/stamper/red/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/red/ready.png -------------------------------------------------------------------------------- /public/images/stamper/red/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/red/small.png -------------------------------------------------------------------------------- /public/images/stamper/red/tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/red/tiny.png -------------------------------------------------------------------------------- /spec/models/stamp_image_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe StampImage do 4 | end 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | tmp/**/* 5 | database.yml 6 | public/assets 7 | config/session_secret.txt 8 | -------------------------------------------------------------------------------- /public/images/stamp_image_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamp_image_overlay.png -------------------------------------------------------------------------------- /public/images/stamper/black/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/black/ready.png -------------------------------------------------------------------------------- /public/images/stamper/black/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/black/small.png -------------------------------------------------------------------------------- /public/images/stamper/blue/holding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/blue/holding.png -------------------------------------------------------------------------------- /public/images/stamper/green/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/green/ready.png -------------------------------------------------------------------------------- /public/images/stamper/green/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/green/small.png -------------------------------------------------------------------------------- /public/images/stamper/purple/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/purple/ready.png -------------------------------------------------------------------------------- /public/images/stamper/purple/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/purple/small.png -------------------------------------------------------------------------------- /public/images/stamper/purple/tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/purple/tiny.png -------------------------------------------------------------------------------- /public/images/stamper/red/holding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/red/holding.png -------------------------------------------------------------------------------- /public/images/stamper/red/stamping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/red/stamping.png -------------------------------------------------------------------------------- /public/images/stamper/black/holding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/black/holding.png -------------------------------------------------------------------------------- /public/images/stamper/black/stamping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/black/stamping.png -------------------------------------------------------------------------------- /public/images/stamper/blue/stamping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/blue/stamping.png -------------------------------------------------------------------------------- /public/images/stamper/green/holding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/green/holding.png -------------------------------------------------------------------------------- /public/images/stamper/green/stamping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/green/stamping.png -------------------------------------------------------------------------------- /public/images/stamper/purple/holding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/purple/holding.png -------------------------------------------------------------------------------- /public/images/stamper/purple/stamping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/stamper/purple/stamping.png -------------------------------------------------------------------------------- /public/images/instructions/instruction1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/instructions/instruction1.gif -------------------------------------------------------------------------------- /public/images/instructions/instruction2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/instructions/instruction2.gif -------------------------------------------------------------------------------- /public/images/instructions/instruction3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diasks2/dailystamp/master/public/images/instructions/instruction3.gif -------------------------------------------------------------------------------- /config/preinitializer.rb: -------------------------------------------------------------------------------- 1 | # load app_config.yml 2 | require 'yaml' 3 | APP_CONFIG = YAML.load(File.read("#{Rails.root}/config/app_config.yml")) 4 | -------------------------------------------------------------------------------- /config/session_secret.example.txt: -------------------------------------------------------------------------------- 1 | eb56cbf5d5e3b0d7c8de4271c428534b2dd3ce59f13488cb60abab274eee8b5be08eeae2d0aedbc40d4da3985f91c56a0a1651775c0a98aa75f3f9a239f66df4 -------------------------------------------------------------------------------- /Capfile: -------------------------------------------------------------------------------- 1 | load 'deploy' if respond_to?(:namespace) # cap2 differentiator 2 | Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) } 3 | load 'config/deploy' -------------------------------------------------------------------------------- /spec/fixtures/stamps.yml: -------------------------------------------------------------------------------- 1 | one: 2 | user: one 3 | name: MyString 4 | private: false 5 | 6 | two: 7 | user: two 8 | name: MyString 9 | private: false 10 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/lib/tasks/table_builder_tasks.rake: -------------------------------------------------------------------------------- 1 | # desc "Explaining what the task does" 2 | # task :table_builder do 3 | # # Task goes here 4 | # end 5 | -------------------------------------------------------------------------------- /app/models/favorite.rb: -------------------------------------------------------------------------------- 1 | class Favorite < ActiveRecord::Base 2 | attr_accessible :stamp_id 3 | belongs_to :user 4 | belongs_to :stamp 5 | validates_presence_of :stamp_id 6 | end 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 Dailystamp::Application 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Dailystamp::Application.initialize! 6 | -------------------------------------------------------------------------------- /spec/fixtures/marks.yml: -------------------------------------------------------------------------------- 1 | one: 2 | skip: false 3 | marked_on: 2009-08-21 4 | stamp: one 5 | 6 | two: 7 | stamp_id: 1 8 | skip: false 9 | marked_on: 2009-08-21 10 | stamp: two 11 | -------------------------------------------------------------------------------- /public/stylesheets/generic.css: -------------------------------------------------------------------------------- 1 | #container { 2 | width: 600px; 3 | max-width: 600px; 4 | } 5 | 6 | form { 7 | background-color: #DDD; 8 | border: solid 1px #999; 9 | padding: 10px 20px; 10 | } 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20090823022359_add_color_to_stamps.rb: -------------------------------------------------------------------------------- 1 | class AddColorToStamps < ActiveRecord::Migration 2 | def self.up 3 | add_column :stamps, :color, :string 4 | end 5 | 6 | def self.down 7 | remove_column :stamps, :color 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20091031172658_add_time_zone_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddTimeZoneToUsers < ActiveRecord::Migration 2 | def self.up 3 | add_column :users, :time_zone, :string 4 | end 5 | 6 | def self.down 7 | remove_column :users, :time_zone 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20090822161359_add_score_cache_to_stamps.rb: -------------------------------------------------------------------------------- 1 | class AddScoreCacheToStamps < ActiveRecord::Migration 2 | def self.up 3 | add_column :stamps, :score_cache, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :stamps, :score_cache 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20090823183047_add_guest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddGuestToUsers < ActiveRecord::Migration 2 | def self.up 3 | add_column :users, :guest, :boolean, :default => false, :null => false 4 | end 5 | 6 | def self.down 7 | remove_column :users, :guest 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20090823043842_add_stamp_image_id_to_stamps.rb: -------------------------------------------------------------------------------- 1 | class AddStampImageIdToStamps < ActiveRecord::Migration 2 | def self.up 3 | add_column :stamps, :stamp_image_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :stamps, :stamp_image_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20090823171102_add_current_stamp_id_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddCurrentStampIdToUsers < ActiveRecord::Migration 2 | def self.up 3 | add_column :users, :current_stamp_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :users, :current_stamp_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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' 6 | 7 | Dailystamp::Application.load_tasks 8 | -------------------------------------------------------------------------------- /db/migrate/20090823182059_add_openid_identifier_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddOpenidIdentifierToUsers < ActiveRecord::Migration 2 | def self.up 3 | add_column :users, :openid_identifier, :string 4 | end 5 | 6 | def self.down 7 | remove_column :users, :openid_identifier 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/marks/destroy.js.erb: -------------------------------------------------------------------------------- 1 | $("#day_<%= @mark.marked_on.strftime("%Y%m%d") %> .mark").remove(); 2 | $("#day_<%= @mark.marked_on.strftime("%Y%m%d") %> .mark_link").attr("href", "<%= escape_javascript marks_path(:stamp_id => @mark.stamp, :date => @mark.marked_on.to_s("%Y-%m-%d")) %>"); 3 | <%= render "reset_points" %> 4 | -------------------------------------------------------------------------------- /spec/fixtures/month_caches.yml: -------------------------------------------------------------------------------- 1 | one: 2 | stamp_id: 1 3 | positive_points: 1 4 | negative_points: 1 5 | position: 1 6 | score: 1 7 | for_month: 2009-08-23 8 | 9 | two: 10 | stamp_id: 1 11 | positive_points: 1 12 | negative_points: 1 13 | position: 1 14 | score: 1 15 | for_month: 2009-08-23 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20090912015155_create_favorites.rb: -------------------------------------------------------------------------------- 1 | class CreateFavorites < ActiveRecord::Migration 2 | def self.up 3 | create_table :favorites do |t| 4 | t.integer :user_id 5 | t.integer :stamp_id 6 | t.timestamps 7 | end 8 | end 9 | 10 | def self.down 11 | drop_table :favorites 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/views/stamp_images/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @stamp_image, :html => { :multipart => true } do |f| %> 2 | <%= f.error_messages %> 3 |

<%= f.label :photo, "Choose an image for your stamp:" %>

4 |

<%= f.file_field :photo %>

5 |

<%= f.submit "Upload" %> or <%= link_to "cancel", root_url %>

6 | <% end %> 7 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/lib/table_builder.rb: -------------------------------------------------------------------------------- 1 | require(File.expand_path(File.join(File.dirname(__FILE__), 'table_builder', 'table_builder.rb'))) 2 | require(File.expand_path(File.join(File.dirname(__FILE__), 'table_builder', 'calendar_helper.rb'))) 3 | 4 | ActionView::Base.send :include, TableHelper 5 | ActionView::Base.send :include, CalendarHelper -------------------------------------------------------------------------------- /db/migrate/20090822005303_create_stamps.rb: -------------------------------------------------------------------------------- 1 | class CreateStamps < ActiveRecord::Migration 2 | def self.up 3 | create_table :stamps do |t| 4 | t.integer :user_id 5 | t.string :name 6 | t.boolean :private 7 | t.timestamps 8 | end 9 | end 10 | 11 | def self.down 12 | drop_table :stamps 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20090822040759_add_position_to_marks.rb: -------------------------------------------------------------------------------- 1 | class AddPositionToMarks < ActiveRecord::Migration 2 | def self.up 3 | add_column :marks, :position_x, :integer 4 | add_column :marks, :position_y, :integer 5 | end 6 | 7 | def self.down 8 | remove_column :marks, :position_y 9 | remove_column :marks, :position_x 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/marks/create.js.erb: -------------------------------------------------------------------------------- 1 | $("#day_<%= @mark.marked_on.strftime("%Y%m%d") %>").prepend("<%= escape_javascript mark_image(@mark) %>"); 2 | $("#day_<%= @mark.marked_on.strftime("%Y%m%d") %> .mark_link").attr("href", "<%= escape_javascript mark_path(@mark) %>"); 3 | $("#stamp_cursor").hide(); 4 | $("#stamper a img").change_image("ready.png"); 5 | <%= render "reset_points" %> 6 | -------------------------------------------------------------------------------- /db/migrate/20090822031324_create_marks.rb: -------------------------------------------------------------------------------- 1 | class CreateMarks < ActiveRecord::Migration 2 | def self.up 3 | create_table :marks do |t| 4 | t.integer :stamp_id 5 | t.boolean :skip, :default => false, :null => false 6 | t.date :marked_on 7 | t.timestamps 8 | end 9 | end 10 | 11 | def self.down 12 | drop_table :marks 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | gemfile = File.expand_path('../../Gemfile', __FILE__) 5 | begin 6 | ENV['BUNDLE_GEMFILE'] = gemfile 7 | require 'bundler' 8 | Bundler.setup 9 | rescue Bundler::GemNotFound => e 10 | STDERR.puts e.message 11 | STDERR.puts "Try running `bundle install`." 12 | exit! 13 | end if File.exist?(gemfile) 14 | -------------------------------------------------------------------------------- /db/migrate/20090905213233_add_goal_to_stamps.rb: -------------------------------------------------------------------------------- 1 | class AddGoalToStamps < ActiveRecord::Migration 2 | def self.up 3 | add_column :stamps, :goal_score, :integer 4 | add_column :stamps, :goal_reward, :text 5 | Stamp.update_all("goal_score = 100") 6 | end 7 | 8 | def self.down 9 | remove_column :stamps, :goal_reward 10 | remove_column :stamps, :goal_score 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /public/stylesheets/edit_goal.css: -------------------------------------------------------------------------------- 1 | #suggestions { 2 | float: right; 3 | color: #555; 4 | } 5 | 6 | #suggestions h3 { 7 | font-size: 16px; 8 | font-weight: bold; 9 | margin-top: 0; 10 | margin-bottom: 3px; 11 | } 12 | 13 | #suggestions ul { 14 | margin: 0; 15 | padding: 0; 16 | list-style: none; 17 | } 18 | 19 | #suggestions li { 20 | margin: 0; 21 | padding: 2px 0; 22 | } 23 | -------------------------------------------------------------------------------- /vendor/plugins/open_id_authentication/init.rb: -------------------------------------------------------------------------------- 1 | if Rails.version < '3' 2 | config.gem 'rack-openid', :lib => 'rack/openid', :version => '>=0.2.1' 3 | end 4 | 5 | require 'open_id_authentication' 6 | 7 | config.middleware.use OpenIdAuthentication 8 | 9 | config.after_initialize do 10 | OpenID::Util.logger = Rails.logger 11 | ActionController::Base.send :include, OpenIdAuthentication 12 | end 13 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/.autotest: -------------------------------------------------------------------------------- 1 | Autotest.add_hook :initialize do |at| 2 | at.clear_mappings 3 | at.add_mapping(/^lib\/.*\.rb$/) do |filename, _| 4 | possible = File.basename(filename).gsub '_', '_?' 5 | at.files_matching %r%^test/.*#{possible}$% 6 | end 7 | 8 | at.add_mapping(/^test\/.*\_test.rb/) do |f, _| 9 | at.files_matching(/^test\/.*_test.rb$/) 10 | end 11 | 12 | end -------------------------------------------------------------------------------- /db/migrate/20090822004632_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def self.up 3 | create_table :users do |t| 4 | t.string :username 5 | t.string :email 6 | t.string :persistence_token 7 | t.string :crypted_password 8 | t.string :password_salt 9 | t.timestamps 10 | end 11 | end 12 | 13 | def self.down 14 | drop_table :users 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/views/marks/_reset_points.js.erb: -------------------------------------------------------------------------------- 1 | <% if @mark.stamp %> 2 | <% @mark.stamp.month_points(@mark.marked_on).each_with_index do |day_points, index| %> 3 | $("#day_<%= @mark.marked_on.strftime("%Y%m#{(index+1).to_s.rjust(2, '0')}") %> .points").html("<%= escape_javascript points(day_points) %>"); 4 | <% end %> 5 | <% end %> 6 | $("#score").html("<%= escape_javascript render(:partial => "stamps/score", :locals => { :stamp => @mark.stamp }) %>"); 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | 3 | require "rubygems" 4 | require 'active_support' 5 | require 'action_pack' 6 | require 'action_controller' 7 | require 'action_view' 8 | require 'action_controller' 9 | require 'action_view' 10 | require 'action_view/base' 11 | require 'action_view/template' 12 | require 'action_view/test_case' 13 | 14 | require(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'table_builder'))) 15 | -------------------------------------------------------------------------------- /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 | Dailystamp::Application.config.secret_token = File.read(Rails.root.join("config/session_secret.txt")) 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Dailystamp::Application.config.session_store :cookie_store, :key => '_dailystamp_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 | # Dailystamp::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /db/migrate/20090823070608_create_month_caches.rb: -------------------------------------------------------------------------------- 1 | class CreateMonthCaches < ActiveRecord::Migration 2 | def self.up 3 | create_table :month_caches do |t| 4 | t.integer :stamp_id 5 | t.integer :positive_points 6 | t.integer :negative_points 7 | t.integer :position 8 | t.integer :score 9 | t.date :for_month 10 | t.timestamps 11 | end 12 | end 13 | 14 | def self.down 15 | drop_table :month_caches 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /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 | inflect.singular /(cache)s/i, '\1' 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20090823033051_create_stamp_images.rb: -------------------------------------------------------------------------------- 1 | class CreateStampImages < ActiveRecord::Migration 2 | def self.up 3 | create_table :stamp_images do |t| 4 | t.integer :user_id 5 | t.datetime :generated_at 6 | t.string :photo_file_name 7 | t.string :photo_content_type 8 | t.integer :photo_file_size 9 | t.datetime :photo_updated_at 10 | t.timestamps 11 | end 12 | end 13 | 14 | def self.down 15 | drop_table :stamp_images 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include Authentication 3 | protect_from_forgery 4 | 5 | before_filter :set_user_time_zone 6 | 7 | private 8 | 9 | def current_user_or_guest 10 | unless logged_in? 11 | @current_user = User.create!(:guest => true) 12 | end 13 | current_user 14 | end 15 | 16 | def set_user_time_zone 17 | Time.zone = current_user.time_zone if logged_in? && !current_user.time_zone.blank? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/stamps/_score.html.erb: -------------------------------------------------------------------------------- 1 |
score <%=h stamp.score %>
2 |
3 | <%= stamp.goal_score %> 4 | <% if stamp.user == current_user %> 5 | (<%= link_to "change goal", edit_goal_stamp_path(stamp) %>) 6 | <% end %> 7 |
8 | <% if stamp.goal_reached? && !stamp.goal_reward.blank? %> 9 |
10 | Reward: 11 | <%=h stamp.goal_reward %> 12 |
13 | <% end %> -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem "rails", "3.0.3" 4 | gem "mysql2" 5 | gem "authlogic" 6 | gem "authlogic-oid", :require => "authlogic_openid" 7 | gem "ruby-openid", :require => "openid" 8 | gem "rack-openid", :require => "rack/openid" 9 | gem "paperclip" 10 | gem "rmagick", :require => "RMagick" 11 | gem "jquery-rails" 12 | 13 | group :development, :test do 14 | gem "mocha" 15 | gem "rspec-rails" 16 | gem "factory_girl_rails" 17 | gem "autotest" 18 | gem "autotest-rails" 19 | end 20 | 21 | group :development do 22 | gem "nifty-generators" 23 | end 24 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Dailystamp::Application.routes.draw do 2 | root :to => 'stamps#index' 3 | 4 | match 'signup' => 'users#new', :as => 'signup' 5 | match 'logout' => 'user_sessions#destroy', :as => 'logout' 6 | match 'login' => 'user_sessions#new', :as => 'login' 7 | match 'home' => 'stamps#index', :as => 'home', :no_redirect => 'true' 8 | 9 | resources :user_sessions 10 | resources :users 11 | resources :favorites 12 | resources :stamp_images 13 | resources :marks 14 | resources :stamps do 15 | member do 16 | get :edit_goal 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/mark.rb: -------------------------------------------------------------------------------- 1 | class Mark < ActiveRecord::Base 2 | attr_accessible :skip, :marked_on, :position_x, :position_y 3 | belongs_to :stamp 4 | after_save :reset_score 5 | after_destroy :reset_score 6 | 7 | def image_path 8 | stamp && stamp.stamp_image && stamp.stamp_image.photo.url(stamp.color).sub(/[^\.]+$/, "png") 9 | end 10 | 11 | private 12 | 13 | def reset_score 14 | if stamp 15 | stamp.update_attribute(:score_cache, nil) 16 | MonthCache.delete_all(["for_month >= ? and stamp_id=?", marked_on.beginning_of_month, stamp.id]) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /public/javascripts/home.js: -------------------------------------------------------------------------------- 1 | var current_suggestion = 1; 2 | 3 | $(function() { 4 | $("#suggestions .more").click(function() { 5 | $("#suggestions_" + current_suggestion).slideUp("normal", function() { 6 | if (current_suggestion == 4) { 7 | current_suggestion = 1 8 | } else { 9 | current_suggestion++; 10 | } 11 | $("#suggestions_" + current_suggestion).slideDown("normal"); 12 | }); 13 | return false; 14 | }); 15 | $("#suggestions ul a").click(function() { 16 | $("#stamp_name").attr("value", this.innerHTML) 17 | return false; 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /app/controllers/user_sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class UserSessionsController < ApplicationController 2 | def new 3 | @user_session = UserSession.new(:remember_me => true) 4 | end 5 | 6 | def create 7 | @user_session = UserSession.new(params[:user_session]) 8 | @user_session.save do |result| 9 | if result 10 | redirect_to_target_or_default(root_url) 11 | else 12 | render :action => 'new' 13 | end 14 | end 15 | end 16 | 17 | def destroy 18 | @user_session = UserSession.find 19 | @user_session.destroy 20 | flash[:notice] = "You have been logged out." 21 | redirect_to root_url 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/favorites_controller.rb: -------------------------------------------------------------------------------- 1 | class FavoritesController < ApplicationController 2 | before_filter :login_required 3 | 4 | def index 5 | @favorites = current_user.favorites 6 | end 7 | 8 | def create 9 | @favorite = current_user.favorites.build(:stamp_id => params[:stamp_id]) 10 | if @favorite.save 11 | flash[:notice] = "Started watching stamp." 12 | else 13 | flash[:error] = "Unable to watch this stamp." 14 | end 15 | redirect_to favorites_url 16 | end 17 | 18 | def destroy 19 | @favorite = current_user.favorites.find(params[:id]) 20 | @favorite.destroy 21 | redirect_to favorites_url 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/models/month_cache.rb: -------------------------------------------------------------------------------- 1 | class MonthCache < ActiveRecord::Base 2 | belongs_to :stamp 3 | 4 | def self.tracker_for(stamp, date) 5 | month_cache = stamp.month_caches.first(:conditions => { :for_month => date.beginning_of_month }) 6 | ScoreTracker.new(month_cache.attributes.symbolize_keys) if month_cache 7 | end 8 | 9 | def self.save_tracker(tracker, stamp, date) 10 | stamp.month_caches.create!( 11 | :score => tracker.score, 12 | :positive_points => tracker.positive_points, 13 | :negative_points => tracker.negative_points, 14 | :position => tracker.position, 15 | :for_month => date.beginning_of_month 16 | ) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/marks_controller.rb: -------------------------------------------------------------------------------- 1 | class MarksController < ApplicationController 2 | before_filter :login_required 3 | 4 | def create 5 | @stamp = current_user.stamps.find(params[:stamp_id]) 6 | @mark = @stamp.marks.create!(:marked_on => params[:date], :position_x => params[:x], :position_y => params[:y], :skip => (params[:skip] == "true")) 7 | respond_to do |format| 8 | format.html { redirect_to root_url } 9 | format.js 10 | end 11 | end 12 | 13 | def destroy 14 | @mark = current_user.marks.find(params[:id]) 15 | @mark.destroy 16 | respond_to do |format| 17 | format.html { redirect_to root_url } 18 | format.js 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/helpers/layout_helper.rb: -------------------------------------------------------------------------------- 1 | # These helper methods can be called in your template to set variables to be used in the layout 2 | # This module should be included in all views globally, 3 | # to do so you may need to add this line to your ApplicationController 4 | # helper :layout 5 | module LayoutHelper 6 | def title(page_title, show_title = true) 7 | content_for(:title) { h(page_title.to_s) } 8 | @show_title = show_title 9 | end 10 | 11 | def show_title? 12 | @show_title 13 | end 14 | 15 | def stylesheet(*args) 16 | content_for(:head) { stylesheet_link_tag(*args) } 17 | end 18 | 19 | def javascript(*args) 20 | content_for(:head) { javascript_include_tag(*args) } 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20090823182048_add_open_id_authentication_tables.rb: -------------------------------------------------------------------------------- 1 | class AddOpenIdAuthenticationTables < ActiveRecord::Migration 2 | def self.up 3 | create_table :open_id_authentication_associations, :force => true do |t| 4 | t.integer :issued, :lifetime 5 | t.string :handle, :assoc_type 6 | t.binary :server_url, :secret 7 | end 8 | 9 | create_table :open_id_authentication_nonces, :force => true do |t| 10 | t.integer :timestamp, :null => false 11 | t.string :server_url, :null => true 12 | t.string :salt, :null => false 13 | end 14 | end 15 | 16 | def self.down 17 | drop_table :open_id_authentication_associations 18 | drop_table :open_id_authentication_nonces 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /public/stylesheets/edit_stamp.css: -------------------------------------------------------------------------------- 1 | #stamp_name { 2 | font-size: 20px; 3 | width: 500px; 4 | } 5 | 6 | #colors { 7 | margin: 30px 0; 8 | } 9 | 10 | #colors .color { 11 | float: left; 12 | margin-right: 20px; 13 | } 14 | 15 | #colors .radio_button { 16 | text-align: center; 17 | } 18 | 19 | #stamp_images { 20 | margin: 30px 0; 21 | } 22 | 23 | #stamp_images .stamp_image { 24 | float: left; 25 | margin-right: 10px; 26 | } 27 | 28 | #stamp_images .radio_button { 29 | text-align: center; 30 | } 31 | 32 | #stamp_images .delete { 33 | text-align: center; 34 | font-size: 10px; 35 | margin-top: 5px; 36 | } 37 | 38 | form .buttons { 39 | border-top: solid 2px #999; 40 | text-align: center; 41 | padding-top: 10px; 42 | } 43 | -------------------------------------------------------------------------------- /spec/models/month_cache_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe MonthCache do 4 | it "should generate tracker for a given month" do 5 | stamp = Factory(:stamp) 6 | stamp.month_caches.create!(:for_month => "2009-10-01", :score => "123") 7 | tracker = MonthCache.tracker_for(stamp, Date.parse("2009-10-02")) 8 | tracker.should be_kind_of(ScoreTracker) 9 | tracker.score.should == 123 10 | end 11 | 12 | it "should save cache from tracker" do 13 | stamp = Factory(:stamp) 14 | tracker = ScoreTracker.new(:score => 456) 15 | MonthCache.save_tracker(tracker, stamp, Date.parse("2009-10-02")) 16 | tracker = MonthCache.tracker_for(stamp, Date.parse("2009-10-03")) 17 | tracker.score.should == 456 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/stamps/edit_goal.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Change Goal" %> 2 | <% stylesheet "generic", "edit_goal" %> 3 | 4 | <%= form_for @stamp do |f| %> 5 |
6 |

suggestions

7 | 15 |
16 |

17 | <%= label_tag :goal_score, "Target Score" %>
18 | <%= f.text_field :goal_score, :size => 10 %> 19 |

20 |

21 | <%= label_tag :goal_reward, "Reward Yourself" %>
22 | <%= f.text_field :goal_reward, :size => 45 %> 23 |

24 |

<%= f.submit "Submit" %> or <%= link_to "cancel", @stamp %>

25 |
26 | <% end %> 27 | -------------------------------------------------------------------------------- /app/views/user_sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Log in" %> 2 | <% stylesheet "generic" %> 3 | 4 |

Don't have an account? <%= link_to "Sign up!", signup_path %>

5 | 6 | <%= form_for @user_session, :as => :user_session, :url => { :action => "create" } do |f| %> 7 | <%= f.error_messages %> 8 |

9 | <%= f.label :username %>
10 | <%= f.text_field :username %> 11 |

12 |

13 | <%= f.label :password %>
14 | <%= f.password_field :password %> 15 |

16 |

17 | <%= f.check_box :remember_me %> 18 | <%= f.label :remember_me %> 19 |

20 |

<%= f.submit "Log in" %>

21 | 22 |

Or use OpenID

23 |

24 | <%= f.label :openid_identifier, "OpenID URL" %>
25 | <%= f.text_field :openid_identifier %> 26 |

27 |

<%= f.submit "Log in" %>

28 | <% end %> 29 | -------------------------------------------------------------------------------- /spec/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # password: "secret" 2 | one: 3 | username: foo 4 | email: foo@example.com 5 | persistence_token: d5ddba13ed4408ea2b0a12ab18ed2d2eda086279736bdc121ca726a11f1e4b99217d9c534c2cc4ebb22729349c8c5fdbe1529e1f2c3c5859c62ef4dd9feea25c 6 | crypted_password: 3d16c326648cccafe3d4b4cb024475c381dda92f430dfedf6f933e1f61203bacb6bae2437849bdb43b06be335e23790e4aa03902b3c28c3bbbbe27d501e521f3 7 | password_salt: n6z_wtpWoIsHgQb5IcFd 8 | 9 | two: 10 | username: bar 11 | email: bar@example.com 12 | persistence_token: 19e074bd7cb506ab3e7e53e41f24f0ab3221c8cb68111f4c1aa43965114ad734233979a50a9463537487cdca18c279ac91c4bc83693d589625d446493322394c 13 | crypted_password: 3bc9f4113ca645a186765df3d31a9352d0067bf2304ba0cdd6b08a7f3d58c6668ab1762fa3e76aef466ea2ff188399d8e6c40244fa59312bb4112292dac9f7f0 14 | password_salt: UiAh9ejabnKRxqsiK0xO 15 | -------------------------------------------------------------------------------- /spec/controllers/user_sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe UserSessionsController do 4 | fixtures :all 5 | render_views 6 | 7 | it "new action should render new template" do 8 | get :new 9 | response.should render_template(:new) 10 | end 11 | 12 | it "create action should render new template when authentication is invalid" do 13 | post :create, :user_session => { :username => "foo", :password => "badpassword" } 14 | response.should render_template(:new) 15 | UserSession.find.should be_nil 16 | end 17 | 18 | it "create action should redirect when authentication is valid" do 19 | post :create, :user_session => { :username => "foo", :password => "secret" } 20 | response.should redirect_to(root_url) 21 | UserSession.find.user.should == users(:one) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /config/initializers/new_rails_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # These settings change the behavior of Rails 2 apps and will be defaults 4 | # for Rails 3. You can remove this initializer when Rails 3 is released. 5 | 6 | if defined?(ActiveRecord) 7 | # Include Active Record class name as root for JSON serialized output. 8 | ActiveRecord::Base.include_root_in_json = true 9 | 10 | # Store the full class name (including module namespace) in STI type column. 11 | ActiveRecord::Base.store_full_sti_class = true 12 | end 13 | 14 | # Use ISO 8601 format for JSON serialized times and dates. 15 | ActiveSupport.use_standard_json_time_format = true 16 | 17 | # Don't escape HTML entities in JSON, leave that for the #json_escape helper. 18 | # if you're including raw json in an HTML page. 19 | ActiveSupport.escape_html_entities_in_json = false -------------------------------------------------------------------------------- /app/controllers/stamp_images_controller.rb: -------------------------------------------------------------------------------- 1 | class StampImagesController < ApplicationController 2 | before_filter :login_required 3 | 4 | def new 5 | @stamp_image = StampImage.new 6 | end 7 | 8 | def create 9 | @stamp_image = StampImage.new(params[:stamp_image]) 10 | @stamp_image.user = current_user 11 | if @stamp_image.save 12 | @stamp_image.generate_graphics 13 | respond_to do |format| 14 | format.html { redirect_to((current_user && edit_stamp_path(current_user.current_stamp)) || root_url) } 15 | format.js 16 | end 17 | else 18 | render :action => 'new' 19 | end 20 | end 21 | 22 | def destroy 23 | @stamp_image = current_user.stamp_images.find(params[:id]) 24 | @stamp_image.destroy 25 | respond_to do |format| 26 | format.html { redirect_to root_url } 27 | format.js 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_filter :login_required, :only => [:edit, :update] 3 | 4 | def new 5 | @user = User.new 6 | end 7 | 8 | def create 9 | @user = User.new(params[:user]) 10 | @user.save do |result| 11 | if result 12 | flash[:notice] = "Thank you for signing up! You are now logged in." 13 | redirect_to root_url 14 | else 15 | render :action => 'new' 16 | end 17 | end 18 | end 19 | 20 | def edit 21 | @user = current_user 22 | end 23 | 24 | def update 25 | @user = current_user 26 | @user.attributes = params[:user] 27 | @user.guest = false 28 | @user.save do |result| 29 | if result 30 | flash[:notice] = "Successfully updated profile." 31 | redirect_to root_url 32 | else 33 | render :action => 'edit' 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /public/javascripts/edit_stamp.js: -------------------------------------------------------------------------------- 1 | $(document).ajaxSend(function(event, request, settings) { 2 | if (typeof(AUTH_TOKEN) == "undefined") return; 3 | // settings.data is a serialized string like "foo=bar&baz=boink" (or null) 4 | settings.data = settings.data || ""; 5 | settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN); 6 | }); 7 | 8 | jQuery.fn.change_color = function(color) { 9 | this.each(function() { 10 | $(this).attr("src", $(this).attr("src").replace(/\/(red|green|blue|purple|black)\//, "/" + color + "/")); 11 | }); 12 | return this; 13 | }; 14 | 15 | $(function() { 16 | $("#colors input").click(function() { 17 | $("#stamp_images img").change_color(this.value); 18 | }); 19 | $("#stamp_images .delete a").removeAttr("onclick").click(function () { 20 | if (confirm("Are you sure you want to delete this stamp image?")) { 21 | $.post(this.href, "_method=delete", null, "script"); 22 | } 23 | return false; 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /app/views/stamps/_calendar.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <%= link_to "<", :month => (@date.beginning_of_month-1).strftime("%Y-%m") %> 3 | <%=h @date.strftime("%B %Y") %> 4 | <%= link_to ">", :month => (@date.end_of_month+1).strftime("%Y-%m") %> 5 |

6 | <%= calendar_for(@stamp.marks, :year => @date.year, :month => @date.month, :today => Time.zone.today) do |calendar| %> 7 | <%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %> 8 | <%= calendar.day(:day_method => :marked_on, :id => "day_%Y%m%d") do |day, marks| %> 9 | <% if day.month == @date.month %> 10 | <% if marks.first %> 11 | <%= mark_image(marks.first) %> 12 | <%= link_to "", marks.first, :class => "mark_link" %> 13 | <% else %> 14 | <%= link_to "", marks_path(:stamp_id => @stamp, :date => day.to_s("%Y-%m-%d")), :class => "mark_link" %> 15 | <% end %> 16 |
<%= points @stamp.day_points(day) %>
17 | <%= day.day %> 18 | <% end %> 19 | <% end %> 20 | <% end %> 21 | -------------------------------------------------------------------------------- /app/views/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @user do |f| %> 2 | <%= f.error_messages %> 3 |

4 | <%= f.label :username %>
5 | <%= f.text_field :username %> 6 |

7 |

8 | <%= f.label :email, "Email Address" %>
9 | <%= f.text_field :email %> 10 |

11 |

12 | <%= f.label :time_zone %>
13 | <%= f.time_zone_select :time_zone, ActiveSupport::TimeZone.us_zones, :include_blank => true %> 14 |

15 | <% if @user.openid_identifier.blank? %> 16 |

17 | <%= f.label :password %>
18 | <%= f.password_field :password %> 19 |

20 |

21 | <%= f.label :password_confirmation, "Confirm Password" %>
22 | <%= f.password_field :password_confirmation %> 23 |

24 |

<%= f.submit "Submit" %>

25 | 26 |

Or use OpenID

27 | <% end %> 28 |

29 | <%= f.label :openid_identifier, "OpenID URL" %>
30 | <%= f.text_field :openid_identifier %> 31 |

32 |

<%= f.submit "Submit" %>

33 | <% end %> 34 | -------------------------------------------------------------------------------- /app/helpers/error_messages_helper.rb: -------------------------------------------------------------------------------- 1 | module ErrorMessagesHelper 2 | # Render error messages for the given objects. The :message and :header_message options are allowed. 3 | def error_messages_for(*objects) 4 | options = objects.extract_options! 5 | options[:header_message] ||= "Invalid Fields" 6 | options[:message] ||= "Correct the following errors and try again." 7 | messages = objects.compact.map { |o| o.errors.full_messages }.flatten 8 | unless messages.empty? 9 | content_tag(:div, :class => "error_messages") do 10 | list_items = messages.map { |msg| content_tag(:li, msg) } 11 | content_tag(:h2, options[:header_message]) + content_tag(:p, options[:message]) + content_tag(:ul, list_items.join.html_safe) 12 | end 13 | end 14 | end 15 | 16 | module FormBuilderAdditions 17 | def error_messages(options = {}) 18 | @template.error_messages_for(@object, options) 19 | end 20 | end 21 | end 22 | 23 | ActionView::Helpers::FormBuilder.send(:include, ErrorMessagesHelper::FormBuilderAdditions) 24 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Rejected (422) 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

Rejected

14 |

The change you wanted was rejected. Maybe you tried to change something you didn't have access to.

15 |

Back to Home Page

16 |
17 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def mark_image(mark, size = 70) 3 | options = {:size => "#{size}x#{size}", :class => "mark"} 4 | if mark.position_x && mark.position_y 5 | options[:style] = "margin-left: #{mark.position_x-size/2-6}px; margin-top: #{mark.position_y-size/2-6}px;" 6 | end 7 | image_tag mark_image_name(mark), options 8 | end 9 | 10 | def small_mark_image(mark) 11 | image_tag mark_image_name(mark), :size => "25x25" 12 | end 13 | 14 | def mark_image_name(mark) 15 | if mark.nil? 16 | "spacer.gif" 17 | elsif mark.skip? 18 | "skip.png" 19 | elsif mark.image_path.nil? 20 | "question_mark.png" 21 | else 22 | mark.image_path 23 | end 24 | end 25 | 26 | def points(num) 27 | if num > 0 28 | "+#{num}" 29 | elsif num.zero? 30 | num.to_s 31 | else 32 | content_tag(:span, num, :class => "negative") 33 | end 34 | end 35 | 36 | def stamp_owner? 37 | @stamp && @stamp.user == current_user 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /public/stylesheets/favorites.css: -------------------------------------------------------------------------------- 1 | #container { 2 | width: 675px; 3 | max-width: 675px; 4 | } 5 | 6 | .stamp { 7 | border: solid 2px #CCC; 8 | padding: 10px 20px; 9 | -moz-border-radius: 10px; 10 | -webkit-border-radius: 10px; 11 | margin-bottom: 10px; 12 | } 13 | 14 | .stamp p { 15 | margin: 0; 16 | } 17 | 18 | .stamp h2 { 19 | margin: 0; 20 | margin-bottom: 3px; 21 | } 22 | 23 | .stamper { 24 | float: left; 25 | padding-right: 15px; 26 | } 27 | 28 | .score { 29 | font-weight: bold; 30 | float: right; 31 | } 32 | 33 | .score_bar { 34 | width: 200px; 35 | border: solid 1px #777; 36 | background-color: #D9D9D9; 37 | text-align: center; 38 | padding: 2px 0; 39 | -moz-border-radius: 5px; 40 | -webkit-border-radius: 5px; 41 | background-image: url(/images/greenbar.png); 42 | background-repeat: no-repeat; 43 | background-position: -200px; 44 | margin-bottom: 5px; 45 | } 46 | 47 | .days { 48 | border-collapse: collapse; 49 | clear: both; 50 | width: 200px; 51 | } 52 | 53 | .day { 54 | border: solid 1px #AAA; 55 | } 56 | -------------------------------------------------------------------------------- /config/initializers/authlogic_fixes.rb: -------------------------------------------------------------------------------- 1 | # See https://github.com/mreinsch/authlogic_openid/commit/9b802c347f5addebcbce945af3b5f80b3ee7b214 2 | # and http://stackoverflow.com/questions/2092694/authlogic-openid-error-uninitialized-constant-openidauthenticationinvalidopeni 3 | module AuthlogicOpenid 4 | module ActsAsAuthentic 5 | module Methods 6 | def openid_identifier=(value) 7 | write_attribute(:openid_identifier, value.blank? ? nil : OpenID.normalize_url(value)) 8 | reset_persistence_token if openid_identifier_changed? 9 | rescue OpenID::DiscoveryFailure => e 10 | @openid_error = e.message 11 | end 12 | end 13 | end 14 | end 15 | 16 | module AuthlogicOpenid 17 | module Session 18 | module Methods 19 | def openid_identifier=(value) 20 | @openid_identifier = value.blank? ? nil : OpenID.normalize_url(value) 21 | @openid_error = nil 22 | rescue OpenID::DiscoveryFailure => e 23 | @openid_identifier = nil 24 | @openid_error = e.message 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Page Not Found (404) 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

Page Not Found

14 |

The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved.

15 |

Back to Home Page

16 |
17 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /config/database.example.yml: -------------------------------------------------------------------------------- 1 | # MySQL. Versions 4.1 and 5.0 are recommended. 2 | # 3 | # Install the MySQL driver: 4 | # gem install mysql2 5 | # 6 | # And be sure to use new-style password hashing: 7 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.html 8 | development: 9 | adapter: mysql2 10 | encoding: utf8 11 | reconnect: false 12 | database: dailystamp_development 13 | pool: 5 14 | username: root 15 | password: 16 | socket: /tmp/mysql.sock 17 | 18 | # Warning: The database defined as "test" will be erased and 19 | # re-generated from your development database when you run "rake". 20 | # Do not set this db to the same as development or production. 21 | test: 22 | adapter: mysql2 23 | encoding: utf8 24 | reconnect: false 25 | database: dailystamp_test 26 | pool: 5 27 | username: root 28 | password: 29 | socket: /tmp/mysql.sock 30 | 31 | production: 32 | adapter: mysql2 33 | encoding: utf8 34 | reconnect: false 35 | database: dailystamp_production 36 | pool: 5 37 | username: root 38 | password: 39 | socket: /var/run/mysqld/mysqld.sock 40 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Internal Error (500) 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

Internal Error

14 |

We're sorry, but something went wrong. If you came to this page by clicking on a link, please send an email to ryan AT railscasts DOT com.

15 |

Back to Home Page

16 |
17 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Dailystamp::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 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Petrik de Heus 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= 'test' 3 | require File.expand_path("../../config/environment", __FILE__) 4 | require 'rspec/rails' 5 | require 'authlogic/test_case' 6 | 7 | # Requires supporting ruby files with custom matchers and macros, etc, 8 | # in spec/support/ and its subdirectories. 9 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 10 | 11 | RSpec.configure do |config| 12 | # == Mock Framework 13 | # 14 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 15 | # 16 | # config.mock_with :mocha 17 | # config.mock_with :flexmock 18 | # config.mock_with :rr 19 | config.mock_with :mocha 20 | 21 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 22 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 23 | 24 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 25 | # examples within a transaction, remove the following line or assign false 26 | # instead of true. 27 | config.use_transactional_fixtures = true 28 | 29 | config.include Authlogic::TestCase 30 | end 31 | -------------------------------------------------------------------------------- /db/migrate/20090823035809_create_delayed_jobs.rb: -------------------------------------------------------------------------------- 1 | class CreateDelayedJobs < ActiveRecord::Migration 2 | def self.up 3 | create_table :delayed_jobs, :force => true do |table| 4 | table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue 5 | table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. 6 | table.text :handler # YAML-encoded string of the object that will do work 7 | table.text :last_error # reason for last failure (See Note below) 8 | table.datetime :run_at # When to run. Could be Time.now for immediately, or sometime in the future. 9 | table.datetime :locked_at # Set when a client is working on this object 10 | table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) 11 | table.string :locked_by # Who is working on this object (if locked) 12 | table.timestamps 13 | end 14 | 15 | end 16 | 17 | def self.down 18 | drop_table :delayed_jobs 19 | end 20 | end -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Daily Stamp 2 | 3 | This is an entry for Rails Rumble 2009 by Ryan Bates. 4 | 5 | -- 6 | 7 | Copyright (c) 2009 Ryan Bates 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining 10 | a copy of this software and associated documentation files (the 11 | "Software"), to deal in the Software without restriction, including 12 | without limitation the rights to use, copy, modify, merge, publish, 13 | distribute, sublicense, and/or sell copies of the Software, and to 14 | permit persons to whom the Software is furnished to do so, subject to 15 | the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /app/models/score_tracker.rb: -------------------------------------------------------------------------------- 1 | class ScoreTracker 2 | attr_reader :score, :position, :positive_points, :negative_points 3 | 4 | def initialize(options = {}) 5 | @score = options[:score] || 0 6 | @positive_points = options[:positive_points] || 0 7 | @negative_points = options[:negative_points] || 0 8 | @position = options[:position] || 0 9 | end 10 | 11 | def mark 12 | if @negative_points > 0 13 | @negative_points = 0 14 | @position = 0 15 | end 16 | if @position >= @positive_points 17 | @positive_points += 1 18 | @position = 0 19 | end 20 | @position += 1 21 | @score += @positive_points 22 | @positive_points 23 | end 24 | 25 | def skip 26 | # keep scores as they are 27 | 0 28 | end 29 | 30 | def miss 31 | @position = 0 if @negative_points.zero? 32 | if @position == @negative_points 33 | @positive_points -= 1 unless @positive_points.zero? 34 | @negative_points += 1 35 | @position = 0 36 | end 37 | @position += 1 38 | if @score < @negative_points 39 | @position = 0 40 | @negative_points = @score 41 | end 42 | @score -= @negative_points 43 | -@negative_points 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe User do 4 | it "should not have validations for guest user" do 5 | User.new(:guest => true).should be_valid 6 | end 7 | 8 | it "should not have username/email validations for openid user" do 9 | User.new(:openid_identifier => "http://myopenid.com").should be_valid 10 | end 11 | 12 | it "should validate email format for openid identifier" do 13 | User.new(:openid_identifier => "http://myopenid.com", :email => "bad email").should have(1).error_on(:email) 14 | end 15 | 16 | it "should validate username format for openid identifier" do 17 | User.new(:openid_identifier => "http://myopenid.com", :username => "x").should have(1).error_on(:username) 18 | end 19 | 20 | it "should require username when password is given" do 21 | User.new(:password => "secret", :password_confirmation => "secret").should have(1).error_on(:username) 22 | end 23 | 24 | it "should require username when password is given" do 25 | User.new(:password => "secret", :password_confirmation => "secret").should_not be_valid 26 | end 27 | 28 | it "should require username when password when nothing given" do 29 | user = User.new 30 | user.should_not be_valid 31 | user.errors[:username].to_s.should =~ /too short/ 32 | user.errors[:password].to_s.should =~ /too short/ 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/models/mark_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe Mark do 4 | it "should clear score cache of stamp when creating" do 5 | stamp = Factory(:stamp) 6 | stamp.update_attribute(:score_cache, 123) 7 | stamp.marks.create!(:marked_on => Time.zone.today) 8 | stamp.reload.score_cache.should be_nil 9 | end 10 | 11 | it "should clear score cache of stamp when destroying" do 12 | stamp = Factory(:stamp) 13 | stamp.marks.create!(:marked_on => Time.zone.today) 14 | stamp.update_attribute(:score_cache, 123) 15 | stamp.marks.first.destroy 16 | stamp.reload.score_cache.should be_nil 17 | end 18 | 19 | it "image_path should use stamp image" do 20 | stamp = Stamp.new(:color => "blue") 21 | stamp_image = stamp.build_stamp_image(:photo_file_name => "foo.jpg") 22 | mark = stamp.marks.build 23 | mark.stamp = stamp 24 | mark.image_path.should == stamp_image.photo.url("blue").sub(/[^\.]+$/, "png") 25 | end 26 | 27 | it "should clear future month cache of stamp when creating" do 28 | stamp = Factory(:stamp) 29 | cache1 = stamp.month_caches.create!(:for_month => 2.months.ago) 30 | cache2 = stamp.month_caches.create!(:for_month => Time.now.beginning_of_month) 31 | stamp.marks.create!(:marked_on => Time.zone.today) 32 | MonthCache.exists?(cache1).should be_true 33 | MonthCache.exists?(cache2).should be_false 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rake/rdoctask' 4 | 5 | desc 'Default: run unit tests.' 6 | task :default => :test 7 | 8 | desc 'Test the table_builder plugin.' 9 | Rake::TestTask.new(:test) do |t| 10 | t.libs << 'lib' 11 | t.pattern = 'test/**/*_test.rb' 12 | t.verbose = true 13 | end 14 | 15 | desc 'Generate documentation for the table_builder plugin.' 16 | Rake::RDocTask.new(:rdoc) do |rdoc| 17 | rdoc.rdoc_dir = 'rdoc' 18 | rdoc.title = 'TableBuilder' 19 | rdoc.options << '--line-numbers' << '--inline-source' 20 | rdoc.rdoc_files.include('README') 21 | rdoc.rdoc_files.include('lib/**/*.rb') 22 | end 23 | 24 | 25 | begin 26 | require 'jeweler' 27 | Jeweler::Tasks.new do |gem| 28 | gem.name = "table_builder" 29 | gem.summary = %Q{Rails builder for creating tables and calendars inspired by ActionView's FormBuilder.} 30 | gem.description = %Q{Rails builder for creating tables and calendars inspired by ActionView's FormBuilder.} 31 | gem.email = "" 32 | gem.homepage = "http://github.com/maca/table_builder" 33 | gem.authors = ["Petrik de Heus"] 34 | # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings 35 | end 36 | Jeweler::GemcutterTasks.new 37 | rescue LoadError 38 | puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" 39 | end 40 | -------------------------------------------------------------------------------- /spec/controllers/favorites_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe FavoritesController, "as guest" do 4 | fixtures :all 5 | render_views 6 | 7 | it "index action should redirect to login" do 8 | get :index 9 | response.should redirect_to(login_path) 10 | end 11 | 12 | it "create action should redirect to login" do 13 | post :create 14 | response.should redirect_to(login_path) 15 | end 16 | 17 | it "destroy action should redirect to login" do 18 | delete :destroy, :id => Favorite.first 19 | response.should redirect_to(login_path) 20 | end 21 | end 22 | 23 | describe FavoritesController, "as owner" do 24 | fixtures :all 25 | render_views 26 | 27 | before(:each) do 28 | activate_authlogic 29 | UserSession.create(Favorite.first.user) 30 | end 31 | 32 | it "index action should render index template" do 33 | get :index 34 | response.should render_template(:index) 35 | end 36 | 37 | it "create action should redirect to index" do 38 | post :create, :stamp_id => Stamp.first 39 | response.should redirect_to(favorites_url) 40 | end 41 | 42 | it "destroy action should destroy model and redirect to index action" do 43 | favorite = Favorite.first 44 | delete :destroy, :id => favorite 45 | response.should redirect_to(favorites_url) 46 | Favorite.exists?(favorite.id).should be_false 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Dailystamp::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 | -------------------------------------------------------------------------------- /app/views/favorites/index.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Watched Stamps" %> 2 | <% stylesheet "favorites" %> 3 | 4 | <% if @favorites.empty? %> 5 |

6 | You currently are not watching any stamps.
7 | Ask your friends to share their stamp URL with you. 8 |

9 | <% else %> 10 | <% for favorite in @favorites %> 11 | <% if favorite.stamp && !favorite.stamp.private? %> 12 |
13 |
14 |
score <%=h favorite.stamp.score %>
15 | 16 | 17 | <% (7.days.ago.to_date..Time.zone.today).each do |date| %> 18 | 21 | <% end %> 22 | 23 |
19 | <%= small_mark_image(favorite.stamp.marks.first(:conditions => {:marked_on => date})) %> 20 |
24 |
25 |
<%= link_to image_tag("stamper/#{h(favorite.stamp.color)}/small.png", :size => "40x52", :alt => h(favorite.stamp.name)), favorite.stamp %>
26 |

<%=h favorite.stamp.name %>

27 |

28 | by <%=h favorite.stamp.user.display_name if favorite.stamp.user %> | 29 | <%= link_to "View Details", favorite.stamp %> | 30 | <%= link_to "Stop Watching", favorite, :method => :delete, :confirm => "Are you sure you want to stop watching this stamp?" %> 31 |

32 |
33 |
34 | <% end %> 35 | <% end %> 36 | <% end %> 37 | -------------------------------------------------------------------------------- /app/models/stamp_image.rb: -------------------------------------------------------------------------------- 1 | class StampImage < ActiveRecord::Base 2 | attr_accessible :photo, :photo_file_name, :photo_content_type, :photo_file_size, :photo_updated_at 3 | 4 | belongs_to :user 5 | has_many :stamps, :dependent => :nullify 6 | 7 | has_attached_file :photo, :url => "/assets/stamp_images/:id/:style/:basename.:extension", 8 | :path => ":rails_root/public/assets/stamp_images/:id/:style/:basename.:extension" 9 | validates_attachment_presence :photo 10 | validates_attachment_size :photo, :less_than => 3.megabytes 11 | 12 | def generate_graphics 13 | STAMP_COLORS.each do |color| 14 | generate_graphic_for_color(color) 15 | end 16 | update_attribute(:generated_at, Time.zone.now) 17 | end 18 | 19 | def generate_graphic_for_color(color) 20 | source = Magick::Image.read(photo.path).first 21 | stamp_overlay = Magick::Image.read("#{Rails.root}/public/images/stamp_image_overlay.png").first 22 | source.resize_to_fill!(stamp_overlay.columns, stamp_overlay.rows) 23 | source = source.quantize(256, Magick::GRAYColorspace).contrast(true) 24 | source.composite!(stamp_overlay, Magick::CenterGravity, 0, 0, Magick::OverCompositeOp) 25 | colored = Magick::ImageList.new 26 | colored.new_image(70, 70) { self.background_color = color } 27 | source.matte = false 28 | colored.composite!(source.negate, Magick::CenterGravity, Magick::CopyOpacityCompositeOp) 29 | output_dir = File.dirname(photo.path(color)) 30 | output = File.join(output_dir, (File.basename(photo.path(color), '.*') + '.png')) 31 | FileUtils.mkdir_p(output_dir) unless File.exist? output_dir 32 | File.delete(output) if File.exist? output 33 | colored.write(output) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | acts_as_authentic do |c| 3 | c.openid_optional_fields = [:nickname, :email] 4 | c.merge_validates_confirmation_of_password_field_options :unless => :guest? 5 | c.merge_validates_length_of_password_field_options :unless => :guest? 6 | c.merge_validates_length_of_password_confirmation_field_options :unless => :guest? 7 | c.merge_validates_length_of_login_field_options :unless => :guest_or_openid? 8 | c.merge_validates_format_of_login_field_options :allow_blank => true 9 | c.merge_validates_uniqueness_of_login_field_options :allow_blank => true 10 | c.merge_validates_length_of_email_field_options :unless => :guest_or_openid? 11 | c.merge_validates_format_of_email_field_options :allow_blank => true 12 | c.merge_validates_uniqueness_of_email_field_options :allow_blank => true 13 | end 14 | 15 | has_many :stamps 16 | has_many :marks, :through => :stamps 17 | has_many :stamp_images 18 | has_many :favorites, :dependent => :destroy 19 | has_many :favorite_stamps, :through => :favorites, :source => :stamp 20 | belongs_to :current_stamp, :class_name => "Stamp" 21 | 22 | def guest_or_openid? 23 | guest? || !openid_identifier.blank? 24 | end 25 | 26 | def unused_color 27 | (STAMP_COLORS - stamps.map(&:color)).first 28 | end 29 | 30 | def available_stamp_images 31 | StampImage.all(:conditions => ["user_id = ? OR user_id is null", self]) 32 | end 33 | 34 | def display_name 35 | username.blank? ? "guest" : username 36 | end 37 | 38 | private 39 | 40 | def map_openid_registration(registration) 41 | self.email = registration["email"] if email.blank? 42 | self.username = registration["nickname"] if username.blank? 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/authentication.rb: -------------------------------------------------------------------------------- 1 | # This module is included in your application controller which makes 2 | # several methods available to all controllers and views. Here's a 3 | # common example you might add to your application layout file. 4 | # 5 | # <% if logged_in? %> 6 | # Welcome <%=h current_user.username %>! Not you? 7 | # <%= link_to "Log out", logout_path %> 8 | # <% else %> 9 | # <%= link_to "Sign up", signup_path %> or 10 | # <%= link_to "log in", login_path %>. 11 | # <% end %> 12 | # 13 | # You can also restrict unregistered users from accessing a controller using 14 | # a before filter. For example. 15 | # 16 | # before_filter :login_required, :except => [:index, :show] 17 | module Authentication 18 | def self.included(controller) 19 | controller.send :helper_method, :current_user, :logged_in?, :redirect_to_target_or_default 20 | end 21 | 22 | def current_user_session 23 | return @current_user_session if defined?(@current_user_session) 24 | @current_user_session = UserSession.find 25 | end 26 | 27 | def current_user 28 | return @current_user if defined?(@current_user) 29 | @current_user = current_user_session && current_user_session.record 30 | end 31 | 32 | def logged_in? 33 | current_user 34 | end 35 | 36 | def login_required 37 | unless logged_in? 38 | flash[:error] = "You must first log in or sign up before accessing this page." 39 | store_target_location 40 | redirect_to login_url 41 | end 42 | end 43 | 44 | def redirect_to_target_or_default(default) 45 | redirect_to(session[:return_to] || default) 46 | session[:return_to] = nil 47 | end 48 | 49 | private 50 | 51 | def store_target_location 52 | session[:return_to] = request.url 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/views/stamps/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Customize Stamp" %> 2 | <% stylesheet "generic", "edit_stamp" %> 3 | <% javascript "edit_stamp" %> 4 | <% content_for :head do %> 5 | <%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> 6 | <% end %> 7 | 8 | <%= form_for @stamp do |f| %> 9 | <%= f.error_messages %> 10 |

<%= f.text_field :name %>

11 |

<%= f.check_box :private %> <%= f.label :private, "Keep this stamp private" %>

12 | 13 |
14 | <% for color in STAMP_COLORS %> 15 |
16 | <%= label_tag "stamp_color_#{color}", image_tag("stamper/#{color}/ready.png", :size => "87x114", :alt => color) %> 17 |
<%= f.radio_button :color, color %>
18 |
19 | <% end %> 20 |
21 |
22 | 23 |
24 | <% for stamp_image in current_user.available_stamp_images %> 25 |
26 | <%= label_tag "stamp_stamp_image_id_#{stamp_image.id}", image_tag(stamp_image.photo.url(@stamp.color).sub(/[^\.]+$/, "png"), :size => "70x70") %> 27 |
<%= f.radio_button :stamp_image_id, stamp_image.id %>
28 | <% if stamp_image.user_id == current_user.id %> 29 |
<%= link_to "delete", stamp_image, :method => :delete, :confirm => "Are you sure?" %>
30 | <% end %> 31 |
32 | <% end %> 33 |
34 |
35 |

Or <%= link_to "add your own stamp", new_stamp_image_path, :id => "new_stamp_image_link" %>

36 | 37 |

<%= f.submit "Submit" %> or <%= link_to "cancel", @stamp %>

38 | <% end %> 39 | -------------------------------------------------------------------------------- /spec/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe UsersController, "as a guest" do 4 | fixtures :all 5 | render_views 6 | 7 | it "new action should render new template" do 8 | get :new 9 | response.should render_template(:new) 10 | end 11 | 12 | it "create action should render new template when model is invalid" do 13 | User.any_instance.stubs(:valid?).returns(false) 14 | post :create 15 | response.should render_template(:new) 16 | end 17 | 18 | it "create action should redirect when model is valid" do 19 | User.any_instance.stubs(:valid?).returns(true) 20 | post :create 21 | response.should redirect_to(root_url) 22 | end 23 | 24 | it "edit action should redirect to login" do 25 | get :edit, :id => "current" 26 | response.should redirect_to(login_url) 27 | end 28 | 29 | it "update action should redirect to login" do 30 | put :update, :id => "current" 31 | response.should redirect_to(login_url) 32 | end 33 | end 34 | 35 | describe UsersController, "as a user" do 36 | fixtures :all 37 | render_views 38 | 39 | before(:each) do 40 | activate_authlogic 41 | UserSession.create(User.first) 42 | end 43 | 44 | it "edit action should render edit template" do 45 | get :edit, :id => "current" 46 | response.should render_template(:edit) 47 | end 48 | 49 | it "update action should render edit template when model is invalid" do 50 | User.any_instance.stubs(:valid?).returns(false) 51 | put :update, :id => "current" 52 | response.should render_template(:edit) 53 | end 54 | 55 | it "create action should redirect when model is valid" do 56 | User.any_instance.stubs(:valid?).returns(false) 57 | put :update, :id => "current" 58 | response.should render_template(:edit) 59 | end 60 | end -------------------------------------------------------------------------------- /vendor/plugins/table_builder/table_builder.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{table_builder} 8 | s.version = "0.2.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Petrik de Heus"] 12 | s.date = %q{2010-07-03} 13 | s.description = %q{Rails builder for creating tables and calendars inspired by ActionView's FormBuilder.} 14 | s.email = %q{} 15 | s.extra_rdoc_files = [ 16 | "README.rdoc" 17 | ] 18 | s.files = [ 19 | ".autotest", 20 | ".gitignore", 21 | "MIT-LICENSE", 22 | "README.rdoc", 23 | "Rakefile", 24 | "VERSION", 25 | "init.rb", 26 | "lib/table_builder.rb", 27 | "lib/table_builder/calendar_helper.rb", 28 | "lib/table_builder/table_builder.rb", 29 | "lib/tasks/table_builder_tasks.rake", 30 | "table_builder.gemspec", 31 | "test/calendar_helper_test.rb", 32 | "test/table_builder_test.rb", 33 | "test/test_helper.rb" 34 | ] 35 | s.homepage = %q{http://github.com/maca/table_builder} 36 | s.rdoc_options = ["--charset=UTF-8"] 37 | s.require_paths = ["lib"] 38 | s.rubygems_version = %q{1.3.7} 39 | s.summary = %q{Rails builder for creating tables and calendars inspired by ActionView's FormBuilder.} 40 | s.test_files = [ 41 | "test/calendar_helper_test.rb", 42 | "test/table_builder_test.rb", 43 | "test/test_helper.rb" 44 | ] 45 | 46 | if s.respond_to? :specification_version then 47 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 48 | s.specification_version = 3 49 | 50 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 51 | else 52 | end 53 | else 54 | end 55 | end 56 | 57 | -------------------------------------------------------------------------------- /app/controllers/stamps_controller.rb: -------------------------------------------------------------------------------- 1 | class StampsController < ApplicationController 2 | before_filter :login_required, :only => [:new, :edit, :edit_goal, :update, :destroy] 3 | 4 | def index 5 | if !params[:no_redirect] && current_user && current_user.current_stamp 6 | redirect_to current_user.current_stamp 7 | else 8 | @stamp = Stamp.new 9 | end 10 | end 11 | 12 | def show 13 | @stamp = Stamp.find(params[:id]) 14 | @date = params[:month] ? Date.new(*params[:month].split("-").map(&:to_i)) : Time.zone.today 15 | raise "Date is too far in the future" if @date.to_time > 5.years.from_now 16 | if current_user && @stamp.user_id == current_user.id 17 | current_user.current_stamp_id = @stamp.id 18 | current_user.save! 19 | elsif @stamp.private? 20 | flash[:error] = "You are not authorized to access that stamp." 21 | redirect_to login_url 22 | end 23 | end 24 | 25 | def new 26 | @stamp = Stamp.new 27 | @stamp.color = current_user.unused_color 28 | render :action => "index" 29 | end 30 | 31 | def create 32 | @stamp = Stamp.new(params[:stamp]) 33 | @stamp.stamp_image ||= StampImage.first 34 | @stamp.user = current_user_or_guest 35 | if @stamp.save 36 | redirect_to @stamp 37 | else 38 | render :action => "index" 39 | end 40 | end 41 | 42 | def edit 43 | @stamp = current_user.stamps.find(params[:id]) 44 | end 45 | 46 | def edit_goal 47 | @stamp = current_user.stamps.find(params[:id]) 48 | end 49 | 50 | def update 51 | @stamp = current_user.stamps.find(params[:id]) 52 | if @stamp.update_attributes(params[:stamp]) 53 | redirect_to @stamp 54 | else 55 | render :action => 'edit' 56 | end 57 | end 58 | 59 | def destroy 60 | @stamp = current_user.stamps.find(params[:id]) 61 | @stamp.destroy 62 | redirect_to(current_user.stamps.first || root_url) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Dailystamp::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 | -------------------------------------------------------------------------------- /spec/controllers/marks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe MarksController, "as guest" do 4 | fixtures :all 5 | render_views 6 | 7 | it "create action should redirect to login" do 8 | post :create 9 | response.should redirect_to(login_path) 10 | end 11 | 12 | it "destroy action should redirect to login" do 13 | delete :destroy, :id => Mark.first 14 | response.should redirect_to(login_path) 15 | end 16 | end 17 | 18 | describe MarksController, "as stamp owner" do 19 | fixtures :all 20 | render_views 21 | 22 | before(:each) do 23 | activate_authlogic 24 | UserSession.create(Stamp.first.user) 25 | end 26 | 27 | it "create action should create mark with given stamp id and date" do 28 | post :create, :stamp_id => Stamp.first.id, :date => "2009-02-01", :x => 123, :y => 456 29 | response.should redirect_to(root_url) 30 | mark = Mark.last 31 | mark.marked_on.should == Date.parse("2009-02-01") 32 | mark.stamp_id.should == Stamp.first.id 33 | mark.position_x.should == 123 34 | mark.position_y.should == 456 35 | end 36 | 37 | it "create action should create mark with skip" do 38 | post :create, :stamp_id => Stamp.first.id, :date => "2009-02-01", :skip => "true" 39 | response.should redirect_to(root_url) 40 | mark = Mark.last 41 | mark.skip.should be_true 42 | end 43 | 44 | it "create action should render template when js action" do 45 | post :create, :format => "js", :stamp_id => Stamp.first.id, :date => "2009-02-01" 46 | response.should render_template("create") 47 | end 48 | 49 | it "destroy action should destroy model and redirect to index action" do 50 | mark = Mark.first 51 | delete :destroy, :id => mark 52 | response.should redirect_to(root_url) 53 | Mark.exists?(mark.id).should be_false 54 | end 55 | 56 | it "destroy action should render template when js action" do 57 | mark = Mark.first 58 | delete :destroy, :id => mark, :format => "js" 59 | response.should render_template("destroy") 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /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 Dailystamp 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}/lib) 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 | config.time_zone = "Pacific Time (US & Canada)" 29 | 30 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 31 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 32 | # config.i18n.default_locale = :de 33 | 34 | # JavaScript files you want as :defaults (application.js is always included). 35 | # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) 36 | 37 | # Configure the default encoding used in templates for Ruby 1.9. 38 | config.encoding = "utf-8" 39 | 40 | # Configure sensitive parameters which will be filtered from the log file. 41 | config.filter_parameters += [:password] 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /config/deploy.rb: -------------------------------------------------------------------------------- 1 | require "bundler/capistrano" 2 | 3 | set :application, "beta.dailystamp.com" 4 | role :app, application 5 | role :web, application 6 | role :db, application, :primary => true 7 | 8 | set :user, "rbates" 9 | set :deploy_to, "/var/apps/dailystamp" 10 | set :deploy_via, :remote_cache 11 | set :use_sudo, false 12 | 13 | set :scm, "git" 14 | set :repository, "git://github.com/ryanb/dailystamp.git" 15 | set :branch, "master" 16 | 17 | namespace :deploy do 18 | desc "Tell Passenger to restart." 19 | task :restart, :roles => :web do 20 | run "touch #{deploy_to}/current/tmp/restart.txt" 21 | end 22 | 23 | desc "Do nothing on startup so we don't get a script/spin error." 24 | task :start do 25 | puts "You may need to restart Apache." 26 | end 27 | 28 | desc "Symlink extra configs and folders." 29 | task :symlink_extras do 30 | run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml" 31 | run "ln -nfs #{shared_path}/config/session_secret.txt #{release_path}/config/session_secret.txt" 32 | run "ln -nfs #{shared_path}/assets #{release_path}/public/assets" 33 | end 34 | 35 | desc "Setup shared directory." 36 | task :setup_shared do 37 | run "mkdir #{shared_path}/assets" 38 | run "mkdir #{shared_path}/config" 39 | run "mkdir #{shared_path}/db" 40 | put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml" 41 | put File.read("config/session_secret.example.txt"), "#{shared_path}/config/session_secret.txt" 42 | puts "Now edit the config files and fill assets folder in #{shared_path}." 43 | end 44 | 45 | desc "Make sure there is something to deploy" 46 | task :check_revision, :roles => :web do 47 | unless `git rev-parse HEAD` == `git rev-parse origin/master` 48 | puts "WARNING: HEAD is not the same as origin/master" 49 | puts "Run `git push` to sync changes." 50 | exit 51 | end 52 | end 53 | end 54 | 55 | before "deploy", "deploy:check_revision" 56 | after "deploy", "deploy:cleanup" # keeps only last 5 releases 57 | after "deploy:setup", "deploy:setup_shared" 58 | after "deploy:update_code", "deploy:symlink_extras" 59 | -------------------------------------------------------------------------------- /vendor/plugins/open_id_authentication/CHANGELOG: -------------------------------------------------------------------------------- 1 | * Dump heavy lifting off to rack-openid gem. OpenIdAuthentication is just a simple controller concern. 2 | 3 | * Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek] 4 | 5 | * OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek] 6 | 7 | * Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler] 8 | 9 | * Add Timeout protection [Rick] 10 | 11 | * An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block. 12 | 13 | * Allow a return_to option to be used instead of the requested url [Josh Peek] 14 | 15 | * Updated plugin to use Ruby OpenID 2.x.x [Josh Peek] 16 | 17 | * Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH] 18 | 19 | * Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb] 20 | 21 | * Allow -'s in #normalize_url [Rick] 22 | 23 | * remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick] 24 | 25 | * Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH] 26 | 27 | * Just use the path for the return URL, so extra query parameters don't interfere [DHH] 28 | 29 | * Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH] 30 | 31 | * Added normalize_url and applied it to all operations going through the plugin [DHH] 32 | 33 | * Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH] 34 | 35 | * Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH] 36 | 37 | * Stop relying on root_url being defined, we can just grab the current url instead [DHH] -------------------------------------------------------------------------------- /spec/controllers/stamp_images_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe StampImagesController, "as guest" do 4 | it "create action should redirect to login" do 5 | post :create 6 | response.should redirect_to(login_path) 7 | end 8 | 9 | it "destroy action should redirect to login" do 10 | delete :destroy, :id => StampImage.first, :format => "js" 11 | response.should redirect_to(login_path) 12 | end 13 | 14 | it "new action should redirect to login" do 15 | get :new 16 | response.should redirect_to(login_path) 17 | end 18 | end 19 | 20 | describe StampImagesController, "as user" do 21 | fixtures :all 22 | render_views 23 | 24 | before(:each) do 25 | activate_authlogic 26 | @current_user = User.first 27 | UserSession.create(@current_user) 28 | end 29 | 30 | it "create action should render new template when model is invalid" do 31 | StampImage.any_instance.stubs(:valid?).returns(false) 32 | post :create 33 | response.should render_template(:new) 34 | end 35 | 36 | it "create action should redirect to current stamp edit form" do 37 | @current_user.current_stamp = Stamp.first 38 | @current_user.save! 39 | StampImage.any_instance.stubs(:valid?).returns(true) 40 | StampImage.any_instance.expects(:generate_graphics) 41 | post :create 42 | response.should redirect_to(edit_stamp_path(Stamp.first)) 43 | end 44 | 45 | it "destroy action should destroy model and redirect to index action" do 46 | stamp_image = StampImage.first 47 | delete :destroy, :id => stamp_image.id 48 | response.should redirect_to(root_url) 49 | StampImage.exists?(stamp_image.id).should be_false 50 | end 51 | 52 | it "destroy action should destroy model and render js template" do 53 | stamp_image = StampImage.first 54 | delete :destroy, :id => stamp_image.id, :format => "js" 55 | response.should render_template(:destroy) 56 | StampImage.exists?(stamp_image.id).should be_false 57 | end 58 | 59 | it "new action should render new template" do 60 | get :new 61 | response.should render_template(:new) 62 | end 63 | 64 | it "new action should render new template with js format" do 65 | get :new, :format => "js" 66 | response.should render_template(:new) 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /spec/models/score_tracker_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe ScoreTracker do 4 | before(:each) do 5 | @tracker = ScoreTracker.new 6 | end 7 | 8 | it "should be zero by default" do 9 | @tracker.score.should be_zero 10 | end 11 | 12 | it "marks should build up" do 13 | (1..4).map { @tracker.mark }.should == [1, 2, 2, 3] 14 | end 15 | 16 | it "stay at zero when missing" do 17 | (1..4).map { @tracker.miss }.should == [0, 0, 0, 0] 18 | end 19 | 20 | it "misses should detract points" do 21 | 4.times { @tracker.mark } 22 | (1..5).map { @tracker.miss }.should == [-1, -2, -2, -3, 0] 23 | end 24 | 25 | it "should slowly deduct positive points when misses" do 26 | 4.times { @tracker.mark } 27 | 2.times { @tracker.miss } 28 | @tracker.mark.should == 1 29 | end 30 | 31 | it "should reset position when mark is after misses" do 32 | 4.times { @tracker.miss } 33 | @tracker.mark.should == 1 34 | end 35 | 36 | it "should alternate misses and marks properly" do 37 | [@tracker.mark, @tracker.miss, @tracker.mark, @tracker.miss].should == [1, -1, 1, -1] 38 | end 39 | 40 | it "should handle complex scenarios" do 41 | [@tracker.mark, @tracker.mark, @tracker.mark, @tracker.miss, @tracker.mark, @tracker.mark, 42 | @tracker.miss, @tracker.miss, @tracker.miss, @tracker.miss, @tracker.mark].should == 43 | [1, 2, 2, -1, 1, 2, -1, -2, -2, -2, 1] 44 | end 45 | 46 | it "skip should continue on as if nothing happened" do 47 | [@tracker.mark, @tracker.skip, @tracker.skip, @tracker.mark].should == [1, 0, 0, 2] 48 | end 49 | 50 | it "should prefix options with those passed" do 51 | tracker = ScoreTracker.new(:score => 123) 52 | tracker.score.should == 123 53 | end 54 | 55 | it "should remove remaining points from score (don't go past zero)" do 56 | tracker = ScoreTracker.new(:score => 1, :negative_points => 3) 57 | tracker.miss.should == -1 58 | tracker.score.should == 0 59 | end 60 | 61 | it "should go down one positive point when missing a day" do 62 | tracker = ScoreTracker.new(:score => 1, :positive_points => 4, :position => 2) 63 | tracker.miss.should == -1 64 | tracker.mark.should == 3 65 | tracker.positive_points.should == 3 66 | tracker.position.should == 1 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | <%= content_for?(:title) ? yield(:title) : "Untitled" %> 6 | <%= stylesheet_link_tag "application" %> 7 | <%= javascript_include_tag :defaults %> 8 | <%= csrf_meta_tag %> 9 | <%= yield(:head) %> 10 | 11 | 12 | 40 |
41 | <%- flash.each do |name, msg| -%> 42 | <%= content_tag :div, msg, :id => "flash_#{name}" %> 43 | <%- end -%> 44 | 45 | <%- if show_title? -%> 46 |

<%=h yield(:title) %>

47 | <%- end -%> 48 | 49 | <%= yield %> 50 | 51 |
52 | 55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /public/stylesheets/home.css: -------------------------------------------------------------------------------- 1 | #header img { 2 | display: none; 3 | } 4 | 5 | #navigation { 6 | text-align: center; 7 | position: static; 8 | display: block; 9 | } 10 | 11 | #navigation .message { 12 | display: inline; 13 | padding-right: 5px; 14 | } 15 | 16 | #navigation .links { 17 | display: inline; 18 | } 19 | 20 | #logo { 21 | margin: 20px 0; 22 | margin-bottom: 50px; 23 | } 24 | 25 | h2 { 26 | font-weight: normal; 27 | font-size: 28px; 28 | margin-top: 0; 29 | margin-bottom: 10px; 30 | } 31 | 32 | form { 33 | margin: 0; 34 | padding: 0; 35 | } 36 | 37 | form #stamp_name { 38 | font-size: 20px; 39 | width: 330px; 40 | } 41 | 42 | #container { 43 | width: 680px; 44 | max-width: 680px; 45 | } 46 | 47 | #side { 48 | float: right; 49 | padding-top: 10px; 50 | } 51 | 52 | #recent_stamps { 53 | background-color: #EEE; 54 | -moz-border-radius: 15px; 55 | -webkit-border-radius: 15px; 56 | border: solid 2px #CCC; 57 | margin-top: 23px; 58 | width: 185px; 59 | } 60 | 61 | #recent_stamps h4 { 62 | font-size: 15px; 63 | text-align: center; 64 | margin: 0; 65 | padding-top: 4px; 66 | padding-bottom: 7px; 67 | color: #999; 68 | font-weight: normal; 69 | letter-spacing: 0.1em; 70 | } 71 | 72 | #recent_stamps .stamp { 73 | padding: 3px 5px; 74 | font-size: 12px; 75 | border-top: solid 1px #BBB; 76 | } 77 | 78 | #recent_stamps .stamper { 79 | float: left; 80 | padding-left: 5px; 81 | padding-top: 3px; 82 | } 83 | 84 | #recent_stamps .stamp_details { 85 | margin-left: 35px; 86 | padding-top: 3px; 87 | } 88 | 89 | #recent_stamps .score { 90 | font-size: 10px; 91 | color: #777; 92 | } 93 | 94 | #main { 95 | width: 480px; 96 | max-width: 480px; 97 | } 98 | 99 | #suggestions { 100 | margin-top: 20px; 101 | margin-right: 40px; 102 | background-color: #EEE; 103 | border: solid 3px #CCC; 104 | padding: 0 20px; 105 | font-size: 20px; 106 | -moz-border-radius: 20px; 107 | -webkit-border-radius: 20px; 108 | } 109 | 110 | #suggestions p { 111 | margin-top: 8px; 112 | } 113 | 114 | #suggestions h3 { 115 | font-size: 22px; 116 | color: #AAA; 117 | font-weight: normal; 118 | margin-top: 15px; 119 | margin-bottom: 8px; 120 | } 121 | 122 | #suggestions ul { 123 | list-style: none; 124 | margin: 0; 125 | padding: 0; 126 | } 127 | 128 | #suggestions li { 129 | margin: 0; 130 | padding: 4px 0; 131 | } 132 | 133 | #suggestions li, #suggestions li a, #suggestions li a:hover { 134 | color: #444; 135 | text-decoration: none; 136 | } -------------------------------------------------------------------------------- /app/views/stamps/show.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Stamp: #{@stamp.name}", false %> 2 | <% stylesheet "stamp" %> 3 | <% javascript "stamp" %> 4 | <% content_for :head do %> 5 | <%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> 6 | <% end %> 7 | 8 |
"> 9 | <% if @stamp.user %> 10 |
11 |
12 | <% for stamp in (stamp_owner? ? current_user.stamps : @stamp.user.stamps.non_private) %> 13 |
14 | <% if stamp == @stamp %> 15 | <%= image_tag("stamper/current.png", :size => "40x53", :alt => h(stamp.name)) %> 16 | <% else %> 17 | <%= link_to image_tag("stamper/#{h(stamp.color)}/small.png", :size => "40x52", :alt => h(stamp.name)), stamp, :title => h(truncate(stamp.name, :length => 20)) %> 18 | <% end %> 19 |
20 | <% end %> 21 | <% if stamp_owner? %> 22 |
23 | <%= link_to "+", new_stamp_path, :title => "New Stamp" %> 24 |
25 | <% end %> 26 |
27 |
28 |

Stamp Collection

29 |
30 | <% end %> 31 | 32 |
33 |

34 | <%= link_to_if stamp_owner?, image_tag("stamper/#{h(@stamp.color)}/ready.png", :size => "87x114"), "#stamptoday" %> 35 | <%= image_tag "stamper/#{h(@stamp.color)}/holding.png", :size => "81x90", :style => "display: none", :id => "stamp_cursor" %> 36 |

37 |
38 |

<%=h @stamp.name %>

39 | <% if stamp_owner? %> 40 |

41 | <%= link_to "Customize", edit_stamp_path(@stamp) %> | 42 | <%= link_to "Delete", @stamp, :method => :delete, :confirm => "Are you sure you want to delete this stamp? This operation cannot be undone." %> 43 |

44 | <% else %> 45 |

46 | Stamp by <%=h @stamp.user.display_name if @stamp.user %> 47 | <% if logged_in? %> 48 | | <%= link_to "Watch this stamp", favorites_url(:stamp_id => @stamp.id), :method => :post %> 49 | <% end %> 50 |

51 | <% end %> 52 |
53 | <%= render :partial => "score", :locals => { :stamp => @stamp } %> 54 |
55 | <% if stamp_owner? && @stamp.score.zero? %> 56 | <%= image_tag "instructions/instruction1.gif", :size => "135x59", :id => "instructions" %> 57 | <% end %> 58 |
59 |
60 | 61 |
62 | <%= render 'calendar' %> 63 |
64 |
65 | -------------------------------------------------------------------------------- /public/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #FFF; 3 | font-family: Verdana, Helvetica, Arial; 4 | font-size: 14px; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | a img { 10 | border: none; 11 | } 12 | 13 | a { 14 | color: #D00; 15 | text-decoration: none; 16 | } 17 | 18 | a:hover { 19 | text-decoration: underline; 20 | } 21 | 22 | .clear { 23 | clear: both; 24 | height: 0; 25 | overflow: hidden; 26 | } 27 | 28 | #header { 29 | background-color: #E5E5E5; 30 | padding-top: 12px; 31 | padding-bottom: 6px; 32 | border-bottom: solid 4px #AAA; 33 | margin-bottom: 25px; 34 | } 35 | 36 | #header .inner { 37 | width: 800px; 38 | margin: 0 auto; 39 | position: relative; 40 | } 41 | 42 | #navigation { 43 | position: absolute; 44 | text-align: right; 45 | right: 0; 46 | bottom: 5px; 47 | } 48 | 49 | #navigation .links { 50 | padding-top: 5px; 51 | } 52 | 53 | #navigation .links { 54 | color: #777; 55 | } 56 | 57 | #container { 58 | width: 800px; 59 | margin: 0 auto; 60 | } 61 | 62 | #flash_notice, #flash_error { 63 | padding: 5px 8px; 64 | margin: 10px 0; 65 | } 66 | 67 | #flash_notice { 68 | background-color: #CFC; 69 | border: solid 1px #6C6; 70 | } 71 | 72 | #flash_error { 73 | background-color: #FCC; 74 | border: solid 1px #C66; 75 | } 76 | 77 | .fieldWithErrors { 78 | display: inline; 79 | } 80 | 81 | #errorExplanation { 82 | width: 400px; 83 | border: 2px solid #CF0000; 84 | padding: 0px; 85 | padding-bottom: 12px; 86 | margin-bottom: 20px; 87 | background-color: #f0f0f0; 88 | } 89 | 90 | #errorExplanation h2 { 91 | text-align: left; 92 | font-weight: bold; 93 | padding: 5px 5px 5px 15px; 94 | font-size: 12px; 95 | margin: 0; 96 | background-color: #c00; 97 | color: #fff; 98 | } 99 | 100 | #errorExplanation p { 101 | color: #333; 102 | margin-bottom: 0; 103 | padding: 8px; 104 | } 105 | 106 | #errorExplanation ul { 107 | margin: 2px 24px; 108 | } 109 | 110 | #errorExplanation ul li { 111 | font-size: 12px; 112 | list-style: disc; 113 | } 114 | 115 | /* embeds the openid image in the text field */ 116 | input#user_openid_identifier, input#user_session_openid_identifier { 117 | background: url(http://openid.net/images/login-bg.gif) no-repeat; 118 | background-color: #fff; 119 | background-position: 0 50%; 120 | color: #000; 121 | padding-left: 18px; 122 | } 123 | 124 | .guest_notice { 125 | font-weight: bold; 126 | } 127 | 128 | #footer { 129 | font-size: 12px; 130 | margin-top: 25px; 131 | margin-bottom: 10px; 132 | text-align: center; 133 | color: #777; 134 | } 135 | -------------------------------------------------------------------------------- /app/views/stamps/index.html.erb: -------------------------------------------------------------------------------- 1 | <% title "Daily Stamp", false %> 2 | <% stylesheet "home" %> 3 | <% javascript "home" %> 4 | 5 | <%= image_tag("dailystamp_big.gif", :size => "599x157", :alt => "Daily Stamp", :id => "logo") %> 6 | 7 |
8 |
9 | <%= image_tag("screencast.jpg", :size => "185x238") %> 10 |
11 |
12 |

recent stamps

13 | <% for stamp in Stamp.non_private.recent.all(:include => :user, :limit => 3) %> 14 |
15 |
<%= link_to image_tag("stamper/#{h(stamp.color)}/tiny.png", :size => "23x30", :alt => h(stamp.name)), stamp %>
16 |
17 | <%= link_to h(truncate(stamp.name, :length => 20)), stamp %>
18 | <%= stamp.score %> points by <%= h(stamp.user.display_name) if stamp.user %> 19 |
20 |
21 |
22 | <% end %> 23 |
24 |
25 | 26 |
27 |

What do you want to do daily?

28 | 29 | <%= form_for @stamp do |f| %> 30 | <%= f.error_messages %> 31 | <%= f.hidden_field :color %> 32 |

<%= f.text_field :name, :size => 30 %> <%= f.submit "Get Started" %>

33 |

<%= f.check_box :private %> <%= f.label :private, "Keep this private" %>

34 | <% end %> 35 | 36 |
37 |

suggestions

38 | 46 | 54 | 62 | 70 |

more

71 |
72 |
73 | -------------------------------------------------------------------------------- /app/models/stamp.rb: -------------------------------------------------------------------------------- 1 | class Stamp < ActiveRecord::Base 2 | extend ActiveSupport::Memoizable 3 | 4 | belongs_to :user 5 | belongs_to :stamp_image 6 | has_many :marks, :dependent => :destroy 7 | has_many :month_caches, :dependent => :destroy 8 | 9 | attr_accessible :name, :private, :color, :stamp_image_id, :goal_score, :goal_reward 10 | validates_presence_of :name 11 | 12 | scope :non_private, where("private != ?", true) 13 | scope :recent, where("score_cache > 0").order("updated_at desc") 14 | 15 | before_create :default_goal 16 | 17 | def day_points(date) 18 | month_points(date.beginning_of_month)[date.day-1] 19 | end 20 | 21 | def month_points(date) 22 | tracker = last_month_tracker(date) || ScoreTracker.new 23 | track_month_points(tracker, date) 24 | end 25 | memoize :month_points 26 | 27 | def score 28 | score_cache || calculate_score 29 | end 30 | 31 | def color 32 | read_attribute(:color).blank? ? "red" : read_attribute(:color) 33 | end 34 | 35 | def goal_progress 36 | if goal_reached? 37 | 100 38 | elsif goal_score.nil? || goal_score.zero? 39 | 0 40 | else 41 | (score.to_f/goal_score*100).floor 42 | end 43 | end 44 | 45 | def goal_reached? 46 | score >= (goal_score || 0) 47 | end 48 | 49 | private 50 | 51 | def mark_on_day(date) 52 | marks_in_month(date.beginning_of_month).detect { |m| m.marked_on == date } 53 | end 54 | 55 | def marks_in_month(date) 56 | marks.all(:conditions => {:marked_on => date.beginning_of_month..date.end_of_month}) 57 | end 58 | memoize :marks_in_month 59 | 60 | def month_tracker(date) 61 | tracker = last_month_tracker(date) || ScoreTracker.new 62 | track_month_points(tracker, date) 63 | MonthCache.save_tracker(tracker, self, date) 64 | tracker 65 | end 66 | 67 | def track_month_points(tracker, date) 68 | finished = (last_mark && last_mark.marked_on < date.beginning_of_month) 69 | (date.beginning_of_month..date.end_of_month).map do |day| 70 | if finished 71 | 0 72 | elsif mark_on_day(day) 73 | finished = true if last_mark.nil? || last_mark.marked_on <= day 74 | points = mark_on_day(day).skip? ? tracker.skip : tracker.mark 75 | update_attribute(:score_cache, tracker.score) if finished 76 | points 77 | else 78 | tracker.miss 79 | end 80 | end 81 | end 82 | 83 | def last_month_tracker(date) 84 | last_month = date.beginning_of_month-1.day 85 | if marks.exists?(["marked_on <= ?", last_month]) 86 | MonthCache.tracker_for(self, last_month) || month_tracker(last_month) 87 | end 88 | end 89 | 90 | def last_mark 91 | @last_mark ||= marks.last(:conditions => ["skip != ?", true], :order => "marked_on") 92 | end 93 | 94 | def calculate_score 95 | if last_mark 96 | month_points(last_mark.marked_on) 97 | score_cache 98 | else 99 | 0 100 | end 101 | end 102 | 103 | def default_goal 104 | self.goal_score ||= 100 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | ZenTest (4.4.1) 5 | abstract (1.0.0) 6 | actionmailer (3.0.3) 7 | actionpack (= 3.0.3) 8 | mail (~> 2.2.9) 9 | actionpack (3.0.3) 10 | activemodel (= 3.0.3) 11 | activesupport (= 3.0.3) 12 | builder (~> 2.1.2) 13 | erubis (~> 2.6.6) 14 | i18n (~> 0.4) 15 | rack (~> 1.2.1) 16 | rack-mount (~> 0.6.13) 17 | rack-test (~> 0.5.6) 18 | tzinfo (~> 0.3.23) 19 | activemodel (3.0.3) 20 | activesupport (= 3.0.3) 21 | builder (~> 2.1.2) 22 | i18n (~> 0.4) 23 | activerecord (3.0.3) 24 | activemodel (= 3.0.3) 25 | activesupport (= 3.0.3) 26 | arel (~> 2.0.2) 27 | tzinfo (~> 0.3.23) 28 | activeresource (3.0.3) 29 | activemodel (= 3.0.3) 30 | activesupport (= 3.0.3) 31 | activesupport (3.0.3) 32 | arel (2.0.6) 33 | authlogic (2.1.6) 34 | activesupport 35 | authlogic-oid (1.0.4) 36 | authlogic 37 | autotest (4.4.6) 38 | ZenTest (>= 4.4.1) 39 | autotest-rails (4.1.0) 40 | ZenTest 41 | builder (2.1.2) 42 | diff-lcs (1.1.2) 43 | erubis (2.6.6) 44 | abstract (>= 1.0.0) 45 | factory_girl (1.3.2) 46 | factory_girl_rails (1.0) 47 | factory_girl (~> 1.3) 48 | rails (>= 3.0.0.beta4) 49 | i18n (0.5.0) 50 | jquery-rails (0.2.6) 51 | rails (~> 3.0) 52 | thor (~> 0.14.4) 53 | mail (2.2.12) 54 | activesupport (>= 2.3.6) 55 | i18n (>= 0.4.0) 56 | mime-types (~> 1.16) 57 | treetop (~> 1.4.8) 58 | mime-types (1.16) 59 | mocha (0.9.10) 60 | rake 61 | mysql2 (0.2.6) 62 | nifty-generators (0.4.2) 63 | paperclip (2.3.6) 64 | activerecord 65 | activesupport 66 | polyglot (0.3.1) 67 | rack (1.2.1) 68 | rack-mount (0.6.13) 69 | rack (>= 1.0.0) 70 | rack-openid (1.2.0) 71 | rack (>= 1.1.0) 72 | ruby-openid (>= 2.1.8) 73 | rack-test (0.5.6) 74 | rack (>= 1.0) 75 | rails (3.0.3) 76 | actionmailer (= 3.0.3) 77 | actionpack (= 3.0.3) 78 | activerecord (= 3.0.3) 79 | activeresource (= 3.0.3) 80 | activesupport (= 3.0.3) 81 | bundler (~> 1.0) 82 | railties (= 3.0.3) 83 | railties (3.0.3) 84 | actionpack (= 3.0.3) 85 | activesupport (= 3.0.3) 86 | rake (>= 0.8.7) 87 | thor (~> 0.14.4) 88 | rake (0.8.7) 89 | rmagick (2.13.1) 90 | rspec (2.2.0) 91 | rspec-core (~> 2.2) 92 | rspec-expectations (~> 2.2) 93 | rspec-mocks (~> 2.2) 94 | rspec-core (2.2.1) 95 | rspec-expectations (2.2.0) 96 | diff-lcs (~> 1.1.2) 97 | rspec-mocks (2.2.0) 98 | rspec-rails (2.2.1) 99 | actionpack (~> 3.0) 100 | activesupport (~> 3.0) 101 | railties (~> 3.0) 102 | rspec (~> 2.2.0) 103 | ruby-openid (2.1.8) 104 | thor (0.14.6) 105 | treetop (1.4.9) 106 | polyglot (>= 0.3.1) 107 | tzinfo (0.3.23) 108 | 109 | PLATFORMS 110 | ruby 111 | 112 | DEPENDENCIES 113 | authlogic 114 | authlogic-oid 115 | autotest 116 | autotest-rails 117 | factory_girl_rails 118 | jquery-rails 119 | mocha 120 | mysql2 121 | nifty-generators 122 | paperclip 123 | rack-openid 124 | rails (= 3.0.3) 125 | rmagick 126 | rspec-rails 127 | ruby-openid 128 | -------------------------------------------------------------------------------- /public/stylesheets/stamp.css: -------------------------------------------------------------------------------- 1 | #calendar { 2 | clear: both; 3 | } 4 | 5 | #calendar table { 6 | border-collapse: collapse; 7 | width: 100%; 8 | } 9 | 10 | #calendar td, 11 | #calendar th { 12 | font-family: "Lucida Grande",arial,helvetica,sans-serif; 13 | font-size: 10px; 14 | padding: 6px; 15 | border: 1px solid #999; 16 | } 17 | 18 | #calendar th { 19 | background: #DDD; 20 | color: #666; 21 | text-align: center; 22 | width: 14.2857142857143%; 23 | } 24 | 25 | #calendar td { 26 | background: #FFF; 27 | color: #999; 28 | height: 90px; 29 | vertical-align: top; 30 | font-size: 18px; 31 | } 32 | 33 | #calendar #month { 34 | margin: 0; 35 | padding-top: 10px; 36 | padding-bottom: 10px; 37 | text-align: center; 38 | } 39 | 40 | #calendar #month a, #calendar #month a:hover { 41 | text-decoration: none; 42 | padding: 0 10px; 43 | color: #999; 44 | } 45 | 46 | #calendar .mark { 47 | position: absolute; 48 | } 49 | 50 | #calendar .points { 51 | float: right; 52 | margin-top: 70px; 53 | font-size: 13px; 54 | color: #000; 55 | } 56 | 57 | #calendar .points .negative { 58 | color: #F00; 59 | font-size: 16px; 60 | font-weight: bold; 61 | } 62 | 63 | #calendar .today { 64 | background-color: #FFF6DB; 65 | } 66 | 67 | #stamp_cursor { 68 | position: absolute; 69 | z-index: 1; 70 | cursor: none; 71 | } 72 | 73 | #stamps { 74 | float: right; 75 | background-color: #DDD; 76 | -moz-border-radius-topleft:15px; 77 | -moz-border-radius-topright:15px; 78 | -webkit-border-top-left-radius:15px; 79 | -webkit-border-top-right-radius:15px; 80 | } 81 | 82 | #stamps h2 { 83 | background-color: #999; 84 | color: #FFF; 85 | text-align: center; 86 | margin: 0; 87 | padding: 8px; 88 | font-size: 14px; 89 | } 90 | 91 | #stamp_collection .clear { 92 | min-width: 200px; 93 | width: 200px; 94 | } 95 | 96 | #stamps #stamp_collection { 97 | margin: 10px 15px; 98 | margin-bottom: 5px; 99 | } 100 | 101 | #stamps .stamp, #stamps .new_stamp { 102 | margin-left: 10px; 103 | float: left; 104 | } 105 | 106 | #stamps .new_stamp { 107 | padding-top: 10px; 108 | font-size: 30px; 109 | font-weight: bold; 110 | } 111 | 112 | #stamps .new_stamp a { 113 | color: #333; 114 | } 115 | 116 | #stamps .new_stamp a:hover { 117 | color: #D00; 118 | text-decoration: none; 119 | } 120 | 121 | #stamper { 122 | float: left; 123 | margin: 0; 124 | } 125 | 126 | #stamp_details { 127 | margin-left: 120px; 128 | } 129 | 130 | h1 { 131 | margin: 0; 132 | padding-top: 10px; 133 | } 134 | 135 | #score { 136 | padding: 10px 0; 137 | font-weight: bold; 138 | } 139 | 140 | #score .score_bar { 141 | width: 200px; 142 | border: solid 1px #777; 143 | background-color: #D9D9D9; 144 | text-align: center; 145 | padding: 2px 0; 146 | float: left; 147 | -moz-border-radius: 5px; 148 | -webkit-border-radius: 5px; 149 | background-image: url(/images/greenbar.png); 150 | background-repeat: no-repeat; 151 | background-position: -200px; 152 | } 153 | 154 | #score .score_goal { 155 | margin-left: 210px; 156 | padding-top: 3px; 157 | padding-bottom: 7px; 158 | } 159 | 160 | #change_goal { 161 | font-weight: normal; 162 | font-size: 11px; 163 | } 164 | 165 | #stamp_details p { 166 | margin: 0; 167 | } 168 | 169 | #goal_reward { 170 | font-size: 11px; 171 | font-weight: normal; 172 | } 173 | -------------------------------------------------------------------------------- /public/javascripts/stamp.js: -------------------------------------------------------------------------------- 1 | $(document).ajaxSend(function(event, request, settings) { 2 | if (settings.type == "GET" || typeof(AUTH_TOKEN) == "undefined") return; 3 | // settings.data is a serialized string like "foo=bar&baz=boink" (or null) 4 | settings.data = settings.data || ""; 5 | settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN); 6 | }); 7 | 8 | jQuery.fn.change_image = function(image) { 9 | this.attr("src", this.attr("src").replace(/[^\/]+$/, image)); 10 | return this; 11 | }; 12 | 13 | var instruction_level = 0; 14 | function next_instructions() { 15 | instruction_level++; 16 | $("#instructions").attr("src", "/images/instructions/instruction" + instruction_level + ".gif"); 17 | } 18 | 19 | function pick_up_stamp(click_event) { 20 | if (instruction_level == 1) { 21 | next_instructions(); 22 | } 23 | $("#stamper a img").change_image("ink.png"); 24 | $("#stamp_cursor").change_image("holding.png").show().css({ 25 | left: (click_event.pageX - 40) + 'px', 26 | top: (click_event.pageY - 45) + 'px' 27 | }).click(function(event) { 28 | stamp_down(event); 29 | }); 30 | $("body").mousemove(function(event) { 31 | $("#stamp_cursor").css({ 32 | left: (event.pageX - 40) + 'px', 33 | top: (event.pageY - 45) + 'px' 34 | }); 35 | }); 36 | } 37 | 38 | function stamp_down(event) { 39 | $("body").unbind("mousemove"); 40 | $("#stamp_cursor").unbind("click").hide(); 41 | if (!document.elementFromPoint) { 42 | alert("Please upgrade your browser to use this feature."); 43 | } 44 | if (navigator.userAgent.indexOf("Firefox") != -1) { 45 | var element = document.elementFromPoint(event.pageX - window.pageXOffset, event.pageY - window.pageYOffset); 46 | } else { 47 | var element = document.elementFromPoint(event.pageX, event.pageY); 48 | } 49 | if (element.id.search(/day_/) != -1 && $(element).children("a.mark_link").length > 0 && $(element).children("img").length == 0) { 50 | if (instruction_level == 2) { 51 | next_instructions(); 52 | } 53 | $("#stamp_cursor").change_image("stamping.png").show(); 54 | var p = $(element).position(); 55 | var x = (event.pageX - p.left); 56 | var y = (event.pageY - p.top); 57 | $.post($(element).children("a.mark_link").attr("href"), { x: x, y: y }, null, "script"); 58 | } else { 59 | $("#stamper a img").change_image("ready.png"); 60 | } 61 | } 62 | 63 | $(function() { 64 | $("#owner #calendar td").live("click", function(event) { 65 | if ($(this).children("a.mark_link").length > 0) { 66 | if ($(this).children(".mark").length > 0) { 67 | $.post($(this).children("a.mark_link").attr("href"), { _method: "delete" }, null, "script"); 68 | } else { 69 | var p = $(this).position(); 70 | var x = (event.pageX - p.left); 71 | var y = (event.pageY - p.top); 72 | $.post($(this).children("a.mark_link").attr("href"), { x: x, y: y, skip: true }, null, "script"); 73 | } 74 | } 75 | return false; 76 | }); 77 | 78 | $("#calendar #month a").live("click", function(event) { 79 | $.getScript(this.href); 80 | return false; 81 | }); 82 | 83 | $("#stamps a").mouseover(function() { 84 | $("#stamps h2").text(this.title); 85 | }).mouseout(function() { 86 | $("#stamps h2").text("Stamp Collection"); 87 | }); 88 | 89 | $("#owner #stamper a").click(function(click_event) { 90 | pick_up_stamp(click_event); 91 | return false; 92 | }); 93 | 94 | if ($("#instructions").length > 0) { 95 | $("#score").hide(); 96 | instruction_level = 1; 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/lib/table_builder/table_builder.rb: -------------------------------------------------------------------------------- 1 | module TableHelper 2 | 3 | def table_for(objects, *args) 4 | raise ArgumentError, "Missing block" unless block_given? 5 | options = args.last.is_a?(Hash) ? args.pop : {} 6 | html_options = options[:html] 7 | builder = options[:builder] || TableBuilder 8 | 9 | content_tag(:table, html_options) do 10 | yield builder.new(objects || [], self, options) 11 | end 12 | end 13 | 14 | class TableBuilder 15 | include ::ActionView::Helpers::TagHelper 16 | 17 | def initialize(objects, template, options) 18 | raise ArgumentError, "TableBuilder expects an Array but found a #{objects.inspect}" unless objects.is_a? Array 19 | @objects, @template, @options = objects, template, options 20 | end 21 | 22 | def head(*args) 23 | if block_given? 24 | concat(tag(:thead, options_from_hash(args), true)) 25 | yield 26 | concat('') 27 | else 28 | @num_of_columns = args.size 29 | content_tag(:thead, 30 | content_tag(:tr, 31 | args.collect { |c| content_tag(:th, c.html_safe)}.join('').html_safe 32 | ) 33 | ) 34 | end 35 | end 36 | 37 | def head_r(*args) 38 | raise ArgumentError, "Missing block" unless block_given? 39 | options = options_from_hash(args) 40 | head do 41 | concat(tag(:tr, options, true)) 42 | yield 43 | concat('') 44 | end 45 | end 46 | 47 | def body(*args) 48 | raise ArgumentError, "Missing block" unless block_given? 49 | options = options_from_hash(args) 50 | tbody do 51 | @objects.each { |c| yield(c) } 52 | end 53 | end 54 | 55 | def body_r(*args) 56 | raise ArgumentError, "Missing block" unless block_given? 57 | options = options_from_hash(args) 58 | tbody do 59 | @objects.each { |c| 60 | concat(tag(:tr, options, true)) 61 | yield(c) 62 | concat(''.html_safe) 63 | } 64 | end 65 | end 66 | 67 | def r(*args) 68 | raise ArgumentError, "Missing block" unless block_given? 69 | options = options_from_hash(args) 70 | tr(options) do 71 | yield 72 | end 73 | end 74 | 75 | def h(*args) 76 | if block_given? 77 | concat(tag(:th, options_from_hash(args), true)) 78 | yield 79 | concat('') 80 | else 81 | content = args.shift 82 | content_tag(:th, content, options_from_hash(args)) 83 | end 84 | end 85 | 86 | def d(*args) 87 | if block_given? 88 | concat(tag(:td, options_from_hash(args), true)) 89 | yield 90 | concat('') 91 | else 92 | content = args.shift 93 | content_tag(:td, content, options_from_hash(args)) 94 | end 95 | end 96 | 97 | 98 | private 99 | 100 | def options_from_hash(args) 101 | args.last.is_a?(Hash) ? args.pop : {} 102 | end 103 | 104 | def concat(tag) 105 | @template.safe_concat(tag) 106 | "" 107 | end 108 | 109 | def content_tag(tag, content, *args) 110 | options = options_from_hash(args) 111 | @template.content_tag(tag, content, options) 112 | end 113 | 114 | def tbody 115 | concat('') 116 | yield 117 | concat('') 118 | end 119 | 120 | def tr options 121 | concat(tag(:tr, options, true)) 122 | yield 123 | concat('') 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /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 => 20091031172658) do 14 | 15 | create_table "delayed_jobs", :force => true do |t| 16 | t.integer "priority", :default => 0 17 | t.integer "attempts", :default => 0 18 | t.text "handler" 19 | t.text "last_error" 20 | t.datetime "run_at" 21 | t.datetime "locked_at" 22 | t.datetime "failed_at" 23 | t.string "locked_by" 24 | t.datetime "created_at" 25 | t.datetime "updated_at" 26 | end 27 | 28 | create_table "favorites", :force => true do |t| 29 | t.integer "user_id" 30 | t.integer "stamp_id" 31 | t.datetime "created_at" 32 | t.datetime "updated_at" 33 | end 34 | 35 | create_table "marks", :force => true do |t| 36 | t.integer "stamp_id" 37 | t.boolean "skip", :default => false, :null => false 38 | t.date "marked_on" 39 | t.datetime "created_at" 40 | t.datetime "updated_at" 41 | t.integer "position_x" 42 | t.integer "position_y" 43 | end 44 | 45 | create_table "month_caches", :force => true do |t| 46 | t.integer "stamp_id" 47 | t.integer "positive_points" 48 | t.integer "negative_points" 49 | t.integer "position" 50 | t.integer "score" 51 | t.date "for_month" 52 | t.datetime "created_at" 53 | t.datetime "updated_at" 54 | end 55 | 56 | create_table "open_id_authentication_associations", :force => true do |t| 57 | t.integer "issued" 58 | t.integer "lifetime" 59 | t.string "handle" 60 | t.string "assoc_type" 61 | t.binary "server_url" 62 | t.binary "secret" 63 | end 64 | 65 | create_table "open_id_authentication_nonces", :force => true do |t| 66 | t.integer "timestamp", :null => false 67 | t.string "server_url" 68 | t.string "salt", :null => false 69 | end 70 | 71 | create_table "stamp_images", :force => true do |t| 72 | t.integer "user_id" 73 | t.datetime "generated_at" 74 | t.string "photo_file_name" 75 | t.string "photo_content_type" 76 | t.integer "photo_file_size" 77 | t.datetime "photo_updated_at" 78 | t.datetime "created_at" 79 | t.datetime "updated_at" 80 | end 81 | 82 | create_table "stamps", :force => true do |t| 83 | t.integer "user_id" 84 | t.string "name" 85 | t.boolean "private" 86 | t.datetime "created_at" 87 | t.datetime "updated_at" 88 | t.integer "score_cache" 89 | t.string "color" 90 | t.integer "stamp_image_id" 91 | t.integer "goal_score" 92 | t.text "goal_reward" 93 | end 94 | 95 | create_table "users", :force => true do |t| 96 | t.string "username" 97 | t.string "email" 98 | t.string "persistence_token" 99 | t.string "crypted_password" 100 | t.string "password_salt" 101 | t.datetime "created_at" 102 | t.datetime "updated_at" 103 | t.integer "current_stamp_id" 104 | t.string "openid_identifier" 105 | t.boolean "guest", :default => false, :null => false 106 | t.string "time_zone" 107 | end 108 | 109 | end 110 | -------------------------------------------------------------------------------- /vendor/plugins/open_id_authentication/lib/open_id_authentication.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | require 'openid' 3 | require 'rack/openid' 4 | 5 | module OpenIdAuthentication 6 | def self.new(app) 7 | store = OpenIdAuthentication.store 8 | if store.nil? 9 | Rails.logger.warn "OpenIdAuthentication.store is nil. Using in-memory store." 10 | end 11 | 12 | ::Rack::OpenID.new(app, OpenIdAuthentication.store) 13 | end 14 | 15 | def self.store 16 | @@store 17 | end 18 | 19 | def self.store=(*store_option) 20 | store, *parameters = *([ store_option ].flatten) 21 | 22 | @@store = case store 23 | when :memory 24 | require 'openid/store/memory' 25 | OpenID::Store::Memory.new 26 | when :file 27 | require 'openid/store/filesystem' 28 | OpenID::Store::Filesystem.new(Rails.root.join('tmp/openids')) 29 | when :memcache 30 | require 'memcache' 31 | require 'openid/store/memcache' 32 | OpenID::Store::Memcache.new(MemCache.new(parameters)) 33 | else 34 | store 35 | end 36 | end 37 | 38 | self.store = nil 39 | 40 | class Result 41 | ERROR_MESSAGES = { 42 | :missing => "Sorry, the OpenID server couldn't be found", 43 | :invalid => "Sorry, but this does not appear to be a valid OpenID", 44 | :canceled => "OpenID verification was canceled", 45 | :failed => "OpenID verification failed", 46 | :setup_needed => "OpenID verification needs setup" 47 | } 48 | 49 | def self.[](code) 50 | new(code) 51 | end 52 | 53 | def initialize(code) 54 | @code = code 55 | end 56 | 57 | def status 58 | @code 59 | end 60 | 61 | ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } } 62 | 63 | def successful? 64 | @code == :successful 65 | end 66 | 67 | def unsuccessful? 68 | ERROR_MESSAGES.keys.include?(@code) 69 | end 70 | 71 | def message 72 | ERROR_MESSAGES[@code] 73 | end 74 | end 75 | 76 | protected 77 | # The parameter name of "openid_identifier" is used rather than 78 | # the Rails convention "open_id_identifier" because that's what 79 | # the specification dictates in order to get browser auto-complete 80 | # working across sites 81 | def using_open_id?(identifier = nil) #:doc: 82 | identifier ||= open_id_identifier 83 | !identifier.blank? || request.env[Rack::OpenID::RESPONSE] 84 | end 85 | 86 | def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc: 87 | identifier ||= open_id_identifier 88 | 89 | if request.env[Rack::OpenID::RESPONSE] 90 | complete_open_id_authentication(&block) 91 | else 92 | begin_open_id_authentication(identifier, options, &block) 93 | end 94 | end 95 | 96 | private 97 | def open_id_identifier 98 | params[:openid_identifier] || params[:openid_url] 99 | end 100 | 101 | def begin_open_id_authentication(identifier, options = {}) 102 | options[:identifier] = identifier 103 | value = Rack::OpenID.build_header(options) 104 | response.headers[Rack::OpenID::AUTHENTICATE_HEADER] = value 105 | head :unauthorized 106 | end 107 | 108 | def complete_open_id_authentication 109 | response = request.env[Rack::OpenID::RESPONSE] 110 | identifier = response.display_identifier 111 | 112 | case response.status 113 | when OpenID::Consumer::SUCCESS 114 | yield Result[:successful], identifier, 115 | OpenID::SReg::Response.from_success_response(response) 116 | when :missing 117 | yield Result[:missing], identifier, nil 118 | when :invalid 119 | yield Result[:invalid], identifier, nil 120 | when OpenID::Consumer::CANCEL 121 | yield Result[:canceled], identifier, nil 122 | when OpenID::Consumer::FAILURE 123 | yield Result[:failed], identifier, nil 124 | when OpenID::Consumer::SETUP_NEEDED 125 | yield Result[:setup_needed], response.setup_url, nil 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/README.rdoc: -------------------------------------------------------------------------------- 1 | = TableBuilder 2 | 3 | Rails builder for creating tables and calendars inspired by ActionView's FormBuilder. 4 | 5 | == Examples 6 | 7 | table_for has methods for each tag used in a table (, , ') 34 | concat('') if(day.wday == @calendar.last_weekday) 35 | end 36 | end 37 | end 38 | 39 | private 40 | 41 | def objects_for_days 42 | @calendar.objects_for_days(@objects) 43 | end 44 | 45 | def td_options(day, id_pattern) 46 | options = {} 47 | css_classes = [] 48 | css_classes << 'today' if day.strftime("%Y-%m-%d") == @today.strftime("%Y-%m-%d") 49 | css_classes << 'notmonth' if day.month != @calendar.month 50 | css_classes << 'weekend' if day.wday == 0 or day.wday == 6 51 | css_classes << 'future' if day > @today.to_date 52 | options[:class] = css_classes.join(' ') unless css_classes.empty? 53 | options[:id] = day.strftime(id_pattern) if id_pattern 54 | options 55 | end 56 | 57 | end 58 | 59 | class Calendar 60 | attr_accessor :first_weekday, :last_weekday, :month 61 | 62 | # :first lets you set the first day to start the calendar on (default is the first day of the given :month and :year). 63 | # :first => :today will use Date.today 64 | # :last lets you set the last day of the calendar (default is the last day of the given :month and :year). 65 | # :last => :thirty will show 30 days from :first 66 | # :last => :week will show one week 67 | def initialize(options={}) 68 | @year = options[:year] || Time.now.year 69 | @month = options[:month] || Time.now.month 70 | @first_day_of_week = options[:first_day_of_week] || 0 71 | @first_weekday = first_day_of_week(@first_day_of_week) 72 | @last_weekday = last_day_of_week(@first_day_of_week) 73 | 74 | @first = options[:first]==:today ? Date.today : options[:first] || Date.civil(@year, @month, 1) 75 | 76 | if options[:last] == :thirty_days || options[:last] == :thirty 77 | @last = @first + 30 78 | elsif options[:last] == :one_week || options[:last] == :week 79 | @last = @first 80 | else 81 | @last = options[:last] || Date.civil(@year, @month, -1) 82 | end 83 | 84 | end 85 | 86 | def each_day 87 | first_day.upto(last_day) do |day| 88 | yield(day) 89 | end 90 | end 91 | 92 | def last_day 93 | last = @last 94 | while(last.wday % 7 != @last_weekday % 7) 95 | last = last.next 96 | end 97 | last 98 | end 99 | 100 | def first_day 101 | first = @first - 6 102 | while(first.wday % 7 != (@first_weekday) % 7) 103 | first = first.next 104 | end 105 | first 106 | end 107 | 108 | def objects_for_days(objects, day_method) 109 | unless @objects_for_days 110 | @objects_for_days = {} 111 | days.each{|day| @objects_for_days[day.strftime("%Y-%m-%d")] = [day, []]} 112 | objects.each do |o| 113 | date = o.send(day_method.to_sym).strftime("%Y-%m-%d") 114 | if @objects_for_days[date] 115 | @objects_for_days[date][1] << o 116 | end 117 | end 118 | end 119 | @objects_for_days 120 | end 121 | 122 | def days 123 | unless @days 124 | @days = [] 125 | each_day{|day| @days << day} 126 | end 127 | @days 128 | end 129 | 130 | def mjdays 131 | unless @mjdays 132 | @mdays = [] 133 | each_day{|day| @days << day} 134 | end 135 | @days 136 | end 137 | 138 | def first_day_of_week(day) 139 | day 140 | end 141 | 142 | def last_day_of_week(day) 143 | if day > 0 144 | day - 1 145 | else 146 | 6 147 | end 148 | end 149 | end 150 | 151 | end 152 | -------------------------------------------------------------------------------- /spec/controllers/stamps_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe StampsController, "as guest" do 4 | fixtures :all 5 | render_views 6 | 7 | it "index action should render index template" do 8 | get :index 9 | response.should render_template(:index) 10 | end 11 | 12 | it "show action should render show template" do 13 | get :show, :id => Stamp.first 14 | response.should render_template(:show) 15 | end 16 | 17 | it "new action should redirect to login" do 18 | get :new 19 | response.should redirect_to(login_path) 20 | end 21 | 22 | it "create action should render new template when model is invalid" do 23 | Stamp.any_instance.stubs(:valid?).returns(false) 24 | post :create 25 | response.should render_template(:index) 26 | end 27 | 28 | it "create action should redirect when model is valid and create guest user" do 29 | Stamp.any_instance.stubs(:valid?).returns(true) 30 | post :create 31 | response.should redirect_to(stamp_url(assigns[:stamp])) 32 | assigns[:stamp].user.should_not be_nil 33 | end 34 | 35 | it "edit action should redirect to login" do 36 | get :edit, :id => Stamp.first 37 | response.should redirect_to(login_path) 38 | end 39 | 40 | it "edit_goal action should redirect to login" do 41 | get :edit_goal, :id => Stamp.first 42 | response.should redirect_to(login_path) 43 | end 44 | 45 | it "update action should redirect to login" do 46 | Stamp.any_instance.stubs(:valid?).returns(false) 47 | put :update, :id => Stamp.first 48 | response.should redirect_to(login_path) 49 | end 50 | 51 | it "destroy action should redirect to login" do 52 | delete :destroy, :id => Stamp.first 53 | response.should redirect_to(login_path) 54 | end 55 | end 56 | 57 | 58 | describe StampsController, "as stamp owner" do 59 | fixtures :all 60 | render_views 61 | 62 | before(:each) do 63 | activate_authlogic 64 | @current_user = Stamp.first.user 65 | UserSession.create(Stamp.first.user) 66 | end 67 | 68 | it "index action should redirect to current stamp" do 69 | @current_user.current_stamp_id = Stamp.first.id 70 | @current_user.save 71 | get :index 72 | response.should redirect_to(stamp_url(Stamp.first)) 73 | end 74 | 75 | it "index action should render index when no redirect is specified" do 76 | @current_user.current_stamp_id = Stamp.first.id 77 | @current_user.save 78 | get :index, :no_redirect => "true" 79 | response.should render_template(:index) 80 | end 81 | 82 | it "new action should render new template" do 83 | get :new 84 | response.should render_template(:index) 85 | end 86 | 87 | it "show action should render template even if private" do 88 | Stamp.first.update_attribute(:private, true) 89 | get :show, :id => Stamp.first 90 | response.should render_template(:show) 91 | Stamp.first.user.reload.current_stamp.should == Stamp.first 92 | end 93 | 94 | it "edit action should render edit template" do 95 | get :edit, :id => Stamp.first 96 | response.should render_template(:edit) 97 | end 98 | 99 | it "edit_goal action should render edit_goal template" do 100 | get :edit_goal, :id => Stamp.first 101 | response.should render_template(:edit_goal) 102 | end 103 | 104 | it "update action should render edit template when model is invalid" do 105 | Stamp.any_instance.stubs(:valid?).returns(false) 106 | put :update, :id => Stamp.first 107 | response.should render_template(:edit) 108 | end 109 | 110 | it "update action should redirect when model is valid" do 111 | Stamp.any_instance.stubs(:valid?).returns(true) 112 | put :update, :id => Stamp.first 113 | response.should redirect_to(stamp_url(assigns[:stamp])) 114 | end 115 | 116 | it "destroy action should destroy model and redirect to next one in list" do 117 | @current_user.stamps.delete_all 118 | first_stamp = Factory(:stamp, :user => @current_user) 119 | second_stamp = Factory(:stamp, :user => @current_user) 120 | delete :destroy, :id => first_stamp 121 | response.should redirect_to(stamp_url(second_stamp)) 122 | end 123 | 124 | it "destroy action should destroy model and redirect to index when no other stamps" do 125 | @current_user.stamps.delete_all 126 | first_stamp = Factory(:stamp, :user => @current_user) 127 | delete :destroy, :id => first_stamp 128 | response.should redirect_to(root_url) 129 | end 130 | end 131 | 132 | 133 | describe StampsController, "as another user" do 134 | fixtures :all 135 | render_views 136 | 137 | before(:each) do 138 | activate_authlogic 139 | UserSession.create(User.last) 140 | end 141 | 142 | it "show action should redirect to login if private" do 143 | Stamp.first.update_attribute(:private, true) 144 | get :show, :id => Stamp.first 145 | response.should redirect_to(login_path) 146 | end 147 | 148 | it "show action should be able to see public stamp" do 149 | Stamp.first.update_attribute(:private, false) 150 | get :show, :id => Stamp.first 151 | response.should render_template(:show) 152 | User.last.reload.current_stamp.should_not == Stamp.first 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/test/table_builder_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'test_helper.rb') 2 | 3 | class TableBuilderTest < ActionView::TestCase 4 | include ActionView::Helpers::TextHelper 5 | include ActionView::Helpers::TagHelper 6 | include TableHelper 7 | attr_accessor :output_buffer 8 | 9 | def setup 10 | @drummer1 = Drummer.new(1, 'John "Stumpy" Pepys') 11 | @drummer2 = Drummer.new(2, 'Eric "Stumpy Joe" Childs') 12 | @drummer3 = Drummer.new(3, 'Peter "James" Bond') 13 | @drummer4 = Drummer.new(4, 'Mick Shrimpton (R. J. "Ric" Parnell)') 14 | end 15 | 16 | def test_table_for 17 | output = table_for([], :html => { :id => 'id', :style => 'style', :class => 'class'}) do |t| 18 | end 19 | expected = %(
, etc.) 8 | 9 | A basic example would look like this: 10 | 11 | @front_men = [FrontMan.new(1, 'David St. Hubbins'), FrontMan.new(2, 'David Lee Roth')] 12 | 13 | <% table_for(@front_men) do |t| %> 14 | <% t.head do %> 15 | <% t.r do %> 16 | <%= t.h('Id') %> 17 | <%= t.h('Name') %> 18 | <% end %> 19 | <% end %> 20 | <% t.body do |front_man| %> 21 | <% t.r do %> 22 | <%= t.d(h(front_man.id)) %> 23 | <%= t.d(h(front_man.name)) %> 24 | <% end %> 25 | <% end %> 26 | <% end %> 27 | 28 | You can pass an array to the head method: 29 | 30 | <%= t.head('Id', 'Name') %> 31 | 32 | 33 | The body and r method can be combined for easier usage: 34 | 35 | <% t.body_r do |front_man| %> 36 | <%= t.d(h(front_man.id)) %> 37 | <%= t.d(h(front_man.name)) %> 38 | <% end %> 39 | 40 | You can also pass blocks to the d and h methods for more flexibility: 41 | 42 | <%= t.d(:class => 'name') do %> 43 | <%= link_to(h(front_man.name), front_man_url(front_man)) %> 44 | <% end %> 45 | 46 | All tag methods are rails tag methods, so they can have extra html options. 47 | 48 | @drummers = [Drummer.new(1, 'John "Stumpy" Pepys'), Drummer.new(2, 'Eric "Stumpy Joe" Childs')] 49 | 50 | <% table_for(@drummers, :html => { :id => 'spinal_tap', :class => 'drummers'}) do |t| %> 51 | <% t.body_r(:class => 'row') do |e| %> 52 | <%= t.d(h(e.id), :title => 'id') %> 53 | <%= t.d(h(e.name)) %> 54 | <% end %> 55 | <% end %> 56 | 57 | ... which produces the following html: 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
1John "Stumpy" Pepys
2Eric "Stumpy Joe" Childs
71 | 72 | You can customize the table by creating your own TableBuilder: 73 | 74 | <% table_for(@drummers, :builder => PagedTableBuilder) do |t| %> 75 | 76 | 77 | == Calendar Table 78 | 79 | calendar_for creates a table like table_for. 80 | All objects get sorted per day of the month 81 | 82 | A basic example would look like this: 83 | 84 | @tasks = Task.this_month 85 | 86 | <% calendar_for(@tasks) do |t| %> 87 | <%= t.head('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') %> 88 | <% t.day do |day, tasks| %> 89 | <%= day.day %> 90 | <% tasks.each do |task| %> 91 | <%= h(task.name) %> 92 | <% end %> 93 | <% end %> 94 | <% end %> 95 | 96 | To show a different month you can pass the :year and :month options: 97 | 98 | <% calendar_for(@tasks, :year => 2009, :month => 1) do |t| %> 99 | 100 | To highlight a different day you can pass the :today option: 101 | 102 | <% calendar_for(@tasks, :today => Date.civil(2008, 12, 26)) do |t| %> 103 | 104 | By default the :date method is called on the objects for sorting. 105 | To use another method you can pass the :day_method option: 106 | 107 | <% t.day(:day_method => :calendar_date) do |day, tasks| %> 108 | 109 | If you want to add id's to your td tag you can pass a pattern: 110 | 111 | <% t.day(:id => 'day_%d') do |day, tasks| %> 112 | 113 | 114 | == Install 115 | 116 | script/plugin install git://github.com/p8/table_builder.git 117 | 118 | For a pre rails 3.0 table_builder: 119 | script/plugin install git://github.com/p8/table_builder.git 120 | cd vendor/plugins/table_builder/ 121 | git checkout pre-rails-2.2 122 | 123 | For a pre rails 2.2 table_builder: 124 | script/plugin install git://github.com/p8/table_builder.git 125 | cd vendor/plugins/table_builder/ 126 | git checkout pre-rails-2.2 127 | 128 | == Contributors 129 | 130 | Thanks to Sean Dague, F. Kocherga, John Duff. 131 | 132 | Copyright (c) 2008 Petrik de Heus, released under the MIT license 133 | -------------------------------------------------------------------------------- /spec/models/stamp_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe Stamp do 4 | describe "month points" do 5 | before(:each) do 6 | @stamp = Factory(:stamp) 7 | @stamp.marks.create!(:marked_on => "2010-01-01") 8 | end 9 | 10 | it "should be zero if no marks" do 11 | @stamp.month_points(Date.new(2009, 1)).should == [0] * 31 12 | end 13 | 14 | it "should be 1 on first mark but -1 on next miss" do 15 | @stamp.marks.create!(:marked_on => "2009-02-01") 16 | @stamp.month_points(Date.new(2009, 2)).should == [1, -1] + [0]*26 17 | end 18 | 19 | it "should build up points and tear down points" do 20 | ["2009-04-01", "2009-04-02", "2009-04-03", "2009-04-04", "2009-04-07"].each do |date| 21 | @stamp.marks.create!(:marked_on => date) 22 | end 23 | @stamp.month_points(Date.new(2009, 4, 3)).should == [1, 2, 2, 3, -1, -2, 1, -1, -2, -2, -1] + [0]*19 24 | end 25 | 26 | it "should apply score to previous month" do 27 | ["2009-03-31", "2009-04-01"].each do |date| 28 | @stamp.marks.create!(:marked_on => date) 29 | end 30 | @stamp.month_points(Date.new(2009, 4, 3)).should == [2, -1, -2] + [0]*27 31 | end 32 | 33 | it "should have points for a specific day" do 34 | @stamp.marks.create!(:marked_on => "2009-04-01") 35 | @stamp.day_points(Date.new(2009, 4, 2)).should == -1 36 | end 37 | 38 | it "should be 1 on first mark but -1 on next miss" do 39 | @stamp.marks.create!(:marked_on => "2009-04-01") 40 | @stamp.marks.create!(:marked_on => "2009-04-02", :skip => true) 41 | @stamp.month_points(Date.new(2009, 4)).should == [1, 0, -1] + [0]*27 42 | end 43 | 44 | it "should not subtract points after last mark" do 45 | Mark.delete_all 46 | @stamp.marks.create!(:marked_on => "2009-04-01") 47 | @stamp.marks.create!(:marked_on => "2009-04-02") 48 | @stamp.month_points(Date.new(2009, 4)).should == [1, 2] + [0]*28 49 | end 50 | 51 | it "should not subtract points after last mark on previous month" do 52 | Mark.delete_all 53 | @stamp.marks.create!(:marked_on => "2009-03-31") 54 | @stamp.month_points(Date.new(2009, 4)).should == [0]*30 55 | end 56 | 57 | it "should not subtract points when skip is later" do 58 | Mark.delete_all 59 | @stamp.marks.create!(:marked_on => "2009-04-01") 60 | @stamp.marks.create!(:marked_on => "2009-04-02") 61 | @stamp.marks.create!(:marked_on => "2009-04-10", :skip => true) 62 | @stamp.month_points(Date.new(2009, 4)).should == [1, 2] + [0]*28 63 | end 64 | 65 | it "should only take into account first mark if there are multiple on same day" do 66 | Mark.delete_all 67 | @stamp.marks.create!(:marked_on => "2009-04-01") 68 | @stamp.marks.create!(:marked_on => "2009-04-02", :skip => true) 69 | @stamp.marks.create!(:marked_on => "2009-04-02") 70 | @stamp.reload.month_points(Date.new(2009, 4)).should == [1] + [0]*29 71 | @stamp.score_cache.should == 1 72 | end 73 | 74 | it "should work with just one skip" do 75 | Mark.delete_all 76 | @stamp.marks.create!(:marked_on => "2009-04-02", :skip => true) 77 | @stamp.reload.month_points(Date.new(2009, 4)).should == [0]*30 78 | @stamp.score_cache.should == 0 79 | end 80 | end 81 | 82 | it "should use score cache if there is one" do 83 | stamp = Stamp.new 84 | stamp.score_cache = 123 85 | stamp.score.should == 123 86 | end 87 | 88 | it "should calculate score and set cache" do 89 | stamp = Factory(:stamp) 90 | stamp.marks.create!(:marked_on => "2009-01-01") 91 | stamp.marks.create!(:marked_on => "2009-01-02") 92 | stamp.score.should == 3 93 | stamp.reload.score_cache.should == 3 94 | end 95 | 96 | it "should have zero score if no marks" do 97 | Stamp.new.score.should be_zero 98 | end 99 | 100 | it "should default color to 'red'" do 101 | Stamp.new.color.should == "red" 102 | end 103 | 104 | it "should default goal_score to 100 when not set" do 105 | Factory(:stamp, :goal_score => "").goal_score.should == 100 106 | end 107 | 108 | it "should have a goal progress as percentage" do 109 | Factory(:stamp, :score_cache => 5, :goal_score => 10).goal_progress.should == 50 110 | end 111 | 112 | it "should not have goal progress go above 100" do 113 | Factory(:stamp, :score_cache => 15, :goal_score => 10).goal_progress.should == 100 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /vendor/plugins/table_builder/lib/table_builder/calendar_helper.rb: -------------------------------------------------------------------------------- 1 | module CalendarHelper 2 | 3 | def calendar_for(objects, *args) 4 | raise ArgumentError, "Missing block" unless block_given? 5 | options = args.last.is_a?(Hash) ? args.pop : {} 6 | html_options = options[:html] 7 | builder = options[:builder] || CalendarBuilder 8 | calendar = options[:calendar] || Calendar 9 | content_tag(:table, nil, html_options) do 10 | yield builder.new(objects || [], self, calendar, options) 11 | end 12 | end 13 | 14 | class CalendarBuilder < TableHelper::TableBuilder 15 | def initialize(objects, template, calendar, options) 16 | super(objects, template, options) 17 | @calendar = calendar.new(options) 18 | @today = options[:today] || Time.now 19 | end 20 | 21 | def day(*args) 22 | raise ArgumentError, "Missing block" unless block_given? 23 | options = options_from_hash(args) 24 | day_method = options.delete(:day_method) || :date 25 | id_pattern = options.delete(:id) 26 | tbody do 27 | @calendar.objects_for_days(@objects, day_method).to_a.sort{|a1, a2| a1.first <=> a2.first }.each do |o| 28 | key, array = o 29 | day, objects = array 30 | concat(tag(:tr, options, true)) if(day.wday == @calendar.first_weekday) 31 | concat(tag(:td, td_options(day, id_pattern), true)) 32 | yield(day, objects) 33 | concat('
) << 20 | %(
) 21 | assert_dom_equal expected, output 22 | end 23 | 24 | def test_table_for_without_an_array_raises_error 25 | assert_raises(ArgumentError) do 26 | table_for('a') {|t| } 27 | end 28 | end 29 | 30 | def test_head 31 | output = table_for([]) do |t| 32 | t.head do 33 | t.r do 34 | output_buffer.concat t.h('Id') 35 | output_buffer.concat t.h('Name') 36 | end 37 | end 38 | end 39 | expected = %() << 40 | %() << 41 | %() << 42 | %() << 43 | %() << 44 | %() << 45 | %() << 46 | %(
IdName
) 47 | assert_dom_equal expected, output 48 | end 49 | 50 | def test_head_r 51 | output = table_for([]) do |t| 52 | t.head_r do 53 | output_buffer.concat t.h('Id') 54 | output_buffer.concat t.h('Name') 55 | end 56 | end 57 | expected = %() << 58 | %() << 59 | %() << 60 | %() << 61 | %() << 62 | %() << 63 | %() << 64 | %(
IdName
) 65 | assert_dom_equal expected, output 66 | end 67 | 68 | def test_head_with_array 69 | output = table_for([@drummer1, @drummer2]) do |t| 70 | concat t.head('Id', 'Name') 71 | end 72 | expected = %() << 73 | %() << 74 | %() << 75 | %() << 76 | %() << 77 | %() << 78 | %() << 79 | %(
IdName
) 80 | assert_dom_equal expected, output 81 | end 82 | 83 | def test_body 84 | output = table_for([@drummer3, @drummer4]) do |t| 85 | t.body do |e| 86 | t.r do 87 | concat t.d(e.id) 88 | concat t.d(e.name) 89 | end 90 | end 91 | end 92 | expected = %() << 93 | %() << 94 | %() << 95 | %() << 96 | %() << 97 | %() << 98 | %() << 99 | %() << 100 | %() << 101 | %() << 102 | %() << 103 | %(
3Peter "James" Bond
4Mick Shrimpton (R. J. "Ric" Parnell)
) 104 | assert_dom_equal expected, output 105 | end 106 | 107 | def test_body_r 108 | output = table_for([@drummer3, @drummer4]) do |t| 109 | t.body_r do |e| 110 | concat t.d(e.id) 111 | concat t.d(e.name) 112 | end 113 | end 114 | expected = %() << 115 | %() << 116 | %() << 117 | %() << 118 | %() << 119 | %() << 120 | %() << 121 | %() << 122 | %() << 123 | %() << 124 | %() << 125 | %(
3Peter "James" Bond
4Mick Shrimpton (R. J. "Ric" Parnell)
) 126 | assert_dom_equal expected, output 127 | end 128 | 129 | def test_td_with_options 130 | output = table_for([@drummer1]) do |t| 131 | t.body_r do |e| 132 | output_buffer.concat t.d(e.name, :class => 'class') 133 | end 134 | end 135 | expected = %() << 136 | %() << 137 | %() << 138 | %() << 139 | %() << 140 | %() << 141 | %(
John "Stumpy" Pepys
) 142 | assert_dom_equal expected, output 143 | end 144 | 145 | def test_td_with_block 146 | output = table_for([@drummer1]) do |t| 147 | t.body_r do |e| 148 | t.d do 149 | concat 'content' 150 | end 151 | end 152 | end 153 | expected = %() << 154 | %() << 155 | %() << 156 | %() << 157 | %() << 158 | %() << 159 | %(
content
) 160 | assert_dom_equal expected, output 161 | end 162 | 163 | def test_td_with_block_and_options 164 | output = table_for([@drummer1]) do |t| 165 | t.body_r do |e| 166 | t.d(:class => 'class') do 167 | concat 'content' 168 | end 169 | end 170 | end 171 | expected = %() << 172 | %() << 173 | %() << 174 | %() << 175 | %() << 176 | %() << 177 | %(
content
) 178 | assert_dom_equal expected, output 179 | end 180 | 181 | end 182 | 183 | class Drummer < Struct.new(:id, :name); end 184 | -------------------------------------------------------------------------------- /public/javascripts/rails.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jquery-ujs 3 | * 4 | * http://github.com/rails/jquery-ujs/blob/master/src/rails.js 5 | * 6 | * This rails.js file supports jQuery 1.4.3 and 1.4.4 . 7 | * 8 | */ 9 | 10 | jQuery(function ($) { 11 | var csrf_token = $('meta[name=csrf-token]').attr('content'), 12 | csrf_param = $('meta[name=csrf-param]').attr('content'); 13 | 14 | $.fn.extend({ 15 | /** 16 | * Triggers a custom event on an element and returns the event result 17 | * this is used to get around not being able to ensure callbacks are placed 18 | * at the end of the chain. 19 | * 20 | * TODO: deprecate with jQuery 1.4.2 release, in favor of subscribing to our 21 | * own events and placing ourselves at the end of the chain. 22 | */ 23 | triggerAndReturn: function (name, data) { 24 | var event = new $.Event(name); 25 | this.trigger(event, data); 26 | 27 | return event.result !== false; 28 | }, 29 | 30 | /** 31 | * Handles execution of remote calls. Provides following callbacks: 32 | * 33 | * - ajax:before - is execute before the whole thing begings 34 | * - ajax:loading - is executed before firing ajax call 35 | * - ajax:success - is executed when status is success 36 | * - ajax:complete - is execute when status is complete 37 | * - ajax:failure - is execute in case of error 38 | * - ajax:after - is execute every single time at the end of ajax call 39 | */ 40 | callRemote: function () { 41 | var el = this, 42 | method = el.attr('method') || el.attr('data-method') || 'GET', 43 | url = el.attr('action') || el.attr('href'), 44 | dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType); 45 | 46 | if (url === undefined) { 47 | throw "No URL specified for remote call (action or href must be present)."; 48 | } else { 49 | if (el.triggerAndReturn('ajax:before')) { 50 | var data = el.is('form') ? el.serializeArray() : []; 51 | $.ajax({ 52 | url: url, 53 | data: data, 54 | dataType: dataType, 55 | type: method.toUpperCase(), 56 | beforeSend: function (xhr) { 57 | xhr.setRequestHeader("Accept", "text/javascript"); 58 | el.trigger('ajax:loading', xhr); 59 | }, 60 | success: function (data, status, xhr) { 61 | el.trigger('ajax:success', [data, status, xhr]); 62 | }, 63 | complete: function (xhr) { 64 | el.trigger('ajax:complete', xhr); 65 | }, 66 | error: function (xhr, status, error) { 67 | el.trigger('ajax:failure', [xhr, status, error]); 68 | } 69 | }); 70 | } 71 | 72 | el.trigger('ajax:after'); 73 | } 74 | } 75 | }); 76 | 77 | /** 78 | * confirmation handler 79 | */ 80 | 81 | $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () { 82 | var el = $(this); 83 | if (el.triggerAndReturn('confirm')) { 84 | if (!confirm(el.attr('data-confirm'))) { 85 | return false; 86 | } 87 | } 88 | }); 89 | 90 | 91 | 92 | /** 93 | * remote handlers 94 | */ 95 | $('form[data-remote]').live('submit.rails', function (e) { 96 | $(this).callRemote(); 97 | e.preventDefault(); 98 | }); 99 | 100 | $('a[data-remote],input[data-remote]').live('click.rails', function (e) { 101 | $(this).callRemote(); 102 | e.preventDefault(); 103 | }); 104 | 105 | /** 106 | * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %> 107 | * 108 | * Delete 109 | */ 110 | $('a[data-method]:not([data-remote])').live('click.rails', function (e){ 111 | var link = $(this), 112 | href = link.attr('href'), 113 | method = link.attr('data-method'), 114 | form = $('
'), 115 | metadata_input = ''; 116 | 117 | if (csrf_param !== undefined && csrf_token !== undefined) { 118 | metadata_input += ''; 119 | } 120 | 121 | form.hide() 122 | .append(metadata_input) 123 | .appendTo('body'); 124 | 125 | e.preventDefault(); 126 | form.submit(); 127 | }); 128 | 129 | /** 130 | * disable-with handlers 131 | */ 132 | var disable_with_input_selector = 'input[data-disable-with]', 133 | disable_with_form_remote_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')', 134 | disable_with_form_not_remote_selector = 'form:not([data-remote]):has(' + disable_with_input_selector + ')'; 135 | 136 | var disable_with_input_function = function () { 137 | $(this).find(disable_with_input_selector).each(function () { 138 | var input = $(this); 139 | input.data('enable-with', input.val()) 140 | .attr('value', input.attr('data-disable-with')) 141 | .attr('disabled', 'disabled'); 142 | }); 143 | }; 144 | 145 | $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function); 146 | $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function); 147 | 148 | $(disable_with_form_remote_selector).live('ajax:complete.rails', function () { 149 | $(this).find(disable_with_input_selector).each(function () { 150 | var input = $(this); 151 | input.removeAttr('disabled') 152 | .val(input.data('enable-with')); 153 | }); 154 | }); 155 | 156 | var jqueryVersion = $().jquery; 157 | 158 | if (!( (jqueryVersion === '1.4.3') || (jqueryVersion === '1.4.4'))){ 159 | alert('This rails.js does not support the jQuery version you are using. Please read documentation.'); 160 | } 161 | 162 | 163 | }); 164 | -------------------------------------------------------------------------------- /vendor/plugins/open_id_authentication/README: -------------------------------------------------------------------------------- 1 | OpenIdAuthentication 2 | ==================== 3 | 4 | Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first: 5 | 6 | gem install ruby-openid 7 | 8 | To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb 9 | from that gem. 10 | 11 | The specification used is http://openid.net/specs/openid-authentication-2_0.html. 12 | 13 | 14 | Prerequisites 15 | ============= 16 | 17 | OpenID authentication uses the session, so be sure that you haven't turned that off. 18 | 19 | Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb: 20 | 21 | OpenIdAuthentication.store = :file 22 | 23 | This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations. 24 | If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb. 25 | 26 | The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb: 27 | 28 | map.root :controller => 'articles' 29 | 30 | This plugin relies on Rails Edge revision 6317 or newer. 31 | 32 | 33 | Example 34 | ======= 35 | 36 | This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add 37 | salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point, 38 | not a destination. 39 | 40 | Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever 41 | model you are using for authentication. 42 | 43 | Also of note is the following code block used in the example below: 44 | 45 | authenticate_with_open_id do |result, identity_url| 46 | ... 47 | end 48 | 49 | In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' - 50 | If you are storing just 'example.com' with your user, the lookup will fail. 51 | 52 | There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs. 53 | 54 | OpenIdAuthentication.normalize_url(user.identity_url) 55 | 56 | The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/' 57 | It will also raise an InvalidOpenId exception if the URL is determined to not be valid. 58 | Use the above code in your User model and validate OpenID URLs before saving them. 59 | 60 | config/routes.rb 61 | 62 | map.root :controller => 'articles' 63 | map.resource :session 64 | 65 | 66 | app/views/sessions/new.erb 67 | 68 | <% form_tag(session_url) do %> 69 |

70 | 71 | <%= text_field_tag "name" %> 72 |

73 | 74 |

75 | 76 | <%= password_field_tag %> 77 |

78 | 79 |

80 | ...or use: 81 |

82 | 83 |

84 | 85 | <%= text_field_tag "openid_identifier" %> 86 |

87 | 88 |

89 | <%= submit_tag 'Sign in', :disable_with => "Signing in…" %> 90 |

91 | <% end %> 92 | 93 | app/controllers/sessions_controller.rb 94 | class SessionsController < ApplicationController 95 | def create 96 | if using_open_id? 97 | open_id_authentication 98 | else 99 | password_authentication(params[:name], params[:password]) 100 | end 101 | end 102 | 103 | 104 | protected 105 | def password_authentication(name, password) 106 | if @current_user = @account.users.authenticate(params[:name], params[:password]) 107 | successful_login 108 | else 109 | failed_login "Sorry, that username/password doesn't work" 110 | end 111 | end 112 | 113 | def open_id_authentication 114 | authenticate_with_open_id do |result, identity_url| 115 | if result.successful? 116 | if @current_user = @account.users.find_by_identity_url(identity_url) 117 | successful_login 118 | else 119 | failed_login "Sorry, no user by that identity URL exists (#{identity_url})" 120 | end 121 | else 122 | failed_login result.message 123 | end 124 | end 125 | end 126 | 127 | 128 | private 129 | def successful_login 130 | session[:user_id] = @current_user.id 131 | redirect_to(root_url) 132 | end 133 | 134 | def failed_login(message) 135 | flash[:error] = message 136 | redirect_to(new_session_url) 137 | end 138 | end 139 | 140 | 141 | 142 | If you're fine with the result messages above and don't need individual logic on a per-failure basis, 143 | you can collapse the case into a mere boolean: 144 | 145 | def open_id_authentication 146 | authenticate_with_open_id do |result, identity_url| 147 | if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url) 148 | successful_login 149 | else 150 | failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})") 151 | end 152 | end 153 | end 154 | 155 | 156 | Simple Registration OpenID Extension 157 | ==================================== 158 | 159 | Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension 160 | 161 | You can support it in your app by changing #open_id_authentication 162 | 163 | def open_id_authentication(identity_url) 164 | # Pass optional :required and :optional keys to specify what sreg fields you want. 165 | # Be sure to yield registration, a third argument in the #authenticate_with_open_id block. 166 | authenticate_with_open_id(identity_url, 167 | :required => [ :nickname, :email ], 168 | :optional => :fullname) do |result, identity_url, registration| 169 | case result.status 170 | when :missing 171 | failed_login "Sorry, the OpenID server couldn't be found" 172 | when :invalid 173 | failed_login "Sorry, but this does not appear to be a valid OpenID" 174 | when :canceled 175 | failed_login "OpenID verification was canceled" 176 | when :failed 177 | failed_login "Sorry, the OpenID verification failed" 178 | when :successful 179 | if @current_user = @account.users.find_by_identity_url(identity_url) 180 | assign_registration_attributes!(registration) 181 | 182 | if current_user.save 183 | successful_login 184 | else 185 | failed_login "Your OpenID profile registration failed: " + 186 | @current_user.errors.full_messages.to_sentence 187 | end 188 | else 189 | failed_login "Sorry, no user by that identity URL exists" 190 | end 191 | end 192 | end 193 | end 194 | 195 | # registration is a hash containing the valid sreg keys given above 196 | # use this to map them to fields of your user model 197 | def assign_registration_attributes!(registration) 198 | model_to_registration_mapping.each do |model_attribute, registration_attribute| 199 | unless registration[registration_attribute].blank? 200 | @current_user.send("#{model_attribute}=", registration[registration_attribute]) 201 | end 202 | end 203 | end 204 | 205 | def model_to_registration_mapping 206 | { :login => 'nickname', :email => 'email', :display_name => 'fullname' } 207 | end 208 | 209 | Attribute Exchange OpenID Extension 210 | =================================== 211 | 212 | Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html 213 | 214 | Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example: 215 | 216 | authenticate_with_open_id(identity_url, 217 | :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration| 218 | 219 | This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate' 220 | 221 | 222 | 223 | Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license -------------------------------------------------------------------------------- /vendor/plugins/table_builder/test/calendar_helper_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'test_helper.rb') 2 | 3 | class CalendarHelperTest < ActionView::TestCase 4 | include ActionView::Helpers::TextHelper 5 | include ActionView::Helpers::TagHelper 6 | include CalendarHelper 7 | attr_accessor :output_buffer 8 | 9 | def setup 10 | @events = [Event.new(3, 'Jimmy Page', Date.civil(2008, 12, 26)), 11 | Event.new(4, 'Robert Plant', Date.civil(2008, 12, 26))] 12 | end 13 | 14 | def test_calendar_for 15 | output = calendar_for(@events, :html => { :id => 'id', :style => 'style', :class => 'class'}) do |t| 16 | end 17 | expected = %() << 18 | %(
) 19 | assert_dom_equal expected, output 20 | end 21 | 22 | def test_calendar_for_without_an_array 23 | self.output_buffer = '' 24 | assert_raises(ArgumentError) do 25 | calendar_for('a') {|t| } 26 | end 27 | end 28 | 29 | def test_calendar_for_with_empty_array 30 | output = calendar_for([], :year=> 2008, :month => 12) do |c| 31 | c.day do |day, events| 32 | concat(events.collect{|e| e.id}.join) 33 | end 34 | end 35 | expected = %() << 36 | %() << 37 | %() << 38 | %() << 39 | %() << 40 | %() << 41 | %() << 42 | %() << 43 | %(
) 44 | assert_dom_equal expected, output 45 | end 46 | 47 | def test_calendar_for_with_events 48 | output = calendar_for(@events, :year=> 2008, :month => 12) do |c| 49 | c.day do |day, events| 50 | content = events.collect{|e| e.id}.join 51 | concat("(#{day.day})#{content}") 52 | end 53 | end 54 | expected = %() << 55 | %() << 56 | %() << 57 | %() << 58 | %() << 59 | %() << 60 | %() << 61 | %() << 62 | %(
(30)(1)(2)(3)(4)(5)(6)
(7)(8)(9)(10)(11)(12)(13)
(14)(15)(16)(17)(18)(19)(20)
(21)(22)(23)(24)(25)(26)34(27)
(28)(29)(30)(31)(1)(2)(3)
) 63 | assert_dom_equal expected, output 64 | end 65 | 66 | def test_calendar_for_sets_css_classes 67 | output = calendar_for([], :year=> 2008, :month => 12, :today => Date.civil(2008, 12, 15)) do |c| 68 | c.day do |day, events| 69 | concat(events.collect{|e| e.id}.join) 70 | end 71 | end 72 | expected = %() << 73 | %() << 74 | %() << 75 | %() << 76 | %() << 77 | %() << 78 | %() << 79 | %() << 80 | %(
) 81 | assert_dom_equal expected, output 82 | end 83 | 84 | def test_calendar_for_thirty_days 85 | today = Date.civil(2008, 12, 15) 86 | output = calendar_for([], :today => today, :year=>2008, :month=>12, :first=>today, :last=>:thirty_days) do |c| 87 | c.day do |day, events| 88 | concat(events.collect{|e| e.id}.join) 89 | end 90 | end 91 | expected = %() << 92 | %() << 93 | %() << 94 | %() << 95 | %() << 96 | %() << 97 | %() << 98 | %() << 99 | %(
) 100 | assert_dom_equal expected, output 101 | end 102 | 103 | def test_calendar_for_week 104 | today = Date.civil(2008, 12, 15) 105 | output = calendar_for([], :today => today, :year=>2008, :month=>12, :first=>today, :last=>:week) do |c| 106 | c.day do |day, events| 107 | concat(events.collect{|e| e.id}.join) 108 | end 109 | end 110 | expected = %() << 111 | %() << 112 | %() << 113 | %() << 114 | %(
) 115 | assert_dom_equal expected, output 116 | end 117 | 118 | def test_calendar_for_sets_css_ids 119 | output = calendar_for([], :year=> 2008, :month => 12, :today => Date.civil(2008, 12, 15)) do |c| 120 | c.day(:id => 'day_%d') do |day, events| 121 | concat(events.collect{|e| e.id}.join) 122 | end 123 | end 124 | expected = %() << 125 | %() << 126 | %() << 127 | %() << 128 | %() << 129 | %() << 130 | %() << 131 | %() << 132 | %(
) 133 | assert_dom_equal expected, output 134 | end 135 | 136 | end 137 | 138 | class CalendarHelperTest < ActionView::TestCase 139 | 140 | def setup 141 | @events = [Event.new(3, 'Jimmy Page', Date.civil(2008, 12, 26)), 142 | Event.new(4, 'Robert Plant', Date.civil(2008, 12, 26))] 143 | end 144 | 145 | def test_objects_for_days_with_events 146 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 147 | objects_for_days = {} 148 | Date.civil(2008, 11, 30).upto(Date.civil(2009, 1, 3)){|day| objects_for_days[day.strftime("%Y-%m-%d")] = [day, []]} 149 | objects_for_days['2008-12-26'][1] = @events 150 | assert_equal objects_for_days, calendar.objects_for_days(@events, :date) 151 | end 152 | 153 | def test_objects_for_days 154 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 155 | objects_for_days = {} 156 | Date.civil(2008, 11, 30).upto(Date.civil(2009, 1, 3)){|day| objects_for_days[day.strftime("%Y-%m-%d")] = [day, []]} 157 | assert_equal objects_for_days, calendar.objects_for_days([], :date) 158 | end 159 | 160 | def test_days 161 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 162 | days = [] 163 | Date.civil(2008, 11, 30).upto(Date.civil(2009, 1, 3)){|day| days << day} 164 | assert_equal days, calendar.days 165 | end 166 | 167 | def test_days_with_first_day_of_week_set 168 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12, :first_day_of_week => 1) 169 | days = [] 170 | Date.civil(2008, 12, 1).upto(Date.civil(2009, 1, 4)){|day| days << day} 171 | assert_equal days, calendar.days 172 | end 173 | 174 | def test_first_day 175 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 176 | assert_equal Date.civil(2008, 11, 30), calendar.first_day 177 | end 178 | 179 | def test_last_day 180 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 181 | assert_equal Date.civil(2009, 1, 3), calendar.last_day 182 | end 183 | 184 | def test_last_day_with_first_day_of_week_set 185 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12, :first_day_of_week => 1) 186 | assert_equal Date.civil(2009, 1, 4), calendar.last_day 187 | end 188 | end 189 | 190 | class Event < Struct.new(:id, :name, :date); end 191 | --------------------------------------------------------------------------------