diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 1503e62..f94967c 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -19,4 +19,8 @@ $(function() {
$('input[data-behavior=focus]:first').focus().select();
$('[data-toggle="popover"]').popover({ html: true });
+
+ if ($('meta[name=referrer][content=none]').length > 0) {
+ $('a[href*=http]').attr('rel', 'noreferrer');
+ }
});
diff --git a/app/controllers/api/asciicasts_controller.rb b/app/controllers/api/asciicasts_controller.rb
index 285b5dc..a5afab9 100644
--- a/app/controllers/api/asciicasts_controller.rb
+++ b/app/controllers/api/asciicasts_controller.rb
@@ -19,7 +19,7 @@ module Api
end
def show
- @asciicast = Asciicast.find(params[:id])
+ @asciicast = Asciicast.find_by_id_or_secret_token!(params[:id])
respond_with(asciicast) do |format|
format.html do
diff --git a/app/controllers/asciicasts_controller.rb b/app/controllers/asciicasts_controller.rb
index f0eeea7..3808706 100644
--- a/app/controllers/asciicasts_controller.rb
+++ b/app/controllers/asciicasts_controller.rb
@@ -69,7 +69,7 @@ class AsciicastsController < ApplicationController
private
def load_resource
- @asciicast = Asciicast.find(params[:id])
+ @asciicast = Asciicast.find_by_id_or_secret_token!(params[:id])
end
def view_counter
diff --git a/app/decorators/asciicast_decorator.rb b/app/decorators/asciicast_decorator.rb
index dc5988c..1d8ea05 100644
--- a/app/decorators/asciicast_decorator.rb
+++ b/app/decorators/asciicast_decorator.rb
@@ -26,7 +26,7 @@ class AsciicastDecorator < ApplicationDecorator
end
def title
- model.title.presence || command || "asciicast:#{id}"
+ model.title.presence || command || "asciicast:#{to_param}"
end
def command
diff --git a/app/models/asciicast.rb b/app/models/asciicast.rb
index 8164f0a..be772ea 100644
--- a/app/models/asciicast.rb
+++ b/app/models/asciicast.rb
@@ -24,10 +24,19 @@ class Asciicast < ActiveRecord::Base
scope :featured, -> { where(featured: true) }
scope :by_recency, -> { order("created_at DESC") }
scope :by_random, -> { order("RANDOM()") }
- scope :latest_limited, -> (n) { by_recency.limit(n).includes(:user) }
- scope :random_featured_limited, -> (n) {
- featured.by_random.limit(n).includes(:user)
- }
+ scope :non_private, -> { where(private: false) }
+ scope :homepage_latest, -> { non_private.by_recency.limit(6).includes(:user) }
+ scope :homepage_featured, -> { non_private.featured.by_random.limit(6).includes(:user) }
+
+ before_create :generate_secret_token
+
+ def self.find_by_id_or_secret_token!(thing)
+ if thing.size == 25
+ find_by_secret_token!(thing)
+ else
+ non_private.find(thing)
+ end
+ end
def self.cache_key
timestamps = scoped.select(:updated_at).map { |o| o.updated_at.to_i }
@@ -39,7 +48,7 @@ class Asciicast < ActiveRecord::Base
end
def self.for_category_ordered(category, order, page = nil, per_page = nil)
- collection = all
+ collection = non_private
if category == :featured
collection = collection.featured
@@ -62,6 +71,18 @@ class Asciicast < ActiveRecord::Base
value ? super(value.strip[0...255]) : super
end
+ def self.generate_secret_token
+ SecureRandom.hex.to_i(16).to_s(36).rjust(25, '0')
+ end
+
+ def to_param
+ if private?
+ secret_token
+ else
+ id.to_s
+ end
+ end
+
def stdout
return @stdout if @stdout
@stdout = Stdout::Buffered.new(get_stdout)
@@ -86,6 +107,10 @@ class Asciicast < ActiveRecord::Base
!image.file || (image.file.filename != image_filename)
end
+ def owner?(user)
+ user && self.user == user
+ end
+
private
def get_stdout
@@ -103,4 +128,10 @@ class Asciicast < ActiveRecord::Base
Digest::SHA1.hexdigest(input)
end
+ def generate_secret_token
+ begin
+ self.secret_token = self.class.generate_secret_token
+ end while self.class.exists?(secret_token: secret_token)
+ end
+
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 2534092..457e8f7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -103,16 +103,20 @@ class User < ActiveRecord::Base
end
end
+ def public_asciicast_count
+ asciicasts.non_private.count
+ end
+
def asciicast_count
asciicasts.count
end
- def asciicasts_excluding(asciicast, limit)
- asciicasts.where('id <> ?', asciicast.id).order('RANDOM()').limit(limit)
+ def other_asciicasts(asciicast, limit)
+ asciicasts.non_private.where('id <> ?', asciicast.id).order('RANDOM()').limit(limit)
end
- def paged_asciicasts(page, per_page)
- asciicasts.
+ def paged_asciicasts(page, per_page, include_private)
+ asciicasts_scope(include_private).
includes(:user).
order("created_at DESC").
paginate(page, per_page)
@@ -131,7 +135,15 @@ class User < ActiveRecord::Base
def generate_auth_token
begin
self[:auth_token] = self.class.generate_auth_token
- end while User.exists?(auth_token: self[:auth_token])
+ end while self.class.exists?(auth_token: self[:auth_token])
+ end
+
+ def asciicasts_scope(include_private)
+ if include_private
+ asciicasts
+ else
+ asciicasts.non_private
+ end
end
end
diff --git a/app/policies/asciicast_policy.rb b/app/policies/asciicast_policy.rb
index 707c257..7b6bf87 100644
--- a/app/policies/asciicast_policy.rb
+++ b/app/policies/asciicast_policy.rb
@@ -7,9 +7,10 @@ class AsciicastPolicy < ApplicationPolicy
end
def permitted_attributes
- if user.admin? || record.user == user
+ if user.admin? || record.owner?(user)
attrs = [:title, :description, :theme_name, :snapshot_at]
- attrs << :featured if user.admin?
+ attrs << :featured if change_featured?
+ attrs << :private if change_visibility?
attrs
else
@@ -20,22 +21,22 @@ class AsciicastPolicy < ApplicationPolicy
def update?
return false unless user
- user.admin? || record.user == user
+ user.admin? || record.owner?(user)
end
def destroy?
return false unless user
- user.admin? || record.user == user
+ user.admin? || record.owner?(user)
end
- def feature?
+ def change_featured?
return false unless user
user.admin?
end
- def unfeature?
+ def change_visibility?
return false unless user
user.admin?
diff --git a/app/presenters/asciicast_page_presenter.rb b/app/presenters/asciicast_page_presenter.rb
index fa70729..d105967 100644
--- a/app/presenters/asciicast_page_presenter.rb
+++ b/app/presenters/asciicast_page_presenter.rb
@@ -87,11 +87,19 @@ class AsciicastPagePresenter
end
def show_set_featured_link?
- !asciicast.featured? && policy.feature?
+ !asciicast.featured? && policy.change_featured?
end
def show_unset_featured_link?
- asciicast.featured? && policy.unfeature?
+ asciicast.featured? && policy.change_featured?
+ end
+
+ def show_make_private_link?
+ !asciicast.private? && policy.change_visibility?
+ end
+
+ def show_make_public_link?
+ asciicast.private? && policy.change_visibility?
end
def show_description?
@@ -110,12 +118,8 @@ class AsciicastPagePresenter
end
end
- def show_other_asciicasts_by_author?
- author.asciicast_count > 1
- end
-
def other_asciicasts_by_author
- author.asciicasts_excluding(asciicast, 3).decorate
+ @other_asciicasts_by_author ||= author.other_asciicasts(asciicast, 3).decorate
end
def asciicast_oembed_url(format)
diff --git a/app/presenters/bare_asciicast_page_presenter.rb b/app/presenters/bare_asciicast_page_presenter.rb
index 35daf51..0cc313a 100644
--- a/app/presenters/bare_asciicast_page_presenter.rb
+++ b/app/presenters/bare_asciicast_page_presenter.rb
@@ -18,7 +18,7 @@ class BareAsciicastPagePresenter
end
def asciicast_id
- asciicast.id
+ asciicast.to_param
end
end
diff --git a/app/presenters/home_page_presenter.rb b/app/presenters/home_page_presenter.rb
index d37e69d..1e7ac91 100644
--- a/app/presenters/home_page_presenter.rb
+++ b/app/presenters/home_page_presenter.rb
@@ -11,11 +11,11 @@ class HomePagePresenter
end
def latest_asciicasts
- Asciicast.latest_limited(6).decorate
+ Asciicast.homepage_latest.decorate
end
def featured_asciicasts
- Asciicast.random_featured_limited(6).decorate
+ Asciicast.homepage_featured.decorate
end
def install_script_url
diff --git a/app/presenters/user_page_presenter.rb b/app/presenters/user_page_presenter.rb
index adebd16..820f1e5 100644
--- a/app/presenters/user_page_presenter.rb
+++ b/app/presenters/user_page_presenter.rb
@@ -39,15 +39,17 @@ class UserPagePresenter
def asciicast_count_text(h)
if current_users_profile?
- if user.asciicast_count > 0
- count = h.pluralize(user.asciicast_count, 'asciicast')
+ count = user.asciicast_count
+ if count > 0
+ count = h.pluralize(count, 'asciicast')
"You have recorded #{count}"
else
"Record your first asciicast"
end
else
- if user.asciicast_count > 0
- count = h.pluralize(user.asciicast_count, 'asciicast')
+ count = user.public_asciicast_count
+ if count > 0
+ count = h.pluralize(count, 'asciicast')
"#{count} by #{user.display_name}"
else
"#{user.display_name} hasn't recorded anything yet"
@@ -70,7 +72,8 @@ class UserPagePresenter
private
def get_asciicasts
- PaginatingDecorator.new(user.paged_asciicasts(page, per_page))
+ asciicasts = user.paged_asciicasts(page, per_page, current_users_profile?)
+ PaginatingDecorator.new(asciicasts)
end
end
diff --git a/app/serializers/asciicast_serializer.rb b/app/serializers/asciicast_serializer.rb
index add532f..e52b926 100644
--- a/app/serializers/asciicast_serializer.rb
+++ b/app/serializers/asciicast_serializer.rb
@@ -4,4 +4,9 @@ class AsciicastSerializer < ActiveModel::Serializer
attributes :id, :duration, :stdout_frames_url, :snapshot
attribute :terminal_columns, key: :width
attribute :terminal_lines, key: :height
+
+ def private?
+ object.private?
+ end
+
end
diff --git a/app/views/api/asciicasts/show.html.slim b/app/views/api/asciicasts/show.html.slim
index 0479350..1cff2e9 100644
--- a/app/views/api/asciicasts/show.html.slim
+++ b/app/views/api/asciicasts/show.html.slim
@@ -9,7 +9,7 @@ javascript:
if (typeof target != "undefined" && window !== window.parent) {
var w = $('.asciinema-player').width();
var h = $(document).height();
- target.postMessage(['asciicast:size', { id: #{page.asciicast_id}, width: w, height: h }], '*');
+ target.postMessage(['asciicast:size', { id: '#{page.asciicast_id}', width: w, height: h }], '*');
}
function onMessage(e) {
diff --git a/app/views/asciicasts/_player.html.erb b/app/views/asciicasts/_player.html.erb
index 4d82cbb..2e25b70 100644
--- a/app/views/asciicasts/_player.html.erb
+++ b/app/views/asciicasts/_player.html.erb
@@ -1,3 +1,10 @@
+<% if asciicast.private? %>
+ <% content_for(:head) do %>
+
+
+ <% end %>
+<% end %>
+
diff --git a/app/views/asciicasts/example.html.slim b/app/views/asciicasts/example.html.slim
index e09ad72..3e6984e 100644
--- a/app/views/asciicasts/example.html.slim
+++ b/app/views/asciicasts/example.html.slim
@@ -5,12 +5,12 @@ p
Some pilots get picked and become television programs. Some don't, become
nothing. She starred in one of the ones that became nothing.
-script[type="text/javascript" src=asciicast_url(@asciicast, format: 'js') id="asciicast-#{@asciicast.id}" async data-speed="2"]
+script[type="text/javascript" src=asciicast_url(@asciicast, format: 'js') id="asciicast-#{@asciicast.to_param}" async data-speed="2"]
p
' And now again. There should be an embedded player below this paragraph.
-script[type="text/javascript" src=asciicast_url(@asciicast, format: 'js') id="asciicast-#{@asciicast.id}" async data-speed="2"]
+script[type="text/javascript" src=asciicast_url(@asciicast, format: 'js') id="asciicast-#{@asciicast.to_param}" async data-speed="2"]
p
' This is at the bottom of the page, below all players.
diff --git a/app/views/asciicasts/show.html.slim b/app/views/asciicasts/show.html.slim
index 8825f6f..9e29aba 100644
--- a/app/views/asciicasts/show.html.slim
+++ b/app/views/asciicasts/show.html.slim
@@ -55,6 +55,16 @@
= link_to(asciicast_path(page.asciicast, 'asciicast[featured]' => 0), method: :put) do
span.glyphicon.glyphicon-eye-close
' Make not featured
+ - if page.show_make_public_link?
+ li
+ = link_to(asciicast_path(page.asciicast, 'asciicast[private]' => 0), method: :put) do
+ span.glyphicon.glyphicon-eye-open
+ ' Make public
+ - if page.show_make_private_link?
+ li
+ = link_to(asciicast_path(page.asciicast, 'asciicast[private]' => 1), method: :put) do
+ span.glyphicon.glyphicon-eye-close
+ ' Make private
- if page.show_delete_link?
li
= link_to(asciicast_path(page.asciicast), method: :delete, data: { confirm: 'Really delete this asciicast?' }) do
@@ -72,7 +82,7 @@
.container
.content = page.description
- - if page.show_other_asciicasts_by_author?
+ - unless page.other_asciicasts_by_author.empty?
section.even
.container
.other-asciicasts
diff --git a/app/views/layouts/bare.html.slim b/app/views/layouts/bare.html.slim
index aacade3..4f59ffc 100644
--- a/app/views/layouts/bare.html.slim
+++ b/app/views/layouts/bare.html.slim
@@ -6,5 +6,6 @@ html[lang="en"]
title = page_title
= stylesheet_link_tag 'embed', :media => 'all'
= javascript_include_tag 'embed'
+ = content_for(:head)
body.iframe
= yield
diff --git a/app/views/layouts/screenshot.html.slim b/app/views/layouts/screenshot.html.slim
index efea0c2..14788d8 100644
--- a/app/views/layouts/screenshot.html.slim
+++ b/app/views/layouts/screenshot.html.slim
@@ -4,5 +4,6 @@ html[lang="en"]
meta[charset="utf-8"]
= screenshot_javascript_tag
= screenshot_stylesheet_tag
+ = content_for(:head)
body.screenshot
= yield
diff --git a/db/migrate/20141115172153_add_secret_token_to_asciicasts.rb b/db/migrate/20141115172153_add_secret_token_to_asciicasts.rb
new file mode 100644
index 0000000..07d0757
--- /dev/null
+++ b/db/migrate/20141115172153_add_secret_token_to_asciicasts.rb
@@ -0,0 +1,11 @@
+class AddSecretTokenToAsciicasts < ActiveRecord::Migration
+ def change
+ add_column :asciicasts, :secret_token, :string
+
+ Asciicast.find_each do |asciicast|
+ asciicast.update_attribute(:secret_token, Asciicast.generate_secret_token)
+ end
+
+ change_column :asciicasts, :secret_token, :string, null: false
+ end
+end
diff --git a/db/migrate/20141115172558_add_private_to_asciicasts.rb b/db/migrate/20141115172558_add_private_to_asciicasts.rb
new file mode 100644
index 0000000..7ec6eea
--- /dev/null
+++ b/db/migrate/20141115172558_add_private_to_asciicasts.rb
@@ -0,0 +1,5 @@
+class AddPrivateToAsciicasts < ActiveRecord::Migration
+ def change
+ add_column :asciicasts, :private, :boolean, null: false, default: false
+ end
+end
diff --git a/db/migrate/20141115172711_add_index_on_asciicasts_private.rb b/db/migrate/20141115172711_add_index_on_asciicasts_private.rb
new file mode 100644
index 0000000..0fa3072
--- /dev/null
+++ b/db/migrate/20141115172711_add_index_on_asciicasts_private.rb
@@ -0,0 +1,5 @@
+class AddIndexOnAsciicastsPrivate < ActiveRecord::Migration
+ def change
+ add_index :asciicasts, :private
+ end
+end
diff --git a/db/migrate/20141115174443_add_unique_index_to_asciicasts_secret_token.rb b/db/migrate/20141115174443_add_unique_index_to_asciicasts_secret_token.rb
new file mode 100644
index 0000000..df3f92f
--- /dev/null
+++ b/db/migrate/20141115174443_add_unique_index_to_asciicasts_secret_token.rb
@@ -0,0 +1,5 @@
+class AddUniqueIndexToAsciicastsSecretToken < ActiveRecord::Migration
+ def change
+ add_index :asciicasts, :secret_token, unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c8575be..3b62c01 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -59,11 +59,15 @@ ActiveRecord::Schema.define(version: 20150401161102) do
t.string "image"
t.integer "image_width"
t.integer "image_height"
+ t.string "secret_token", null: false
+ t.boolean "private", default: false, null: false
end
add_index "asciicasts", ["created_at"], name: "index_asciicasts_on_created_at", using: :btree
add_index "asciicasts", ["featured"], name: "index_asciicasts_on_featured", using: :btree
add_index "asciicasts", ["likes_count"], name: "index_asciicasts_on_likes_count", using: :btree
+ add_index "asciicasts", ["private"], name: "index_asciicasts_on_private", using: :btree
+ add_index "asciicasts", ["secret_token"], name: "index_asciicasts_on_secret_token", unique: true, using: :btree
add_index "asciicasts", ["user_id"], name: "index_asciicasts_on_user_id", using: :btree
add_index "asciicasts", ["views_count"], name: "index_asciicasts_on_views_count", using: :btree
diff --git a/spec/api/asciicast_show_spec.rb b/spec/api/asciicast_show_spec.rb
index 469d788..15bf364 100644
--- a/spec/api/asciicast_show_spec.rb
+++ b/spec/api/asciicast_show_spec.rb
@@ -1,5 +1,24 @@
require 'rails_helper'
+shared_examples_for "asciicast iframe response" do
+ it "responds with status 200" do
+ expect(response.status).to eq(200)
+ end
+
+ it "responds with html content type" do
+ expect(response.headers['Content-Type']).to match('text/html')
+ end
+
+ it "responds without X-Frame-Options header" do
+ pending "the header is added back by Rails only in tests O_o"
+ expect(response.headers).to_not have_key('Content-Type')
+ end
+
+ it "responds with player page using iframe layout" do
+ expect(response.body).to have_selector('body.iframe div.player')
+ end
+end
+
describe "Asciicast retrieval" do
let(:asciicast) { create(:asciicast) }
@@ -18,24 +37,15 @@ describe "Asciicast retrieval" do
include Capybara::RSpecMatchers
before do
- get "/api/asciicasts/#{asciicast.id}", format: 'html'
- end
-
- it "responds with status 200" do
- expect(response.status).to eq(200)
+ get "/api/asciicasts/#{asciicast.to_param}", format: 'html'
end
- it "responds with html content type" do
- expect(response.headers['Content-Type']).to match('text/html')
- end
+ it_behaves_like "asciicast iframe response"
- it "responds without X-Frame-Options header" do
- pending "the header is added back by Rails only in tests O_o"
- expect(response.headers).to_not have_key('Content-Type')
- end
+ context "for private asciicast" do
+ let(:asciicast) { create(:asciicast, private: true) }
- it "responds with player page using iframe layout" do
- expect(response.body).to have_selector('body.iframe div.player')
+ it_behaves_like "asciicast iframe response"
end
end
diff --git a/spec/controllers/asciicasts_controller_spec.rb b/spec/controllers/asciicasts_controller_spec.rb
index 68db172..4c0afee 100644
--- a/spec/controllers/asciicasts_controller_spec.rb
+++ b/spec/controllers/asciicasts_controller_spec.rb
@@ -41,7 +41,7 @@ describe AsciicastsController do
before do
allow(controller).to receive(:view_counter) { view_counter }
- expect(Asciicast).to receive(:find).and_return(asciicast)
+ expect(Asciicast).to receive(:find_by_id_or_secret_token!).and_return(asciicast)
end
let(:asciicast_presenter) { double('asciicast_presenter') }
@@ -74,7 +74,7 @@ describe AsciicastsController do
let(:make_request) { get :edit, :id => asciicast.id }
before do
- expect(Asciicast).to receive(:find).and_return(asciicast)
+ expect(Asciicast).to receive(:find_by_id_or_secret_token!).and_return(asciicast)
asciicast.user = user
end
@@ -114,7 +114,7 @@ describe AsciicastsController do
before do
allow(controller).to receive(:asciicast_updater) { asciicast_updater }
- expect(Asciicast).to receive(:find).and_return(asciicast)
+ expect(Asciicast).to receive(:find_by_id_or_secret_token!).and_return(asciicast)
asciicast.user = user
end
@@ -165,7 +165,7 @@ describe AsciicastsController do
let(:make_request) { delete :destroy, :id => asciicast.id }
before do
- expect(Asciicast).to receive(:find).and_return(asciicast)
+ expect(Asciicast).to receive(:find_by_id_or_secret_token!).and_return(asciicast)
asciicast.user = user
end
diff --git a/spec/features/asciicast_spec.rb b/spec/features/asciicast_spec.rb
index a6d02b4..951bdc1 100644
--- a/spec/features/asciicast_spec.rb
+++ b/spec/features/asciicast_spec.rb
@@ -15,4 +15,15 @@ feature "Asciicast page", :js => true do
expect(page).to have_selector('.cinema .play-button')
end
+ scenario 'Visiting as guest when asciicast is private' do
+ asciicast.update(private: true)
+
+ visit asciicast_path(asciicast)
+
+ expect(page).to have_content('the title')
+ expect(page).to have_link('aaron')
+ expect(page).to have_link('Embed')
+ expect(page).to have_selector('.cinema .play-button')
+ end
+
end
diff --git a/spec/features/playback_spec.rb b/spec/features/playback_spec.rb
index 2b30cb4..6fe924a 100644
--- a/spec/features/playback_spec.rb
+++ b/spec/features/playback_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
-describe 'Asciicast playback', :js => true, :slow => true do
-
- let(:asciicast) { create(:asciicast) }
+describe 'Asciicast playback', js: true, slow: true do
describe "from fixture" do
before do
@@ -14,10 +12,24 @@ describe 'Asciicast playback', :js => true, :slow => true do
Capybara.default_wait_time = @old_wait_time
end
- it "is successful" do
- visit asciicast_path(asciicast, speed: 5)
- find(".start-prompt .play-button").click
- expect(page).to have_css('.time-remaining', visible: false, text: '-00:0')
+ context "for public asciicast" do
+ let(:asciicast) { create(:asciicast, private: false) }
+
+ it "is successful" do
+ visit asciicast_path(asciicast, speed: 5)
+ find(".start-prompt .play-button").click
+ expect(page).to have_css('.time-remaining', visible: false, text: '-00:0')
+ end
+ end
+
+ context "for private asciicast" do
+ let(:asciicast) { create(:asciicast, private: true) }
+
+ it "is successful" do
+ visit asciicast_path(asciicast, speed: 5)
+ find(".start-prompt .play-button").click
+ expect(page).to have_css('.time-remaining', visible: false, text: '-00:0')
+ end
end
end
diff --git a/spec/models/asciicast_spec.rb b/spec/models/asciicast_spec.rb
index c6de820..1f5b4fb 100644
--- a/spec/models/asciicast_spec.rb
+++ b/spec/models/asciicast_spec.rb
@@ -3,6 +3,50 @@ require 'tempfile'
describe Asciicast do
+ describe '.find_by_id_or_secret_token!' do
+ subject { Asciicast.find_by_id_or_secret_token!(thing) }
+
+ context 'for public asciicast' do
+ let(:asciicast) { create(:asciicast, private: false) }
+
+ context 'when looked up by id' do
+ let(:thing) { asciicast.id }
+
+ it { should eq(asciicast) }
+ end
+
+ context 'when looked up by secret token' do
+ let(:thing) { asciicast.secret_token }
+
+ it { should eq(asciicast) }
+ end
+ end
+
+ context 'for private asciicast' do
+ let(:asciicast) { create(:asciicast, private: true) }
+
+ context 'when looked up by id' do
+ let(:thing) { asciicast.id }
+
+ it 'raises RecordNotFound' do
+ expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'when looked up by secret token' do
+ let(:thing) { asciicast.secret_token }
+
+ it { should eq(asciicast) }
+ end
+ end
+ end
+
+ describe '.generate_secret_token' do
+ subject { Asciicast.generate_secret_token }
+
+ it { should match(/^[a-z0-9]{25}$/) }
+ end
+
describe '.for_category_ordered' do
subject { described_class.for_category_ordered(category, order) }
@@ -18,6 +62,7 @@ describe Asciicast do
let!(:asciicast_4) { create(:asciicast, created_at: 3.hours.ago,
views_count: 40,
featured: true) }
+ let!(:asciicast_5) { create(:asciicast, private: true) }
context "when category is :all" do
let(:category) { :all }
@@ -52,7 +97,27 @@ describe Asciicast do
end
end
- let(:asciicast) { described_class.new }
+ describe '#to_param' do
+ subject { asciicast.to_param }
+
+ let(:asciicast) { Asciicast.new(id: 123, secret_token: 'sekrit') }
+
+ context 'for public asciicast' do
+ before do
+ asciicast.private = false
+ end
+
+ it { should eq('123') }
+ end
+
+ context 'for private asciicast' do
+ before do
+ asciicast.private = true
+ end
+
+ it { should eq('sekrit') }
+ end
+ end
describe '#stdout' do
context 'for single-file, JSON asciicast' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 26c99aa..35b4233 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -148,8 +148,8 @@ describe User do
it { should eq(2) }
end
- describe '#asciicasts_excluding' do
- subject { user.asciicasts_excluding(asciicast, 1) }
+ describe '#other_asciicasts' do
+ subject { user.other_asciicasts(asciicast, 1) }
let(:user) { create(:user) }
let(:asciicast) { create(:asciicast, user: user) }
diff --git a/spec/policies/asciicast_policy_spec.rb b/spec/policies/asciicast_policy_spec.rb
index 443ebac..aae4713 100644
--- a/spec/policies/asciicast_policy_spec.rb
+++ b/spec/policies/asciicast_policy_spec.rb
@@ -13,7 +13,7 @@ describe AsciicastPolicy do
let(:user) { stub_model(User, admin?: true) }
it "includes form fields + featured" do
- expect(subject).to eq([:title, :description, :theme_name, :snapshot_at, :featured])
+ expect(subject).to eq([:title, :description, :theme_name, :snapshot_at, :featured, :private])
end
end
@@ -27,7 +27,7 @@ describe AsciicastPolicy do
context "and is creator of the asciicast" do
let(:asciicast) { Asciicast.new(user: user) }
- it "includes form field, but no featured" do
+ it "doesn't include featured but includes private" do
expect(subject).to eq([:title, :description, :theme_name, :snapshot_at])
end
end
@@ -74,23 +74,7 @@ describe AsciicastPolicy do
end
end
- permissions :feature? do
- it "denies access if user is nil" do
- expect(subject).not_to permit(nil, Asciicast.new)
- end
-
- it "grants access if user is admin" do
- user = stub_model(User, admin?: true)
- expect(subject).to permit(user, Asciicast.new)
- end
-
- it "denies access if user isn't admin" do
- user = stub_model(User, admin?: false)
- expect(subject).not_to permit(user, Asciicast.new)
- end
- end
-
- permissions :unfeature? do
+ permissions :change_featured? do
it "denies access if user is nil" do
expect(subject).not_to permit(nil, Asciicast.new)
end
diff --git a/spec/presenters/asciicast_page_presenter_spec.rb b/spec/presenters/asciicast_page_presenter_spec.rb
index 2d39523..1bded65 100644
--- a/spec/presenters/asciicast_page_presenter_spec.rb
+++ b/spec/presenters/asciicast_page_presenter_spec.rb
@@ -155,26 +155,6 @@ describe AsciicastPagePresenter do
it { should eq('i am description') }
end
- describe '#show_other_asciicasts_by_author?' do
- subject { presenter.show_other_asciicasts_by_author? }
-
- before do
- allow(author).to receive(:asciicast_count) { count }
- end
-
- context "when user has more than 1 asciicast" do
- let(:count) { 2 }
-
- it { should be(true) }
- end
-
- context "when user doesn't have more than 1 asciicasts" do
- let(:count) { 1 }
-
- it { should be(false) }
- end
- end
-
describe '#other_asciicasts_by_author' do
subject { presenter.other_asciicasts_by_author }
@@ -182,7 +162,7 @@ describe AsciicastPagePresenter do
let(:decorated_others) { double('decorated_others') }
before do
- allow(author).to receive(:asciicasts_excluding).
+ allow(author).to receive(:other_asciicasts).
with(asciicast, 3) { others }
end
diff --git a/spec/presenters/bare_asciicast_page_presenter_spec.rb b/spec/presenters/bare_asciicast_page_presenter_spec.rb
index 3264319..b487726 100644
--- a/spec/presenters/bare_asciicast_page_presenter_spec.rb
+++ b/spec/presenters/bare_asciicast_page_presenter_spec.rb
@@ -25,7 +25,7 @@ describe BareAsciicastPagePresenter do
describe '#asciicast_id' do
subject { presenter.asciicast_id }
- it { should eq(123) }
+ it { should eq('123') }
end
end
diff --git a/spec/presenters/home_page_presenter_spec.rb b/spec/presenters/home_page_presenter_spec.rb
index f87bc2b..54b3a5d 100644
--- a/spec/presenters/home_page_presenter_spec.rb
+++ b/spec/presenters/home_page_presenter_spec.rb
@@ -43,7 +43,7 @@ describe HomePagePresenter do
let(:decorated_latest) { double('decorated_latest') }
before do
- allow(Asciicast).to receive(:latest_limited) { latest }
+ allow(Asciicast).to receive(:homepage_latest) { latest }
end
it "returns decorated latest asciicasts" do
@@ -58,10 +58,10 @@ describe HomePagePresenter do
let(:decorated_featured) { double('decorated_featured') }
before do
- allow(Asciicast).to receive(:random_featured_limited) { featured }
+ allow(Asciicast).to receive(:homepage_featured) { featured }
end
- it "returns decorated random featured asciicasts" do
+ it "returns decorated featured asciicasts" do
expect(subject).to be(decorated_featured)
end
end
diff --git a/spec/presenters/user_page_presenter_spec.rb b/spec/presenters/user_page_presenter_spec.rb
index 7bae402..5f2a463 100644
--- a/spec/presenters/user_page_presenter_spec.rb
+++ b/spec/presenters/user_page_presenter_spec.rb
@@ -117,11 +117,23 @@ describe UserPagePresenter do
describe '#asciicast_count_text' do
subject { presenter.asciicast_count_text(view_context) }
- before do
- allow(user).to receive(:asciicast_count) { 3 }
+ context 'for non author' do
+ before do
+ allow(user).to receive(:public_asciicast_count) { 2 }
+ end
+
+ it { should match(/2.+cartman/) }
end
- it { should eq('3 asciicasts by cartman') }
+ context 'for author' do
+ let(:current_user) { user }
+
+ before do
+ allow(user).to receive(:asciicast_count) { 3 }
+ end
+
+ it { should match(/you.+3/i) }
+ end
end
describe '#user_username' do
@@ -143,7 +155,7 @@ describe UserPagePresenter do
it "gets user's asciicasts paged" do
subject
- expect(user).to have_received(:paged_asciicasts).with(2, 5)
+ expect(user).to have_received(:paged_asciicasts).with(2, 5, false)
end
it "wraps the asciicasts with paginating decorator" do