Made some progress
This commit is contained in:
		
							
								
								
									
										2
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Gemfile
									
									
									
									
									
								
							@@ -38,6 +38,8 @@ group :development do
 | 
			
		||||
  gem 'web-console', '~> 2.0'
 | 
			
		||||
  gem 'spring'
 | 
			
		||||
  gem 'rubocop', require: false
 | 
			
		||||
  gem 'better_errors'
 | 
			
		||||
  gem 'binding_of_caller'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
group :production do
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,10 @@ GEM
 | 
			
		||||
      descendants_tracker (~> 0.0.4)
 | 
			
		||||
      ice_nine (~> 0.11.0)
 | 
			
		||||
      thread_safe (~> 0.3, >= 0.3.1)
 | 
			
		||||
    better_errors (2.1.1)
 | 
			
		||||
      coderay (>= 1.0.0)
 | 
			
		||||
      erubis (>= 2.6.6)
 | 
			
		||||
      rack (>= 0.9.0)
 | 
			
		||||
    binding_of_caller (0.7.2)
 | 
			
		||||
      debug_inspector (>= 0.0.1)
 | 
			
		||||
    builder (3.2.2)
 | 
			
		||||
@@ -284,6 +288,8 @@ PLATFORMS
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES
 | 
			
		||||
  addressable
 | 
			
		||||
  better_errors
 | 
			
		||||
  binding_of_caller
 | 
			
		||||
  byebug
 | 
			
		||||
  coffee-rails (~> 4.1.0)
 | 
			
		||||
  dotenv-rails
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@ module Mastodon
 | 
			
		||||
    class Account < Grape::Entity
 | 
			
		||||
      expose :username
 | 
			
		||||
      expose :domain
 | 
			
		||||
      expose :display_name
 | 
			
		||||
      expose :note
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    class Status < Grape::Entity
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,10 @@ module Mastodon
 | 
			
		||||
 | 
			
		||||
    resource :subscriptions do
 | 
			
		||||
      helpers do
 | 
			
		||||
        def subscription_url(account)
 | 
			
		||||
          "https://649841dc.ngrok.io/api#{subscriptions_path(id: account.id)}"
 | 
			
		||||
        end
 | 
			
		||||
        include ApplicationHelper
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      desc 'Receive updates from a feed'
 | 
			
		||||
      desc 'Receive updates from an account'
 | 
			
		||||
 | 
			
		||||
      params do
 | 
			
		||||
        requires :id, type: String, desc: 'Account ID'
 | 
			
		||||
