Merge branch 'follow_tags'

This commit is contained in:
Ilyaaaaaaaaaaaaa Zhitomirskiy 2011-07-05 15:03:51 -07:00
commit 47e0558c3e
21 changed files with 373 additions and 37 deletions

View file

@ -10,6 +10,9 @@ class AspectsController < ApplicationController
respond_to :html, :js
respond_to :json, :only => [:show, :create]
helper_method :tags, :tag_followings
helper_method :all_aspects_selected?
def index
if params[:a_ids]
@aspects = current_user.aspects.where(:id => params[:a_ids])
@ -158,11 +161,23 @@ class AspectsController < ApplicationController
params[:max_time] ||= Time.now + 1
end
helper_method :all_aspects_selected?
def all_aspects_selected?
@aspect == :all
end
def tag_followings
if current_user
if @tag_followings == nil
@tag_followings = current_user.tag_followings
end
@tag_followings
end
end
def tags
@tags ||= current_user.followed_tags
end
private
def save_sort_order
if params[:sort_order].present?

View file

@ -0,0 +1,32 @@
class TagFollowingsController < ApplicationController
before_filter :authenticate_user!
# POST /tag_followings
# POST /tag_followings.xml
def create
@tag = ActsAsTaggableOn::Tag.find_or_create_by_name(params[:name])
@tag_following = current_user.tag_followings.new(:tag_id => @tag.id)
if @tag_following.save
flash[:notice] = I18n.t('tag_followings.create.success', :name => params[:name])
else
flash[:error] = I18n.t('tag_followings.create.failure', :name => params[:name])
end
redirect_to tag_path(:name => params[:name])
end
# DELETE /tag_followings/1
# DELETE /tag_followings/1.xml
def destroy
@tag = ActsAsTaggableOn::Tag.find_by_name(params[:name])
@tag_following = current_user.tag_followings.where(:tag_id => @tag.id).first
if @tag_following && @tag_following.destroy
flash[:notice] = I18n.t('tag_followings.destroy.success', :name => params[:name])
else
flash[:error] = I18n.t('tag_followings.destroy.failure', :name => params[:name])
end
redirect_to tag_path(:name => params[:name])
end
end

View file

