Refactor Post Presenter

and comment presenter
This commit is contained in:
Dennis Collinson 2012-05-10 12:24:13 -07:00
parent 80511d065b
commit efa79a4ad7
27 changed files with 157 additions and 172 deletions

View file

@ -1,12 +1,4 @@
app.collections.Posts = Backbone.Collection.extend({
url : "/posts",
model: function(attrs, options) {
var modelClass = app.models.Post
return new modelClass(attrs, options);
},
parse: function(resp){
return resp.posts;
}
model: app.models.Post,
url : "/posts"
});

View file

@ -1,35 +1,16 @@
(function(){
var dateFormatter = function dateFormatter() {
};
dateFormatter.parse = function(date_string) {
var timestamp = new Date(date_string).getTime();
if (isNaN(timestamp)) {
timestamp = dateFormatter.parseISO8601UTC(date_string);
}
return timestamp;
app.helpers.dateFormatter = {
parse:function (dateString) {
return new Date(dateString).getTime() || this.parseISO8601UTC(dateString || "");
},
dateFormatter.parseISO8601UTC = function(date_string) {
var iso8601_utc_pattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.(\d{3}))?Z$/;
var time_components = date_string.match(iso8601_utc_pattern);
var timestamp = 0;
parseISO8601UTC:function (dateString) {
var iso8601_utc_pattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.(\d{3}))?Z$/
, time_components = dateString.match(iso8601_utc_pattern)
, timestamp = time_components && Date.UTC(time_components[1], time_components[2] - 1, time_components[3],
time_components[4], time_components[5], time_components[6], time_components[8] || 0);
if (time_components != null) {
if (time_components[8] == undefined) {
time_components[8] = 0;
return timestamp || 0;
}
timestamp = Date.UTC(time_components[1], time_components[2] - 1, time_components[3],
time_components[4], time_components[5], time_components[6],
time_components[8]);
}
return timestamp;
},
app.helpers.dateFormatter = dateFormatter;
})();

View file

@ -1,8 +1,6 @@
//= require ./content_view
app.views.Comment = app.views.Content.extend({
templateName: "comment",
className : "comment media",
events : function() {

View file

@ -1,6 +1,5 @@
//= require ./stream_object_view
app.views.Content = app.views.StreamObject.extend({
events: {
"click .expander": "expandPost"
},

View file

@ -20,7 +20,7 @@ class CommentsController < ApplicationController
if @comment
respond_to do |format|
format.json{ render :json => @comment.as_api_response(:backbone), :status => 201 }
format.json{ render :json => CommentPresenter.new(@comment), :status => 201 }
format.html{ render :nothing => true, :status => 201 }
format.mobile{ render :partial => 'comment', :locals => {:post => @comment.post, :comment => @comment} }
end
@ -56,7 +56,7 @@ class CommentsController < ApplicationController
@comments = @post.comments.for_a_stream
respond_with do |format|
format.json { render :json => @comments.as_api_response(:backbone), :status => 200 }
format.json { render :json => CommentPresenter.new(@comments), :status => 200 }
format.mobile{render :layout => false}
end
end

View file

@ -70,9 +70,11 @@ class LikesController < ApplicationController
def find_json_for_like
if @like.parent.is_a? Post
PostPresenter.new(@like.parent, current_user).to_json
ExtremePostPresenter.new(@like.parent, current_user).as_json
elsif @like.parent.is_a? Comment
CommentPresenter.new(@like.parent)
else
@like.parent.as_api_response(:backbone)
@like.parent.respond_to?(:as_api_response) ? @like.parent.as_api_response(:backbone) : @like.parent.as_json
end
end
end

View file

@ -17,7 +17,7 @@ class ParticipationsController < ApplicationController
if @participation
respond_to do |format|
format.mobile { redirect_to post_path(@participation.post_id) }
format.json { render :json => PostPresenter.new(@participation.parent, current_user).to_json, :status => 201 }
format.json { render :json => ExtremePostPresenter.new(@participation.parent, current_user), :status => 201 }
end
else
render :nothing => true, :status => 422
@ -30,7 +30,7 @@ class ParticipationsController < ApplicationController
if @participation
current_user.retract(@participation)
respond_to do |format|
format.json { render :json => PostPresenter.new(@participation.parent, current_user).to_json, :status => 202 }
format.json { render :json => ExtremePostPresenter.new(@participation.parent, current_user), :status => 202 }
end
else
respond_to do |format|

View file

@ -91,8 +91,7 @@ class PeopleController < ApplicationController
@aspect = :profile
@share_with = (params[:share_with] == 'true')
@stream = Stream::Person.new(current_user, @person,
:max_time => max_time)
@stream = Stream::Person.new(current_user, @person, :max_time => max_time)
@profile = @person.profile
@ -120,14 +119,15 @@ class PeopleController < ApplicationController
if params[:ex]
@page = :experimental
gon.person = PersonPresenter.new(@person, current_user)
gon.stream = @stream.stream_posts.as_api_response(:backbone)
gon.stream = PostPresenter.collection_json(@stream.stream_posts, current_user)
render :nothing => true, :layout => 'post'
else
respond_with @person, :locals => {:post_type => :all}
end
end
format.json{ render_for_api :backbone, :json => @stream.stream_posts, :root => :posts }
format.json { render :json => PostPresenter.collection_json(@stream.stream_posts, current_user) }
end
end

View file

@ -30,10 +30,10 @@ class PostsController < ApplicationController
mark_corresponding_notification_read if user_signed_in?
respond_to do |format|
format.html{ gon.post = post_json(@post); render 'posts/show.html.haml' }
format.html{ gon.post = ExtremePostPresenter.new(@post, current_user); render 'posts/show.html.haml' }
format.xml{ render :xml => @post.to_diaspora_xml }
format.mobile{render 'posts/show.mobile.haml', :layout => "application"}
format.json{ render :json => post_json(@post) }
format.json{ render :json => ExtremePostPresenter.new(@post, current_user) }
end
end
@ -81,7 +81,7 @@ class PostsController < ApplicationController
respond_to do |format|
format.html{ redirect_to post_path(next_post) }
format.json{ render :json => post_json(next_post) }
format.json{ render :json => ExtremePostPresenter.new(next_post, current_user)}
end
end
@ -90,7 +90,7 @@ class PostsController < ApplicationController
respond_to do |format|
format.html{ redirect_to post_path(previous_post) }
format.json{ render :json => post_json(previous_post) }
format.json{ render :json => ExtremePostPresenter.new(previous_post, current_user)}
end
end
@ -111,10 +111,6 @@ class PostsController < ApplicationController
Post.visible_from_author(@post.author, current_user)
end
def post_json(post)
PostPresenter.new(post, current_user).to_json
end
def find_by_guid_or_id_with_current_user(id)
key = id.to_s.length <= 8 ? :id : :guid
if user_signed_in?

View file

@ -9,6 +9,6 @@ class ResharesController < ApplicationController
current_user.dispatch_post(@reshare, :url => post_url(@reshare), :additional_subscribers => @reshare.root.author)
end
render :json => PostPresenter.new(@reshare, current_user).to_json, :status => 201
render :json => ExtremePostPresenter.new(@reshare, current_user), :status => 201
end
end

View file

@ -72,7 +72,7 @@ class StatusMessagesController < ApplicationController
respond_to do |format|
format.html { redirect_to :back }
format.mobile { redirect_to stream_path }
format.json { render :json => @status_message.as_api_response(:backbone), :status => 201 }
format.json { render :json => PostPresenter.new(@status_message, current_user), :status => 201 }
end
else
respond_to do |format|

View file

@ -65,7 +65,7 @@ class StreamsController < ApplicationController
respond_with do |format|
format.html { render 'layouts/main_stream' }
format.mobile { render 'layouts/main_stream' }
format.json {render_for_api :backbone, :json => @stream.stream_posts, :root => :posts }
format.json { render :json => PostPresenter.collection_json(@stream.stream_posts, current_user) }
end
end

View file

@ -35,7 +35,7 @@ class TagsController < ApplicationController
@stream = Stream::Tag.new(current_user, params[:name], :max_time => max_time, :page => params[:page])
respond_with do |format|
format.json{ render_for_api :backbone, :json => @stream.stream_posts, :root => :posts }
format.json{ render :json => PostPresenter.collection_json(@stream.stream_posts, current_user) }
end
end

View file

@ -16,16 +16,6 @@ class Comment < ActiveRecord::Base
extract_tags_from :text
before_create :build_tags
# NOTE API V1 to be extracted
acts_as_api
api_accessible :backbone do |t|
t.add :id
t.add :guid
t.add :text
t.add :author
t.add :created_at
end
xml_attr :text
xml_attr :diaspora_handle

View file

@ -17,37 +17,6 @@ class Post < ActiveRecord::Base
attr_accessor :user_like,
:user_participation
# NOTE API V1 to be extracted
acts_as_api
api_accessible :backbone do |t|
t.add :id
t.add :guid
t.add lambda { |post|
post.raw_message
}, :as => :text
t.add :public
t.add :created_at
t.add :interacted_at
t.add :comments_count
t.add :likes_count
t.add :reshares_count
t.add :last_three_comments
t.add :provider_display_name
t.add :author
t.add :post_type
t.add :image_url
t.add :object_url
t.add :root
t.add :o_embed_cache
t.add :user_like
t.add :user_participation
t.add :mentioned_people
t.add :photos
t.add :nsfw
t.add :favorite
t.add :frame_name
end
xml_attr :provider_display_name
has_many :mentions, :dependent => :destroy
@ -129,6 +98,21 @@ class Post < ActiveRecord::Base
scope
end
def reshare_for(user)
return unless user
reshares.where(:author_id => user.person.id).first
end
def participation_for(user)
return unless user
participations.where(:author_id => user.person.id).first
end
def like_for(user)
return unless user
likes.where(:author_id => user.person.id).first
end
#############
def self.diaspora_initialize(params)

View file

@ -42,7 +42,7 @@ class Reshare < Post
end
def photos
self.root ? root.photos : nil
self.root ? root.photos : []
end
def frame_name
@ -66,7 +66,7 @@ class Reshare < Post
end
def nsfw
root.nsfw
root.try(:nsfw)
end
private

View file

@ -0,0 +1,15 @@
class CommentPresenter < BasePresenter
def initialize(comment)
@comment = comment
end
def as_json(opts={})
{
:id => @comment.id,
:guid => @comment.guid,
:text => @comment.text,
:author => @comment.author.as_api_response(:backbone),
:created_at => @comment.created_at
}
end
end

View file

@ -0,0 +1,14 @@
#this file should go away, hence the name that is so full of lulz
#post interactions should probably be a decorator, and used in very few places... maybe?
class ExtremePostPresenter
def initialize(post, current_user)
@post = post
@current_user = current_user
end
def as_json(options={})
post = PostPresenter.new(@post, @current_user)
interactions = PostInteractionPresenter.new(@post, @current_user)
post.as_json.merge!(interactions.as_json)
end
end

View file

@ -8,24 +8,41 @@ class PostPresenter
@current_user = current_user
end
def to_json(options = {})
@post.as_api_response(:backbone).update(
def self.collection_json(collection, current_user)
collection.map {|post| PostPresenter.new(post, current_user)}
end
def as_json(options={})
{
:user_like => user_like,
:user_participation => user_participation,
:likes_count => @post.likes.count,
:participations_count => @post.participations.count,
:reshares_count => @post.reshares.count,
:user_reshare => user_reshare,
:id => @post.id,
:guid => @post.guid,
:text => @post.raw_message,
:public => @post.public,
:created_at => @post.created_at,
:interacted_at => @post.interacted_at,
:comments_count => @post.comments_count,
:likes_count => @post.likes_count,
:reshares_count => @post.reshares_count,
:provider_display_name => @post.provider_display_name,
:post_type => @post.post_type,
:image_url => @post.image_url,
:object_url => @post.object_url,
:nsfw => @post.nsfw,
:favorite => @post.favorite,
:last_three_comments => CommentPresenter.as_collection(@post.last_three_comments),
:author => @post.author.as_api_response(:backbone),
:o_embed_cache => @post.o_embed_cache.try(:as_api_response, :backbone),
:mentioned_people => @post.mentioned_people.as_api_response(:backbone),
:photos => @post.photos.map {|p| p.as_api_response(:backbone)},
:frame_name => @post.frame_name || template_name,
:root => root,
:title => title,
:next_post => next_post_path,
:previous_post => previous_post_path,
:likes => likes,
:reshares => reshares,
:comments => comments,
:participations => participations,
:frame_name => @post.frame_name || template_name,
:title => title
})
:user_like => user_like,
:user_participation => user_participation,
:user_reshare => user_reshare,
}
end
def next_post_path
@ -36,56 +53,31 @@ class PostPresenter
Rails.application.routes.url_helpers.previous_post_path(@post)
end
def comments
as_api(@post.comments)
end
def likes
as_api(@post.likes)
end
def reshares
as_api(@post.reshares)
end
def participations
as_api(@post.participations)
end
def user_like
return unless user_signed_in?
@post.likes.where(:author_id => person.id).first.try(:as_api_response, :backbone)
@post.like_for(@current_user).try(:as_api_response, :backbone)
end
def user_participation
return unless user_signed_in?
@post.participations.where(:author_id => person.id).first.try(:as_api_response, :backbone)
@post.participation_for(@current_user).try(:as_api_response, :backbone)
end
def user_reshare
return unless user_signed_in?
@post.reshares.where(:author_id => person.id).first
@post.reshare_for(@current_user)
end
def title
if @post.text.present?
@post.text(:plain_text => true)
else
I18n.translate('posts.presenter.title', :name => @post.author.name)
end
@post.text.present? ? @post.text(:plain_text => true) : I18n.translate('posts.presenter.title', :name => @post.author.name)
end
def template_name #kill me, lol, I should be client side
@template_name ||= TemplatePicker.new(@post).template_name
end
protected
def root
PostPresenter.new(@post.root, current_user).as_json if @post.respond_to?(:root)
end
def as_api(collection)
collection.includes(:author => :profile).all.map do |element|
element.as_api_response(:backbone)
end
end
protected
def person
@current_user.person
@ -95,3 +87,25 @@ class PostPresenter
@current_user.present?
end
end
class PostInteractionPresenter
def initialize(post, current_user)
@post = post
@current_user = current_user
end
def as_json(options={})
{
:likes => as_api(@post.likes),
:reshares => as_api(@post.reshares),
:comments => CommentPresenter.as_collection(@post.comments),
:participations => as_api(@post.participations)
}
end
def as_api(collection)
collection.includes(:author => :profile).all.map do |element|
element.as_api_response(:backbone)
end
end
end

View file

@ -12,7 +12,7 @@ module Diaspora
# @return [Array<Comment>]
def last_three_comments
return if self.comments_count == 0
return [] if self.comments_count == 0
# DO NOT USE .last(3) HERE. IT WILL FETCH ALL COMMENTS AND RETURN THE LAST THREE
# INSTEAD OF DOING THE FOLLOWING, AS EXPECTED (THX AR):
self.comments.order('created_at DESC').limit(3).includes(:author => :profile).reverse

View file

@ -178,10 +178,10 @@ describe PostsController do
let(:next_post){ mock_model(StatusMessage, :id => 34)}
context "GET .json" do
let(:mock_presenter) { mock(:to_json => {:title => "the unbearable lightness of being"}) }
let(:mock_presenter) { mock(:as_json => {:title => "the unbearable lightness of being"}) }
it "should return a show presenter the next post" do
PostPresenter.should_receive(:new).with(next_post, alice).and_return(mock_presenter)
ExtremePostPresenter.should_receive(:new).with(next_post, alice).and_return(mock_presenter)
get :next, :id => 14, :format => :json
response.body.should == {:title => "the unbearable lightness of being"}.to_json
end
@ -205,10 +205,10 @@ describe PostsController do
let(:previous_post){ mock_model(StatusMessage, :id => 11)}
context "GET .json" do
let(:mock_presenter) { mock(:to_json => {:title => "existential crises"})}
let(:mock_presenter) { mock(:as_json => {:title => "existential crises"})}
it "should return a show presenter the next post" do
PostPresenter.should_receive(:new).with(previous_post, alice).and_return(mock_presenter)
ExtremePostPresenter.should_receive(:new).with(previous_post, alice).and_return(mock_presenter)
get :previous, :id => 14, :format => :json
response.body.should == {:title => "existential crises"}.to_json
end

View file

@ -32,7 +32,7 @@ describe("app.models.Stream", function() {
var fetchedSpy = jasmine.createSpy()
this.stream.bind('fetched', fetchedSpy)
this.stream.fetch()
postFetch.resolve({posts : [1,2,3]})
postFetch.resolve([1,2,3])
expect(fetchedSpy).toHaveBeenCalled()
})
@ -40,7 +40,7 @@ describe("app.models.Stream", function() {
var fetchedSpy = jasmine.createSpy()
this.stream.bind('allItemsLoaded', fetchedSpy)
this.stream.fetch()
postFetch.resolve({posts : []})
postFetch.resolve([])
expect(fetchedSpy).toHaveBeenCalled()
})
@ -48,7 +48,7 @@ describe("app.models.Stream", function() {
var fetchedSpy = jasmine.createSpy()
this.stream.bind('allItemsLoaded', fetchedSpy)
this.stream.fetch()
postFetch.resolve({posts : factory.post().attributes})
postFetch.resolve(factory.post().attributes)
expect(fetchedSpy).toHaveBeenCalled()
})
});

View file

@ -9,7 +9,7 @@ describe("app.views.Feedback", function(){
'limited' : "Limted"
}})
var posts = $.parseJSON(spec.readFixture("stream_json"))["posts"];
var posts = $.parseJSON(spec.readFixture("stream_json"));
this.post = new app.models.Post(posts[0]);
this.view = new app.views.Feedback({model: this.post});

View file

@ -9,7 +9,7 @@ describe("app.views.LikesInfo", function(){
}
})
var posts = $.parseJSON(spec.readFixture("stream_json"))["posts"];
var posts = $.parseJSON(spec.readFixture("stream_json"));
this.post = new app.models.Post(posts[0]); // post with a like
this.view = new app.views.LikesInfo({model: this.post});
});

View file

@ -19,7 +19,7 @@ describe("app.views.StreamPost", function(){
}
}})
var posts = $.parseJSON(spec.readFixture("stream_json"))["posts"];
var posts = $.parseJSON(spec.readFixture("stream_json"));
this.collection = new app.collections.Posts(posts);
this.statusMessage = this.collection.models[0];

View file

@ -2,7 +2,7 @@ describe("app.views.Stream", function() {
beforeEach(function() {
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
this.posts = $.parseJSON(spec.readFixture("stream_json"))["posts"];
this.posts = $.parseJSON(spec.readFixture("stream_json"));
this.stream = new app.models.Stream();
this.stream.add(this.posts);

View file

@ -11,13 +11,13 @@ describe PostPresenter do
@presenter.should_not be_nil
end
describe '#to_json' do
describe '#as_json' do
it 'works with a user' do
@presenter.to_json.should be_a Hash
@presenter.as_json.should be_a Hash
end
it 'works without a user' do
@unauthenticated_presenter.to_json.should be_a Hash
@unauthenticated_presenter.as_json.should be_a Hash
end
end