-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/add rich text reaction model #451
Changes from all commits
8b589c4
12c40b3
dd54eef
869b1e6
ca3a2f9
e34fa78
feed2fa
8b8e4a0
dbd7f68
285c7d6
e90e62b
a8b5330
d03a799
1f1523b
135b632
c1b5c04
f97b927
c21e34e
99ae7bc
b43cbb9
62dbb9b
c5f9186
5063d0d
7348de7
fb1e1d3
b958d53
290d63b
e413d32
a094daa
9e38c27
f692cc3
dbaf208
f11a8d0
6c34708
1388b6d
042a8e8
56156d4
008127f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
class RichTextReactionsController < ApplicationController | ||
before_action :build_rich_text_reaction, only: %i[create] | ||
|
||
# POST /rich_text_reactions.turbo_stream | ||
def create | ||
if @rich_text_reaction.save | ||
render :create, status: :created | ||
else | ||
flash.now[:alert] = 'Reaction could not be created.' | ||
end | ||
end | ||
|
||
# DELETE /rich_text_reactions/:id.turbo_stream | ||
def destroy | ||
@rich_text_reaction = RichTextReaction.find(params[:id]) | ||
|
||
msg = 'Reaction could not be deleted.' | ||
flash.now[:alert] = msg unless @rich_text_reaction.destroy | ||
rescue ActiveRecord::RecordNotFound | ||
flash.now[:alert] = "RichTextReaction with id #{params[:id]} could not be found." | ||
end | ||
|
||
private | ||
|
||
def build_rich_text_reaction | ||
@rich_text_reaction = current_user.rich_text_reactions.build( | ||
rich_text_reaction_params | ||
) | ||
end | ||
|
||
def rich_text_reaction_params | ||
params.require(:rich_text_reaction).permit(:emoji_caption, :rich_text_id) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# == Schema Information | ||
# | ||
# Table name: rich_text_reactions | ||
# | ||
# id :bigint not null, primary key | ||
# emoji_caption :string not null | ||
# created_at :datetime not null | ||
# updated_at :datetime not null | ||
# rich_text_id :bigint not null | ||
# user_id :bigint not null | ||
# | ||
# Indexes | ||
# | ||
# index_rich_text_reactions_on_rich_text_id (rich_text_id) | ||
# index_rich_text_reactions_on_user_id (user_id) | ||
# | ||
# Foreign Keys | ||
# | ||
# fk_rails_... (rich_text_id => action_text_rich_texts.id) | ||
# fk_rails_... (user_id => users.id) | ||
# | ||
require 'emoji' | ||
|
||
class RichTextReaction < ApplicationRecord | ||
belongs_to :user | ||
belongs_to :rich_text, class_name: 'ActionText::RichText' | ||
|
||
validates :emoji_caption, | ||
inclusion: { | ||
in: Emoji.captions, | ||
message: 'must be present in permissible set' | ||
} | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<%= "PLACEHOLDER" %> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,4 +69,6 @@ | |
scope controller: :static do | ||
get :faq | ||
end | ||
|
||
resources :rich_text_reactions, only: %i[create destroy] | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class CreateRichTextReactions < ActiveRecord::Migration[7.0] | ||
def change | ||
create_table :rich_text_reactions do |t| | ||
t.references :user, null: false, foreign_key: true | ||
t.references :rich_text, null: false, foreign_key: { to_table: :action_text_rich_texts } | ||
t.string :emoji_caption, null: false | ||
|
||
t.timestamps | ||
end | ||
end | ||
end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# frozen_string_literal: true | ||
|
||
class Emoji | ||
DICTIONARY = { | ||
'THUMBS_UP' => '👍', | ||
'THUMBS_DOWN' => '👎', | ||
'THINKING' => '🤔', | ||
'HOORAY' => '🎉', | ||
'SHRUG' => '🤷', | ||
'EYES' => '👀' | ||
}.freeze | ||
|
||
attr_reader :caption | ||
|
||
def initialize(caption = DICTIONARY.keys.sample) | ||
@caption = caption | ||
end | ||
|
||
def emoji | ||
DICTIONARY[@caption] | ||
end | ||
|
||
class << self | ||
def captions | ||
DICTIONARY.keys | ||
end | ||
|
||
def emojis | ||
DICTIONARY.values | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
FactoryBot.define do | ||
factory :action_text_rich_text, class: 'ActionText::RichText' do | ||
# +rich_text_owner+ must be given by the user of this factory, and it may | ||
# be any persisted ActiveRecord model record. | ||
transient do | ||
rich_text_owner { nil } | ||
end | ||
|
||
name { Faker::Lorem.word } | ||
body { ActionText::Content.new "<div>#{Faker::Lorem.sentence}</div>" } | ||
|
||
after(:build) do |action_text_rich_text, evaluator| | ||
if evaluator.rich_text_owner | ||
action_text_rich_text.record_type = evaluator.rich_text_owner.class.name | ||
action_text_rich_text.record_id = evaluator.rich_text_owner.id | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# == Schema Information | ||
# | ||
# Table name: rich_text_reactions | ||
# | ||
# id :bigint not null, primary key | ||
# emoji_caption :string not null | ||
# created_at :datetime not null | ||
# updated_at :datetime not null | ||
# rich_text_id :bigint not null | ||
# user_id :bigint not null | ||
# | ||
# Indexes | ||
# | ||
# index_rich_text_reactions_on_rich_text_id (rich_text_id) | ||
# index_rich_text_reactions_on_user_id (user_id) | ||
# | ||
# Foreign Keys | ||
# | ||
# fk_rails_... (rich_text_id => action_text_rich_texts.id) | ||
# fk_rails_... (user_id => users.id) | ||
# | ||
FactoryBot.define do | ||
factory :rich_text_reaction do | ||
# +rich_text_owner+ can be given by the user of this factory, and it may | ||
# be any persisted ActiveRecord model record. By default, a User record is | ||
# used. | ||
transient do | ||
rich_text_owner { user } | ||
end | ||
|
||
user | ||
emoji_caption { Emoji.captions.sample } | ||
rich_text { association(:action_text_rich_text, rich_text_owner:) } | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ | |
|
||
RSpec.describe 'FactoryBot' do | ||
FactoryBot.factories.each do |factory| | ||
next if factory.name == :action_text_rich_text | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JoshDevHub |
||
|
||
# rubocop:disable RSpec/NoExpectationExample | ||
it "#{factory.name} should pass lint" do | ||
FactoryBot::Linter.new([factory]).lint! | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
require 'rails_helper' | ||
require 'emoji' | ||
|
||
RSpec.describe Emoji do | ||
let(:caption) { described_class.captions.sample } | ||
|
||
describe '#emoji' do | ||
subject { described_class.new(caption) } | ||
|
||
it 'returns the emoji in string format' do | ||
expect(subject.emoji).to eq(Emoji::DICTIONARY[caption]) | ||
end | ||
end | ||
|
||
describe '.captions' do | ||
it 'returns the set of allowed emoji captions' do | ||
expect(described_class.captions).to eq(described_class::DICTIONARY.keys) | ||
end | ||
end | ||
|
||
describe '.emojis' do | ||
it 'returns the set of allowed emojis' do | ||
expect(described_class.emojis).to eq(described_class::DICTIONARY.values) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe RichTextReaction do | ||
it 'is valid with an emoji in the permissible set' do | ||
emoji_caption = Emoji.captions.sample | ||
rich_text_reaction = described_class.new(emoji_caption:) | ||
|
||
expect(rich_text_reaction.errors[:emoji_caption]).to be_empty | ||
end | ||
|
||
it 'is invalid with an emoji not in the permissible set' do | ||
rich_text_reaction = described_class.new(emoji_caption: 'disguised_face') | ||
rich_text_reaction.valid? | ||
|
||
expect(rich_text_reaction.errors[:emoji_caption]).to be_present | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
require 'rails_helper' | ||
require 'emoji' | ||
|
||
RSpec.describe 'RichTextReaction requests' do | ||
let!(:user) { create(:user) } | ||
|
||
before do | ||
sign_in user | ||
end | ||
|
||
describe 'POST /rich_text_reactions' do | ||
it 'creates a rich text reaction' do | ||
count = RichTextReaction.count | ||
params = { | ||
rich_text_reaction: { | ||
emoji_caption: Emoji.captions.sample, | ||
rich_text_id: create(:standup_meeting).yesterday_work_description.id | ||
}, | ||
format: :turbo_stream | ||
} | ||
|
||
post(rich_text_reactions_url, params:) | ||
|
||
expect(RichTextReaction.count).to eq(count + 1) | ||
expect(response).to have_http_status :created | ||
end | ||
|
||
it 'sets a flash message to indicate failure to create' do | ||
post rich_text_reactions_url, | ||
params: { | ||
rich_text_reaction: { | ||
emoji_caption: Emoji.captions.sample, | ||
rich_text_id: 2 | ||
}, | ||
format: :turbo_stream | ||
} | ||
|
||
expect(flash.now[:alert]).to be_present | ||
end | ||
end | ||
|
||
describe 'DELETE /rich_text_reactions/:id' do | ||
it 'deletes a rich text reaction' do | ||
id = create(:rich_text_reaction).id | ||
|
||
delete(rich_text_reaction_url(id), params: { format: :turbo_stream }) | ||
|
||
expect(RichTextReaction.count).to be_zero | ||
expect(response).to have_http_status :no_content | ||
end | ||
|
||
it 'sets a flash message to indicate failure to delete' do | ||
fake_id = '1' | ||
rich_text_reaction = instance_double(RichTextReaction, destroy: false) | ||
allow(RichTextReaction).to receive(:find).with(fake_id).and_return(rich_text_reaction) | ||
|
||
delete rich_text_reaction_url(fake_id), | ||
params: { | ||
format: :turbo_stream | ||
} | ||
|
||
expect(flash.now[:alert]).to be_present | ||
|
||
expect(response).to have_http_status :no_content | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JoshDevHub
Some ActiveRecord model object has to be given for creating ActionText::RichText.
I decided the User model is probably the safest to use since it most likely won’t be going away or changed to a different name.