@ -8,6 +8,8 @@ class TagsController < ApplicationController
skip_before_filter :set_grammatical_gender
before_filter :ensure_page, :only => :show
helper_method :tag_followed?
respond_to :html, :only => [:show]
respond_to :json, :only => [:index]
@ -41,8 +43,10 @@ class TagsController < ApplicationController
def show
@aspect = :tag
if current_user
@posts = StatusMessage.joins(:contacts).where(:pending => false).where(
Contact.arel_table[:user_id].eq(current_user.id).or(
@posts = StatusMessage.
joins("LEFT OUTER JOIN post_visibilities ON post_visibilities.post_id = posts.id").
joins("LEFT OUTER JOIN contacts ON contacts.id = post_visibilities.contact_id").
where(Contact.arel_table[:user_id].eq(current_user.id).or(
StatusMessage.arel_table[:public].eq(true).or(
StatusMessage.arel_table[:author_id].eq(current_user.person.id)
)
@ -55,7 +59,6 @@ class TagsController < ApplicationController
max_time = params[:max_time] ? Time.at(params[:max_time].to_i) : Time.now
@posts = @posts.where(StatusMessage.arel_table[:created_at].lt(max_time))
@posts = @posts.includes(:comments, :photos).order('posts.created_at DESC').limit(15)
@posts = PostsFake.new(@posts)
@ -69,4 +72,11 @@ class TagsController < ApplicationController
@people_count = Person.where(:id => profiles.map{|p| p.person_id}).count
end
end
def tag_followed?
if @tag_followed.nil?
@tag_followed = TagFollowing.joins(:tag).where(:tags => {:name => params[:name]}, :user_id => current_user.id).exists? #,
end
@tag_followed
end
end

View file

@ -0,0 +1,2 @@
module TagFollowingsHelper
end

View file

@ -0,0 +1,6 @@
class TagFollowing < ActiveRecord::Base
belongs_to :user
belongs_to :tag, :class_name => "ActsAsTaggableOn::Tag"
validates_uniqueness_of :tag_id, :scope => :user_id
end

View file

@ -39,6 +39,8 @@ class User < ActiveRecord::Base
has_many :contact_people, :through => :contacts, :source => :person
has_many :services, :dependent => :destroy
has_many :user_preferences, :dependent => :destroy
has_many :tag_followings, :dependent => :destroy
has_many :followed_tags, :through => :tag_followings, :source => :tag
has_many :authorizations, :class_name => 'OAuth2::Provider::Models::ActiveRecord::Authorization', :foreign_key => :resource_owner_id
has_many :applications, :through => :authorizations, :source => :client

View file

@ -15,6 +15,17 @@
.section
= render 'aspects/aspect_listings'
.section
%ul.left_nav
%li
%div.root_element
= t('aspects.index.tags_following')
%ul.sub_nav
- for tg in tags
%li
= link_to "##{tg.name}", tag_path(:name => tg.name), :class => "tag_selector"
.span-13.append-1.prepend-5
#aspect_stream_container.stream_container
= render 'aspect_stream',

View file

@ -2,6 +2,7 @@
-# licensed under the Affero General Public License version 3 or later. See
-# the COPYRIGHT file.
- content_for :page_title do
- if params[:name]
= "##{params[:name]}"
@ -11,18 +12,43 @@
- content_for :head do
= include_javascripts :home
:javascript
$(".people_stream .pagination a").live("click", function() {
$.getScript(this.href);
return false;
$(document).ready(function(){
$(".button.tag_following").hover(function(){
$this = $(this);
$this.removeClass("in_aspects");
$this.val("#{t('.stop_following', :tag => params[:name])}");
},
function(){
$this = $(this);
$this.addClass("in_aspects");
$this.val("#{t('.following', :tag => params[:name])}");
});
});
- content_for :body_class do
= "tags_show"
.span-24.last
%h1.tag
.span-6
%h3
= t('people', :count => @people_count)
.side_stream.stream
= render :partial => 'people/index', :locals => {:people => @people}
.span-15.last
.stream_container
#author_info
- if user_signed_in? && current_user.person != @person
.right
- unless tag_followed?
= button_to t('.follow', :tag => params[:name]), tag_tag_followings_path(:name => params[:name]), :method => :post, :class => 'button take_action'
- else
= button_to t('.following', :tag => params[:name]), tag_tag_followings_path(:name => params[:name]), :method => :delete, :class => 'button red_on_hover tag_following in_aspects take_action'
%h2
= "##{params[:name]}"
.span-13
%hr
#main_stream.stream
- if @posts.length > 0
= render 'shared/stream', :posts => @posts
@ -31,9 +57,3 @@
- else
= t('.nobody_talking', :tag => "##{params[:name]}")
.prepend-2.span-9.last
%h3
= t('people', :count => @people_count)
.side_stream.stream
= render :partial => 'people/index', :locals => {:people => @people}

View file

@ -146,6 +146,7 @@ en:
work: "Work"
index:
your_aspects: "Your Aspects"
tags_following: "Followed Tags"
handle_explanation: "This is your diaspora id. Like an email address, you can give this to people to reach you."
no_contacts: "No contacts"
post_a_message: "post a message >>"
@ -697,6 +698,17 @@ en:
posts_tagged_with: "Posts tagged with #%{tag}"
nobody_talking: "Nobody is talking about %{tag} yet."
people_tagged_with: "People tagged with %{tag}"
follow: "Follow #%{tag}"
following: "Following #%{tag}"
stop_following: "Stop Following #%{tag}"
tag_followings:
create:
success: "Successfully following: #%{name}"
failure: "Failed to follow: #%{name}"
destroy:
success: "Successfully stopped following: #%{name}"
failure: "Failed to stop following: #%{name}"
tokens:
show:

View file

@ -4,6 +4,7 @@
Diaspora::Application.routes.draw do
# Posting and Reading
resources :aspects do
@ -34,6 +35,9 @@ Diaspora::Application.routes.draw do
end
resources :tags, :only => [:index]
post "/tags/:name/tag_followings" => "tag_followings#create", :as => 'tag_tag_followings'
delete "/tags/:name/tag_followings" => "tag_followings#destroy"
get 'tags/:name' => 'tags#show', :as => 'tag'
resources :apps, :only => [:show]

View file

@ -0,0 +1,14 @@
class CreateTagFollowings < ActiveRecord::Migration
def self.up
create_table :tag_followings do |t|
t.integer :tag_id
t.integer :user_id
t.timestamps
end
end
def self.down
drop_table :tag_followings
end
end

View file

@ -326,6 +326,13 @@ ActiveRecord::Schema.define(:version => 20110705003445) do
add_index "services", ["user_id"], :name => "index_services_on_user_id"
create_table "tag_followings", :force => true do |t|
t.integer "tag_id"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "taggings", :force => true do |t|
t.integer "tag_id"
t.integer "taggable_id"

View file

@ -0,0 +1,45 @@
@javascript
Feature: posting
In order to takeover humanity for the good of society
As a rock star
I want to see what humanity is saying about particular tags
Background:
Given a user with username "bob"
And a user with username "alice"
When I sign in as "bob@bob.bob"
And I am on the home page
And I expand the publisher
And I fill in "status_message_fake_text" with "I am da #boss"
And I press the first ".public_icon" within "#publisher"
And I press "Share"
And I wait for the ajax to finish
And I wait for the ajax to finish
And I follow "#boss"
And I wait for the ajax to finish
Then I should see "I am da #boss"
And I go to the destroy user session page
And I sign in as "alice@alice.alice"
And I search for "#boss"
And I press "Follow #boss"
And I wait for the ajax to finish
Scenario: see a tag that I am following
When I go to the home page
And I follow "#boss"
Then I should see "I am da #boss"
Scenario: can stop following a particular tag
When I press "Stop Following #boss"
And I go to the home page
Then I should not see "#boss" within ".left_nav"

View file

@ -16,7 +16,7 @@ Feature: new user registration
And I fill in "profile_last_name" with "Hai"
And I fill in "tags" with "#tags"
And I press "Save and continue"
And I wait for "step 2" to load
And I wait for the ajax to finish
Then I should see "Profile updated"
And I should see "Would you like to find your Facebook friends on Diaspora?"
And I follow "Skip"
@ -31,5 +31,4 @@ Feature: new user registration
Scenario: new user skips the setup wizard
When I follow "skip getting started"
And I wait for "the aspects page" to load
Then I should be on the aspects page

View file

@ -129,15 +129,6 @@ When /^I click ok in the confirm dialog to appear next$/ do
JS
end
When /^I wait for "([^\"]*)" to load$/ do |page_name|
wait_until(10) do
uri = URI.parse(current_url)
current_location = uri.path
current_location << "?#{uri.query}" unless uri.query.blank?
current_location == path_to(page_name)
end
end
Then /^I should get download alert$/ do
page.evaluate_script("window.alert = function() { return true; }")
end
@ -183,7 +174,7 @@ And /^I scroll down$/ do
wait_until(10) { evaluate_script('$("#infscr-loading:visible").length') == 0 }
end
When /^I wait for (\d+) seconds$/ do |seconds|
When /^I wait for (\d+) seconds?$/ do |seconds|
sleep seconds.to_i
end

View file

@ -2786,6 +2786,7 @@ ul#requested-scopes
ul.left_nav
:margin 0
:bottom 20px
:padding 0
li
@ -2793,11 +2794,16 @@ ul.left_nav
:width 100%
a.aspect_selector,
a.home_selector
a.home_selector,
a.tag_selector,
.root_element
:display block
:width 100%
:padding 3px 7px
a.aspect_selector,
a.home_selector,
a.tag_selector
&:hover
@include border-radius(2px)
@ -2841,8 +2847,10 @@ ul.left_nav
:margin 0
li
:width 155px
a.aspect_selector,
a.new_aspect
a.new_aspect,
a.tag_selector
:padding
:left 15px
:width 182px
@ -2862,7 +2870,8 @@ ul.left_nav
:width 140px
a.aspect_selector,
a.new_aspect
a.new_aspect,
a.tag_selector
:width 140px
li:hover

View file

@ -11,6 +11,7 @@
@include border-radius(2px)
@include linear-gradient(rgb(248,250,250),rgb(228,223,223))
@include box-shadow(0,1px,1px,#cfcfcf)
@include transition(width, 3s)
:font
:style normal
@ -82,6 +83,11 @@
&:hover
@include linear-gradient(lighten($creation-blue,3%), darken($creation-blue, 8%))
.button.red_on_hover
&:hover
@include linear-gradient(desaturate(lighten($red, 20%),20%), desaturate(lighten($red,14%),20%))
:color black
.right
:position absolute
:right 0

View file

@ -331,4 +331,23 @@ describe AspectsController do
@alices_aspect_1.reload.contacts_visible.should be_false
end
end
context 'helper methods' do
before do
@tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent")
TagFollowing.create!(:tag => @tag, :user => alice )
alice.should_receive(:followed_tags).once.and_return([42])
end
describe 'tags' do
it 'queries current_users tag if there are tag_followings' do
@controller.tags.should == [42]
end
it 'does not query twice' do
@controller.tags.should == [42]
@controller.tags.should == [42]
end
end
end
end

View file

@ -0,0 +1,89 @@
# Copyright (c) 2010, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
require 'spec_helper'
describe TagFollowingsController do
def valid_attributes
{:name => "partytimeexcellent"}
end
before do
@tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent")
sign_in :user, bob
end
describe "POST create" do
describe "with valid params" do
it "creates a new TagFollowing" do
expect {
post :create, valid_attributes
}.to change(TagFollowing, :count).by(1)
end
it "assigns a newly created tag_following as @tag_following" do
post :create, valid_attributes
assigns(:tag_following).should be_a(TagFollowing)
assigns(:tag_following).should be_persisted
end
it 'creates the tag if it does not already exist' do
expect {
post :create, :name => "tomcruisecontrol"
}.to change(ActsAsTaggableOn::Tag, :count).by(1)
end
it 'does not create the tag following for non signed in user' do
expect {
post :create, valid_attributes.merge(:user_id => alice.id)
}.to_not change(alice.tag_followings, :count).by(1)
end
it "redirects and flashes success to the tag page" do
post :create, valid_attributes
response.should redirect_to(tag_path(:name => valid_attributes[:name]))
flash[:notice].should == "Successfully following: ##{valid_attributes[:name]}"
end
it "redirects and flashes error if you already have a tag" do
TagFollowing.any_instance.stub(:save).and_return(false)
post :create, valid_attributes
response.should redirect_to(tag_path(:name => valid_attributes[:name]))
flash[:error].should == "Failed to follow: ##{valid_attributes[:name]}"
end
end
end
describe "DELETE destroy" do
before do
TagFollowing.create!(:tag => @tag, :user => bob )
TagFollowing.create!(:tag => @tag, :user => alice )
end
it "destroys the requested tag_following" do
expect {
delete :destroy, valid_attributes
}.to change(TagFollowing, :count).by(-1)
end
it "redirects and flashes error if you already don't follow the tag" do
delete :destroy, valid_attributes
response.should redirect_to(tag_path(:name => valid_attributes[:name]))
flash[:notice].should == "Successfully stopped following: ##{valid_attributes[:name]}"
end
it "redirects and flashes error if you already don't follow the tag" do
TagFollowing.any_instance.stub(:destroy).and_return(false)
delete :destroy, valid_attributes
response.should redirect_to(tag_path(:name => valid_attributes[:name]))
flash[:error].should == "Failed to stop following: ##{valid_attributes[:name]}"
end
end
end

View file

@ -62,6 +62,13 @@ describe TagsController do
assigns(:posts).models.should == [other_post]
response.status.should == 200
end
it 'displays a public post that was sent to no one' do
stranger = Factory(:user_with_aspect)
stranger_post = stranger.post(:status_message, :text => "#hello", :public => true, :to => 'all')
get :show, :name => 'hello'
assigns(:posts).models.should == [stranger_post]
end
end
context "not signed in" do
@ -106,4 +113,24 @@ describe TagsController do
end
end
end
context 'helper methods' do
describe 'tag_followed?' do
before do
sign_in bob
@tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent")
@controller.stub(:current_user).and_return(bob)
@controller.stub(:params).and_return({:name => "partytimeexcellent"})
end
it 'returns true if the following already exists' do
TagFollowing.create!(:tag => @tag, :user => bob )
@controller.tag_followed?.should be_true
end
it 'returns false if the following does not already exist' do
@controller.tag_followed?.should be_false
end
end
end
end

View file

@ -0,0 +1,16 @@
require 'spec_helper'
describe TagFollowing do
before do
@tag = ActsAsTaggableOn::Tag.create(:name => "partytimeexcellent")
TagFollowing.create!(:tag => @tag, :user => alice)
end
it 'validates uniqueness of tag_following scoped through user' do
TagFollowing.new(:tag => @tag, :user => alice).valid?.should be_false
end
it 'allows multiple tag followings for different users' do
TagFollowing.new(:tag => @tag, :user => bob).valid?.should be_true
end
end