Refactoring Grape API methods into normal controllers & other things
This commit is contained in:
		
							
								
								
									
										1
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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'
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
module Mastodon
 | 
			
		||||
  class API < Grape::API
 | 
			
		||||
    rescue_from :all
 | 
			
		||||
 | 
			
		||||
    mount Mastodon::Ostatus
 | 
			
		||||
    mount Mastodon::Rest
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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/
 | 
			
		||||
@@ -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/
 | 
			
		||||
@@ -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/
 | 
			
		||||
@@ -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/
 | 
			
		||||
							
								
								
									
										39
									
								
								app/assets/stylesheets/accounts.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/assets/stylesheets/accounts.scss
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
// Place all the styles related to the Atom controller here.
 | 
			
		||||
// 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/
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
// Place all the styles related to the XRD controller here.
 | 
			
		||||
// 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/
 | 
			
		||||
@@ -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';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,59 +1,32 @@
 | 
			
		||||
.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%);
 | 
			
		||||
    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 {
 | 
			
		||||
@@ -92,9 +65,10 @@
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .header {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
    margin-bottom: 5px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    padding-bottom: 0;
 | 
			
		||||
    padding-left: 8px;
 | 
			
		||||
 | 
			
		||||
    .name {
 | 
			
		||||
      text-decoration: none;
 | 
			
		||||
@@ -113,7 +87,7 @@
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .pre-header {
 | 
			
		||||
    border-bottom: 1px solid darken($background-color, 10%);
 | 
			
		||||
    border-bottom: 1px solid darken($background-color, 5%);
 | 
			
		||||
    color: $tertiary-color;
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
    padding-left: 8px;
 | 
			
		||||
@@ -131,9 +105,10 @@
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .content {
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    padding: 0 10px;
 | 
			
		||||
    padding-left: 8px;
 | 
			
		||||
    padding-bottom: 25px;
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      color: $primary-color;
 | 
			
		||||
@@ -153,24 +128,4 @@
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								app/controllers/accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/controllers/accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										14
									
								
								app/controllers/api/salmon_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/controllers/api/salmon_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										28
									
								
								app/controllers/api/subscriptions_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/controllers/api/subscriptions_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
							
								
								
									
										23
									
								
								app/controllers/stream_entries_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/controllers/stream_entries_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								app/helpers/accounts_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/helpers/accounts_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
module AccountsHelper
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										2
									
								
								app/helpers/api/salmon_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/api/salmon_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
module Api::SalmonHelper
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										2
									
								
								app/helpers/api/subscriptions_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/api/subscriptions_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
module Api::SubscriptionsHelper
 | 
			
		||||
end
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
module AtomHelper
 | 
			
		||||
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
 | 
			
		||||
@@ -97,10 +97,10 @@ module AtomHelper
 | 
			
		||||
    xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def disambiguate_uri(target)
 | 
			
		||||
  def uri_for_target(target)
 | 
			
		||||
    if target.local?
 | 
			
		||||
      if target.object_type == :person
 | 
			
		||||
        profile_url(target)
 | 
			
		||||
        account_url(target)
 | 
			
		||||
      else
 | 
			
		||||
        unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
 | 
			
		||||
      end
 | 
			
		||||
@@ -109,12 +109,12 @@ module AtomHelper
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def disambiguate_url(target)
 | 
			
		||||
  def url_for_target(target)
 | 
			
		||||
    if target.local?
 | 
			
		||||
      if target.object_type == :person
 | 
			
		||||
        profile_url(target)
 | 
			
		||||
        account_url(target)
 | 
			
		||||
      else
 | 
			
		||||
        status_url(target)
 | 
			
		||||
        account_stream_entry_url(target.account, target.stream_entry)
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      target.url
 | 
			
		||||
@@ -122,13 +122,13 @@ module AtomHelper
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def link_mention(xml, account)
 | 
			
		||||
    xml.link(rel: 'mentioned', href: disambiguate_uri(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)))
 | 
			
		||||
    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)))
 | 
			
		||||
    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)
 | 
			
		||||
@@ -137,10 +137,10 @@ module AtomHelper
 | 
			
		||||
 | 
			
		||||
  def include_author(xml, account)
 | 
			
		||||
    object_type      xml, :person
 | 
			
		||||
    uri              xml, profile_url(account)
 | 
			
		||||
    uri              xml, url_for_target(account)
 | 
			
		||||
    name             xml, account.username
 | 
			
		||||
    summary          xml, account.note
 | 
			
		||||
    link_alternate   xml, profile_url(account)
 | 
			
		||||
    link_alternate   xml, url_for_target(account)
 | 
			
		||||
    link_avatar      xml, account
 | 
			
		||||
    portable_contact xml, account
 | 
			
		||||
  end
 | 
			
		||||