@@ -23,14 +21,14 @@ module Mastodon
 | 
			
		||||
        body = request.body.read
 | 
			
		||||
 | 
			
		||||
        if @account.subscription(subscription_url(@account)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
 | 
			
		||||
          ProcessFeedUpdateService.new.(body, @account)
 | 
			
		||||
          ProcessFeedService.new.(body, @account)
 | 
			
		||||
          status 201
 | 
			
		||||
        else
 | 
			
		||||
          status 202
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      desc 'Confirm PuSH subscription to a feed'
 | 
			
		||||
      desc 'Confirm PuSH subscription to an account'
 | 
			
		||||
 | 
			
		||||
      params do
 | 
			
		||||
        requires :id, type: String, desc: 'Account ID'
 | 
			
		||||
@@ -49,14 +47,15 @@ module Mastodon
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    resource :salmon do
 | 
			
		||||
      desc 'Receive Salmon updates'
 | 
			
		||||
      desc 'Receive Salmon updates targeted to account'
 | 
			
		||||
 | 
			
		||||
      params do
 | 
			
		||||
        requires :id, type: String, desc: 'Account ID'
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      post ':id' do
 | 
			
		||||
        # todo
 | 
			
		||||
        ProcessInteractionService.new.(request.body.read, @account)
 | 
			
		||||
        status 201
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,34 @@ module Mastodon
 | 
			
		||||
 | 
			
		||||
    resource :statuses do
 | 
			
		||||
      desc 'Return a public timeline'
 | 
			
		||||
 | 
			
		||||
      get :all do
 | 
			
		||||
        present Status.all, with: Mastodon::Entities::Status
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      desc 'Return the home timeline of a logged in user'
 | 
			
		||||
 | 
			
		||||
      get :home do
 | 
			
		||||
        # todo
 | 
			
		||||
      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
 | 
			
		||||
 
 | 
			
		||||
@@ -12,5 +12,4 @@
 | 
			
		||||
//
 | 
			
		||||
//= require jquery
 | 
			
		||||
//= require jquery_ujs
 | 
			
		||||
//= require turbolinks
 | 
			
		||||
//= require_tree .
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/javascripts/atom.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/atom.coffee
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# 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/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/javascripts/home.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/home.coffee
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# 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/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/javascripts/profile.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/profile.coffee
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# 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/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/javascripts/xrd.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/javascripts/xrd.coffee
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# 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/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/atom.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/atom.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// 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/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/home.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/home.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// Place all the styles related to the Home controller here.
 | 
			
		||||
// They will automatically be included in application.css.
 | 
			
		||||
// You can use Sass (SCSS) here: http://sass-lang.com/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/profile.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/profile.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// Place all the styles related to the Profile controller here.
 | 
			
		||||
// They will automatically be included in application.css.
 | 
			
		||||
// You can use Sass (SCSS) here: http://sass-lang.com/
 | 
			
		||||
							
								
								
									
										3
									
								
								app/assets/stylesheets/xrd.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/assets/stylesheets/xrd.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// 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/
 | 
			
		||||
							
								
								
									
										14
									
								
								app/controllers/atom_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/controllers/atom_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
class AtomController < ApplicationController
 | 
			
		||||
  before_filter :set_format
 | 
			
		||||
 | 
			
		||||
  def user_stream
 | 
			
		||||
    @account = Account.find_by!(id: params[:id], domain: nil)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_format
 | 
			
		||||
    request.format = 'xml'
 | 
			
		||||
    response.headers['Content-Type'] = 'application/atom+xml'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										4
									
								
								app/controllers/home_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/controllers/home_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
class HomeController < ApplicationController
 | 
			
		||||
  def index
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										4
									
								
								app/controllers/profile_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/controllers/profile_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
class ProfileController < ApplicationController
 | 
			
		||||
  def show
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										39
									
								
								app/controllers/xrd_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/controllers/xrd_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
class XrdController < ApplicationController
 | 
			
		||||
  before_filter :set_format
 | 
			
		||||
 | 
			
		||||
  def host_meta
 | 
			
		||||
    @webfinger_template = "#{webfinger_url}?resource={uri}"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def webfinger
 | 
			
		||||
    @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)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_format
 | 
			
		||||
    request.format = 'xml'
 | 
			
		||||
    response.headers['Content-Type'] = 'application/xrd+xml'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def username_from_resource
 | 
			
		||||
    params[:resource].split('@').first.gsub('acct:', '')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pem_to_magic_key(public_key)
 | 
			
		||||
    modulus, exponent = [public_key.n, public_key.e].map do |component|
 | 
			
		||||
      result = ""
 | 
			
		||||
 | 
			
		||||
      until component == 0 do
 | 
			
		||||
        result << [component % 256].pack('C')
 | 
			
		||||
        component >>= 8
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      result.reverse!
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    (["RSA"] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,2 +1,19 @@
 | 
			
		||||
module ApplicationHelper
 | 
			
		||||
  include GrapeRouteHelpers::NamedRouteMatcher
 | 
			
		||||
 | 
			
		||||
  def unique_tag(date, id, type)
 | 
			
		||||
    "tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def subscription_url(account)
 | 
			
		||||
    add_base_url_prefix subscription_path(id: account.id, format: '')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def salmon_url(account)
 | 
			
		||||
    add_base_url_prefix salmon_path(id: account.id, format: '')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def add_base_url_prefix(suffix)
 | 
			
		||||
    "#{root_url}api#{suffix}"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								app/helpers/atom_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/helpers/atom_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
module AtomHelper
 | 
			
		||||
  def stream_updated_at
 | 
			
		||||
    @account.stream_entries.last ? @account.stream_entries.last.created_at.iso8601 : @account.updated_at.iso8601
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										2
									
								
								app/helpers/home_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/home_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
module HomeHelper
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										2
									
								
								app/helpers/profile_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/profile_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
module ProfileHelper
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										2
									
								
								app/helpers/xrd_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/helpers/xrd_helper.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
module XrdHelper
 | 
			
		||||
end
 | 
			
		||||
@@ -1,6 +1,38 @@
 | 
			
		||||
class Account < ActiveRecord::Base
 | 
			
		||||
  # Local users
 | 
			
		||||
  has_one :user, inverse_of: :account
 | 
			
		||||
 | 
			
		||||
  # Timelines
 | 
			
		||||
  has_many :stream_entries, inverse_of: :account
 | 
			
		||||
  has_many :statuses, inverse_of: :account
 | 
			
		||||
 | 
			
		||||
  # Follow relations
 | 
			
		||||
  has_many :active_relationships,  class_name: 'Follow', foreign_key: 'account_id',        dependent: :destroy
 | 
			
		||||
  has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_account_id', dependent: :destroy
 | 
			
		||||
 | 
			
		||||
  has_many :following, through: :active_relationships,  source: :target_account
 | 
			
		||||
  has_many :followers, through: :passive_relationships, source: :account
 | 
			
		||||
 | 
			
		||||
  def follow!(other_account)
 | 
			
		||||
    self.active_relationships.create!(target_account: other_account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unfollow!(other_account)
 | 
			
		||||
    self.active_relationships.find_by(target_account: other_account).destroy
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def following?(other_account)
 | 
			
		||||
    following.include?(other_account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def local?
 | 
			
		||||
    self.domain.nil?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def keypair
 | 
			
		||||
    self.private_key.nil? ? OpenSSL::PKey::RSA.new(self.public_key) : OpenSSL::PKey::RSA.new(self.private_key)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def subscription(webhook_url)
 | 
			
		||||
    @subscription ||= OStatus2::Subscription.new(self.remote_url, secret: self.secret, token: self.verify_token, webhook: webhook_url, hub: self.hub_url)
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								app/models/follow.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/models/follow.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
class Follow < ActiveRecord::Base
 | 
			
		||||
  belongs_to :account
 | 
			
		||||
  belongs_to :target_account, class_name: 'Account'
 | 
			
		||||
 | 
			
		||||
  after_create do
 | 
			
		||||
    self.account.stream_entries.create!(activity: self)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,3 +1,7 @@
 | 
			
		||||
class Status < ActiveRecord::Base
 | 
			
		||||
  belongs_to :account, inverse_of: :statuses
 | 
			
		||||
 | 
			
		||||
  after_create do
 | 
			
		||||
    self.account.stream_entries.create!(activity: self)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								app/models/stream_entry.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/models/stream_entry.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
class StreamEntry < ActiveRecord::Base
 | 
			
		||||
  belongs_to :account, inverse_of: :stream_entries
 | 
			
		||||
  belongs_to :activity, polymorphic: true
 | 
			
		||||
 | 
			
		||||
  def object_type
 | 
			
		||||
    case self.activity_type
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      :note
 | 
			
		||||
    when 'Follow'
 | 
			
		||||
      :person
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def verb
 | 
			
		||||
    case self.activity_type
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      :post
 | 
			
		||||
    when 'Follow'
 | 
			
		||||
      :follow
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def target
 | 
			
		||||
    case self.activity_type
 | 
			
		||||
    when 'Follow'
 | 
			
		||||
      self.activity.target_account
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def content
 | 
			
		||||
    self.activity.text if self.activity_type == 'Status'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										3
									
								
								app/models/user.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/models/user.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
class User < ActiveRecord::Base
 | 
			
		||||
  belongs_to :account, inverse_of: :user
 | 
			
		||||
end
 | 
			
		||||
@@ -1,5 +1,15 @@
 | 
			
		||||
class FetchFeedService
 | 
			
		||||
  def call(account)
 | 
			
		||||
    # todo
 | 
			
		||||
    process_service.(http_client.get(account.remote_url), account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def process_service
 | 
			
		||||
    ProcessFeedService.new
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def http_client
 | 
			
		||||
    HTTP
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
class FollowRemoteUserService
 | 
			
		||||
  include GrapeRouteHelpers::NamedRouteMatcher
 | 
			
		||||
class FollowRemoteAccountService
 | 
			
		||||
  include ApplicationHelper
 | 
			
		||||
 | 
			
		||||
  def call(user)
 | 
			
		||||
    username, domain = user.split('@')
 | 
			
		||||
  def call(uri)
 | 
			
		||||
    username, domain = uri.split('@')
 | 
			
		||||
    account = Account.where(username: username, domain: domain).first
 | 
			
		||||
 | 
			
		||||
    return account unless account.nil?
 | 
			
		||||
 | 
			
		||||
    account = Account.new(username: username, domain: domain)
 | 
			
		||||
    data    = Goldfinger.finger("acct:#{user}")
 | 
			
		||||
    data    = Goldfinger.finger("acct:#{uri}")
 | 
			
		||||
 | 
			
		||||
    account.remote_url  = data.link('http://schemas.google.com/g/2010#updates-from').href
 | 
			
		||||
    account.salmon_url  = data.link('salmon').href
 | 
			
		||||
@@ -21,8 +21,9 @@ class FollowRemoteUserService
 | 
			
		||||
    feed = get_feed(account.remote_url)
 | 
			
		||||
    hubs = feed.xpath('//xmlns:link[@rel="hub"]')
 | 
			
		||||
 | 
			
		||||
    return false if hubs.empty? || hubs.first.attribute('href').nil?
 | 
			
		||||
    return false if hubs.empty? || hubs.first.attribute('href').nil? || feed.at_xpath('/xmlns:author/xmlns:uri').nil?
 | 
			
		||||
 | 
			
		||||
    account.uri     = feed.at_xpath('/xmlns:author/xmlns:uri').content
 | 
			
		||||
    account.hub_url = hubs.first.attribute('href').value
 | 
			
		||||
    account.save!
 | 
			
		||||
 | 
			
		||||
@@ -45,7 +46,7 @@ class FollowRemoteUserService
 | 
			
		||||
 | 
			
		||||
    key   = OpenSSL::PKey::RSA.new
 | 
			
		||||
    key.n = modulus
 | 
			
		||||
    key.d = exponent
 | 
			
		||||
    key.e = exponent
 | 
			
		||||
 | 
			
		||||
    key.to_pem
 | 
			
		||||
  end
 | 
			
		||||
@@ -53,8 +54,4 @@ class FollowRemoteUserService
 | 
			
		||||
  def http_client
 | 
			
		||||
    HTTP
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def subscription_url(account)
 | 
			
		||||
    "https://649841dc.ngrok.io/api#{subscriptions_path(id: account.id)}"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										12
									
								
								app/services/follow_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/services/follow_service.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
class FollowService
 | 
			
		||||
  def call(source_account, uri)
 | 
			
		||||
    target_account = follow_remote_account_service.(uri)
 | 
			
		||||
    source_account.follow!(target_account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def follow_remote_account_service
 | 
			
		||||
    FollowRemoteAccountService.new
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
class ProcessFeedUpdateService
 | 
			
		||||
class ProcessFeedService
 | 
			
		||||
  def call(body, account)
 | 
			
		||||
    xml = Nokogiri::XML(body)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								app/services/process_interaction_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/services/process_interaction_service.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
class ProcessInteractionService
 | 
			
		||||
  def call(envelope, target_account)
 | 
			
		||||
    body = salmon.unpack(envelope)
 | 
			
		||||
    xml  = Nokogiri::XML(body)
 | 
			
		||||
 | 
			
		||||
    return if xml.at_xpath('//author/name').nil? || xml.at_xpath('//author/uri').nil?
 | 
			
		||||
 | 
			
		||||
    username = xml.at_xpath('//author/name').content
 | 
			
		||||
    url      = xml.at_xpath('//author/uri').content
 | 
			
		||||
    domain   = Addressable::URI.parse(url).host
 | 
			
		||||
    account  = Account.find_by(username: username, domain: domain)
 | 
			
		||||
 | 
			
		||||
    if account.nil?
 | 
			
		||||
      account = follow_remote_account_service.("acct:#{username}@#{domain}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if salmon.verify(envelope, account.keypair)
 | 
			
		||||
      verb = xml.at_path('//activity:verb').content
 | 
			
		||||
 | 
			
		||||
      case verb
 | 
			
		||||
      when 'http://activitystrea.ms/schema/1.0/follow', 'follow'
 | 
			
		||||
        account.follow!(target_account)
 | 
			
		||||
      when 'http://activitystrea.ms/schema/1.0/unfollow', 'unfollow'
 | 
			
		||||
        account.unfollow!(target_account)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def salmon
 | 
			
		||||
    OStatus2::Salmon.new
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def follow_remote_account_service
 | 
			
		||||
    FollowRemoteAccountService.new
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										14
									
								
								app/services/setup_local_account_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/services/setup_local_account_service.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
class SetupLocalAccountService
 | 
			
		||||
  def call(user, username)
 | 
			
		||||
    user.build_account
 | 
			
		||||
 | 
			
		||||
    user.account.username = username
 | 
			
		||||
    user.account.domain   = nil
 | 
			
		||||
 | 
			
		||||
    keypair = OpenSSL::PKey::RSA.new(2048)
 | 
			
		||||
    user.account.private_key = keypair.to_pem
 | 
			
		||||
    user.account.public_key  = keypair.public_key.to_pem
 | 
			
		||||
 | 
			
		||||
    user.save!
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										35
									
								
								app/views/atom/user_stream.xml.ruby
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/views/atom/user_stream.xml.ruby
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
Nokogiri::XML::Builder.new do |xml|
 | 
			
		||||
  xml.feed(xmlns: 'http://www.w3.org/2005/Atom', 'xmlns:thr': 'http://purl.org/syndication/thread/1.0', 'xmlns:activity': 'http://activitystrea.ms/spec/1.0/') do
 | 
			
		||||
    xml.id_ atom_user_stream_url(id: @account.id)
 | 
			
		||||
    xml.title @account.display_name
 | 
			
		||||
    xml.subtitle @account.note
 | 
			
		||||
    xml.updated stream_updated_at
 | 
			
		||||
 | 
			
		||||
    xml.author do
 | 
			
		||||
      xml['activity'].send('object-type', 'http://activitystrea.ms/schema/1.0/person')
 | 
			
		||||
      xml.uri profile_url(name: @account.username)
 | 
			
		||||
      xml.name @account.username
 | 
			
		||||
      xml.summary @account.note
 | 
			
		||||
 | 
			
		||||
      xml.link(rel: 'alternate', type: 'text/html', href: profile_url(name: @account.username))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    xml.link(rel: 'alternate', type: 'text/html', href: profile_url(name: @account.username))
 | 
			
		||||
    xml.link(rel: 'hub', href: '')
 | 
			
		||||
    xml.link(rel: 'salmon', href: salmon_url(@account))
 | 
			
		||||
    xml.link(rel: 'self', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
 | 
			
		||||
 | 
			
		||||
    @account.stream_entries.each do |stream_entry|
 | 
			
		||||
      xml.entry do
 | 
			
		||||
        xml.id_ unique_tag(stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type)
 | 
			
		||||
        xml.published stream_entry.activity.created_at.iso8601
 | 
			
		||||
        xml.updated   stream_entry.activity.updated_at.iso8601
 | 
			
		||||
        xml.content({ type: 'html' }, stream_entry.content)
 | 
			
		||||
        xml.title
 | 
			
		||||
 | 
			
		||||
        xml['activity'].send('verb', "http://activitystrea.ms/schema/1.0/#{stream_entry.verb}")
 | 
			
		||||
        xml['activity'].send('object-type', "http://activitystrea.ms/schema/1.0/#{stream_entry.object_type}")
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end.to_xml
 | 
			
		||||
							
								
								
									
										1
									
								
								app/views/home/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/views/home/index.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
Mastodon
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
  <title>Mastodon</title>
 | 
			
		||||
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
 | 
			
		||||
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
 | 
			
		||||
  <%= csrf_meta_tags %>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
<%= yield %>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										10
									
								
								app/views/layouts/application.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/views/layouts/application.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
!!!
 | 
			
		||||
%html
 | 
			
		||||
  %head
 | 
			
		||||
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
 | 
			
		||||
    %title Mastodon
 | 
			
		||||
    = stylesheet_link_tag    'application', media: 'all'
 | 
			
		||||
    = javascript_include_tag 'application'
 | 
			
		||||
    = csrf_meta_tags
 | 
			
		||||
  %body
 | 
			
		||||
    = yield
 | 
			
		||||
							
								
								
									
										2
									
								
								app/views/profile/show.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								app/views/profile/show.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
%h1 Profile#show
 | 
			
		||||
%p Find me in app/views/profile/show.html.haml
 | 
			
		||||
							
								
								
									
										5
									
								
								app/views/xrd/host_meta.xml.ruby
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/views/xrd/host_meta.xml.ruby
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
Nokogiri::XML::Builder.new do |xml|
 | 
			
		||||
  xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
 | 
			
		||||
    xml.Link(rel: 'lrdd', type: 'application/xrd+xml', template: @webfinger_template)
 | 
			
		||||
  end
 | 
			
		||||
end.to_xml
 | 
			
		||||
							
								
								
									
										8
									
								
								app/views/xrd/webfinger.xml.ruby
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/views/xrd/webfinger.xml.ruby
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
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.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: 'magic-public-key', href: @magic_key)
 | 
			
		||||
  end
 | 
			
		||||
end.to_xml
 | 
			
		||||
@@ -38,4 +38,6 @@ Rails.application.configure do
 | 
			
		||||
 | 
			
		||||
  # Raises error for missing translations
 | 
			
		||||
  # config.action_view.raise_on_missing_translations = true
 | 
			
		||||
 | 
			
		||||
  config.action_mailer.default_url_options = { host: ENV['NGROK_HOST'] }
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								config/initializers/ostatus.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								config/initializers/ostatus.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
LOCAL_DOMAIN = ENV['LOCAL_DOMAIN'] || 'localhost'
 | 
			
		||||
@@ -1,3 +1,11 @@
 | 
			
		||||
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/:id',   to: 'atom#user_stream', as: :atom_user_stream
 | 
			
		||||
  get 'user/:name', to: 'profile#show', as: :profile
 | 
			
		||||
 | 
			
		||||
  mount Mastodon::API => '/api/'
 | 
			
		||||
 | 
			
		||||
  root 'home#index'
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								db/migrate/20160221003140_create_users.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								db/migrate/20160221003140_create_users.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
class CreateUsers < ActiveRecord::Migration
 | 
			
		||||
  def change
 | 
			
		||||
    create_table :users do |t|
 | 
			
		||||
      t.string :email, null: false, default: ''
 | 
			
		||||
      t.integer :account_id, null: false
 | 
			
		||||
 | 
			
		||||
      t.timestamps null: false
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    add_index :users, :email, unique: true
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										12
									
								
								db/migrate/20160221003621_create_follows.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								db/migrate/20160221003621_create_follows.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
class CreateFollows < ActiveRecord::Migration
 | 
			
		||||
  def change
 | 
			
		||||
    create_table :follows do |t|
 | 
			
		||||
      t.integer :account_id, null: false
 | 
			
		||||
      t.integer :target_account_id, null: false
 | 
			
		||||
 | 
			
		||||
      t.timestamps null: false
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    add_index :follows, [:account_id, :target_account_id], unique: true
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										11
									
								
								db/migrate/20160222122600_create_stream_entries.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								db/migrate/20160222122600_create_stream_entries.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
class CreateStreamEntries < ActiveRecord::Migration
 | 
			
		||||
  def change
 | 
			
		||||
    create_table :stream_entries do |t|
 | 
			
		||||
      t.integer :account_id
 | 
			
		||||
      t.integer :activity_id
 | 
			
		||||
      t.string :activity_type
 | 
			
		||||
 | 
			
		||||
      t.timestamps null: false
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
class AddProfileFieldsToAccounts < ActiveRecord::Migration
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :accounts, :note, :text, null: false, default: ''
 | 
			
		||||
    add_column :accounts, :display_name, :string, null: false, default: ''
 | 
			
		||||
    add_column :accounts, :uri, :string, null: false, default: ''
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										31
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								db/schema.rb
									
									
									
									
									
								
							@@ -11,7 +11,7 @@
 | 
			
		||||
#
 | 
			
		||||
# It's strongly recommended that you check this file into your version control system.
 | 
			
		||||
 | 
			
		||||
ActiveRecord::Schema.define(version: 20160220211917) do
 | 
			
		||||
ActiveRecord::Schema.define(version: 20160222143943) do
 | 
			
		||||
 | 
			
		||||
  # These are extensions that must be enabled in order to support this database
 | 
			
		||||
  enable_extension "plpgsql"
 | 
			
		||||
@@ -28,10 +28,22 @@ ActiveRecord::Schema.define(version: 20160220211917) do
 | 
			
		||||
    t.string   "hub_url",      default: "", null: false
 | 
			
		||||
    t.datetime "created_at",                null: false
 | 
			
		||||
    t.datetime "updated_at",                null: false
 | 
			
		||||
    t.text     "note",         default: "", null: false
 | 
			
		||||
    t.string   "display_name", default: "", null: false
 | 
			
		||||
    t.string   "uri",          default: "", null: false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  add_index "accounts", ["username", "domain"], name: "index_accounts_on_username_and_domain", unique: true, using: :btree
 | 
			
		||||
 | 
			
		||||
  create_table "follows", force: :cascade do |t|
 | 
			
		||||
    t.integer  "account_id",        null: false
 | 
			
		||||
    t.integer  "target_account_id", null: false
 | 
			
		||||
    t.datetime "created_at",        null: false
 | 
			
		||||
    t.datetime "updated_at",        null: false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  add_index "follows", ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree
 | 
			
		||||
 | 
			
		||||
  create_table "statuses", force: :cascade do |t|
 | 
			
		||||
    t.string   "uri",        default: "", null: false
 | 
			
		||||
    t.integer  "account_id",              null: false
 | 
			
		||||
@@ -42,4 +54,21 @@ ActiveRecord::Schema.define(version: 20160220211917) do
 | 
			
		||||
 | 
			
		||||
  add_index "statuses", ["uri"], name: "index_statuses_on_uri", unique: true, using: :btree
 | 
			
		||||
 | 
			
		||||
  create_table "stream_entries", force: :cascade do |t|
 | 
			
		||||
    t.integer  "account_id"
 | 
			
		||||
    t.integer  "activity_id"
 | 
			
		||||
    t.string   "activity_type"
 | 
			
		||||
    t.datetime "created_at",    null: false
 | 
			
		||||
    t.datetime "updated_at",    null: false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  create_table "users", force: :cascade do |t|
 | 
			
		||||
    t.string   "email",      default: "", null: false
 | 
			
		||||
    t.integer  "account_id",              null: false
 | 
			
		||||
    t.datetime "created_at",              null: false
 | 
			
		||||
    t.datetime "updated_at",              null: false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/controllers/atom_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/controllers/atom_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe AtomController, type: :controller do
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/controllers/home_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/controllers/home_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe HomeController, type: :controller do
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										12
									
								
								spec/controllers/profile_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								spec/controllers/profile_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe ProfileController, type: :controller do
 | 
			
		||||
 | 
			
		||||
  describe "GET #show" do
 | 
			
		||||
    it "returns http success" do
 | 
			
		||||
      get :show
 | 
			
		||||
      expect(response).to have_http_status(:success)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/controllers/xrd_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/controllers/xrd_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe XrdController, type: :controller do
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/atom_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/atom_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 AtomHelper. For example:
 | 
			
		||||
#
 | 
			
		||||
# describe AtomHelper 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 AtomHelper, type: :helper do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/home_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/home_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 HomeHelper. For example:
 | 
			
		||||
#
 | 
			
		||||
# describe HomeHelper 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 HomeHelper, type: :helper do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/profile_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/profile_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 ProfileHelper. For example:
 | 
			
		||||
#
 | 
			
		||||
# describe ProfileHelper 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 ProfileHelper, type: :helper do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										15
									
								
								spec/helpers/xrd_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/helpers/xrd_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 XrdHelper. For example:
 | 
			
		||||
#
 | 
			
		||||
# describe XrdHelper 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 XrdHelper, type: :helper do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/models/follow_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/follow_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe Follow, type: :model do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/models/stream_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/stream_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe Stream, type: :model do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/models/user_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/user_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe User, type: :model do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								spec/views/profile/show.html.haml_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/views/profile/show.html.haml_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe "profile/show.html.haml", type: :view do
 | 
			
		||||
  pending "add some examples to (or delete) #{__FILE__}"
 | 
			
		||||
end
 | 
			
		||||
		Reference in New Issue
	
	Block a user