Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

...
  • 17 commits
  • 24 files changed
  • 1 commit comment
  • 2 contributors
Commits on Oct 21, 2011
Evan Light elight Initial spec that fails. Had to stop dev while I get my Lion upgrade …
…sorted!
42f4842
Commits on Nov 16, 2011
Evan Light elight Updated with newer gem versions f5ea351
Evan Light elight Availability domain model logic for User f291443
Commits on Nov 18, 2011
Evan Light Pairing with RT Lechow working toward the controller implementation o…
…f right now
b0bd22e
Commits on Nov 19, 2011
Evan Light Availability controller working 0771722
Evan Light Almost working. Just need the AJAX handler to work 4d34ee6
Evan Light Huh. Looks like I got User::Availability to work but it seems to requ…
…ire a patch to Rails
a7bd10f
Commits on Nov 30, 2011
Evan Light elight Working around Rails bug where Rails expects AvailibilitiesController…
… to map to an Availability class in availibility.rb and instead complained about User::Availability
746f8af
Evan Light elight Paired with gduquesnay on wrapping up the implementation of toggling …
…availability
5fcc9d6
Evan Light elight Added TODOs for this branch ad57300
Commits on Dec 16, 2011
Evan Light elight Now showing Users who marked up available within the last 2 hours sor…
…ted by last_available_time on home page
b438184
Evan Light elight New Available column added to home page to replace new remote/local. …
…Uses UserModel#available_now?
f92d97f
Evan Light elight Refactored slightly for expressiveness 477d416
Commits on Jan 16, 2012
Sam Elliott lenary fixed spelling ef03828
Commits on Jan 17, 2012
Sam Elliott lenary Refactor and fix user availability 8f02e23
Commits on Apr 11, 2012
Evan Light elight Paired with samnang on fixing the SASS and sort order of currently av…
…ailable users
4218d44
Evan Light elight Merged fixed_right_now into master pairing with samnang 6e4e27d
2  .rvmrc
View
@@ -1 +1 @@
-rvm --create 1.9.2@rubypair
+rvm --create 1.9.3@rubypair
2  Gemfile
View
@@ -20,7 +20,6 @@ gem 'omniauth'
group :development, :test do
gem "rspec-rails"
- gem 'ruby-debug19', :require => 'ruby-debug'
gem "guard-rspec"
gem "awesome_print", :require => 'ap'
gem 'pry'
@@ -31,4 +30,5 @@ group :test do
gem "factory_girl_rails"
gem 'database_cleaner'
gem "capybara"
+ gem "timecop"
end
21 Gemfile.lock
View
@@ -31,7 +31,6 @@ GEM
activesupport (3.1.0)
multi_json (~> 1.0)
addressable (2.2.4)
- archive-tar-minitar (0.5.2)
arel (2.2.1)
awesome_print (0.4.0)
bcrypt-ruby (3.0.1)
@@ -56,7 +55,6 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.1.2)
- columnize (0.3.4)
compass (0.12.alpha.0)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
@@ -75,7 +73,7 @@ GEM
faraday (0.6.1)
addressable (~> 2.2.4)
multipart-post (~> 1.1.0)
- rack (< 2, >= 1.1.0)
+ rack (>= 1.1.0, < 2)
ffi (1.0.9)
fssm (0.2.7)
guard (0.6.3)
@@ -91,8 +89,6 @@ GEM
json_pure (1.5.4)
spruz (~> 0.2.8)
launchy (2.0.3)
- linecache19 (0.5.12)
- ruby_core_source (>= 0.1.4)
mail (2.3.0)
i18n (>= 0.4.0)
mime-types (~> 1.16)
@@ -201,19 +197,9 @@ GEM
activesupport (~> 3.0)
railties (~> 3.0)
rspec (~> 2.6.0)
- ruby-debug-base19 (0.11.25)
- columnize (>= 0.3.1)
- linecache19 (>= 0.5.11)
- ruby_core_source (>= 0.1.4)
- ruby-debug19 (0.11.6)
- columnize (>= 0.3.1)
- linecache19 (>= 0.5.11)
- ruby-debug-base19 (>= 0.11.19)
ruby-openid (2.1.8)
ruby-openid-apps-discovery (1.2.0)
ruby-openid (>= 2.1.7)
- ruby_core_source (0.1.5)
- archive-tar-minitar (>= 0.5.2)
ruby_parser (2.3.0)
sexp_processor (~> 3.0)
rubyntlm (0.1.1)
@@ -233,10 +219,11 @@ GEM
sprockets (2.0.0)
hike (~> 1.2)
rack (~> 1.0)
- tilt (!= 1.3.0, ~> 1.1)
+ tilt (~> 1.1, != 1.3.0)
spruz (0.2.13)
thor (0.14.6)
tilt (1.3.3)
+ timecop (0.3.5)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
@@ -270,6 +257,6 @@ DEPENDENCIES
pry
rails (= 3.1.0)
rspec-rails
- ruby-debug19
sass-rails (= 3.1.0)
+ timecop
uglifier
2  Rakefile
View
@@ -51,7 +51,7 @@ require 'rspec/core/rake_task'
desc "Non-rails specs"
RSpec::Core::RakeTask.new(:snr) do |t|
- t.pattern = ["./spec/presenters/*_spec.rb", "./spec/domain_models/*_spec.rb"]
+ t.pattern = ["./spec/presenters/*_spec.rb", "./spec/domain_models/*_spec.rb", "./spec/models/user/*_spec.rb"]
t.skip_bundler = true
end
8 app/assets/javascripts/toolbar.coffee
View
@@ -0,0 +1,8 @@
+$(document).ready ->
+ $(".available").bind "ajax:success", ->
+ $(this).fadeOut();
+ $(".unavailable").fadeIn();
+
+ $(".unavailable").bind "ajax:success", ->
+ $(this).fadeOut();
+ $(".available").fadeIn();
8 app/assets/stylesheets/application.css.sass
View
@@ -92,7 +92,7 @@ body
background: rgba(255,255,255,1) url('/assets/search-icon.png') no-repeat 2px 40%
width: 150px
padding-left: 16px
- #newest_users
+ #users
+clearfix
+grid(12,12)
margin-top: 2px
@@ -103,11 +103,11 @@ body
font-size: 1.1em
font-family: 'Myriad Pro Italic'
color: #1E1F1E
- #newest_remote_users
+ #new_users
+grid-prefix(2,12)
+grid(4,12)
+alpha
- #newest_local_users
+ #available_users
+grid-prefix(2,12)
+grid(4,12)
+omega
@@ -249,3 +249,5 @@ body
&:first-letter
font-size: 1.3em
+.hidden
+ display: none
3  app/controllers/application_controller.rb
View
@@ -1,6 +1,7 @@
class ApplicationController < ActionController::Base
- protect_from_forgery
helper_method :current_user
+ helper :application
+ protect_from_forgery
private
4 app/controllers/home_controller.rb
View
@@ -1,7 +1,7 @@
class HomeController < ApplicationController
def index
- @newest_remote_users = UserModel.recent_remote_users
- @newest_local_users = UserModel.recent_local_users
+ @newest_users = UserModel.newest_users
+ @available_users = UserModel.most_recent_available_users
end
def search
16 app/controllers/user/availabilities_controller.rb
View
@@ -0,0 +1,16 @@
+class User::AvailabilitiesController < ApplicationController
+ before_filter do
+ @user = User.find(params[:user_id])
+ @user.extend User::Availability
+ end
+
+ def create
+ @user.available!
+ render status: 201, nothing: true
+ end
+
+ def destroy
+ @user.unavailable!
+ render status: 200, nothing: true
+ end
+end
21 app/domain_models/user_model.rb
View
@@ -1,13 +1,26 @@
require 'active_support/core_ext/string'
class UserModel
- def self.recent_local_users
- User.desc(:created_at).any_in(remote_local_preference: %w[Local Both]).limit(5)
+ def self.newest_users
+ User.desc(:created_at).limit(5)
+ end
+
+ def self.most_recent_available_users
+ UserModel.currently_available.limit(5)
+ end
+
+ def self.currently_available_offset
+ 2.hours.ago
+ end
+ def self.ever_been_available
+ User.where(:last_available_time.ne => nil)
+ .desc(:last_available_time)
end
- def self.recent_remote_users
- User.desc(:created_at).any_in(remote_local_preference: %w[Remote Both]).limit(5)
+ def self.currently_available
+ ever_been_available.
+ where(:last_available_time.gte => currently_available_offset)
end
def self.fulltext_search(query_string, opts = {})
20 app/helpers/application_helper.rb
View
@@ -0,0 +1,20 @@
+module ApplicationHelper
+ def availability_links
+ # TODO: Consider embedding haml engine call here so that we can use HAML instead of clusterfuck that is content_tag
+ # TODO: Then extract the haml engine call out into a method that takes a string of HAML but handles all of the details of setting up the engine
+ content_tag(:span) do
+ link_to("Mark Available", user_availability_path(current_user), method: :post, class: "available #{availability_css_class(:available)}", remote: true) +
+ link_to("Mark Unavailable", user_availability_path(current_user), method: :delete, class: "unavailable #{availability_css_class(:unavailable)}", remote: true)
+ end
+ end
+
+ def availability_css_class(availability_flag)
+ current_user.extend User::Availability
+ if (availability_flag == :available && !current_user.available?) ||
+ (availability_flag == :unavailable && current_user.available?)
+ ""
+ else
+ "hidden"
+ end
+ end
+end
5 app/models/user.rb
View
@@ -3,6 +3,8 @@ class User
include Mongoid::Timestamps
include Mongoid::FullTextSearch
+ REMOTE_LOCAL_PREFERENCES = ["Local", "Remote", "Both"]
+
field :name, type: String
field :github_login, type: String
field :email, type: String
@@ -14,8 +16,7 @@ class User
field :pairing_toolchain, type: String
field :interests, type: String
field :twitter, type: String
-
- REMOTE_LOCAL_PREFERENCES = ["Local", "Remote", "Both"]
+ field :last_available_time, type: Time
fulltext_search_in :name, :github_login, :interests, :location
17 app/models/user/availability.rb
View
@@ -0,0 +1,17 @@
+class User
+ module Availability
+ def available!
+ self.last_available_time = Time.now
+ save!
+ end
+
+ def unavailable!
+ self.last_available_time = nil
+ save!
+ end
+
+ def available?
+ !!last_available_time
+ end
+ end
+end
10 app/presenters/user_presenter.rb
View
@@ -6,6 +6,8 @@ class UserPresenter
def_delegators :@user, :twitter, :github_login, :location, :email, :gravatar_id,
:name, :remote_local_preference, :interests
+ OFFSET_FOR_EARLIEST_AVAILABLE_TIME = 2 * 60 * 60
+
def initialize(user, template)
@user, @template = user, template
end
@@ -54,6 +56,7 @@ def pairing_preference
end
end
+
def pairing_preference_options
User::REMOTE_LOCAL_PREFERENCES.map do |pref|
case pref
@@ -64,4 +67,11 @@ def pairing_preference_options
end
end
end
+
+ def available_now?
+ earliest_available_time = Time.now - OFFSET_FOR_EARLIEST_AVAILABLE_TIME
+
+ @user.last_available_time &&
+ @user.last_available_time >= earliest_available_time
+ end
end
15 app/views/home/index.html.haml
View
@@ -2,12 +2,13 @@
= render 'layouts/searchbox'
-#newest_users
- #newest_remote_users
- %h2 New <span class="pairing-type">Remote</span>
+#users
+ #new_users
+ %h2 New <span class="pairing-type">Users</span>
%ul
- = render @newest_remote_users
- #newest_local_users
- %h2 New <span class="pairing-type">In-Person</span>
+ = render @newest_users
+
+ #available_users
+ %h2 Available <span class="pairing-type">Users</span>
%ul
- = render @newest_local_users
+ = render @available_users
2  app/views/layouts/application.html.haml
View
@@ -21,6 +21,8 @@
- if current_user
#menu
+ %span= availability_links
+ %span= '/'
%span= link_to 'My profile', edit_user_path(current_user)
%span= '/'
%span= link_to 'Sign out', signout_path
3  app/views/users/show.html.haml
View
@@ -23,6 +23,9 @@
%li.twitter
%label Twitter
%span.value= link_to twitter, twitter_link
+ - if available_now?
+ %li.available
+ %label Available
%li.interests
%label Interests
%span.value.lengthy= interest_links
4 config/routes.rb
View
@@ -7,5 +7,7 @@
root :to => "home#index"
- resources :users, :only => [:edit, :update, :show]
+ resources :users, only: [:edit, :update, :show] do
+ resource :availability, module: :user
+ end
end
43 spec/domain_models/user_model_spec.rb
View
@@ -52,4 +52,47 @@ class User; end
UserModel.tag_cloud(3).should == expected
end
end
+
+ describe "When searching for available users" do
+ it "should only find users with a available mark up time" do
+ Timecop.freeze(Time.now) do
+ available = Factory(:user, last_available_time: Time.now)
+ never_available = Factory(:user, last_available_time: nil)
+
+ result = UserModel.ever_been_available
+ result.should include(available)
+ result.should_not include(never_available)
+ end
+ end
+
+ context "who have marked available recently" do
+ before do
+ Timecop.freeze(Time.now)
+ @available_by_1_sec =
+ Factory(:user, last_available_time: UserModel.currently_available_offset + 1.second)
+ @available_exactly =
+ Factory(:user, last_available_time: UserModel.currently_available_offset)
+ @not_available_by_1_sec =
+ Factory(:user, last_available_time: UserModel.currently_available_offset - 1.second)
+ end
+
+ after do
+ Timecop.return
+ end
+
+ it "should only find users with a markup time within the recent constraint" do
+ result = UserModel.currently_available
+ result.should include(@available_by_1_sec)
+ result.should include(@available_exactly)
+ result.should_not include(@not_available_by_1_sec)
+ end
+
+ it "should sort the available users by last_available_time descending" do
+ result = UserModel.currently_available
+ result.length.should == 2
+ result[0].should == @available_by_1_sec
+ result[1].should == @available_exactly
+ end
+ end
+ end
end
7 spec/factories.rb
View
@@ -8,4 +8,9 @@
f.remote_local_preference "Both"
f.interests "ruby,rails,rubypair"
f.sequence(:twitter){ |n| "foo#{n}" }
-end
+ f.last_available_time nil
+end
+
+Factory.define :available_user, parent: :user do |f|
+ f.last_available_time { Time.now }
+end
33 spec/models/user/availability_spec.rb
View
@@ -0,0 +1,33 @@
+$LOAD_PATH << "."
+
+require 'ostruct'
+require 'timecop'
+
+require 'app/models/user/availability'
+
+
+describe User::Availability do
+ subject do
+ OpenStruct.new.tap do |user|
+ user.extend User::Availability
+ user.stub(:save! => true)
+ end
+ end
+
+ describe "When a user marks up as available" do
+ before do
+ subject.available!
+ end
+
+ its(:last_available_time) { should_not be_nil }
+ end
+
+ describe "When a user marks up as unavailable" do
+ before do
+ subject.last_available_time = Time.now
+ subject.unavailable!
+ end
+
+ its(:last_available_time) { should be_nil }
+ end
+end
7 spec/models/user_spec.rb
View
@@ -7,21 +7,22 @@
it "copes with a prefixed @-symbol" do
user.twitter = "@Lenary"
user.twitter.should == "Lenary"
- end
+ end
it "copes with a space padded handle" do
user.twitter = " @Lenary "
user.twitter.should == "Lenary"
- end
+ end
it "copes with no prefixed @-symbol" do
user.twitter = "Lenary"
user.twitter.should == "Lenary"
end
-
+
it "copes with empty strings" do
user.twitter = ""
user.twitter.should == ""
end
end
+
end
33 spec/presenters/user_presenter_spec.rb
View
@@ -1,10 +1,12 @@
$LOAD_PATH << "."
require 'app/presenters/user_presenter'
+require 'timecop'
describe UserPresenter do
+ let(:user) { stub(:user) }
+
describe "#pairing_preference" do
- let(:user) { stub(:user) }
subject { UserPresenter.new(user, nil).pairing_preference }
it "makes 'Both' appear as 'Local or Remote'" do
@@ -19,4 +21,33 @@
end
end
end
+
+ describe "#available_now?" do
+ before do
+ Timecop.freeze(Time.now)
+ end
+
+ after do
+ Timecop.return
+ end
+
+ subject { UserPresenter.new(user, nil).available_now? }
+
+ it "is available when the last available time is <= 2 hours ago" do
+ two_hours_ago = Time.now - (2 * 60 * 60)
+ user.stub(:last_available_time).and_return(two_hours_ago)
+ subject.should be_true
+ end
+
+ it "is not available when the last available time is > 2 hours ago" do
+ two_hours_one_minute_ago = Time.now - (2 * 60 * 60) - 60
+ user.stub(:last_available_time).and_return(two_hours_one_minute_ago)
+ subject.should be_false
+ end
+
+ it "is not available when the last available time is nil" do
+ user.stub(:last_available_time).and_return(nil)
+ subject.should be_false
+ end
+ end
end
44 spec/requests/availability_request_spec.rb
View
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe User::AvailabilitiesController do
+
+ describe "#create" do
+ let(:user) do
+ u = Factory(:user)
+ u.extend User::Availability
+ u
+ end
+
+ before do
+ post user_availability_path(user)
+ end
+
+ it "should set a user as available" do
+ user.reload.should be_available
+ end
+
+ it "should return a 201 created" do
+ response.status.should == 201
+ end
+ end
+
+ describe "#destroy" do
+ let (:user) do
+ u = Factory(:available_user)
+ u.extend User::Availability
+ u
+ end
+
+ before do
+ delete user_availability_path(user)
+ end
+
+ it "should set a user as unavailable" do
+ user.reload.should_not be_available
+ end
+
+ it "should return a 200 ok" do
+ response.status.should == 200
+ end
+ end
+end

Showing you all comments on commits in this comparison.

Sam Elliott

Pulled all availability stuff back into User::Availability as much as possible. Yes, some iffy names that may require a revisit -- but overall I think the code is much better organised. We also removed some duplication.

We'd love it if you could explain the reasoning behind the UserModel @elight - we're not 100% sure what should live in it, and what should live in controllers. At the moment it feels like another level of indirection.

Something went wrong with that request. Please try again.