acts_as_mentionable 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +66 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +5 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +212 -0
- data/Rakefile +6 -0
- data/acts_as_mentionable.gemspec +37 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/db/migrate/1_acts_as_mentionable_migration.rb +20 -0
- data/lib/acts_as_mentionable/active_record_methods.rb +40 -0
- data/lib/acts_as_mentionable/engine.rb +4 -0
- data/lib/acts_as_mentionable/mention.rb +46 -0
- data/lib/acts_as_mentionable/mentionable.rb +20 -0
- data/lib/acts_as_mentionable/mentionables_manipulator.rb +66 -0
- data/lib/acts_as_mentionable/mentioner.rb +69 -0
- data/lib/acts_as_mentionable/mentioner_parser.rb +41 -0
- data/lib/acts_as_mentionable/mentions_updater.rb +35 -0
- data/lib/acts_as_mentionable/retrieve_polymorphic.rb +26 -0
- data/lib/acts_as_mentionable/transaction_callbacks.rb +42 -0
- data/lib/acts_as_mentionable/version.rb +3 -0
- data/lib/acts_as_mentionable.rb +57 -0
- metadata +184 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a9e8c57fc330401901ba129fabddc2b3383f0c93b018a7bedf86f7a1c98a4835
|
4
|
+
data.tar.gz: da143817eb31788245c13d3ae16e978320db6f8d78de4bdfd1c66405e61df5e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ab5f7ababfd7e3dc833d1a63fde4c1fb1516d636843d80853cb448b274f134bd4a088ec6be6b183278b08746799ff508a38ef76de021e611c783ba8e37c9f76e
|
7
|
+
data.tar.gz: 215a73fc8de11cb20c7c3caf465cdf31cbc1a50dc8a9c7a36158010c1d88fc51378b46796b0a02d58f8768c82c4b7cf4f7348a6979f2d363f38b1aef4bbf535d
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
docker:
|
9
|
+
- image: circleci/ruby:2.3.8-jessie
|
10
|
+
|
11
|
+
# Specify service dependencies here if necessary
|
12
|
+
# CircleCI maintains a library of pre-built images
|
13
|
+
# documented at https://circleci.com/docs/2.0/circleci-images/
|
14
|
+
# - image: circleci/postgres:9.4
|
15
|
+
|
16
|
+
working_directory: ~/acts_as_mentionable
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- checkout
|
20
|
+
|
21
|
+
# Download and cache dependencies
|
22
|
+
- restore_cache:
|
23
|
+
keys:
|
24
|
+
- v1-dependencies-
|
25
|
+
|
26
|
+
- run:
|
27
|
+
name: install dependencies
|
28
|
+
command: |
|
29
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
30
|
+
|
31
|
+
- save_cache:
|
32
|
+
paths:
|
33
|
+
- ./vendor/bundle
|
34
|
+
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
|
35
|
+
|
36
|
+
# run tests
|
37
|
+
- run:
|
38
|
+
name: Run Commit Message Checker
|
39
|
+
command: |
|
40
|
+
bundle exec fuse-dev-tools git validate_commit_message
|
41
|
+
- run:
|
42
|
+
name: Run Pull Request Validator
|
43
|
+
command: |
|
44
|
+
bundle exec fuse-dev-tools git validate_pull_request
|
45
|
+
- run:
|
46
|
+
name: Run Rubocop
|
47
|
+
command: |
|
48
|
+
bundle exec rubocop
|
49
|
+
- run:
|
50
|
+
name: Run Rspec
|
51
|
+
command: |
|
52
|
+
mkdir /tmp/test-results
|
53
|
+
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
|
54
|
+
|
55
|
+
bundle exec rspec --format progress \
|
56
|
+
--format RspecJunitFormatter \
|
57
|
+
--out /tmp/test-results/rspec.xml \
|
58
|
+
--format progress \
|
59
|
+
$TEST_FILES
|
60
|
+
|
61
|
+
# collect reports
|
62
|
+
- store_test_results:
|
63
|
+
path: /tmp/test-results
|
64
|
+
- store_artifacts:
|
65
|
+
path: /tmp/test-results
|
66
|
+
destination: test-results
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in acts_as_mentionable.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
group :development do
|
9
|
+
gem 'fuse-dev-tools', github: 'Fuseit/fuse-dev-tools'
|
10
|
+
gem 'pry'
|
11
|
+
gem 'pry-byebug'
|
12
|
+
gem 'rubocop'
|
13
|
+
gem 'rubocop-rspec'
|
14
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Baron Bloomer
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# ActsAsMentionable
|
2
|
+
|
3
|
+
ActsAsMentionable is an ActiveRecord gem, which is called to help you build your own mentioning system on top of your Ruby On Rails project. With its help you can mention ActiveRecord objects from ActiveRecord objects, such as users from within the comment's text. For instance, when John Doe mentioned Richard Roe in a comment, then Richard Roe usually will receive the notification about being mentioned and mention will be stored in database.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'acts_as_mentionable'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
```shell
|
15
|
+
$ bundle
|
16
|
+
```
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
```shell
|
20
|
+
$ gem install acts_as_mentionable
|
21
|
+
```
|
22
|
+
|
23
|
+
#### Post Installation
|
24
|
+
|
25
|
+
Install migrations
|
26
|
+
```shell
|
27
|
+
rake acts_as_mentionable_engine:install:migrations
|
28
|
+
```
|
29
|
+
|
30
|
+
Review the generated migrations then migrate:
|
31
|
+
```shell
|
32
|
+
rake db:migrate
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
Let's continue with mentioning users from the comment. In a such case comment is a mentioner, because it can mention users, and user is a mentionable, as user can be mentioned.
|
38
|
+
|
39
|
+
### Mentioner model
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
# == Schema Information
|
43
|
+
#
|
44
|
+
# id :integer not null, primary key
|
45
|
+
# body :text(65535)
|
46
|
+
# parsed_body :text(65535)
|
47
|
+
#
|
48
|
+
|
49
|
+
class Comment < ActiveRecord::Base
|
50
|
+
# ...
|
51
|
+
acts_as_mentioner :body
|
52
|
+
# ...
|
53
|
+
def retrieve_mentions_callback
|
54
|
+
mentionables = ActsAsMentionable::MentionerParser.new(self).parse!
|
55
|
+
replace_mentionables(mentionables, save: true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
Mentioner (`Comment`) model is expected to have two fields:
|
61
|
+
1. The field, which contains, let's say, plain version of the comment - in the example above it's `body`. This field, by default, is rewritten by the parser and contains all the mentions as the plain text, e.g. `Hello, @john!`.
|
62
|
+
2. The field, which contains comment with the mentions. It's named as the `parsed_*`, where `*` is meant to be the field, which contains plain version of the comment. In the example above it's named `parsed_body`. Mentions are recognized by the following template - `{#|RECORD_ID|RECORD_MODEL_NAME}`, where `RECORD_ID` is the ID of record being mentioned, for instance, user ID, and, `RECORD_MODEL_NAME` is the name of the model being mentioned, for instance, for `User` model it is `user`. According to this, `parsed_body` field may have the following value - `Hello, {#|42|user}!`.
|
63
|
+
|
64
|
+
So, as you may have already noticed, the argument provided to the `#acts_as_mentioner` method is the name of the field, which is a plain version of the comment - `body`, and the name of the field, which contains mentions is generated automatically - `parsed_body`.
|
65
|
+
|
66
|
+
### Mentionable model
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
# == Schema Information
|
70
|
+
#
|
71
|
+
# id :integer not null, primary key
|
72
|
+
# username :string(255)
|
73
|
+
#
|
74
|
+
|
75
|
+
class User < ActiveRecord::Base
|
76
|
+
# ...
|
77
|
+
acts_as_mentionable :username
|
78
|
+
# ...
|
79
|
+
end
|
80
|
+
```
|
81
|
+
The argument `username` provided to `#acts_as_mentionable` method, is a field on user's model, which is used as a replacement for mention template in order to build a plain version of the comment. For example, we have the following code:
|
82
|
+
```ruby
|
83
|
+
user = User.create! id: 42, username: 'john'
|
84
|
+
comment = Comment.create! parsed_body: 'Hello, {#|42|user}!'
|
85
|
+
puts comment.body
|
86
|
+
# => Hello, @john!
|
87
|
+
```
|
88
|
+
As you can see, it this case `{#|42|user}` in `parsed_body` is replaced by `@` + `username` field of the user in comment's `body`.
|
89
|
+
|
90
|
+
### Retrieving mentions
|
91
|
+
|
92
|
+
Mentioner's `parsed_*` field is not parsed by default. You have to invoke `ActsAsMentionable::MentionerParser#parse!` method in order to parse it and retrieve mentionables (users in our case).
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
mentionables = ActsAsMentionable::MentionerParser.new(comment).parse!
|
96
|
+
```
|
97
|
+
|
98
|
+
The line above parses comment's `parsed_body` field and does the following:
|
99
|
+
1. Generates and saves the plain version of the comment - `body` field.
|
100
|
+
2. Returns mentionables - in our case only users are expected, but it's possible to mix different types of mentionables.
|
101
|
+
|
102
|
+
We can mix together several types of mentionables. Let's check the following code:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
comment = Comment.create! \
|
106
|
+
parsed_body: 'Hello, {#|42|user}! Can you please join the {#|123|community}?'
|
107
|
+
|
108
|
+
mentionables = ActsAsMentionable::MentionerParser.new(comment).parse!
|
109
|
+
|
110
|
+
comment.body
|
111
|
+
# => Hello, @john! Can you please join the @developers?
|
112
|
+
|
113
|
+
mentionables
|
114
|
+
# => [#<User:...>, #<Community:...>]
|
115
|
+
```
|
116
|
+
|
117
|
+
In this case `User` with ID `42` and `Community` with ID `123` are mentioned, but only if `Community` model has `acts_as_mentionable` defined on it. Otherwise, `{#|123|community}` will be left as is:
|
118
|
+
```ruby
|
119
|
+
comment.body
|
120
|
+
# => Hello, @john! Can you please join the {#|123|community}?
|
121
|
+
|
122
|
+
mentionables
|
123
|
+
# => [#<User:...>]
|
124
|
+
```
|
125
|
+
|
126
|
+
Just in case, `ActsAsMentionable::MentionerParser` class does not fit your needs, you can either extend or define your own class, which parses `parsed_body` field, writes `body` and returns mentioned records. Here you can see implementation details of the existing one [mentioner_parser.rb](https://github.com/Fuseit/acts_as_mentionable/blob/master/lib/acts_as_mentionable/mentioner_parser.rb).
|
127
|
+
|
128
|
+
### Saving mentions
|
129
|
+
|
130
|
+
In order to save mentioned records you can use of the following methods defined on mentioner:
|
131
|
+
|
132
|
+
Mention records, `save` option is set to `false` by default:
|
133
|
+
```ruby
|
134
|
+
comment.mention mentionables, save: false
|
135
|
+
```
|
136
|
+
|
137
|
+
Unmention records, `save` option is set to `false` by default:
|
138
|
+
```ruby
|
139
|
+
comment.unmention mentionables, save: false
|
140
|
+
```
|
141
|
+
|
142
|
+
Replace mentioned records, `save` option is set to `false` by default:
|
143
|
+
```ruby
|
144
|
+
comment.replace_mentionables mentionables, save: false
|
145
|
+
```
|
146
|
+
|
147
|
+
Save changes to mentions:
|
148
|
+
```ruby
|
149
|
+
comment.save_mentions
|
150
|
+
```
|
151
|
+
|
152
|
+
### Callbacks
|
153
|
+
|
154
|
+
When there are changes to mentions and mentions being saved a callback with mentioner record and mention changes can be invoked. In order to define a callback, add the Rails initializer like the following:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
# config/initializers/acts_as_mentionable.rb
|
158
|
+
|
159
|
+
ActsAsMentionable.setup do |configuration|
|
160
|
+
configuration.mentions_updated_callback = lambda do |mentioner, changes|
|
161
|
+
p mentioner
|
162
|
+
# => #<Comment:...>
|
163
|
+
|
164
|
+
p changes
|
165
|
+
# =>
|
166
|
+
# {
|
167
|
+
# changed: true,
|
168
|
+
# added: [#<User:...>, #<Community:...>],
|
169
|
+
# removed: [#<User:...>],
|
170
|
+
# previous: [#<User:...>],
|
171
|
+
# current: [#<User:...>, #<Community:...>]
|
172
|
+
# }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
Where, `mentioner` in our case is a comment record, which mentioned users, and changes is a hash of changes, which has the following keys:
|
178
|
+
* `:changed` - detects if changed is being made to mentions
|
179
|
+
* `:added` - defines, which mentions were added
|
180
|
+
* `:removed` - defines, which mentions were removed
|
181
|
+
* `:previous` - defines, which mentions were before
|
182
|
+
* `:current` - defines, which mentions are currently saved
|
183
|
+
|
184
|
+
### Mentioner methods
|
185
|
+
|
186
|
+
* `#mentioner?` - is always set to `true` for mentioner records (e.g. comment), otherwise is set to `false`
|
187
|
+
* `#mentionables` - returns mentioned records
|
188
|
+
* `#mention *mentionables, save: false` - mention records
|
189
|
+
* `#unmention *mentionables, save: false` - unmention records
|
190
|
+
* `#replace_mentionables *mentionables, save: false` - replace mentioned records
|
191
|
+
* `#save_mentions` - save changes to mentions
|
192
|
+
* `#retrieve_mentions_callback` - is a callback method, which should retrieve and save mentions. It does nothing by default, so you have to redefine it.
|
193
|
+
* `#need_retrieve_mentions?` - returns `true` if `#retrieve_mentions_callback` should be invoked. By default it's an alias to mentioner's `parsed_*_changed?` attribute, e.g. `parsed_body_changed?`. You can redefine this method if you want to have custom logic for invoking `#retrieve_mentions_callback`.
|
194
|
+
|
195
|
+
### Mentionable methods
|
196
|
+
|
197
|
+
* `#mentionable?` - is always set to `true` for mentionable records (e.g. user), otherwise is set to `false`
|
198
|
+
* `#mentionables` - returns mentioner records, e. g. an array of comments, where user is mentioned
|
199
|
+
|
200
|
+
## Development
|
201
|
+
|
202
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
203
|
+
|
204
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
205
|
+
|
206
|
+
## Contributing
|
207
|
+
|
208
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Fuseit/acts_as_mentionable.
|
209
|
+
|
210
|
+
## License
|
211
|
+
|
212
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'acts_as_mentionable/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'acts_as_mentionable'
|
7
|
+
spec.version = ActsAsMentionable::VERSION
|
8
|
+
spec.authors = ['Baron Bloomer', 'Dmitry Radionov', 'Nazar Vinnychuk']
|
9
|
+
spec.email = ['baronbloomer@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Add the ability to mention ActiveRecord objects such as users within text!'
|
12
|
+
spec.description = 'With ActsAsMentionable you can mention a different models in different contents.'
|
13
|
+
spec.homepage = 'https://github.com/Fuseit/acts_as_mentionable'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
|
20
|
+
spec.bindir = 'exe'
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
|
23
|
+
spec.require_paths = %w[lib]
|
24
|
+
spec.required_ruby_version = '>= 2.3.4'
|
25
|
+
|
26
|
+
# Need to support different ActiveRecord versions i.e. 4.2, 5+
|
27
|
+
# Differentiate through versioned code?
|
28
|
+
spec.add_runtime_dependency 'activerecord', '~> 4.2'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
31
|
+
spec.add_development_dependency 'database_cleaner', '~> 1.7'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.5'
|
33
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
34
|
+
spec.add_development_dependency 'rspec-its', '~> 1.2'
|
35
|
+
spec.add_development_dependency 'rspec_junit_formatter'
|
36
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3'
|
37
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'acts_as_mentionable'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
if ActiveRecord.gem_version >= Gem::Version.new('5.0')
|
2
|
+
class ActsAsMentionableMigration < ActiveRecord::Migration[4.2]; end
|
3
|
+
else
|
4
|
+
class ActsAsMentionableMigration < ActiveRecord::Migration; end
|
5
|
+
end
|
6
|
+
|
7
|
+
ActsAsMentionableMigration.class_eval do
|
8
|
+
def change
|
9
|
+
create_table ActsAsMentionable.mentions_table do |t|
|
10
|
+
t.references :mentionable, polymorphic: true, index: { name: 'mentions_mentionable_idx' }
|
11
|
+
t.references :mentioner, polymorphic: true, index: { name: 'mentions_mentioner_idx' }
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index ActsAsMentionable.mentions_table,
|
16
|
+
%i[mentionable_id mentionable_type mentioner_id mentioner_type],
|
17
|
+
name: 'mentions_mentionable_mentioner_idx',
|
18
|
+
unique: true
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
module ActiveRecordMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def acts_as_mentionable mentionable_field
|
7
|
+
class_eval do
|
8
|
+
cattr_reader :mentionable_field
|
9
|
+
|
10
|
+
class_variable_set('@@mentionable_field', mentionable_field)
|
11
|
+
end
|
12
|
+
include ActsAsMentionable::Mentionable
|
13
|
+
end
|
14
|
+
|
15
|
+
def acts_as_mentioner mention_field
|
16
|
+
class_eval do
|
17
|
+
cattr_reader :mention_field, :mention_parsed_field
|
18
|
+
|
19
|
+
after_save :retrieve_mentions_callback, if: :need_retrieve_mentions? if self <= ActiveRecord::Base
|
20
|
+
|
21
|
+
define_method(:need_retrieve_mentions?) { send "parsed_#{self.class.mention_field}_changed?" }
|
22
|
+
|
23
|
+
define_method(:retrieve_mentions_callback) { nil }
|
24
|
+
|
25
|
+
class_variable_set('@@mention_field', mention_field)
|
26
|
+
class_variable_set('@@mention_parsed_field', "parsed_#{mention_field}".to_sym)
|
27
|
+
end
|
28
|
+
include ActsAsMentionable::Mentioner
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def mentionable?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def mentioner?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: mentions
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# mentionable_id :integer not null
|
7
|
+
# mentionable_type :string(255) not null
|
8
|
+
# mentioner_id :integer not null
|
9
|
+
# mentioner_type :string(255) not null
|
10
|
+
# created_at :datetime not null
|
11
|
+
# updated_at :datetime not null
|
12
|
+
#
|
13
|
+
|
14
|
+
module ActsAsMentionable
|
15
|
+
class Mention < ::ActiveRecord::Base
|
16
|
+
self.table_name = ActsAsMentionable.mentions_table
|
17
|
+
|
18
|
+
belongs_to :mentioner, polymorphic: true
|
19
|
+
belongs_to :mentionable, polymorphic: true
|
20
|
+
|
21
|
+
scope :by_mentioners, ->(mentioners) { where mentioner: mentioners }
|
22
|
+
scope :by_mentionables, ->(mentionables) { where mentionable: mentionables }
|
23
|
+
|
24
|
+
validate :validate_mentioner
|
25
|
+
validate :validate_mentionable
|
26
|
+
|
27
|
+
def self.remove_mentionables_for_mentioner mentioner, mentionables
|
28
|
+
by_mentioners(mentioner).by_mentionables(mentionables).delete_all
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.add_mentionables_for_mentioner mentioner, mentionables
|
32
|
+
attributes_list = Array(mentionables).map { |mentionable| { mentionable: mentionable } }
|
33
|
+
by_mentioners(mentioner).create! attributes_list
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_mentioner
|
39
|
+
errors.add :mentioner, :invalid unless mentioner.respond_to?(:mentioner?) && mentioner.mentioner?
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_mentionable
|
43
|
+
errors.add :mentionable, :invalid unless mentionable.respond_to?(:mentionable?) && mentionable.mentionable?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
module Mentionable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_many :mentions,
|
7
|
+
as: :mentionable,
|
8
|
+
dependent: :delete_all,
|
9
|
+
class_name: '::ActsAsMentionable::Mention'
|
10
|
+
end
|
11
|
+
|
12
|
+
def mentionable?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def mentioners
|
17
|
+
RetrievePolymorphic.new(mentions, :mentioner).call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
class MentionablesManipulator
|
3
|
+
attr_reader :previous_set, :current_set
|
4
|
+
|
5
|
+
def initialize mentionables
|
6
|
+
@previous_set = mentionables.to_set
|
7
|
+
@current_set = mentionables.to_set
|
8
|
+
end
|
9
|
+
|
10
|
+
def add *mentionables
|
11
|
+
current_set.merge prepare_mentionables(mentionables)
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove *mentionables
|
16
|
+
current_set.subtract prepare_mentionables(mentionables)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def replace *mentionables
|
21
|
+
current_set.replace prepare_mentionables(mentionables)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def fix_changes!
|
26
|
+
previous_set.replace current_set
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def changes
|
31
|
+
{
|
32
|
+
changed: changed?,
|
33
|
+
added: added,
|
34
|
+
removed: removed,
|
35
|
+
previous: previous,
|
36
|
+
current: current
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def changed?
|
41
|
+
current_set != previous_set
|
42
|
+
end
|
43
|
+
|
44
|
+
def added
|
45
|
+
(current_set - previous_set).to_a
|
46
|
+
end
|
47
|
+
|
48
|
+
def removed
|
49
|
+
(previous_set - current_set).to_a
|
50
|
+
end
|
51
|
+
|
52
|
+
def previous
|
53
|
+
previous_set.to_a
|
54
|
+
end
|
55
|
+
|
56
|
+
def current
|
57
|
+
current_set.to_a
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def prepare_mentionables mentionables
|
63
|
+
mentionables.flatten
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
module Mentioner
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_many :mentions,
|
7
|
+
as: :mentioner,
|
8
|
+
dependent: :delete_all,
|
9
|
+
class_name: '::ActsAsMentionable::Mention'
|
10
|
+
end
|
11
|
+
|
12
|
+
def mentioner?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def mentionables
|
17
|
+
RetrievePolymorphic.new(mentions, :mentionable).call
|
18
|
+
end
|
19
|
+
|
20
|
+
def mention *mentionables, save: false
|
21
|
+
mentionables_manipulator.add(*mentionables)
|
22
|
+
return_changes { save_mentions if save }
|
23
|
+
end
|
24
|
+
|
25
|
+
def unmention *mentionables, save: false
|
26
|
+
mentionables_manipulator.remove(*mentionables)
|
27
|
+
return_changes { save_mentions if save }
|
28
|
+
end
|
29
|
+
|
30
|
+
def replace_mentionables *mentionables, save: false
|
31
|
+
mentionables_manipulator.replace(*mentionables)
|
32
|
+
return_changes { save_mentions if save }
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_mentions
|
36
|
+
return unless mentionables_manipulator.changed?
|
37
|
+
|
38
|
+
return_changes do
|
39
|
+
MentionsUpdater.new(self, mentionables_manipulator.changes).call do
|
40
|
+
fix_mentionables_changes!
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def return_changes
|
48
|
+
mentionables_manipulator.changes.tap { yield }
|
49
|
+
end
|
50
|
+
|
51
|
+
def mentionables_manipulator
|
52
|
+
@mentionables_manipulator ||= MentionablesManipulator.new mentionables
|
53
|
+
end
|
54
|
+
|
55
|
+
def refresh_mentionables_manipulator!
|
56
|
+
remove_instance_variable :@mentionables_manipulator
|
57
|
+
end
|
58
|
+
|
59
|
+
def fix_mentionables_changes!
|
60
|
+
mentionables_manipulator.fix_changes!
|
61
|
+
|
62
|
+
TransactionCallbacks.on_rolled_back do
|
63
|
+
current = mentionables_manipulator.current
|
64
|
+
refresh_mentionables_manipulator!
|
65
|
+
mentionables_manipulator.replace current
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
class MentionerParser
|
3
|
+
attr_reader :mentioner
|
4
|
+
|
5
|
+
def initialize mentioner
|
6
|
+
@mentioner = mentioner
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse!
|
10
|
+
mentionables = []
|
11
|
+
new_mention_field = mentioner.send(mentioner.class.mention_parsed_field).gsub(template_for_parsing) do |template|
|
12
|
+
_delimiter, mentionable_id, mentionable_klass = template.gsub(/[\{,\}]/, '').split('|')
|
13
|
+
mentionable = retrieve_mentionable mentionable_klass, mentionable_id
|
14
|
+
mentionables << mentionable if mentionable.respond_to?(:mentionable?) && mentionable.mentionable?
|
15
|
+
metionable_template mentionable, template
|
16
|
+
end
|
17
|
+
mentioner.update_column(mentioner.class.mention_field, new_mention_field)
|
18
|
+
mentionables
|
19
|
+
end
|
20
|
+
|
21
|
+
def retrieve_mentionable mentionable_klass, mentionable_id
|
22
|
+
mentionable_klass.classify.constantize.find mentionable_id
|
23
|
+
rescue NameError
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def template_for_parsing
|
30
|
+
/\{@\|\d+\|\w+\}/
|
31
|
+
end
|
32
|
+
|
33
|
+
def metionable_template mentionable, template
|
34
|
+
if mentionable.respond_to?(:mentionable?) && mentionable.mentionable?
|
35
|
+
"<U+2063>@#{mentionable.send(mentionable.class.mentionable_field)}<U+2063>"
|
36
|
+
else
|
37
|
+
template
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
class MentionsUpdater
|
3
|
+
attr_reader :mentioner, :changes
|
4
|
+
|
5
|
+
def initialize mentioner, changes
|
6
|
+
@mentioner = mentioner
|
7
|
+
@changes = changes
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
Mention.transaction do
|
12
|
+
remove_old_mentionables
|
13
|
+
add_new_mentionables
|
14
|
+
invoke_mentions_updated_callback
|
15
|
+
yield if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def remove_old_mentionables
|
22
|
+
Mention.remove_mentionables_for_mentioner mentioner, changes[:removed] unless changes[:removed].empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_new_mentionables
|
26
|
+
Mention.add_mentionables_for_mentioner mentioner, changes[:added] unless changes[:added].empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def invoke_mentions_updated_callback
|
30
|
+
TransactionCallbacks.on_committed do
|
31
|
+
ActsAsMentionable.mentions_updated_callback.call mentioner, changes
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
class RetrievePolymorphic
|
3
|
+
attr_reader :relation, :polymorphic_association_name
|
4
|
+
|
5
|
+
def initialize relation, polymorphic_association_name
|
6
|
+
@relation = relation
|
7
|
+
@polymorphic_association_name = polymorphic_association_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
type_to_ids.map { |type, ids| type.constantize.unscoped.find ids }.flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def type_and_id_list
|
17
|
+
relation.pluck "#{polymorphic_association_name}_type", "#{polymorphic_association_name}_id"
|
18
|
+
end
|
19
|
+
|
20
|
+
def type_to_ids
|
21
|
+
type_and_id_list.group_by(&:first).each_with_object({}) do |(type, type_and_id), hash|
|
22
|
+
hash[type] = type_and_id.map(&:last)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ActsAsMentionable
|
2
|
+
class TransactionCallbacks
|
3
|
+
class << self
|
4
|
+
def on_committed &block
|
5
|
+
if current_transaction.open?
|
6
|
+
add_transaction_record new(block, nil)
|
7
|
+
else
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_rolled_back &block
|
13
|
+
add_transaction_record new(nil, block)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def add_transaction_record record
|
19
|
+
current_transaction.add_record record
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_transaction
|
23
|
+
Mention.connection.current_transaction
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :on_committed_block, :on_rolled_back_block
|
28
|
+
|
29
|
+
def initialize on_committed_block, on_rolled_back_block
|
30
|
+
@on_committed_block = on_committed_block
|
31
|
+
@on_rolled_back_block = on_rolled_back_block
|
32
|
+
end
|
33
|
+
|
34
|
+
def committed! _should_run_callbacks = true
|
35
|
+
on_committed_block&.call
|
36
|
+
end
|
37
|
+
|
38
|
+
def rolledback! _force_restore_state = false, _should_run_callbacks = true
|
39
|
+
on_rolled_back_block&.call
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'acts_as_mentionable/version'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rails/engine'
|
6
|
+
require 'acts_as_mentionable/engine'
|
7
|
+
rescue LoadError
|
8
|
+
puts 'Rails enviroment is not detected - database migrations are not appended.'
|
9
|
+
end
|
10
|
+
|
11
|
+
module ActsAsMentionable
|
12
|
+
extend ActiveSupport::Autoload
|
13
|
+
|
14
|
+
autoload :Mention
|
15
|
+
autoload :MentionablesManipulator
|
16
|
+
autoload :MentionsUpdater
|
17
|
+
autoload :Mentionable
|
18
|
+
autoload :Mentioner
|
19
|
+
autoload :ActiveRecordMethods
|
20
|
+
autoload :RetrievePolymorphic
|
21
|
+
autoload :TransactionCallbacks
|
22
|
+
autoload :MentionerParser
|
23
|
+
|
24
|
+
def self.setup
|
25
|
+
@configuration ||= Configuration.new
|
26
|
+
yield @configuration if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.respond_to_missing? method_name, include_private = false
|
30
|
+
@configuration.respond_to?(method_name) || super
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.method_missing method_name, *args, &block
|
34
|
+
if @configuration.respond_to?(method_name)
|
35
|
+
@configuration.send(method_name, *args, &block)
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Configuration
|
42
|
+
attr_accessor \
|
43
|
+
:mentions_table,
|
44
|
+
:mentions_updated_callback
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@mentions_table = :acts_as_mentionable_mentions
|
48
|
+
@mentions_updated_callback = ->(_mentioner, _changes) { nil }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
setup
|
53
|
+
end
|
54
|
+
|
55
|
+
ActiveSupport.on_load :active_record do
|
56
|
+
include ActsAsMentionable::ActiveRecordMethods
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_mentionable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Baron Bloomer
|
8
|
+
- Dmitry Radionov
|
9
|
+
- Nazar Vinnychuk
|
10
|
+
autorequire:
|
11
|
+
bindir: exe
|
12
|
+
cert_chain: []
|
13
|
+
date: 2019-06-18 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '4.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '4.2'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: bundler
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.17'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.17'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: database_cleaner
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.7'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1.7'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rake
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '10.5'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '10.5'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rspec
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.8'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '3.8'
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rspec-its
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '1.2'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - "~>"
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '1.2'
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: rspec_junit_formatter
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
type: :development
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: sqlite3
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '1.3'
|
120
|
+
type: :development
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - "~>"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '1.3'
|
127
|
+
description: With ActsAsMentionable you can mention a different models in different
|
128
|
+
contents.
|
129
|
+
email:
|
130
|
+
- baronbloomer@gmail.com
|
131
|
+
executables: []
|
132
|
+
extensions: []
|
133
|
+
extra_rdoc_files: []
|
134
|
+
files:
|
135
|
+
- ".circleci/config.yml"
|
136
|
+
- ".gitignore"
|
137
|
+
- ".rspec"
|
138
|
+
- ".rubocop.yml"
|
139
|
+
- ".travis.yml"
|
140
|
+
- Gemfile
|
141
|
+
- LICENSE.txt
|
142
|
+
- README.md
|
143
|
+
- Rakefile
|
144
|
+
- acts_as_mentionable.gemspec
|
145
|
+
- bin/console
|
146
|
+
- bin/setup
|
147
|
+
- db/migrate/1_acts_as_mentionable_migration.rb
|
148
|
+
- lib/acts_as_mentionable.rb
|
149
|
+
- lib/acts_as_mentionable/active_record_methods.rb
|
150
|
+
- lib/acts_as_mentionable/engine.rb
|
151
|
+
- lib/acts_as_mentionable/mention.rb
|
152
|
+
- lib/acts_as_mentionable/mentionable.rb
|
153
|
+
- lib/acts_as_mentionable/mentionables_manipulator.rb
|
154
|
+
- lib/acts_as_mentionable/mentioner.rb
|
155
|
+
- lib/acts_as_mentionable/mentioner_parser.rb
|
156
|
+
- lib/acts_as_mentionable/mentions_updater.rb
|
157
|
+
- lib/acts_as_mentionable/retrieve_polymorphic.rb
|
158
|
+
- lib/acts_as_mentionable/transaction_callbacks.rb
|
159
|
+
- lib/acts_as_mentionable/version.rb
|
160
|
+
homepage: https://github.com/Fuseit/acts_as_mentionable
|
161
|
+
licenses:
|
162
|
+
- MIT
|
163
|
+
metadata: {}
|
164
|
+
post_install_message:
|
165
|
+
rdoc_options: []
|
166
|
+
require_paths:
|
167
|
+
- lib
|
168
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 2.3.4
|
173
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '0'
|
178
|
+
requirements: []
|
179
|
+
rubyforge_project:
|
180
|
+
rubygems_version: 2.7.8
|
181
|
+
signing_key:
|
182
|
+
specification_version: 4
|
183
|
+
summary: Add the ability to mention ActiveRecord objects such as users within text!
|
184
|
+
test_files: []
|