@@ -15,3 +15,4 @@ | |||||
/log/* | /log/* | ||||
!/log/.keep | !/log/.keep | ||||
/tmp | /tmp | ||||
coverage |
@@ -0,0 +1,3 @@ | |||||
--color | |||||
--require spec_helper | |||||
--format Fuubar |
@@ -28,11 +28,15 @@ gem 'ostatus2' | |||||
gem 'goldfinger' | gem 'goldfinger' | ||||
group :development, :test do | group :development, :test do | ||||
gem 'byebug' | |||||
gem 'rspec-rails' | gem 'rspec-rails' | ||||
gem 'quiet_assets' | gem 'quiet_assets' | ||||
gem 'nyan-cat-formatter' | |||||
gem 'pry-rails' | gem 'pry-rails' | ||||
gem 'nyan-cat-formatter' | |||||
gem 'fuubar' | |||||
end | |||||
group :test do | |||||
gem 'simplecov', require: false | |||||
end | end | ||||
group :development do | group :development do | ||||
@@ -50,7 +50,6 @@ GEM | |||||
binding_of_caller (0.7.2) | binding_of_caller (0.7.2) | ||||
debug_inspector (>= 0.0.1) | debug_inspector (>= 0.0.1) | ||||
builder (3.2.2) | builder (3.2.2) | ||||
byebug (8.2.2) | |||||
coderay (1.1.1) | coderay (1.1.1) | ||||
coercible (1.0.0) | coercible (1.0.0) | ||||
descendants_tracker (~> 0.0.1) | descendants_tracker (~> 0.0.1) | ||||
@@ -66,6 +65,7 @@ GEM | |||||
descendants_tracker (0.0.4) | descendants_tracker (0.0.4) | ||||
thread_safe (~> 0.3, >= 0.3.1) | thread_safe (~> 0.3, >= 0.3.1) | ||||
diff-lcs (1.2.5) | diff-lcs (1.2.5) | ||||
docile (1.1.5) | |||||
domain_name (0.5.20160128) | domain_name (0.5.20160128) | ||||
unf (>= 0.0.5, < 1.0.0) | unf (>= 0.0.5, < 1.0.0) | ||||
dotenv (2.1.0) | dotenv (2.1.0) | ||||
@@ -75,6 +75,9 @@ GEM | |||||
equalizer (0.0.11) | equalizer (0.0.11) | ||||
erubis (2.7.0) | erubis (2.7.0) | ||||
execjs (2.6.0) | execjs (2.6.0) | ||||
fuubar (2.0.0) | |||||
rspec (~> 3.0) | |||||
ruby-progressbar (~> 1.4) | |||||
globalid (0.3.6) | globalid (0.3.6) | ||||
activesupport (>= 4.1.0) | activesupport (>= 4.1.0) | ||||
goldfinger (1.0.1) | goldfinger (1.0.1) | ||||
@@ -249,6 +252,11 @@ GEM | |||||
json (~> 1.7, >= 1.7.7) | json (~> 1.7, >= 1.7.7) | ||||
rdoc (~> 4.0) | rdoc (~> 4.0) | ||||
sexp_processor (4.7.0) | sexp_processor (4.7.0) | ||||
simplecov (0.11.2) | |||||
docile (~> 1.1.0) | |||||
json (~> 1.8) | |||||
simplecov-html (~> 0.10.0) | |||||
simplecov-html (0.10.0) | |||||
slop (3.6.0) | slop (3.6.0) | ||||
spring (1.6.3) | spring (1.6.3) | ||||
sprockets (3.5.2) | sprockets (3.5.2) | ||||
@@ -292,9 +300,9 @@ DEPENDENCIES | |||||
addressable | addressable | ||||
better_errors | better_errors | ||||
binding_of_caller | binding_of_caller | ||||
byebug | |||||
coffee-rails (~> 4.1.0) | coffee-rails (~> 4.1.0) | ||||
dotenv-rails | dotenv-rails | ||||
fuubar | |||||
goldfinger | goldfinger | ||||
grape | grape | ||||
grape-entity | grape-entity | ||||
@@ -318,6 +326,7 @@ DEPENDENCIES | |||||
rubocop | rubocop | ||||
sass-rails (~> 5.0) | sass-rails (~> 5.0) | ||||
sdoc (~> 0.4.0) | sdoc (~> 0.4.0) | ||||
simplecov | |||||
spring | spring | ||||
sqlite3 | sqlite3 | ||||
therubyracer | therubyracer | ||||
@@ -0,0 +1,7 @@ | |||||
class Mention < ActiveRecord::Base | |||||
belongs_to :account, inverse_of: :mentions | |||||
belongs_to :status, inverse_of: :mentions | |||||
validates :account, :status, presence: true | |||||
validates :account, uniqueness: { scope: :status } | |||||
end |
@@ -9,6 +9,7 @@ class Status < ActiveRecord::Base | |||||
has_many :favourites, inverse_of: :status, dependent: :destroy | has_many :favourites, inverse_of: :status, dependent: :destroy | ||||
has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status' | has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status' | ||||
has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status' | has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status' | ||||
has_many :mentioned_accounts, class_name: 'Mention', dependent: :destroy | |||||
validates :account, presence: true | validates :account, presence: true | ||||
validates :uri, uniqueness: true, unless: 'local?' | validates :uri, uniqueness: true, unless: 'local?' | ||||
@@ -6,30 +6,13 @@ class PostStatusService < BaseService | |||||
# @return [Status] | # @return [Status] | ||||
def call(account, text, in_reply_to = nil) | def call(account, text, in_reply_to = nil) | ||||
status = account.statuses.create!(text: text, thread: in_reply_to) | status = account.statuses.create!(text: text, thread: in_reply_to) | ||||
status.text.scan(Account::MENTION_RE).each do |match| | |||||
next if match.first.split('@').size == 1 | |||||
username, domain = match.first.split('@') | |||||
local_account = Account.find_by(username: username, domain: domain) | |||||
next unless local_account.nil? | |||||
follow_remote_account_service.("acct:#{match.first}") | |||||
end | |||||
status.mentions.each do |mentioned_account| | |||||
next if mentioned_account.local? | |||||
send_interaction_service.(status.stream_entry, mentioned_account) | |||||
end | |||||
process_mentions_service.(status) | |||||
status | status | ||||
end | end | ||||
private | private | ||||
def follow_remote_account_service | |||||
@follow_remote_account_service ||= FollowRemoteAccountService.new | |||||
end | |||||
def send_interaction_service | |||||
@send_interaction_service ||= SendInteractionService.new | |||||
def process_mentions_service | |||||
@process_mentions_service ||= ProcessMentionsService.new | |||||
end | end | ||||
end | end |
@@ -21,6 +21,8 @@ class ProcessFeedService < BaseService | |||||
else | else | ||||
add_post!(entry, status) | add_post!(entry, status) | ||||
end | end | ||||
process_mentions_service.(status) unless status.new_record? | |||||
end | end | ||||
end | end | ||||
@@ -120,4 +122,8 @@ class ProcessFeedService < BaseService | |||||
def follow_remote_account_service | def follow_remote_account_service | ||||
@follow_remote_account_service ||= FollowRemoteAccountService.new | @follow_remote_account_service ||= FollowRemoteAccountService.new | ||||
end | end | ||||
def process_mentions_service | |||||
@process_mentions_service ||= ProcessMentionsService.new | |||||
end | |||||
end | end |
@@ -0,0 +1,33 @@ | |||||
class ProcessMentionsService < BaseService | |||||
# Scan status for mentions and fetch remote mentioned users, create | |||||
# local mention pointers, send Salmon notifications to mentioned | |||||
# remote users | |||||
# @param [Status] status | |||||
def call(status) | |||||
status.text.scan(Account::MENTION_RE).each do |match| | |||||
username, domain = match.first.split('@') | |||||
local_account = Account.find_by(username: username, domain: domain) | |||||
if local_account.nil? | |||||
local_account = follow_remote_account_service.("acct:#{match.first}") | |||||
end | |||||
local_account.mentions.first_or_create(status: status) | |||||
end | |||||
status.mentions.each do |mentioned_account| | |||||
next if mentioned_account.local? | |||||
send_interaction_service.(status.stream_entry, mentioned_account) | |||||
end | |||||
end | |||||
private | |||||
def follow_remote_account_service | |||||
@follow_remote_account_service ||= FollowRemoteAccountService.new | |||||
end | |||||
def send_interaction_service | |||||
@send_interaction_service ||= SendInteractionService.new | |||||
end | |||||
end |
@@ -0,0 +1,12 @@ | |||||
class CreateMentions < ActiveRecord::Migration | |||||
def change | |||||
create_table :mentions do |t| | |||||
t.integer :account_id | |||||
t.integer :status_id | |||||
t.timestamps null: false | |||||
end | |||||
add_index :mentions, [:account_id, :status_id], unique: true | |||||
end | |||||
end |
@@ -11,7 +11,7 @@ | |||||
# | # | ||||
# It's strongly recommended that you check this file into your version control system. | # It's strongly recommended that you check this file into your version control system. | ||||
ActiveRecord::Schema.define(version: 20160223171800) do | |||||
ActiveRecord::Schema.define(version: 20160224223247) do | |||||
# These are extensions that must be enabled in order to support this database | # These are extensions that must be enabled in order to support this database | ||||
enable_extension "plpgsql" | enable_extension "plpgsql" | ||||
@@ -54,6 +54,15 @@ ActiveRecord::Schema.define(version: 20160223171800) do | |||||
add_index "follows", ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree | add_index "follows", ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree | ||||
create_table "mentions", force: :cascade do |t| | |||||
t.integer "account_id" | |||||
t.integer "status_id" | |||||
t.datetime "created_at", null: false | |||||
t.datetime "updated_at", null: false | |||||
end | |||||
add_index "mentions", ["account_id", "status_id"], name: "index_mentions_on_account_id_and_status_id", unique: true, using: :btree | |||||
create_table "statuses", force: :cascade do |t| | create_table "statuses", force: :cascade do |t| | ||||
t.string "uri" | t.string "uri" | ||||
t.integer "account_id", null: false | t.integer "account_id", null: false | ||||
@@ -1,5 +1,11 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe AtomController, type: :controller do | RSpec.describe AtomController, type: :controller do | ||||
describe 'GET #user_stream' do | |||||
pending | |||||
end | |||||
describe 'GET #entry' do | |||||
pending | |||||
end | |||||
end | end |
@@ -1,5 +1,7 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe HomeController, type: :controller do | RSpec.describe HomeController, type: :controller do | ||||
describe 'GET #index' do | |||||
pending | |||||
end | |||||
end | end |
@@ -1,12 +1,11 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe ProfileController, type: :controller do | 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 | |||||
describe 'GET #show' do | |||||
pending | |||||
end | end | ||||
describe 'GET #entry' do | |||||
pending | |||||
end | |||||
end | end |
@@ -1,5 +1,11 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe XrdController, type: :controller do | RSpec.describe XrdController, type: :controller do | ||||
describe 'GET #host_meta' do | |||||
pending | |||||
end | |||||
describe 'GET #webfinger' do | |||||
pending | |||||
end | |||||
end | end |
@@ -0,0 +1,15 @@ | |||||
require 'rails_helper' | |||||
# Specs in this file have access to a helper object that includes | |||||
# the RoutingHelper. For example: | |||||
# | |||||
# describe RoutingHelper 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 RoutingHelper, type: :helper do | |||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end |
@@ -1,5 +1,47 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe Account, type: :model do | RSpec.describe Account, type: :model do | ||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
describe '#follow!' do | |||||
pending | |||||
end | |||||
describe '#unfollow!' do | |||||
pending | |||||
end | |||||
describe '#following?' do | |||||
pending | |||||
end | |||||
describe '#local?' do | |||||
pending | |||||
end | |||||
describe '#acct' do | |||||
pending | |||||
end | |||||
describe '#subscribed?' do | |||||
pending | |||||
end | |||||
describe '#keypair' do | |||||
pending | |||||
end | |||||
describe '#subscription' do | |||||
pending | |||||
end | |||||
describe '#object_type' do | |||||
pending | |||||
end | |||||
describe '#title' do | |||||
pending | |||||
end | |||||
describe '#content' do | |||||
pending | |||||
end | |||||
end | end |
@@ -1,5 +1,31 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe Favourite, type: :model do | RSpec.describe Favourite, type: :model do | ||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
describe '#verb' do | |||||
pending | |||||
end | |||||
describe '#title' do | |||||
pending | |||||
end | |||||
describe '#content' do | |||||
pending | |||||
end | |||||
describe '#object_type' do | |||||
pending | |||||
end | |||||
describe '#target' do | |||||
pending | |||||
end | |||||
describe '#mentions' do | |||||
pending | |||||
end | |||||
describe '#thread' do | |||||
pending | |||||
end | |||||
end | end |
@@ -1,5 +1,27 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe Follow, type: :model do | RSpec.describe Follow, type: :model do | ||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
describe '#verb' do | |||||
pending | |||||
end | |||||
describe '#title' do | |||||
pending | |||||
end | |||||
describe '#content' do | |||||
pending | |||||
end | |||||
describe '#object_type' do | |||||
pending | |||||
end | |||||
describe '#target' do | |||||
pending | |||||
end | |||||
describe '#mentions' do | |||||
pending | |||||
end | |||||
end | end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe Mention, type: :model do | |||||
end |
@@ -1,5 +1,35 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe Status, type: :model do | RSpec.describe Status, type: :model do | ||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
describe '#local?' do | |||||
pending | |||||
end | |||||
describe '#reblog?' do | |||||
pending | |||||
end | |||||
describe '#reply?' do | |||||
pending | |||||
end | |||||
describe '#mentions' do | |||||
pending | |||||
end | |||||
describe '#verb' do | |||||
pending | |||||
end | |||||
describe '#object_type' do | |||||
pending | |||||
end | |||||
describe '#title' do | |||||
pending | |||||
end | |||||
describe '#target' do | |||||
pending | |||||
end | |||||
end | end |
@@ -0,0 +1,11 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe StreamEntry, type: :model do | |||||
describe '#targeted?' do | |||||
pending | |||||
end | |||||
describe '#threaded?' do | |||||
pending | |||||
end | |||||
end |
@@ -1,5 +0,0 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe Stream, type: :model do | |||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end |
@@ -1,5 +1,5 @@ | |||||
require 'rails_helper' | require 'rails_helper' | ||||
RSpec.describe User, type: :model do | RSpec.describe User, type: :model do | ||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end | end |
@@ -0,0 +1,16 @@ | |||||
ENV['RAILS_ENV'] ||= 'test' | |||||
require File.expand_path('../../config/environment', __FILE__) | |||||
abort("The Rails environment is running in production mode!") if Rails.env.production? | |||||
require 'spec_helper' | |||||
require 'rspec/rails' | |||||
ActiveRecord::Migration.maintain_test_schema! | |||||
RSpec.configure do |config| | |||||
config.fixture_path = "#{::Rails.root}/spec/fixtures" | |||||
config.use_transactional_fixtures = true | |||||
config.infer_spec_type_from_file_location! | |||||
config.filter_rails_from_backtrace! | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe FetchFeedService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe FollowRemoteAccountService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe FollowService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe PostStatusService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe ProcessFeedService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe ProcessInteractionService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe ProcessMentionsService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe ReblogService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe SendInteractionService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe SetupLocalAccountService do | |||||
pending | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe UnfollowService do | |||||
pending | |||||
end |
@@ -0,0 +1,15 @@ | |||||
require 'simplecov' | |||||
SimpleCov.start 'rails' do | |||||
add_group "Services", "app/services" | |||||
end | |||||
RSpec.configure do |config| | |||||
config.expect_with :rspec do |expectations| | |||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true | |||||
end | |||||
config.mock_with :rspec do |mocks| | |||||
mocks.verify_partial_doubles = true | |||||
end | |||||
end |
@@ -1,5 +0,0 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe "profile/show.html.haml", type: :view do | |||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end |