convo 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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +156 -0
- data/Rakefile +8 -0
- data/app/assets/stylesheets/convo/application.css +15 -0
- data/app/controllers/convo/application_controller.rb +4 -0
- data/app/helpers/convo/application_helper.rb +4 -0
- data/app/jobs/convo/application_job.rb +4 -0
- data/app/mailers/convo/application_mailer.rb +6 -0
- data/app/models/concerns/convo/metadata_extension.rb +21 -0
- data/app/models/convo/application_record.rb +5 -0
- data/app/models/convo/message.rb +18 -0
- data/app/models/convo/topic.rb +82 -0
- data/app/models/user.rb +5 -0
- data/app/views/layouts/convo/application.html.erb +17 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20250111142150_create_convo_topics.rb +33 -0
- data/db/migrate/20250111142238_create_convo_messages.rb +22 -0
- data/db/migrate/20250111142300_create_active_storage_tables.rb +57 -0
- data/lib/convo/engine.rb +25 -0
- data/lib/convo/has_convo.rb +34 -0
- data/lib/convo/version.rb +3 -0
- data/lib/convo.rb +6 -0
- data/lib/tasks/convo_tasks.rake +4 -0
- metadata +184 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3416a31cc10bcf8ed359acdf7d4944406aab4517fb2bbdefb072994991596c30
|
4
|
+
data.tar.gz: 02746a993546ab98e41cc6263816f0e30e5eec6176a82f93f68f53438208528d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0cb462b86cee06311d65dada1896f98de3beb64671a2cd471f73d41b5b0adfaeba4a18a7683cba239eb6545a972471b50d3ad9da6b416a5d927c0af5574627e8
|
7
|
+
data.tar.gz: 8c7e6875855a9a65c42ee4bc56c7826dd9414b299c1961d821937fda5eea872df955bb28d6f81110db88092d08aec9ddc0574ef8470322cb0f8f1ad98296d40c
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright Ben
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
# Convo
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/convo)
|
4
|
+
[](LICENSE)
|
5
|
+
|
6
|
+
Convo is a Rails engine that provides a flexible and customizable conversation system for your Rails application. It enables easy implementation of messaging and conversation features between users of your application.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- Easy-to-use conversation system
|
11
|
+
- Support for multiple participants in conversations
|
12
|
+
- Automatic conversation threading and topic management
|
13
|
+
- Model-agnostic implementation (works with any user model)
|
14
|
+
- Built-in validations and security measures
|
15
|
+
- Clean and simple API
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'convo'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
```bash
|
28
|
+
$ bundle install
|
29
|
+
```
|
30
|
+
|
31
|
+
Or install it yourself as:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
$ gem install convo
|
35
|
+
```
|
36
|
+
|
37
|
+
## Setup
|
38
|
+
|
39
|
+
1. Install the migrations:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
$ rails convo:install:migrations
|
43
|
+
$ rails db:migrate
|
44
|
+
```
|
45
|
+
|
46
|
+
2. Include the `HasConvo` module in your user model:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class User < ApplicationRecord
|
50
|
+
include Convo::HasConvo
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
### Starting a Conversation
|
57
|
+
|
58
|
+
To send a message to one or multiple recipients:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# Send to a single recipient
|
62
|
+
user.send_message(
|
63
|
+
recipients: recipient,
|
64
|
+
body: "Hello!"
|
65
|
+
)
|
66
|
+
|
67
|
+
# Send to multiple recipients
|
68
|
+
user.send_message(
|
69
|
+
recipients: [recipient1, recipient2],
|
70
|
+
body: "Hello everyone!"
|
71
|
+
)
|
72
|
+
```
|
73
|
+
|
74
|
+
### Accessing Conversations
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# Get all topics for a user
|
78
|
+
user.topics
|
79
|
+
|
80
|
+
# Get all messages for a user
|
81
|
+
user.messages
|
82
|
+
|
83
|
+
# Get messages from a specific topic
|
84
|
+
topic.messages
|
85
|
+
```
|
86
|
+
|
87
|
+
### Understanding Topics
|
88
|
+
|
89
|
+
Topics are unique per combination of participants. For example:
|
90
|
+
|
91
|
+
- If a topic exists between User1 and User2
|
92
|
+
- And you send a message to User1, User2, and User3
|
93
|
+
- A new topic will be created for this three-person conversation
|
94
|
+
- The original topic between User1 and User2 remains separate
|
95
|
+
|
96
|
+
This ensures that conversations between different groups of participants stay organized and separate, even if there's overlap in the participants.
|
97
|
+
|
98
|
+
### Context and Tenant Support
|
99
|
+
|
100
|
+
Messages can be associated with a context (any record) and a tenant (for broader visibility):
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# Send a message with context and tenant
|
104
|
+
user.send_message(
|
105
|
+
recipients: recipient,
|
106
|
+
body: "Hello!",
|
107
|
+
context: some_record, # Optional: Any ActiveRecord model
|
108
|
+
tenant: organization # Optional: Any ActiveRecord model
|
109
|
+
)
|
110
|
+
|
111
|
+
# Find topics by context
|
112
|
+
Topic.with_context(some_record)
|
113
|
+
|
114
|
+
# Find topics by tenant
|
115
|
+
Topic.with_tenant(organization)
|
116
|
+
|
117
|
+
# Find topics by both context and tenant
|
118
|
+
Topic.with_context_and_tenant(some_record, organization)
|
119
|
+
```
|
120
|
+
|
121
|
+
This allows you to:
|
122
|
+
|
123
|
+
- Organize conversations around specific records (e.g., Order, Project, Task)
|
124
|
+
- Control visibility of conversations across different scopes (e.g., Organizations, Teams)
|
125
|
+
- Query conversations based on their context or tenant
|
126
|
+
|
127
|
+
## Requirements
|
128
|
+
|
129
|
+
- Ruby >= 3.0.0
|
130
|
+
- Rails >= 8.0.1
|
131
|
+
- PostgreSQL (for array type support)
|
132
|
+
|
133
|
+
## Development
|
134
|
+
|
135
|
+
After checking out the repo, run:
|
136
|
+
|
137
|
+
```bash
|
138
|
+
$ bundle install
|
139
|
+
$ bundle exec rake spec
|
140
|
+
```
|
141
|
+
|
142
|
+
## Contributing
|
143
|
+
|
144
|
+
1. Fork it
|
145
|
+
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
146
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
147
|
+
4. Push to the branch (`git push origin feature/my-new-feature`)
|
148
|
+
5. Create new Pull Request
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
153
|
+
|
154
|
+
## Credits
|
155
|
+
|
156
|
+
Developed and maintained by [Ben](https://github.com/getnvoi).
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Convo
|
2
|
+
module MetadataExtension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def metadata_accessor(*fields)
|
7
|
+
fields.each do |field|
|
8
|
+
# Getter
|
9
|
+
define_method(field) do
|
10
|
+
metadata[field.to_s]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Setter
|
14
|
+
define_method("#{field}=") do |value|
|
15
|
+
self.metadata = metadata.merge(field.to_s => value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Convo
|
2
|
+
class Message < ApplicationRecord
|
3
|
+
include(Convo::MetadataExtension)
|
4
|
+
|
5
|
+
belongs_to(:topic)
|
6
|
+
belongs_to(:sender, polymorphic: true)
|
7
|
+
belongs_to(:recipient, polymorphic: true)
|
8
|
+
belongs_to(:context, polymorphic: true, optional: true)
|
9
|
+
belongs_to(:tenant, polymorphic: true, optional: true)
|
10
|
+
|
11
|
+
validates(:body, presence: true)
|
12
|
+
validates(:sender, presence: true)
|
13
|
+
validates(:recipient, presence: true)
|
14
|
+
validates(:topic, presence: true)
|
15
|
+
|
16
|
+
has_many_attached(:files)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Convo
|
2
|
+
class Topic < ApplicationRecord
|
3
|
+
include Convo::MetadataExtension
|
4
|
+
|
5
|
+
validates :participant_ids, presence: true
|
6
|
+
validates :model_class, presence: true
|
7
|
+
validate :unique_participant_combination
|
8
|
+
|
9
|
+
has_many :messages, dependent: :destroy
|
10
|
+
has_many :files, through: :messages, source: :files_attachments
|
11
|
+
|
12
|
+
# Scopes for finding topics by context and tenant
|
13
|
+
scope :with_context, ->(context) {
|
14
|
+
joins(:messages).where(convo_messages: { context: }).distinct
|
15
|
+
}
|
16
|
+
|
17
|
+
scope :with_tenant, ->(tenant) {
|
18
|
+
joins(:messages).where(convo_messages: { tenant: }).distinct
|
19
|
+
}
|
20
|
+
|
21
|
+
scope :with_context_and_tenant, ->(context, tenant) {
|
22
|
+
joins(:messages)
|
23
|
+
.where(convo_messages: { context:, tenant: })
|
24
|
+
.distinct
|
25
|
+
}
|
26
|
+
|
27
|
+
def self.find_or_create_by_participant_ids(model_class, participant_ids, metadata: {})
|
28
|
+
sorted_ids = participant_ids.map(&:to_i).sort
|
29
|
+
transaction do
|
30
|
+
find_by(model_class:, participant_ids: sorted_ids) ||
|
31
|
+
create!(model_class:, participant_ids: sorted_ids, metadata:)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.send_message(sender:, recipients:, body:, context: nil, tenant: nil, metadata: {})
|
36
|
+
recipients = Array(recipients)
|
37
|
+
validate_participants_class!(sender, recipients)
|
38
|
+
|
39
|
+
participant_ids = ([ sender.id ] + recipients.map(&:id)).sort
|
40
|
+
topic = find_or_create_by_participant_ids(
|
41
|
+
sender.class.name,
|
42
|
+
participant_ids,
|
43
|
+
metadata:
|
44
|
+
)
|
45
|
+
|
46
|
+
transaction do
|
47
|
+
recipients.each do |recipient|
|
48
|
+
topic.messages.create!(
|
49
|
+
sender:,
|
50
|
+
recipient:,
|
51
|
+
body:,
|
52
|
+
context:,
|
53
|
+
tenant:,
|
54
|
+
metadata:
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
topic
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def unique_participant_combination
|
65
|
+
return unless model_class.present? && participant_ids.present?
|
66
|
+
|
67
|
+
existing = self.class.where(model_class:)
|
68
|
+
.where("participant_ids @> ARRAY[?]::bigint[] AND participant_ids <@ ARRAY[?]::bigint[]",
|
69
|
+
participant_ids, participant_ids)
|
70
|
+
.where.not(id:)
|
71
|
+
.exists?
|
72
|
+
|
73
|
+
errors.add(:participant_ids, "combination already exists") if existing
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.validate_participants_class!(sender, recipients)
|
77
|
+
unless recipients.all? { |r| r.class == sender.class }
|
78
|
+
raise ArgumentError, "Sender and recipients must be of same class"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Convo</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= yield :head %>
|
9
|
+
|
10
|
+
<%= stylesheet_link_tag "convo/application", media: "all" %>
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
|
14
|
+
<%= yield %>
|
15
|
+
|
16
|
+
</body>
|
17
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class CreateConvoTopics < ActiveRecord::Migration[7.1]
|
2
|
+
def up
|
3
|
+
create_table :convo_topics do |t|
|
4
|
+
t.string :model_class, null: false
|
5
|
+
t.bigint :participant_ids, array: true, null: false, default: []
|
6
|
+
t.jsonb :metadata, default: {}, null: false
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
# Create the sort_array function
|
11
|
+
execute <<-SQL
|
12
|
+
CREATE OR REPLACE FUNCTION sort_array(anyarray)
|
13
|
+
RETURNS anyarray AS $$
|
14
|
+
SELECT array_agg(x ORDER BY x)
|
15
|
+
FROM unnest($1) x;
|
16
|
+
$$ LANGUAGE SQL IMMUTABLE;
|
17
|
+
SQL
|
18
|
+
|
19
|
+
# Create the unique index using our new function
|
20
|
+
execute <<-SQL
|
21
|
+
CREATE UNIQUE INDEX index_convo_topics_on_sorted_participant_ids
|
22
|
+
ON convo_topics (model_class, sort_array(participant_ids::bigint[]));
|
23
|
+
SQL
|
24
|
+
|
25
|
+
# Add GIN index for metadata
|
26
|
+
add_index :convo_topics, :metadata, using: :gin
|
27
|
+
end
|
28
|
+
|
29
|
+
def down
|
30
|
+
drop_table :convo_topics
|
31
|
+
execute "DROP FUNCTION IF EXISTS sort_array(anyarray);"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateConvoMessages < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
create_table :convo_messages do |t|
|
4
|
+
t.references :topic, null: false, foreign_key: { to_table: :convo_topics }
|
5
|
+
t.references :sender, polymorphic: true, null: false
|
6
|
+
t.references :recipient, polymorphic: true, null: false
|
7
|
+
t.references :context, polymorphic: true
|
8
|
+
t.references :tenant, polymorphic: true
|
9
|
+
t.text :body
|
10
|
+
t.jsonb :metadata, default: {}, null: false
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add composite indexes for polymorphic associations
|
16
|
+
add_index :convo_messages, [ :sender_type, :sender_id ]
|
17
|
+
add_index :convo_messages, [ :recipient_type, :recipient_id ]
|
18
|
+
add_index :convo_messages, [ :context_type, :context_id ]
|
19
|
+
add_index :convo_messages, [ :tenant_type, :tenant_id ]
|
20
|
+
add_index :convo_messages, :metadata, using: :gin
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# This migration comes from active_storage (originally 20170806125915)
|
2
|
+
class CreateActiveStorageTables < ActiveRecord::Migration[7.1]
|
3
|
+
def change
|
4
|
+
# Use Active Record's configured type for primary and foreign keys
|
5
|
+
primary_key_type, foreign_key_type = primary_and_foreign_key_types
|
6
|
+
|
7
|
+
create_table :active_storage_blobs, id: primary_key_type do |t|
|
8
|
+
t.string :key, null: false
|
9
|
+
t.string :filename, null: false
|
10
|
+
t.string :content_type
|
11
|
+
t.text :metadata
|
12
|
+
t.string :service_name, null: false
|
13
|
+
t.bigint :byte_size, null: false
|
14
|
+
t.string :checksum
|
15
|
+
|
16
|
+
if connection.supports_datetime_with_precision?
|
17
|
+
t.datetime :created_at, precision: 6, null: false
|
18
|
+
else
|
19
|
+
t.datetime :created_at, null: false
|
20
|
+
end
|
21
|
+
|
22
|
+
t.index [ :key ], unique: true
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :active_storage_attachments, id: primary_key_type do |t|
|
26
|
+
t.string :name, null: false
|
27
|
+
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
|
28
|
+
t.references :blob, null: false, type: foreign_key_type
|
29
|
+
|
30
|
+
if connection.supports_datetime_with_precision?
|
31
|
+
t.datetime :created_at, precision: 6, null: false
|
32
|
+
else
|
33
|
+
t.datetime :created_at, null: false
|
34
|
+
end
|
35
|
+
|
36
|
+
t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
|
37
|
+
t.foreign_key :active_storage_blobs, column: :blob_id
|
38
|
+
end
|
39
|
+
|
40
|
+
create_table :active_storage_variant_records, id: primary_key_type do |t|
|
41
|
+
t.belongs_to :blob, null: false, index: false, type: foreign_key_type
|
42
|
+
t.string :variation_digest, null: false
|
43
|
+
|
44
|
+
t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
|
45
|
+
t.foreign_key :active_storage_blobs, column: :blob_id
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def primary_and_foreign_key_types
|
51
|
+
config = Rails.configuration.generators
|
52
|
+
setting = config.options[config.orm][:primary_key_type]
|
53
|
+
primary_key_type = setting || :primary_key
|
54
|
+
foreign_key_type = setting || :bigint
|
55
|
+
[ primary_key_type, foreign_key_type ]
|
56
|
+
end
|
57
|
+
end
|
data/lib/convo/engine.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Convo
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Convo
|
4
|
+
|
5
|
+
config.generators do |g|
|
6
|
+
g.test_framework :rspec
|
7
|
+
g.fixture_replacement :factory_bot
|
8
|
+
g.factory_bot dir: "spec/factories"
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer "convo.active_record" do
|
12
|
+
ActiveSupport.on_load(:active_record) do
|
13
|
+
include Convo::HasConvo
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer "convo.migrations" do |app|
|
18
|
+
unless app.root.to_s.match?(root.to_s)
|
19
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
20
|
+
app.config.paths["db/migrate"] << expanded_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Convo
|
2
|
+
module HasConvo
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_many :topics,
|
7
|
+
-> { distinct },
|
8
|
+
class_name: "Convo::Topic",
|
9
|
+
foreign_key: :participant_ids,
|
10
|
+
primary_key: :id
|
11
|
+
|
12
|
+
has_many :sent_messages,
|
13
|
+
class_name: "Convo::Message",
|
14
|
+
foreign_key: :sender_id
|
15
|
+
|
16
|
+
has_many :received_messages,
|
17
|
+
class_name: "Convo::Message",
|
18
|
+
foreign_key: :recipient_id
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_message(to:, body:)
|
22
|
+
Convo::Topic.send_message(
|
23
|
+
sender: self,
|
24
|
+
recipients: Array(to),
|
25
|
+
body: body
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def conversations
|
30
|
+
Convo::Topic.where(model_class: self.class.name)
|
31
|
+
.where("? = ANY(participant_ids)", id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/convo.rb
ADDED
metadata
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: convo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-01-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 8.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 8.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activestorage
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 8.0.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 8.0.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_bot_rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: shoulda-matchers
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pg
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: database_cleaner-active_record
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: faker
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Convo is a Rails engine that provides a flexible and customizable conversation
|
126
|
+
system for your Rails application, supporting threaded comments, notifications,
|
127
|
+
and moderation.
|
128
|
+
email:
|
129
|
+
- ben@dee.mx
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- MIT-LICENSE
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- app/assets/stylesheets/convo/application.css
|
138
|
+
- app/controllers/convo/application_controller.rb
|
139
|
+
- app/helpers/convo/application_helper.rb
|
140
|
+
- app/jobs/convo/application_job.rb
|
141
|
+
- app/mailers/convo/application_mailer.rb
|
142
|
+
- app/models/concerns/convo/metadata_extension.rb
|
143
|
+
- app/models/convo/application_record.rb
|
144
|
+
- app/models/convo/message.rb
|
145
|
+
- app/models/convo/topic.rb
|
146
|
+
- app/models/user.rb
|
147
|
+
- app/views/layouts/convo/application.html.erb
|
148
|
+
- config/routes.rb
|
149
|
+
- db/migrate/20250111142150_create_convo_topics.rb
|
150
|
+
- db/migrate/20250111142238_create_convo_messages.rb
|
151
|
+
- db/migrate/20250111142300_create_active_storage_tables.rb
|
152
|
+
- lib/convo.rb
|
153
|
+
- lib/convo/engine.rb
|
154
|
+
- lib/convo/has_convo.rb
|
155
|
+
- lib/convo/version.rb
|
156
|
+
- lib/tasks/convo_tasks.rake
|
157
|
+
homepage: https://github.com/getnvoi/convo
|
158
|
+
licenses:
|
159
|
+
- MIT
|
160
|
+
metadata:
|
161
|
+
allowed_push_host: https://rubygems.org
|
162
|
+
homepage_uri: https://github.com/getnvoi/convo
|
163
|
+
source_code_uri: https://github.com/getnvoi/convo
|
164
|
+
changelog_uri: https://github.com/getnvoi/convo/blob/main/CHANGELOG.md
|
165
|
+
post_install_message:
|
166
|
+
rdoc_options: []
|
167
|
+
require_paths:
|
168
|
+
- lib
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
requirements:
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: '0'
|
179
|
+
requirements: []
|
180
|
+
rubygems_version: 3.4.19
|
181
|
+
signing_key:
|
182
|
+
specification_version: 4
|
183
|
+
summary: A Rails engine for adding conversation and commenting functionality
|
184
|
+
test_files: []
|