rs-rails-base 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +75 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/LICENSE.md +9 -0
- data/README.md +61 -0
- data/Rakefile +7 -0
- data/bin/bundle +3 -0
- data/bin/rails +7 -0
- data/bin/rake +16 -0
- data/bin/rs_rails_base +37 -0
- data/bin/setup +8 -0
- data/circle.yml +12 -0
- data/config.reek +109 -0
- data/docs/chat.md +14 -0
- data/docs/facebook.md +10 -0
- data/docs/twilio.md +21 -0
- data/images/chat_flow.png +0 -0
- data/images/chat_struct.png +0 -0
- data/lib/rs-rails-base/cli_actions.rb +16 -0
- data/lib/rs-rails-base/commands.rb +81 -0
- data/lib/rs-rails-base/constants.rb +7 -0
- data/lib/rs-rails-base/features.rb +16 -0
- data/lib/rs-rails-base/features/chat.rb +91 -0
- data/lib/rs-rails-base/features/facebook.rb +41 -0
- data/lib/rs-rails-base/features/twilio.rb +18 -0
- data/lib/rs-rails-base/file_manipulation.rb +55 -0
- data/lib/rs-rails-base/git_actions.rb +13 -0
- data/lib/rs-rails-base/rails_actions.rb +27 -0
- data/lib/rs_rails_base.rb +10 -0
- data/lib/templates/chat/channel.rb +4 -0
- data/lib/templates/chat/chat_channel.rb +15 -0
- data/lib/templates/chat/chat_service.rb +28 -0
- data/lib/templates/chat/chats_controller.rb +56 -0
- data/lib/templates/chat/connection.rb +24 -0
- data/lib/templates/chat/jbuilder/_info.json.jbuilder +2 -0
- data/lib/templates/chat/jbuilder/_message.json.jbuilder +2 -0
- data/lib/templates/chat/jbuilder/index.json.jbuilder +3 -0
- data/lib/templates/chat/jbuilder/messages_create.json.jbuilder +3 -0
- data/lib/templates/chat/jbuilder/messages_create_info.json.jbuilder +7 -0
- data/lib/templates/chat/jbuilder/show.json.jbuilder +3 -0
- data/lib/templates/chat/messages_controller.rb +23 -0
- data/lib/templates/chat/migration-models/add_chat.rb +9 -0
- data/lib/templates/chat/migration-models/add_message.rb +15 -0
- data/lib/templates/chat/migration-models/add_participant.rb +12 -0
- data/lib/templates/chat/migration-models/chat.rb +30 -0
- data/lib/templates/chat/migration-models/message.rb +43 -0
- data/lib/templates/chat/migration-models/participant.rb +28 -0
- data/lib/templates/chat/migration-models/user.rb +3 -0
- data/lib/templates/chat/routes.rb +6 -0
- data/lib/templates/chat/routes_2.rb +1 -0
- data/lib/templates/chat/sessions_controller.rb +7 -0
- data/lib/templates/chat/specs/chat_channel_spec.rb +25 -0
- data/lib/templates/chat/specs/chat_factory.rb +20 -0
- data/lib/templates/chat/specs/chat_service_spec.rb +19 -0
- data/lib/templates/chat/specs/chat_spec.rb +27 -0
- data/lib/templates/chat/specs/connection_spec.rb +10 -0
- data/lib/templates/chat/specs/message_factory.rb +7 -0
- data/lib/templates/chat/specs/message_spec.rb +40 -0
- data/lib/templates/chat/specs/participant_factory.rb +7 -0
- data/lib/templates/chat/specs/participant_spec.rb +21 -0
- data/lib/templates/chat/specs/rails_helper.rb +1 -0
- data/lib/templates/facebook/apiary.apib +28 -0
- data/lib/templates/facebook/en.yml +4 -0
- data/lib/templates/facebook/facebook_service.rb +13 -0
- data/lib/templates/facebook/facebook_spec.rb +146 -0
- data/lib/templates/facebook/factories_user.rb +6 -0
- data/lib/templates/facebook/routes.rb +3 -0
- data/lib/templates/facebook/sessions_controller.rb +9 -0
- data/lib/templates/facebook/sessions_controller_2.rb +7 -0
- data/lib/templates/facebook/sessions_routing_spec.rb +4 -0
- data/lib/templates/facebook/user_spec.rb +5 -0
- data/lib/templates/twilio/twilio.rb +4 -0
- data/lib/templates/twilio/twilio_service.rb +21 -0
- data/rs-rails-base.gemspec +29 -0
- data/tasks/code_analysis.rake +4 -0
- metadata +206 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
class ChatChannel < ApplicationCable::Channel
|
2
|
+
def subscribed
|
3
|
+
chat = Chat.find(params[:chat_id])
|
4
|
+
stream_for(chat)
|
5
|
+
end
|
6
|
+
|
7
|
+
def unsubscribed; end
|
8
|
+
|
9
|
+
def send_message(data)
|
10
|
+
content = data['message']
|
11
|
+
chat_id = data['chat_id']
|
12
|
+
chat = Chat.find(chat_id)
|
13
|
+
ChatService.create_message(chat, content, current_user)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ChatService
|
2
|
+
extend self
|
3
|
+
def create_message(chat, content, user)
|
4
|
+
message = user.messages.create!(chat: chat, content: content)
|
5
|
+
chat.messages << message
|
6
|
+
chat.save!
|
7
|
+
broadcast_message(chat, message)
|
8
|
+
message
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def broadcast_message(chat, message)
|
14
|
+
message_data = form_message_data(message)
|
15
|
+
ChatChannel.broadcast_to(chat, message_data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def form_message_data(message)
|
19
|
+
user = message.user
|
20
|
+
{
|
21
|
+
action: 'new_message',
|
22
|
+
content: message.content,
|
23
|
+
user: user.username,
|
24
|
+
user_id: user.id,
|
25
|
+
date: Time.zone.now.iso8601
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Api
|
2
|
+
module V1
|
3
|
+
class ChatsController < Api::V1::ApiController
|
4
|
+
def index
|
5
|
+
@chats = Chat.joins(:participants).where('participants.user' => current_user)
|
6
|
+
end
|
7
|
+
|
8
|
+
def show
|
9
|
+
chat = Chat.find(params[:id])
|
10
|
+
if !chat || chat.messages.empty?
|
11
|
+
return render json: { messages: {} }, status: :ok
|
12
|
+
end
|
13
|
+
chat.mark_user_seen(current_user)
|
14
|
+
get_messages(chat, params[:page])
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
chat = Chat.new(name: chat_name)
|
19
|
+
chat.users << params[:users].each { |user| User.find(user) }
|
20
|
+
chat.save!
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit
|
24
|
+
chat = Chat.find(params[:chat_id])
|
25
|
+
participant = Participant.find_by(user: current_user, chat: chat)
|
26
|
+
participant.touch(:last_connection)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def get_messages(chat, page)
|
32
|
+
@messages = chat.messages.order_by_date.page(page)
|
33
|
+
@messages = @messages.sort_by(&:created_at)
|
34
|
+
end
|
35
|
+
|
36
|
+
def chat_name
|
37
|
+
Chat.find_or_create_by_name(infer_chat_type)
|
38
|
+
end
|
39
|
+
|
40
|
+
def infer_chat_type
|
41
|
+
room = params[:room]
|
42
|
+
room.present ? room : personal_chat
|
43
|
+
end
|
44
|
+
|
45
|
+
def personal_chat
|
46
|
+
username = params[:user]
|
47
|
+
current_user_name = current_user.username
|
48
|
+
if (current_user_name <=> username).negative?
|
49
|
+
"#{current_user_name}_#{username}"
|
50
|
+
else
|
51
|
+
"#{username}_#{current_user_name}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ApplicationCable
|
2
|
+
class Connection < ActionCable::Connection::Base
|
3
|
+
identified_by :current_user
|
4
|
+
|
5
|
+
def connect
|
6
|
+
self.current_user = find_verified_user
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def find_verified_user
|
12
|
+
if (current_user = find_user)
|
13
|
+
current_user
|
14
|
+
else
|
15
|
+
reject_unauthorized_connection
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_user
|
20
|
+
user_id = request.headers['HTTP_AUTHORIZATION'] || request.query_parameters['user'].to_i
|
21
|
+
User.find_by(id: user_id)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Api
|
2
|
+
module V1
|
3
|
+
class MessagesController < Api::V1::ApiController
|
4
|
+
include Concerns::ActAsApiRequest
|
5
|
+
protect_from_forgery with: :null_session
|
6
|
+
before_action :chat, only: :create
|
7
|
+
|
8
|
+
def create
|
9
|
+
@message = ChatService.create_message(chat, message_params[:content], current_user)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def message_params
|
15
|
+
params.require(:message).permit(:content)
|
16
|
+
end
|
17
|
+
|
18
|
+
def chat
|
19
|
+
@chat ||= current_user.chats.find(params[:chat_id])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class AddMessage < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :messages do |t|
|
4
|
+
t.belongs_to :user
|
5
|
+
t.belongs_to :chat
|
6
|
+
t.string :content
|
7
|
+
t.string :image
|
8
|
+
t.integer :image_orientation
|
9
|
+
t.string :message_type
|
10
|
+
t.boolean :sent
|
11
|
+
t.boolean :read
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddParticipant < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :participants do |t|
|
4
|
+
t.belongs_to :user
|
5
|
+
t.belongs_to :chat
|
6
|
+
t.datetime :last_connection
|
7
|
+
t.string :last_message_user
|
8
|
+
t.boolean :seen_data
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: chats
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# name :string
|
7
|
+
# last_message :string
|
8
|
+
# created_at :datetime not null
|
9
|
+
# updated_at :datetime not null
|
10
|
+
#
|
11
|
+
|
12
|
+
class Chat < ApplicationRecord
|
13
|
+
has_many :participants
|
14
|
+
has_many :messages
|
15
|
+
has_many :users, through: :participants
|
16
|
+
has_many :authors_messages, through: :messages, source: :users
|
17
|
+
|
18
|
+
validates :name, presence: true
|
19
|
+
validate :users?
|
20
|
+
|
21
|
+
def mark_user_seen(user)
|
22
|
+
participants.find_by(user: user).update_column(:seen_data, true)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def users?
|
28
|
+
errors.add(:base, 'A chat must have at least two users') if users.length < 2
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: messages
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# user_id :integer
|
7
|
+
# chat_id :integer
|
8
|
+
# content :string
|
9
|
+
# image :string
|
10
|
+
# image_orientation :integer
|
11
|
+
# message_type :string
|
12
|
+
# sent :boolean
|
13
|
+
# read :boolean
|
14
|
+
# created_at :datetime not null
|
15
|
+
# updated_at :datetime not null
|
16
|
+
#
|
17
|
+
# Indexes
|
18
|
+
#
|
19
|
+
# index_messages_on_chat_id (chat_id)
|
20
|
+
# index_messages_on_user_id (user_id)
|
21
|
+
#
|
22
|
+
|
23
|
+
class Message < ApplicationRecord
|
24
|
+
belongs_to :chat
|
25
|
+
belongs_to :user
|
26
|
+
|
27
|
+
validates :content, presence: true
|
28
|
+
validate :user_in_chat
|
29
|
+
|
30
|
+
scope :order_by_date, -> { order(created_at: :desc) }
|
31
|
+
|
32
|
+
after_save :update_chat_generic_data
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def update_chat_generic_data
|
37
|
+
chat.update_column(:last_message, content)
|
38
|
+
end
|
39
|
+
|
40
|
+
def user_in_chat
|
41
|
+
chat.users.includes(user)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: participants
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# user_id :integer
|
7
|
+
# chat_id :integer
|
8
|
+
# last_connection :datetime
|
9
|
+
# last_message_user :string
|
10
|
+
# seen_data :boolean
|
11
|
+
# created_at :datetime not null
|
12
|
+
# updated_at :datetime not null
|
13
|
+
#
|
14
|
+
# Indexes
|
15
|
+
#
|
16
|
+
# index_participants_on_chat_id (chat_id)
|
17
|
+
# index_participants_on_user_id (user_id)
|
18
|
+
#
|
19
|
+
|
20
|
+
class Participant < ApplicationRecord
|
21
|
+
belongs_to :chat
|
22
|
+
belongs_to :user
|
23
|
+
|
24
|
+
validates :user, uniqueness: { scope: :chat }
|
25
|
+
|
26
|
+
# Please add conversation statistics or useful data here,
|
27
|
+
# instead of fetching all the messages and re process eveytime
|
28
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
mount ActionCable.server => '/cable'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe ChatChannel, type: :channel do
|
4
|
+
let!(:user) { create(:user) }
|
5
|
+
|
6
|
+
describe '.send_message' do
|
7
|
+
let!(:chat) do
|
8
|
+
create(:chat, :with_messages, user_ids: [user.id])
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'successfully subscribes' do
|
12
|
+
subscribe(chat_id: chat.id)
|
13
|
+
expect(subscription).to be_confirmed
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'on a private chat' do
|
17
|
+
it 'broadcasts to both users' do
|
18
|
+
expect { ChatService.create_message(chat, 'some content', user) }
|
19
|
+
.to have_broadcasted_to(chat)
|
20
|
+
.from_channel(ChatChannel)
|
21
|
+
.with(a_hash_including(content: 'some content'))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :chat do
|
3
|
+
name { Faker::Name.unique.name }
|
4
|
+
|
5
|
+
after(:build) do |chat|
|
6
|
+
chat.users << create(:user)
|
7
|
+
chat.users << create(:user)
|
8
|
+
end
|
9
|
+
|
10
|
+
trait :with_messages do
|
11
|
+
transient do
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:create) do |chat, _evaluator|
|
15
|
+
chat.messages.create!(user_id: chat.users.first,
|
16
|
+
content: Faker::ChuckNorris.fact)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe ChatService do
|
4
|
+
let!(:user) { create(:user) }
|
5
|
+
let!(:chat) { create(:chat) }
|
6
|
+
let!(:participant) { create(:participant, user: user, chat: chat) }
|
7
|
+
|
8
|
+
describe '.create_message' do
|
9
|
+
it 'creates a message' do
|
10
|
+
expect do
|
11
|
+
ChatService.create_message(chat, 'hello', user)
|
12
|
+
end.to change(Message, :count).by(1)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns a message' do
|
16
|
+
expect(ChatService.create_message(chat, 'hello', user)).to be_a(Message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe Chat do
|
4
|
+
describe 'chat validations' do
|
5
|
+
it 'have a name' do
|
6
|
+
subject build :chat
|
7
|
+
it { should validate_presence_of(:name) }
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'have no user and fail' do
|
11
|
+
subject build :chat
|
12
|
+
subject.users = nil
|
13
|
+
it { should_not be_valid }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'have 1 user and fail' do
|
17
|
+
subject build :chat
|
18
|
+
subject.users.pop
|
19
|
+
it { should_not be_valid }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'have 2 user and be created' do
|
23
|
+
subject build :chat
|
24
|
+
it { should be_valid }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|