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 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
+ [![Gem Version](https://badge.fury.io/rb/convo.svg)](https://badge.fury.io/rb/convo)
4
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](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,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -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,4 @@
1
+ module Convo
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Convo
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Convo
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Convo
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -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,5 @@
1
+ module Convo
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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
@@ -0,0 +1,5 @@
1
+ class User < ApplicationRecord
2
+ include Convo::HasConvo
3
+
4
+ # ... rest of the User model code
5
+ end
@@ -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,2 @@
1
+ Convo::Engine.routes.draw do
2
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Convo
2
+ VERSION = "0.1.0"
3
+ end
data/lib/convo.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "convo/engine"
2
+ require "convo/has_convo"
3
+
4
+ module Convo
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :convo do
3
+ # # Task goes here
4
+ # end
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: []