@@ -152,20 +152,20 @@ module AtomHelper
 | 
			
		||||
    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)
 | 
			
		||||
    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, disambiguate_uri(stream_entry.thread), disambiguate_url(stream_entry.thread)
 | 
			
		||||
      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, disambiguate_uri(stream_entry.target)
 | 
			
		||||
        simple_id      xml, uri_for_target(stream_entry.target)
 | 
			
		||||
        title          xml, stream_entry.target.title
 | 
			
		||||
        link_alternate xml, disambiguate_url(stream_entry.target)
 | 
			
		||||
        link_alternate xml, url_for_target(stream_entry.target)
 | 
			
		||||
 | 
			
		||||
        # People have summary and portable contacts information
 | 
			
		||||
        if stream_entry.target.object_type == :person
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
module ProfileHelper
 | 
			
		||||
module StreamEntriesHelper
 | 
			
		||||
  def display_name(account)
 | 
			
		||||
    account.display_name.blank? ? account.username : account.display_name
 | 
			
		||||
  end
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
class BaseService
 | 
			
		||||
  include RoutingHelper
 | 
			
		||||
  include ApplicationHelper
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -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!
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
Nokogiri::XML::Builder.new do |xml|
 | 
			
		||||
  feed(xml) do
 | 
			
		||||
    simple_id  xml, atom_user_stream_url(id: @account.id)
 | 
			
		||||
    simple_id  xml, account_url(@account, format: 'atom')
 | 
			
		||||
    title      xml, @account.display_name
 | 
			
		||||
    subtitle   xml, @account.note
 | 
			
		||||
    updated_at xml, stream_updated_at
 | 
			
		||||
@@ -10,10 +10,10 @@ Nokogiri::XML::Builder.new do |xml|
 | 
			
		||||
      include_author xml, @account
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    link_alternate xml, profile_url(@account)
 | 
			
		||||
    link_self      xml, atom_user_stream_url(id: @account.id)
 | 
			
		||||
    link_alternate xml, url_for_target(@account)
 | 
			
		||||
    link_self      xml, account_url(@account, format: 'atom')
 | 
			
		||||
    link_hub       xml, HUB_URL
 | 
			
		||||
    link_salmon    xml, salmon_url(@account)
 | 
			
		||||
    link_salmon    xml, api_salmon_url(@account.id)
 | 
			
		||||
 | 
			
		||||
    @account.stream_entries.order('id desc').each do |stream_entry|
 | 
			
		||||
      entry(xml, false) do
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
- 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) }/
 | 
			
		||||
  %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)
 | 
			
		||||
@@ -11,4 +11,4 @@
 | 
			
		||||
 | 
			
		||||
.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 }
 | 
			
		||||
    = render partial: 'stream_entries/status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
- if status.reply?
 | 
			
		||||
  = link_to "In response to #{status.thread.account.acct}", status_url(status.thread), class: 'conversation-link'
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -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 }
 | 
			
		||||
@@ -6,17 +6,23 @@
 | 
			
		||||
    .pre-header
 | 
			
		||||
      %i.fa.fa-retweet
 | 
			
		||||
      Shared by
 | 
			
		||||
      = link_to display_name(status.account), profile_url(status.account), class: 'name'
 | 
			
		||||
      = 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
 | 
			
		||||
        = render partial: 'status_header', locals: { status: status.reblog? ? status.reblog : status }
 | 
			
		||||
        = 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
 | 
			
		||||
      .counters
 | 
			
		||||
        = render partial: 'status_footer', locals: { status: status.reblog? ? status.reblog : status }
 | 
			
		||||
 | 
			
		||||
- if include_threads
 | 
			
		||||
  - status.replies.each do |status|
 | 
			
		||||
							
								
								
									
										5
									
								
								app/views/stream_entries/show.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/views/stream_entries/show.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -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 }
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								spec/controllers/accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								spec/controllers/accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										7
									
								
								spec/controllers/api/salmon_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spec/controllers/api/salmon_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe Api::SalmonController, type: :controller do
 | 
			
		||||
  describe 'POST #update' do
 | 
			
		||||
    pending
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										11
									
								
								spec/controllers/api/subscriptions_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								spec/controllers/api/subscriptions_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
							
								
								
									
										18
									
								
								spec/controllers/stream_entries_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								spec/controllers/stream_entries_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/accounts_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/accounts_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/api/salmon_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/api/salmon_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/api/subscriptions_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/api/subscriptions_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe AtomHelper, type: :helper do
 | 
			
		||||
RSpec.describe AtomBuilderHelper, type: :helper do
 | 
			
		||||
  describe '#stream_updated_at' do
 | 
			
		||||
    pending
 | 
			
		||||
  end
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe ProfileHelper, type: :helper do
 | 
			
		||||
RSpec.describe StreamEntriesHelper, type: :helper do
 | 
			
		||||
  describe '#display_name' do
 | 
			
		||||
    pending
 | 
			
		||||
  end
 | 
			
		||||
		Reference in New Issue
	
	Block a user