commit: 0e8f59c16fcb21301c736ecbc4424cb4c5388c42
parent: 11ff92c9d7b27c2c9ed86f649aef8d956cc8b989
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 29 Feb 2016 19:42:08 +0100
Refactoring Grape API methods into normal controllers & other things
Diffstat:
73 files changed, 842 insertions(+), 912 deletions(-)
diff --git a/Gemfile b/Gemfile
@@ -22,6 +22,7 @@ gem 'grape-entity'
gem 'hashie-forbidden_attributes'
gem 'paranoia', '~> 2.0'
gem 'paperclip', '~> 4.3'
+gem 'backport_new_renderer'
gem 'http'
gem 'addressable'
diff --git a/Gemfile.lock b/Gemfile.lock
@@ -43,6 +43,8 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
+ backport_new_renderer (1.0.0)
+ rails
better_errors (2.1.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
@@ -320,6 +322,7 @@ PLATFORMS
DEPENDENCIES
addressable
+ backport_new_renderer
better_errors
binding_of_caller
coffee-rails (~> 4.1.0)
diff --git a/app/api/mastodon/api.rb b/app/api/mastodon/api.rb
@@ -1,8 +0,0 @@
-module Mastodon
- class API < Grape::API
- rescue_from :all
-
- mount Mastodon::Ostatus
- mount Mastodon::Rest
- end
-end
diff --git a/app/api/mastodon/entities.rb b/app/api/mastodon/entities.rb
@@ -1,54 +0,0 @@
-module Mastodon
- module Entities
- class Account < Grape::Entity
- include ApplicationHelper
-
- expose :id
- expose :username
-
- expose :domain do |account|
- account.local? ? LOCAL_DOMAIN : account.domain
- end
-
- expose :display_name
- expose :note
-
- expose :url do |account|
- account.local? ? profile_url(name: account.username) : account.url
- end
- end
-
- class Status < Grape::Entity
- include ApplicationHelper
-
- format_with(:iso_timestamp) { |dt| dt.iso8601 }
-
- expose :id
-
- expose :uri do |status|
- status.local? ? unique_tag(status.stream_entry.created_at, status.stream_entry.activity_id, status.stream_entry.activity_type) : status.uri
- end
-
- expose :url do |status|
- status.local? ? status_url(name: status.account.username, id: status.id) : status.url
- end
-
- expose :text
- expose :in_reply_to_id
-
- expose :reblog_of_id
- expose :reblog, using: Mastodon::Entities::Status
-
- expose :account, using: Mastodon::Entities::Account
-
- with_options(format_with: :iso_timestamp) do
- expose :created_at
- expose :updated_at
- end
- end
-
- class StreamEntry < Grape::Entity
- expose :activity, using: Mastodon::Entities::Status
- end
- end
-end
diff --git a/app/api/mastodon/ostatus.rb b/app/api/mastodon/ostatus.rb
@@ -1,62 +0,0 @@
-module Mastodon
- class Ostatus < Grape::API
- format :txt
-
- before do
- @account = Account.find(params[:id])
- end
-
- resource :subscriptions do
- helpers do
- include ApplicationHelper
- end
-
- desc 'Receive updates from an account'
-
- params do
- requires :id, type: String, desc: 'Account ID'
- end
-
- post ':id' do
- body = request.body.read
-
- if @account.subscription(subscription_url(@account)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
- ProcessFeedService.new.(body, @account)
- status 201
- else
- status 202
- end
- end
-
- desc 'Confirm PuSH subscription to an account'
-
- params do
- requires :id, type: String, desc: 'Account ID'
- requires 'hub.topic', type: String, desc: 'Topic URL'
- requires 'hub.verify_token', type: String, desc: 'Verification token'
- requires 'hub.challenge', type: String, desc: 'Hub challenge'
- end
-
- get ':id' do
- if @account.subscription(subscription_url(@account)).valid?(params['hub.topic'], params['hub.verify_token'])
- params['hub.challenge']
- else
- error! :not_found, 404
- end
- end
- end
-
- resource :salmon do
- desc 'Receive Salmon updates targeted to account'
-
- params do
- requires :id, type: String, desc: 'Account ID'
- end
-
- post ':id' do
- ProcessInteractionService.new.(request.body.read, @account)
- status 201
- end
- end
- end
-end
diff --git a/app/api/mastodon/rest.rb b/app/api/mastodon/rest.rb
@@ -1,44 +0,0 @@
-module Mastodon
- class Rest < Grape::API
- version 'v1', using: :path
- format :json
-
- helpers do
- def current_user
- User.first
- end
- end
-
- resource :timelines do
- desc 'Return a public timeline'
-
- get :public do
- # todo
- end
-
- desc 'Return the home timeline of a logged in user'
-
- get :home do
- present current_user.timeline, with: Mastodon::Entities::StreamEntry
- end
-
- desc 'Return the notifications timeline of a logged in user'
-
- get :notifications do
- # todo
- end
- end
-
- resource :accounts do
- desc 'Return a user profile'
-
- params do
- requires :id, type: String, desc: 'Account ID'
- end
-
- get ':id' do
- present Account.find(params[:id]), with: Mastodon::Entities::Account
- end
- end
- end
-end
diff --git a/app/assets/javascripts/atom.coffee b/app/assets/javascripts/atom.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/home.coffee b/app/assets/javascripts/home.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/profile.coffee b/app/assets/javascripts/profile.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/xrd.coffee b/app/assets/javascripts/xrd.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/stylesheets/accounts.scss b/app/assets/stylesheets/accounts.scss
@@ -0,0 +1,39 @@
+.card {
+ display: flex;
+ background: $primary-color;
+ box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
+
+ .bio {
+ flex-grow: 1;
+ }
+
+ .name {
+ font-size: 20px;
+ line-height: 18px * 1.5;
+ color: $quaternary-color;
+
+ small {
+ display: block;
+ font-size: 14px;
+ color: $quaternary-color;
+ }
+ }
+
+ .avatar {
+ width: 96px;
+ float: left;
+ margin-right: 10px;
+ padding: 10px;
+ padding-right: 0;
+ padding-left: 9px;
+ margin-top: -30px;
+
+ img {
+ width: 94px;
+ height: 94px;
+ display: block;
+ border-radius: 5px;
+ box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
+ }
+ }
+}
diff --git a/app/assets/stylesheets/api/salmon.scss b/app/assets/stylesheets/api/salmon.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the API::Salmon controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/api/subscriptions.scss b/app/assets/stylesheets/api/subscriptions.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the API::Subscriptions controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
@@ -20,7 +20,7 @@ body {
}
.container {
- width: 800px;
+ width: 700px;
margin: 0 auto;
margin-top: 40px;
}
@@ -40,4 +40,5 @@ body {
@import 'home';
-@import 'profile';
+@import 'accounts';
+@import 'stream_entries';
diff --git a/app/assets/stylesheets/atom.scss b/app/assets/stylesheets/atom.scss
@@ -1,3 +0,0 @@
-// Place all the styles related to the Atom controller here.
-// They will automatically be included in application.css.
-// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/profile.scss b/app/assets/stylesheets/profile.scss
@@ -1,176 +0,0 @@
-.card {
- display: flex;
- background: $darker-background-color;
- border: 1px solid darken($darker-background-color, 15%);
- box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
-
- .bio {
- flex-grow: 1;
- }
-
- .name {
- font-size: 24px;
- line-height: 18px * 1.5;
- color: $text-color;
-
- small {
- display: block;
- font-size: 14px;
- color: $lighter-text-color;
- }
- }
-
- .avatar {
- width: 96px;
- float: left;
- margin-right: 10px;
- padding: 10px;
- padding-left: 9px;
- margin-top: -30px;
-
- img {
- width: 94px;
- height: 94px;
- display: block;
- border: 2px solid $lighter-text-color;
- border-radius: 5px;
- }
- }
-}
-
-.activity-stream {
- clear: both;
- box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
-
- .entry {
- border-bottom: 1px solid darken($background-color, 10%);
- background: $background-color;
- border-left: 2px solid $primary-color;
-
- &.entry-reblog {
- border-left: 2px solid $tertiary-color;
- }
-
- &.entry-predecessor, &.entry-successor {
- border-left: 2px solid $lighter-text-color;
- background: darken($background-color, 5%);
- }
-
- &.entry-follow, &.entry-favourite {
- .content {
- padding-top: 10px;
- padding-bottom: 10px;
- }
- }
-
- &:last-child {
- border-bottom: 0;
- }
- }
-
- .entry__container {
- display: flex;
- }
-
- .avatar {
- width: 48px;
- padding: 10px;
- padding-left: 8px;
- padding-right: 0;
- padding-top: 12px;
-
- img {
- width: 48px;
- height: 48px;
- display: block;
- border-radius: 5px;
- }
- }
-
- .entry__container__container {
- flex-grow: 1;
- }
-
- .header {
- margin-bottom: 10px;
- padding: 10px;
- padding-bottom: 0;
-
- .name {
- text-decoration: none;
- color: $lighter-text-color;
-
- strong {
- color: $text-color;
- }
-
- &:hover {
- strong {
- text-decoration: underline;
- }
- }
- }
- }
-
- .pre-header {
- border-bottom: 1px solid darken($background-color, 10%);
- color: $tertiary-color;
- padding: 5px 10px;
- padding-left: 8px;
- clear: both;
-
- .name {
- color: $tertiary-color;
- font-weight: bold;
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
- }
- }
-
- .content {
- font-size: 16px;
- padding: 0 10px;
- padding-left: 8px;
-
- a {
- color: $primary-color;
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
- }
- }
-
- .time {
- text-decoration: none;
- color: $lighter-text-color;
-
- &:hover {
- text-decoration: underline;
- }
- }
-
- .counters {
- margin-top: 15px;
- color: $lighter-text-color;
- cursor: default;
- padding: 10px;
- padding-top: 0;
-
- .counter {
- display: inline-block;
- margin-right: 10px;
- color: $lighter-text-color;
- }
-
- .conversation-link {
- color: $primary-color;
- text-decoration: underline;
- float: right;
- }
- }
-}
diff --git a/app/assets/stylesheets/stream_entries.scss b/app/assets/stylesheets/stream_entries.scss
@@ -0,0 +1,131 @@
+
+.activity-stream {
+ clear: both;
+ box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
+
+ .entry {
+ border-bottom: 1px solid $darker-background-color;
+ background: $background-color;
+ border-left: 2px solid $primary-color;
+
+ &.entry-reblog {
+ border-left: 2px solid $tertiary-color;
+
+ .content {
+ a {
+ color: $tertiary-color;
+ }
+ }
+ }
+
+ &.entry-predecessor, &.entry-successor {
+ border-left: 2px solid $lighter-text-color;
+ background: darken($background-color, 5%);
+
+ .content {
+ a {
+ color: $lighter-text-color;
+ }
+ }
+ }
+
+ &.entry-follow, &.entry-favourite {
+ .content {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ }
+ }
+
+ &:last-child {
+ border-bottom: 0;
+ }
+ }
+
+ .entry__container {
+ display: flex;
+ }
+
+ .avatar {
+ width: 48px;
+ padding: 10px;
+ padding-left: 8px;
+ padding-right: 0;
+ padding-top: 12px;
+
+ img {
+ width: 48px;
+ height: 48px;
+ display: block;
+ border-radius: 5px;
+ }
+ }
+
+ .entry__container__container {
+ flex-grow: 1;
+ }
+
+ .header {
+ margin-bottom: 5px;
+ padding: 10px;
+ padding-bottom: 0;
+ padding-left: 8px;
+
+ .name {
+ text-decoration: none;
+ color: $lighter-text-color;
+
+ strong {
+ color: $text-color;
+ }
+
+ &:hover {
+ strong {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+
+ .pre-header {
+ border-bottom: 1px solid darken($background-color, 5%);
+ color: $tertiary-color;
+ padding: 5px 10px;
+ padding-left: 8px;
+ clear: both;
+
+ .name {
+ color: $tertiary-color;
+ font-weight: bold;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .content {
+ font-size: 14px;
+ padding: 0 10px;
+ padding-left: 8px;
+ padding-bottom: 25px;
+
+ a {
+ color: $primary-color;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .time {
+ text-decoration: none;
+ color: $lighter-text-color;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/xrd.scss b/app/assets/stylesheets/xrd.scss
@@ -1,3 +0,0 @@
-// Place all the styles related to the XRD controller here.
-// They will automatically be included in application.css.
-// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
@@ -0,0 +1,16 @@
+class AccountsController < ApplicationController
+ before_action :set_account
+
+ def show
+ respond_to do |format|
+ format.html
+ format.atom
+ end
+ end
+
+ private
+
+ def set_account
+ @account = Account.find_by!(username: params[:username], domain: nil)
+ end
+end
diff --git a/app/controllers/api/salmon_controller.rb b/app/controllers/api/salmon_controller.rb
@@ -0,0 +1,14 @@
+class Api::SalmonController < ApplicationController
+ before_action :set_account
+
+ def update
+ ProcessInteractionService.new.(request.body.read, @account)
+ render nothing: true, status: 201
+ end
+
+ private
+
+ def set_account
+ @account = Account.find(params[:id])
+ end
+end
diff --git a/app/controllers/api/subscriptions_controller.rb b/app/controllers/api/subscriptions_controller.rb
@@ -0,0 +1,28 @@
+class Api::SubscriptionsController < ApplicationController
+ before_action :set_account
+
+ def show
+ if @account.subscription(api_subscription_url(@account.id)).valid?(params['hub.topic'], params['hub.verify_token'])
+ render text: params['hub.challenge'], status: 200
+ else
+ render nothing: true, status: 404
+ end
+ end
+
+ def update
+ body = request.body.read
+
+ if @account.subscription(api_subscription_url(@account.id)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
+ ProcessFeedService.new.(body, @account)
+ render nothing: true, status: 201
+ else
+ render nothing: true, status: 202
+ end
+ end
+
+ private
+
+ def set_account
+ @account = Account.find(params[:id])
+ end
+end
diff --git a/app/controllers/atom_controller.rb b/app/controllers/atom_controller.rb
@@ -1,18 +0,0 @@
-class AtomController < ApplicationController
- before_filter :set_format
-
- def user_stream
- @account = Account.find_by!(id: params[:id], domain: nil)
- end
-
- def entry
- @entry = StreamEntry.find(params[:id])
- end
-
- private
-
- def set_format
- request.format = 'xml'
- response.headers['Content-Type'] = 'application/atom+xml'
- end
-end
diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb
@@ -1,17 +0,0 @@
-class ProfileController < ApplicationController
- before_action :set_account
-
- def show
- end
-
- def entry
- @entry = @account.stream_entries.find(params[:id])
- @type = @entry.activity_type.downcase
- end
-
- private
-
- def set_account
- @account = Account.find_by!(username: params[:name], domain: nil)
- end
-end
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
@@ -0,0 +1,23 @@
+class StreamEntriesController < ApplicationController
+ before_action :set_account
+ before_action :set_stream_entry
+
+ def show
+ @type = @stream_entry.activity_type.downcase
+
+ respond_to do |format|
+ format.html
+ format.atom
+ end
+ end
+
+ private
+
+ def set_account
+ @account = Account.find_by!(username: params[:account_username], domain: nil)
+ end
+
+ def set_stream_entry
+ @stream_entry = @account.stream_entries.find(params[:id])
+ end
+end
diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb
@@ -9,6 +9,8 @@ class XrdController < ApplicationController
@account = Account.find_by!(username: username_from_resource, domain: nil)
@canonical_account_uri = "acct:#{@account.username}@#{LOCAL_DOMAIN}"
@magic_key = pem_to_magic_key(@account.keypair.public_key)
+ rescue ActiveRecord::RecordNotFound
+ render nothing: true, status: 404
end
private
diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
@@ -0,0 +1,3 @@
+module AccountsHelper
+
+end
diff --git a/app/helpers/api/salmon_helper.rb b/app/helpers/api/salmon_helper.rb
@@ -0,0 +1,2 @@
+module Api::SalmonHelper
+end
diff --git a/app/helpers/api/subscriptions_helper.rb b/app/helpers/api/subscriptions_helper.rb
@@ -0,0 +1,2 @@
+module Api::SubscriptionsHelper
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
@@ -1,6 +1,4 @@
module ApplicationHelper
- include RoutingHelper
-
def unique_tag(date, id, type)
"tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
end
@@ -13,24 +11,4 @@ module ApplicationHelper
def local_id?(id)
id.start_with?("tag:#{LOCAL_DOMAIN}")
end
-
- def subscription_url(account)
- add_base_url_prefix subscriptions_path(id: account.id, format: '')
- end
-
- def salmon_url(account)
- add_base_url_prefix salmon_path(id: account.id, format: '')
- end
-
- def profile_url(account)
- account.local? ? super(name: account.username) : account.url
- end
-
- def status_url(status)
- status.local? ? super(name: status.account.username, id: status.stream_entry.id) : status.url
- end
-
- def add_base_url_prefix(suffix)
- File.join(root_url, "api", suffix)
- end
end
diff --git a/app/helpers/atom_builder_helper.rb b/app/helpers/atom_builder_helper.rb
@@ -0,0 +1,194 @@
+module AtomBuilderHelper
+ def stream_updated_at
+ @account.stream_entries.last ? (@account.updated_at > @account.stream_entries.last.created_at ? @account.updated_at : @account.stream_entries.last.created_at) : @account.updated_at
+ end
+
+ def entry(xml, is_root, &block)
+ if is_root
+ root_tag(xml, :entry, &block)
+ else
+ xml.entry(&block)
+ end
+ end
+
+ def feed(xml, &block)
+ root_tag(xml, :feed, &block)
+ end
+
+ def unique_id(xml, date, id, type)
+ xml.id_ unique_tag(date, id, type)
+ end
+
+ def simple_id(xml, id)
+ xml.id_ id
+ end
+
+ def published_at(xml, date)
+ xml.published date.iso8601
+ end
+
+ def updated_at(xml, date)
+ xml.updated date.iso8601
+ end
+
+ def verb(xml, verb)
+ xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{verb}")
+ end
+
+ def content(xml, content)
+ xml.content({ type: 'html' }, content)
+ end
+
+ def title(xml, title)
+ xml.title title
+ end
+
+ def author(xml, &block)
+ xml.author(&block)
+ end
+
+ def target(xml, &block)
+ xml['activity'].object(&block)
+ end
+
+ def object_type(xml, type)
+ xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{type}")
+ end
+
+ def uri(xml, uri)
+ xml.uri uri
+ end
+
+ def name(xml, name)
+ xml.name name
+ end
+
+ def summary(xml, summary)
+ xml.summary summary
+ end
+
+ def subtitle(xml, subtitle)
+ xml.subtitle subtitle
+ end
+
+ def link_alternate(xml, url)
+ xml.link(rel: 'alternate', type: 'text/html', href: url)
+ end
+
+ def link_self(xml, url)
+ xml.link(rel: 'self', type: 'application/atom+xml', href: url)
+ end
+
+ def link_hub(xml, url)
+ xml.link(rel: 'hub', href: url)
+ end
+
+ def link_salmon(xml, url)
+ xml.link(rel: 'salmon', href: url)
+ end
+
+ def portable_contact(xml, account)
+ xml['poco'].preferredUsername account.username
+ xml['poco'].displayName account.display_name
+ xml['poco'].note account.note
+ end
+
+ def in_reply_to(xml, uri, url)
+ xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
+ end
+
+ def uri_for_target(target)
+ if target.local?
+ if target.object_type == :person
+ account_url(target)
+ else
+ unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
+ end
+ else
+ target.uri
+ end
+ end
+
+ def url_for_target(target)
+ if target.local?
+ if target.object_type == :person
+ account_url(target)
+ else
+ account_stream_entry_url(target.account, target.stream_entry)
+ end
+ else
+ target.url
+ end
+ end
+
+ def link_mention(xml, account)
+ xml.link(rel: 'mentioned', href: uri_for_target(account))
+ end
+
+ def link_avatar(xml, account)
+ xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large, false)))
+ xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium, false)))
+ xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small, false)))
+ end
+
+ def logo(xml, url)
+ xml.logo url
+ end
+
+ def include_author(xml, account)
+ object_type xml, :person
+ uri xml, url_for_target(account)
+ name xml, account.username
+ summary xml, account.note
+ link_alternate xml, url_for_target(account)
+ link_avatar xml, account
+ portable_contact xml, account
+ end
+
+ def include_entry(xml, stream_entry)
+ unique_id xml, stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type
+ published_at xml, stream_entry.activity.created_at
+ updated_at xml, stream_entry.activity.updated_at
+ title xml, stream_entry.title
+ content xml, stream_entry.content
+ verb xml, stream_entry.verb
+ link_self xml, account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom')
+ object_type xml, stream_entry.object_type
+
+ # Comments need thread element
+ if stream_entry.threaded?
+ in_reply_to xml, uri_for_target(stream_entry.thread), url_for_target(stream_entry.thread)
+ end
+
+ if stream_entry.targeted?
+ target(xml) do
+ object_type xml, stream_entry.target.object_type
+ simple_id xml, uri_for_target(stream_entry.target)
+ title xml, stream_entry.target.title
+ link_alternate xml, url_for_target(stream_entry.target)
+
+ # People have summary and portable contacts information
+ if stream_entry.target.object_type == :person
+ summary xml, stream_entry.target.content
+ portable_contact xml, stream_entry.target
+ link_avatar xml, stream_entry.target
+ end
+
+ # Statuses have content
+ if [:note, :comment].include? stream_entry.target.object_type
+ content xml, stream_entry.target.content
+ end
+ end
+ end
+
+ stream_entry.mentions.each do |mentioned|
+ link_mention xml, mentioned
+ end
+ end
+
+ private
+
+ def root_tag(xml, tag, &block)
+ xml.send(tag, { :xmlns => 'http://www.w3.org/2005/Atom', 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:media' => 'http://purl.org/syndication/atommedia' }, &block)
+ end
+end
diff --git a/app/helpers/atom_helper.rb b/app/helpers/atom_helper.rb
@@ -1,194 +0,0 @@
-module AtomHelper
- def stream_updated_at
- @account.stream_entries.last ? (@account.updated_at > @account.stream_entries.last.created_at ? @account.updated_at : @account.stream_entries.last.created_at) : @account.updated_at
- end
-
- def entry(xml, is_root, &block)
- if is_root
- root_tag(xml, :entry, &block)
- else
- xml.entry(&block)
- end
- end
-
- def feed(xml, &block)
- root_tag(xml, :feed, &block)
- end
-
- def unique_id(xml, date, id, type)
- xml.id_ unique_tag(date, id, type)
- end
-
- def simple_id(xml, id)
- xml.id_ id
- end
-
- def published_at(xml, date)
- xml.published date.iso8601
- end
-
- def updated_at(xml, date)
- xml.updated date.iso8601
- end
-
- def verb(xml, verb)
- xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{verb}")
- end
-
- def content(xml, content)
- xml.content({ type: 'html' }, content)
- end
-
- def title(xml, title)
- xml.title title
- end
-
- def author(xml, &block)
- xml.author(&block)
- end
-
- def target(xml, &block)
- xml['activity'].object(&block)
- end
-
- def object_type(xml, type)
- xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{type}")
- end
-
- def uri(xml, uri)
- xml.uri uri
- end
-
- def name(xml, name)
- xml.name name
- end
-
- def summary(xml, summary)
- xml.summary summary
- end
-
- def subtitle(xml, subtitle)
- xml.subtitle subtitle
- end
-
- def link_alternate(xml, url)
- xml.link(rel: 'alternate', type: 'text/html', href: url)
- end
-
- def link_self(xml, url)
- xml.link(rel: 'self', type: 'application/atom+xml', href: url)
- end
-
- def link_hub(xml, url)
- xml.link(rel: 'hub', href: url)
- end
-
- def link_salmon(xml, url)
- xml.link(rel: 'salmon', href: url)
- end
-
- def portable_contact(xml, account)
- xml['poco'].preferredUsername account.username
- xml['poco'].displayName account.display_name
- xml['poco'].note account.note
- end
-
- def in_reply_to(xml, uri, url)
- xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
- end
-
- def disambiguate_uri(target)
- if target.local?
- if target.object_type == :person
- profile_url(target)
- else
- unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
- end
- else
- target.uri
- end
- end
-
- def disambiguate_url(target)
- if target.local?
- if target.object_type == :person
- profile_url(target)
- else
- status_url(target)
- end
- else
- target.url
- end
- end
-
- def link_mention(xml, account)
- xml.link(rel: 'mentioned', href: disambiguate_uri(account))
- end
-
- def link_avatar(xml, account)
- xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large)))
- xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium)))
- xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small)))
- end
-
- def logo(xml, url)
- xml.logo url
- end
-
- def include_author(xml, account)
- object_type xml, :person
- uri xml, profile_url(account)
- name xml, account.username
- summary xml, account.note
- link_alternate xml, profile_url(account)
- link_avatar xml, account
- portable_contact xml, account
- end
-
- def include_entry(xml, stream_entry)
- unique_id xml, stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type
- published_at xml, stream_entry.activity.created_at
- updated_at xml, stream_entry.activity.updated_at
- title xml, stream_entry.title
- content xml, stream_entry.content
- verb xml, stream_entry.verb
- link_self xml, atom_entry_url(id: stream_entry.id)
- object_type xml, stream_entry.object_type
-
- # Comments need thread element
- if stream_entry.threaded?
- in_reply_to xml, disambiguate_uri(stream_entry.thread), disambiguate_url(stream_entry.thread)
- end
-
- if stream_entry.targeted?
- target(xml) do
- object_type xml, stream_entry.target.object_type
- simple_id xml, disambiguate_uri(stream_entry.target)
- title xml, stream_entry.target.title
- link_alternate xml, disambiguate_url(stream_entry.target)
-
- # People have summary and portable contacts information
- if stream_entry.target.object_type == :person
- summary xml, stream_entry.target.content
- portable_contact xml, stream_entry.target
- link_avatar xml, stream_entry.target
- end
-
- # Statuses have content
- if [:note, :comment].include? stream_entry.target.object_type
- content xml, stream_entry.target.content
- end
- end
- end
-
- stream_entry.mentions.each do |mentioned|
- link_mention xml, mentioned
- end
- end
-
- private
-
- def root_tag(xml, tag, &block)
- xml.send(tag, { :xmlns => 'http://www.w3.org/2005/Atom', 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:media' => 'http://purl.org/syndication/atommedia' }, &block)
- end
-end
diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb
@@ -1,22 +0,0 @@
-module ProfileHelper
- def display_name(account)
- account.display_name.blank? ? account.username : account.display_name
- end
-
- def avatar_for_status_url(status)
- status.reblog? ? status.reblog.account.avatar.url(:small) : status.account.avatar.url(:small)
- end
-
- def entry_classes(status, is_predecessor, is_successor, include_threads)
- classes = ['entry']
- classes << 'entry-reblog' if status.reblog?
- classes << 'entry-predecessor' if is_predecessor
- classes << 'entry-successor' if is_successor
- classes << 'entry-center' if include_threads
- classes.join(' ')
- end
-
- def relative_time(date)
- date < 5.days.ago ? date.strftime("%d.%m.%Y") : "#{time_ago_in_words(date)} ago"
- end
-end
diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb
@@ -0,0 +1,22 @@
+module StreamEntriesHelper
+ def display_name(account)
+ account.display_name.blank? ? account.username : account.display_name
+ end
+
+ def avatar_for_status_url(status)
+ status.reblog? ? status.reblog.account.avatar.url(:small) : status.account.avatar.url(:small)
+ end
+
+ def entry_classes(status, is_predecessor, is_successor, include_threads)
+ classes = ['entry']
+ classes << 'entry-reblog' if status.reblog?
+ classes << 'entry-predecessor' if is_predecessor
+ classes << 'entry-successor' if is_successor
+ classes << 'entry-center' if include_threads
+ classes.join(' ')
+ end
+
+ def relative_time(date)
+ date < 5.days.ago ? date.strftime("%d.%m.%Y") : "#{time_ago_in_words(date)} ago"
+ end
+end
diff --git a/app/models/account.rb b/app/models/account.rb
@@ -76,6 +76,10 @@ class Account < ActiveRecord::Base
@avatar_remote_url = url
end
+ def to_param
+ self.username
+ end
+
before_create do
if local?
keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048)
diff --git a/app/models/user.rb b/app/models/user.rb
@@ -1,9 +1,4 @@
class User < ActiveRecord::Base
belongs_to :account, inverse_of: :user
-
validates :account, presence: true
-
- def timeline
- StreamEntry.where(account_id: self.account.following, activity_type: 'Status').order('id desc')
- end
end
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
@@ -1,3 +1,4 @@
class BaseService
+ include RoutingHelper
include ApplicationHelper
end
diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb
@@ -38,7 +38,7 @@ class FollowRemoteAccountService < BaseService
account.secret = SecureRandom.hex
account.verify_token = SecureRandom.hex
- subscription = account.subscription(subscription_url(account))
+ subscription = account.subscription(api_subscription_url(account.id))
subscription.subscribe
account.save!
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
@@ -9,7 +9,7 @@ class FollowService < BaseService
follow = source_account.follow!(target_account)
send_interaction_service.(follow.stream_entry, target_account)
- source_account.ping!(atom_user_stream_url(id: source_account.id), [HUB_URL])
+ source_account.ping!(account_url(account, format: 'atom'), [HUB_URL])
end
private
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
@@ -7,7 +7,7 @@ class PostStatusService < BaseService
def call(account, text, in_reply_to = nil)
status = account.statuses.create!(text: text, thread: in_reply_to)
process_mentions_service.(status)
- account.ping!(atom_user_stream_url(id: account.id), [HUB_URL])
+ account.ping!(account_url(account, format: 'atom'), [HUB_URL])
status
end
diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb
@@ -43,7 +43,7 @@ class ProcessInteractionService < BaseService
end
def mentions_account?(xml, account)
- xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == profile_url(account) }
+ xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == url_for_target(account) }
false
end
diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb
@@ -5,7 +5,7 @@ class ReblogService < BaseService
# @return [Status]
def call(account, reblogged_status)
reblog = account.statuses.create!(reblog: reblogged_status, text: '')
- account.ping!(atom_user_stream_url(id: account.id), [HUB_URL])
+ account.ping!(account_url(account, format: 'atom'), [HUB_URL])
return reblog if reblogged_status.local?
send_interaction_service.(reblog.stream_entry, reblogged_status.account)
reblog
diff --git a/app/services/send_interaction_service.rb b/app/services/send_interaction_service.rb
@@ -1,5 +1,5 @@
class SendInteractionService < BaseService
- include AtomHelper
+ include AtomBuilderHelper
# Send an Atom representation of an interaction to a remote Salmon endpoint
# @param [StreamEntry] stream_entry
diff --git a/app/views/accounts/show.atom.ruby b/app/views/accounts/show.atom.ruby
@@ -0,0 +1,24 @@
+Nokogiri::XML::Builder.new do |xml|
+ feed(xml) do
+ simple_id xml, account_url(@account, format: 'atom')
+ title xml, @account.display_name
+ subtitle xml, @account.note
+ updated_at xml, stream_updated_at
+ logo xml, asset_url(@account.avatar.url(:medium))
+
+ author(xml) do
+ include_author xml, @account
+ end
+
+ link_alternate xml, url_for_target(@account)
+ link_self xml, account_url(@account, format: 'atom')
+ link_hub xml, HUB_URL
+ link_salmon xml, api_salmon_url(@account.id)
+
+ @account.stream_entries.order('id desc').each do |stream_entry|
+ entry(xml, false) do
+ include_entry xml, stream_entry
+ end
+ end
+ end
+end.to_xml
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
@@ -0,0 +1,14 @@
+- content_for :header_tags do
+ %link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
+ %link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
+
+.card
+ .avatar= image_tag @account.avatar.url(:medium)
+ .bio
+ %h1.name
+ = @account.display_name.blank? ? @account.username : @account.display_name
+ %small= "@#{@account.username}"
+
+.activity-stream
+ - @account.statuses.order('id desc').each do |status|
+ = render partial: 'stream_entries/status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
diff --git a/app/views/atom/user_stream.xml.ruby b/app/views/atom/user_stream.xml.ruby
@@ -1,24 +0,0 @@
-Nokogiri::XML::Builder.new do |xml|
- feed(xml) do
- simple_id xml, atom_user_stream_url(id: @account.id)
- title xml, @account.display_name
- subtitle xml, @account.note
- updated_at xml, stream_updated_at
- logo xml, asset_url(@account.avatar.url(:medium))
-
- author(xml) do
- include_author xml, @account
- end
-
- link_alternate xml, profile_url(@account)
- link_self xml, atom_user_stream_url(id: @account.id)
- link_hub xml, HUB_URL
- link_salmon xml, salmon_url(@account)
-
- @account.stream_entries.order('id desc').each do |stream_entry|
- entry(xml, false) do
- include_entry xml, stream_entry
- end
- end
- end
-end.to_xml
diff --git a/app/views/profile/_status.html.haml b/app/views/profile/_status.html.haml
@@ -1,23 +0,0 @@
-- if status.reply? && include_threads
- = render partial: 'status', locals: { status: status.thread, include_threads: false, is_predecessor: true, is_successor: false }
-
-.entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) }
- - if status.reblog?
- .pre-header
- %i.fa.fa-retweet
- Shared by
- = link_to display_name(status.account), profile_url(status.account), class: 'name'
- .entry__container
- .avatar
- = image_tag avatar_for_status_url(status)
- .entry__container__container
- .header
- = render partial: 'status_header', locals: { status: status.reblog? ? status.reblog : status }
- .content
- = status.content.html_safe
- .counters
- = render partial: 'status_footer', locals: { status: status.reblog? ? status.reblog : status }
-
-- if include_threads
- - status.replies.each do |status|
- = render partial: 'status', locals: { status: status, include_threads: false, is_successor: true, is_predecessor: false }
diff --git a/app/views/profile/_status_footer.html.haml b/app/views/profile/_status_footer.html.haml
@@ -1,2 +0,0 @@
-- if status.reply?
- = link_to "In response to #{status.thread.account.acct}", status_url(status.thread), class: 'conversation-link'
diff --git a/app/views/profile/_status_header.html.haml b/app/views/profile/_status_header.html.haml
@@ -1,7 +0,0 @@
-= link_to profile_url(status.account), class: 'name' do
- %strong= display_name(status.account)
- = "@#{status.account.acct}"
-
-= link_to status_url(status), class: 'time' do
- %span{ title: status.created_at }
- = relative_time(status.created_at)
diff --git a/app/views/profile/entry.html.haml b/app/views/profile/entry.html.haml
@@ -1,5 +0,0 @@
-- content_for :header_tags do
- %link{ rel: 'alternate', type: 'application/atom+xml', href: atom_entry_url(id: @entry.id) }/
-
-.activity-stream
- = render partial: @type, locals: { @type.to_sym => @entry.activity, include_threads: true, is_predecessor: false, is_successor: false }
diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml
@@ -1,14 +0,0 @@
-- content_for :header_tags do
- %link{ rel: 'salmon', href: salmon_url(@account) }/
- %link{ rel: 'alternate', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id) }/
-
-.card
- .avatar= image_tag @account.avatar.url(:medium)
- .bio
- %h1.name
- = @account.display_name.blank? ? @account.username : @account.display_name
- %small= "@#{@account.username}"
-
-.activity-stream
- - @account.statuses.order('id desc').each do |status|
- = render partial: 'status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
diff --git a/app/views/profile/_favourite.html.haml b/app/views/stream_entries/_favourite.html.haml
diff --git a/app/views/profile/_follow.html.haml b/app/views/stream_entries/_follow.html.haml
diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml
@@ -0,0 +1,29 @@
+- if status.reply? && include_threads
+ = render partial: 'status', locals: { status: status.thread, include_threads: false, is_predecessor: true, is_successor: false }
+
+.entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) }
+ - if status.reblog?
+ .pre-header
+ %i.fa.fa-retweet
+ Shared by
+ = link_to display_name(status.account), url_for_target(status.account), class: 'name'
+
+ .entry__container
+ .avatar
+ = image_tag avatar_for_status_url(status)
+
+ .entry__container__container
+ .header
+ = link_to url_for_target(status.reblog? ? status.reblog.account : status.account), class: 'name' do
+ %strong= display_name(status.reblog? ? status.reblog.account : status.account)
+ = "@#{status.reblog? ? status.reblog.account.acct : status.account.acct}"
+ = link_to url_for_target(status.reblog? ? status.reblog : status), class: 'time' do
+ %span{ title: status.reblog? ? status.reblog.created_at : status.created_at }
+ = relative_time(status.reblog? ? status.reblog.created_at : status.created_at)
+
+ .content
+ = status.content.html_safe
+
+- if include_threads
+ - status.replies.each do |status|
+ = render partial: 'status', locals: { status: status, include_threads: false, is_successor: true, is_predecessor: false }
diff --git a/app/views/atom/entry.xml.ruby b/app/views/stream_entries/show.atom.ruby
diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml
@@ -0,0 +1,5 @@
+- content_for :header_tags do
+ %link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
+
+.activity-stream
+ = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true, is_predecessor: false, is_successor: false }
diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/xrd/webfinger.xml.ruby
@@ -1,10 +1,10 @@
Nokogiri::XML::Builder.new do |xml|
xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
xml.Subject @canonical_account_uri
- xml.Alias profile_url(@account)
- xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_url(@account))
+ xml.Alias url_for_target(@account)
+ xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: url_for_target(@account))
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
- xml.Link(rel: 'salmon', href: salmon_url(@account))
+ xml.Link(rel: 'salmon', href: api_salmon_url(@account.id))
xml.Link(rel: 'magic-public-key', href: @magic_key)
end
end.to_xml
diff --git a/config/routes.rb b/config/routes.rb
@@ -2,12 +2,15 @@ Rails.application.routes.draw do
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
- get 'atom/entries/:id', to: 'atom#entry', as: :atom_entry
- get 'atom/users/:id', to: 'atom#user_stream', as: :atom_user_stream
- get 'users/:name', to: 'profile#show', as: :profile
- get 'users/:name/:id', to: 'profile#entry', as: :status
+ resources :accounts, path: 'users', only: [:show], param: :username do
+ resources :stream_entries, path: 'updates', only: [:show]
+ end
- mount Mastodon::API => '/api/'
+ namespace :api do
+ resources :subscriptions, only: [:show]
+ post '/subscriptions/:id', to: 'subscriptions#update'
+ post '/salmon/:id', to: 'salmon#update', as: :salmon
+ end
root 'home#index'
end
diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb
@@ -0,0 +1,17 @@
+require 'rails_helper'
+
+RSpec.describe AccountsController, type: :controller do
+ let(:alice) { Fabricate(:account, username: 'alice') }
+
+ describe 'GET #show' do
+ it 'returns 200' do
+ get :show, username: alice.username
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'returns 200 with Atom' do
+ get :show, username: alice.username, format: 'atom'
+ expect(response).to have_http_status(:success)
+ end
+ end
+end
diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb
@@ -0,0 +1,7 @@
+require 'rails_helper'
+
+RSpec.describe Api::SalmonController, type: :controller do
+ describe 'POST #update' do
+ pending
+ end
+end
diff --git a/spec/controllers/api/subscriptions_controller_spec.rb b/spec/controllers/api/subscriptions_controller_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+RSpec.describe Api::SubscriptionsController, type: :controller do
+ describe 'GET #show' do
+ pending
+ end
+
+ describe 'POST #update' do
+ pending
+ end
+end
diff --git a/spec/controllers/atom_controller_spec.rb b/spec/controllers/atom_controller_spec.rb
@@ -1,11 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe AtomController, type: :controller do
- describe 'GET #user_stream' do
- pending
- end
-
- describe 'GET #entry' do
- pending
- end
-end
diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb
@@ -2,6 +2,9 @@ require 'rails_helper'
RSpec.describe HomeController, type: :controller do
describe 'GET #index' do
- pending
+ it 'returns 200' do
+ get :index
+ expect(response).to have_http_status(:success)
+ end
end
end
diff --git a/spec/controllers/profile_controller_spec.rb b/spec/controllers/profile_controller_spec.rb
@@ -1,11 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe ProfileController, type: :controller do
- describe 'GET #show' do
- pending
- end
-
- describe 'GET #entry' do
- pending
- end
-end
diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+RSpec.describe StreamEntriesController, type: :controller do
+ let(:alice) { Fabricate(:account, username: 'alice') }
+ let(:status) { Fabricate(:status, account: alice) }
+
+ describe 'GET #show' do
+ it 'returns 200 with HTML' do
+ get :show, account_username: alice.username, id: status.stream_entry.id
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'returns 200 with Atom' do
+ get :show, account_username: alice.username, id: status.stream_entry.id, format: 'atom'
+ expect(response).to have_http_status(:success)
+ end
+ end
+end
diff --git a/spec/controllers/xrd_controller_spec.rb b/spec/controllers/xrd_controller_spec.rb
@@ -2,10 +2,23 @@ require 'rails_helper'
RSpec.describe XrdController, type: :controller do
describe 'GET #host_meta' do
- pending
+ it 'returns 200' do
+ get :host_meta
+ expect(response).to have_http_status(:success)
+ end
end
describe 'GET #webfinger' do
- pending
+ let(:alice) { Fabricate(:account, username: 'alice') }
+
+ it 'returns 200 when account can be found' do
+ get :webfinger, resource: "acct:#{alice.username}@anything.com"
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'returns 404 when account cannot be found' do
+ get :webfinger, resource: 'acct:not@existing.com'
+ expect(response).to have_http_status(:not_found)
+ end
end
end
diff --git a/spec/helpers/accounts_helper_spec.rb b/spec/helpers/accounts_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+# Specs in this file have access to a helper object that includes
+# the AccountsHelper. For example:
+#
+# describe AccountsHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# expect(helper.concat_strings("this","that")).to eq("this that")
+# end
+# end
+# end
+RSpec.describe AccountsHelper, type: :helper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/helpers/api/salmon_helper_spec.rb b/spec/helpers/api/salmon_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+# Specs in this file have access to a helper object that includes
+# the Api::SalmonHelper. For example:
+#
+# describe Api::SalmonHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# expect(helper.concat_strings("this","that")).to eq("this that")
+# end
+# end
+# end
+RSpec.describe Api::SalmonHelper, type: :helper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/helpers/api/subscriptions_helper_spec.rb b/spec/helpers/api/subscriptions_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+# Specs in this file have access to a helper object that includes
+# the Api::SubscriptionsHelper. For example:
+#
+# describe Api::SubscriptionsHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# expect(helper.concat_strings("this","that")).to eq("this that")
+# end
+# end
+# end
+RSpec.describe Api::SubscriptionsHelper, type: :helper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
@@ -28,18 +28,4 @@ RSpec.describe ApplicationHelper, type: :helper do
expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
end
end
-
- describe '#add_base_url_prefix' do
- it 'returns full API URL from base to suffix' do
- expect(helper.add_base_url_prefix('test')).to eql "#{root_url}api/test"
- end
- end
-
- describe '#profile_url' do
- pending
- end
-
- describe '#status_url' do
- pending
- end
end
diff --git a/spec/helpers/atom_builder_helper_spec.rb b/spec/helpers/atom_builder_helper_spec.rb
@@ -0,0 +1,123 @@
+require 'rails_helper'
+
+RSpec.describe AtomBuilderHelper, type: :helper do
+ describe '#stream_updated_at' do
+ pending
+ end
+
+ describe '#entry' do
+ pending
+ end
+
+ describe '#feed' do
+ pending
+ end
+
+ describe '#unique_id' do
+ pending
+ end
+
+ describe '#simple_id' do
+ pending
+ end
+
+ describe '#published_at' do
+ pending
+ end
+
+ describe '#updated_at' do
+ pending
+ end
+
+ describe '#verb' do
+ pending
+ end
+
+ describe '#content' do
+ pending
+ end
+
+ describe '#title' do
+ pending
+ end
+
+ describe '#author' do
+ pending
+ end
+
+ describe '#target' do
+ pending
+ end
+
+ describe '#object_type' do
+ pending
+ end
+
+ describe '#uri' do
+ pending
+ end
+
+ describe '#name' do
+ pending
+ end
+
+ describe '#summary' do
+ pending
+ end
+
+ describe '#subtitle' do
+ pending
+ end
+
+ describe '#link_alternate' do
+ pending
+ end
+
+ describe '#link_self' do
+ pending
+ end
+
+ describe '#link_hub' do
+ pending
+ end
+
+ describe '#link_salmon' do
+ pending
+ end
+
+ describe '#portable_contact' do
+ pending
+ end
+
+ describe '#in_reply_to' do
+ pending
+ end
+
+ describe '#link_mention' do
+ pending
+ end
+
+ describe '#disambiguate_uri' do
+ pending
+ end
+
+ describe '#disambiguate_url' do
+ pending
+ end
+
+ describe '#include_author' do
+ pending
+ end
+
+ describe '#include_entry' do
+ pending
+ end
+
+ describe '#link_avatar' do
+ pending
+ end
+
+ describe '#logo' do
+ pending
+ end
+end
diff --git a/spec/helpers/atom_helper_spec.rb b/spec/helpers/atom_helper_spec.rb
@@ -1,123 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe AtomHelper, type: :helper do
- describe '#stream_updated_at' do
- pending
- end
-
- describe '#entry' do
- pending
- end
-
- describe '#feed' do
- pending
- end
-
- describe '#unique_id' do
- pending
- end
-
- describe '#simple_id' do
- pending
- end
-
- describe '#published_at' do
- pending
- end
-
- describe '#updated_at' do
- pending
- end
-
- describe '#verb' do
- pending
- end
-
- describe '#content' do
- pending
- end
-
- describe '#title' do
- pending
- end
-
- describe '#author' do
- pending
- end
-
- describe '#target' do
- pending
- end
-
- describe '#object_type' do
- pending
- end
-
- describe '#uri' do
- pending
- end
-
- describe '#name' do
- pending
- end
-
- describe '#summary' do
- pending
- end
-
- describe '#subtitle' do
- pending
- end
-
- describe '#link_alternate' do
- pending
- end
-
- describe '#link_self' do
- pending
- end
-
- describe '#link_hub' do
- pending
- end
-
- describe '#link_salmon' do
- pending
- end
-
- describe '#portable_contact' do
- pending
- end
-
- describe '#in_reply_to' do
- pending
- end
-
- describe '#link_mention' do
- pending
- end
-
- describe '#disambiguate_uri' do
- pending
- end
-
- describe '#disambiguate_url' do
- pending
- end
-
- describe '#include_author' do
- pending
- end
-
- describe '#include_entry' do
- pending
- end
-
- describe '#link_avatar' do
- pending
- end
-
- describe '#logo' do
- pending
- end
-end
diff --git a/spec/helpers/profile_helper_spec.rb b/spec/helpers/profile_helper_spec.rb
@@ -1,19 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe ProfileHelper, type: :helper do
- describe '#display_name' do
- pending
- end
-
- describe '#avatar_for_status_url' do
- pending
- end
-
- describe '#entry_classes' do
- pending
- end
-
- describe '#relative_time' do
- pending
- end
-end
diff --git a/spec/helpers/stream_entries_helper_spec.rb b/spec/helpers/stream_entries_helper_spec.rb
@@ -0,0 +1,19 @@
+require 'rails_helper'
+
+RSpec.describe StreamEntriesHelper, type: :helper do
+ describe '#display_name' do
+ pending
+ end
+
+ describe '#avatar_for_status_url' do
+ pending
+ end
+
+ describe '#entry_classes' do
+ pending
+ end
+
+ describe '#relative_time' do
+ pending
+ end
+end