rs-rails-base 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +75 -0
  5. data/.travis.yml +5 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE.md +9 -0
  9. data/README.md +61 -0
  10. data/Rakefile +7 -0
  11. data/bin/bundle +3 -0
  12. data/bin/rails +7 -0
  13. data/bin/rake +16 -0
  14. data/bin/rs_rails_base +37 -0
  15. data/bin/setup +8 -0
  16. data/circle.yml +12 -0
  17. data/config.reek +109 -0
  18. data/docs/chat.md +14 -0
  19. data/docs/facebook.md +10 -0
  20. data/docs/twilio.md +21 -0
  21. data/images/chat_flow.png +0 -0
  22. data/images/chat_struct.png +0 -0
  23. data/lib/rs-rails-base/cli_actions.rb +16 -0
  24. data/lib/rs-rails-base/commands.rb +81 -0
  25. data/lib/rs-rails-base/constants.rb +7 -0
  26. data/lib/rs-rails-base/features.rb +16 -0
  27. data/lib/rs-rails-base/features/chat.rb +91 -0
  28. data/lib/rs-rails-base/features/facebook.rb +41 -0
  29. data/lib/rs-rails-base/features/twilio.rb +18 -0
  30. data/lib/rs-rails-base/file_manipulation.rb +55 -0
  31. data/lib/rs-rails-base/git_actions.rb +13 -0
  32. data/lib/rs-rails-base/rails_actions.rb +27 -0
  33. data/lib/rs_rails_base.rb +10 -0
  34. data/lib/templates/chat/channel.rb +4 -0
  35. data/lib/templates/chat/chat_channel.rb +15 -0
  36. data/lib/templates/chat/chat_service.rb +28 -0
  37. data/lib/templates/chat/chats_controller.rb +56 -0
  38. data/lib/templates/chat/connection.rb +24 -0
  39. data/lib/templates/chat/jbuilder/_info.json.jbuilder +2 -0
  40. data/lib/templates/chat/jbuilder/_message.json.jbuilder +2 -0
  41. data/lib/templates/chat/jbuilder/index.json.jbuilder +3 -0
  42. data/lib/templates/chat/jbuilder/messages_create.json.jbuilder +3 -0
  43. data/lib/templates/chat/jbuilder/messages_create_info.json.jbuilder +7 -0
  44. data/lib/templates/chat/jbuilder/show.json.jbuilder +3 -0
  45. data/lib/templates/chat/messages_controller.rb +23 -0
  46. data/lib/templates/chat/migration-models/add_chat.rb +9 -0
  47. data/lib/templates/chat/migration-models/add_message.rb +15 -0
  48. data/lib/templates/chat/migration-models/add_participant.rb +12 -0
  49. data/lib/templates/chat/migration-models/chat.rb +30 -0
  50. data/lib/templates/chat/migration-models/message.rb +43 -0
  51. data/lib/templates/chat/migration-models/participant.rb +28 -0
  52. data/lib/templates/chat/migration-models/user.rb +3 -0
  53. data/lib/templates/chat/routes.rb +6 -0
  54. data/lib/templates/chat/routes_2.rb +1 -0
  55. data/lib/templates/chat/sessions_controller.rb +7 -0
  56. data/lib/templates/chat/specs/chat_channel_spec.rb +25 -0
  57. data/lib/templates/chat/specs/chat_factory.rb +20 -0
  58. data/lib/templates/chat/specs/chat_service_spec.rb +19 -0
  59. data/lib/templates/chat/specs/chat_spec.rb +27 -0
  60. data/lib/templates/chat/specs/connection_spec.rb +10 -0
  61. data/lib/templates/chat/specs/message_factory.rb +7 -0
  62. data/lib/templates/chat/specs/message_spec.rb +40 -0
  63. data/lib/templates/chat/specs/participant_factory.rb +7 -0
  64. data/lib/templates/chat/specs/participant_spec.rb +21 -0
  65. data/lib/templates/chat/specs/rails_helper.rb +1 -0
  66. data/lib/templates/facebook/apiary.apib +28 -0
  67. data/lib/templates/facebook/en.yml +4 -0
  68. data/lib/templates/facebook/facebook_service.rb +13 -0
  69. data/lib/templates/facebook/facebook_spec.rb +146 -0
  70. data/lib/templates/facebook/factories_user.rb +6 -0
  71. data/lib/templates/facebook/routes.rb +3 -0
  72. data/lib/templates/facebook/sessions_controller.rb +9 -0
  73. data/lib/templates/facebook/sessions_controller_2.rb +7 -0
  74. data/lib/templates/facebook/sessions_routing_spec.rb +4 -0
  75. data/lib/templates/facebook/user_spec.rb +5 -0
  76. data/lib/templates/twilio/twilio.rb +4 -0
  77. data/lib/templates/twilio/twilio_service.rb +21 -0
  78. data/rs-rails-base.gemspec +29 -0
  79. data/tasks/code_analysis.rake +4 -0
  80. metadata +206 -0
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -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,2 @@
1
+ json.id chat.id
2
+ json.name chat.name
@@ -0,0 +1,2 @@
1
+ json.id message.id
2
+ json.content message.content
@@ -0,0 +1,3 @@
1
+ json.chats do
2
+ json.array! @chats, partial: 'info', as: :chat
3
+ end
@@ -0,0 +1,3 @@
1
+ json.message do
2
+ json.partial! 'info', message: @message
3
+ end
@@ -0,0 +1,7 @@
1
+ json.id message.id
2
+ json.content message.content
3
+ json.created_at message.created_at
4
+ json.chat_id message.chat_id
5
+ json.sender do
6
+ json.partial! 'api/v1/users/info', user: message.user
7
+ end
@@ -0,0 +1,3 @@
1
+ json.messages do
2
+ json.array! @messages, partial: 'message', as: :message
3
+ 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,9 @@
1
+ class AddChat < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :chats do |t|
4
+ t.string :name
5
+ t.string :last_message
6
+ t.timestamps
7
+ end
8
+ end
9
+ 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,3 @@
1
+ has_many :participants
2
+ has_many :chats, through: :participants
3
+ has_many :messages
@@ -0,0 +1,6 @@
1
+ resources :chats, only: %i[index show create] do
2
+ resources :messages, only: :create
3
+ member do
4
+ post :visit
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ mount ActionCable.server => '/cable'
@@ -0,0 +1,7 @@
1
+ def create
2
+ super
3
+ end
4
+
5
+ def destroy
6
+ super
7
+ end
@@ -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