followability 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96a742ecd47a5d0cc1f9c5247d991f5da15540fe32f4ed1f5cb340ff42f75560
4
- data.tar.gz: d3f37786e80e7d1adbc6e74de84b43f9c2e923208cc3673d3557a2b3baa73f8c
3
+ metadata.gz: 74981f3a807c733f78c36f78ebec08e18f9469415ca5fe500ba902a244f2490a
4
+ data.tar.gz: 49109e4a45692f73f206b3847610d0c725a8837b7a561d5f99b5641e6e53e89c
5
5
  SHA512:
6
- metadata.gz: 990e5646cb61a4300be524bb48e875012daa5faff1362ebf9ba6ed2547a0b66cb323b27e4e882e30a486986750ae201ec6ad858dcf8f35c34a390fb2f07400c6
7
- data.tar.gz: 1901bab6ec5670d0337357789fb2e8d82192a469d00e7d5345970e21269435281df397fbd50cf39fdce7b98996eb83e5947c5fe1d5302215b83b1ac3a7f0e4e3
6
+ metadata.gz: cd0716f68c519a24544e47d9d88c21a1eb006485c99b92419cd166dcd847c9876e2b3fa3df968afd061e8ce3a13ff481893e8dd09f331aad47e045a505b5ab4b
7
+ data.tar.gz: 29b0bda4d267f4d8b34a1a5f438e64eb5135e775af9cb283815570e417fd35eb322e7e1b612062b3a2972fc58904b70c41bfe353d9f66cd12758f24277170fe2
data/.rubocop.yml CHANGED
@@ -20,4 +20,10 @@ Style/Documentation:
20
20
  Metrics/MethodLength:
21
21
  Enabled: true
22
22
  CountComments: false
23
- Max: 20
23
+ Max: 30
24
+
25
+ Metrics/AbcSize:
26
+ Max: 30
27
+
28
+ Metrics/BlockLength:
29
+ Max: 30
data/CHANGELOG.md CHANGED
@@ -1,4 +1,16 @@
1
- ## [Unreleased]
1
+ ## [1.0.0] - 2022-10-15
2
+ - Added belowing methods
3
+ - decline_follow_request_of
4
+ - remove_follow_request_for
5
+ - send_follow_request_to
6
+ - following?
7
+ - mutual_following_with?
8
+ - sent_follow_request_to?
9
+ - block_to
10
+ - unblock_to
11
+ - blocked?
12
+ - blocked_by?
13
+ - myself?
2
14
 
3
15
  ## [0.1.0] - 2022-10-14
4
16
 
data/Gemfile CHANGED
@@ -25,3 +25,15 @@ gem 'dry-configurable', '~> 0.15.0'
25
25
 
26
26
  # Pry is a runtime developer console and IRB alternative with powerful introspection capabilities [https://github.com/pry/pry]
27
27
  gem 'pry', '~> 0.14.1'
28
+
29
+ # A toolkit of support libraries and Ruby core extensions extracted from the Rails framework [https://github.com/rails/rails]
30
+ gem 'activesupport', '>= 5.0', '>= 7.0.4'
31
+
32
+ # A toolkit for building modeling frameworks like Active Record [https://github.com/rails/rails]
33
+ gem 'activemodel', '>= 5.0', '>= 7.0.4'
34
+
35
+ # Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes [https://github.com/rails/rails]
36
+ gem 'activerecord', '>= 5.0', '>= 7.0.4'
37
+
38
+ # Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity [https://github.com/rails/rails]
39
+ gem 'rails', '>= 5.0', '>= 7.0.4'
data/Gemfile.lock CHANGED
@@ -6,10 +6,77 @@ PATH
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ actioncable (7.0.4)
10
+ actionpack (= 7.0.4)
11
+ activesupport (= 7.0.4)
12
+ nio4r (~> 2.0)
13
+ websocket-driver (>= 0.6.1)
14
+ actionmailbox (7.0.4)
15
+ actionpack (= 7.0.4)
16
+ activejob (= 7.0.4)
17
+ activerecord (= 7.0.4)
18
+ activestorage (= 7.0.4)
19
+ activesupport (= 7.0.4)
20
+ mail (>= 2.7.1)
21
+ net-imap
22
+ net-pop
23
+ net-smtp
24
+ actionmailer (7.0.4)
25
+ actionpack (= 7.0.4)
26
+ actionview (= 7.0.4)
27
+ activejob (= 7.0.4)
28
+ activesupport (= 7.0.4)
29
+ mail (~> 2.5, >= 2.5.4)
30
+ net-imap
31
+ net-pop
32
+ net-smtp
33
+ rails-dom-testing (~> 2.0)
34
+ actionpack (7.0.4)
35
+ actionview (= 7.0.4)
36
+ activesupport (= 7.0.4)
37
+ rack (~> 2.0, >= 2.2.0)
38
+ rack-test (>= 0.6.3)
39
+ rails-dom-testing (~> 2.0)
40
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
41
+ actiontext (7.0.4)
42
+ actionpack (= 7.0.4)
43
+ activerecord (= 7.0.4)
44
+ activestorage (= 7.0.4)
45
+ activesupport (= 7.0.4)
46
+ globalid (>= 0.6.0)
47
+ nokogiri (>= 1.8.5)
48
+ actionview (7.0.4)
49
+ activesupport (= 7.0.4)
50
+ builder (~> 3.1)
51
+ erubi (~> 1.4)
52
+ rails-dom-testing (~> 2.0)
53
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
54
+ activejob (7.0.4)
55
+ activesupport (= 7.0.4)
56
+ globalid (>= 0.3.6)
57
+ activemodel (7.0.4)
58
+ activesupport (= 7.0.4)
59
+ activerecord (7.0.4)
60
+ activemodel (= 7.0.4)
61
+ activesupport (= 7.0.4)
62
+ activestorage (7.0.4)
63
+ actionpack (= 7.0.4)
64
+ activejob (= 7.0.4)
65
+ activerecord (= 7.0.4)
66
+ activesupport (= 7.0.4)
67
+ marcel (~> 1.0)
68
+ mini_mime (>= 1.1.0)
69
+ activesupport (7.0.4)
70
+ concurrent-ruby (~> 1.0, >= 1.0.2)
71
+ i18n (>= 1.6, < 2)
72
+ minitest (>= 5.1)
73
+ tzinfo (~> 2.0)
9
74
  ast (2.4.2)
10
75
  awesome_print (1.9.2)
76
+ builder (3.2.4)
11
77
  coderay (1.1.3)
12
78
  concurrent-ruby (1.1.10)
79
+ crass (1.0.6)
13
80
  dry-configurable (0.15.0)
14
81
  concurrent-ruby (~> 1.0)
15
82
  dry-core (~> 0.6)
@@ -18,15 +85,68 @@ GEM
18
85
  dry-monads (1.4.0)
19
86
  concurrent-ruby (~> 1.0)
20
87
  dry-core (~> 0.7)
88
+ erubi (1.11.0)
89
+ globalid (1.0.0)
90
+ activesupport (>= 5.0)
91
+ i18n (1.12.0)
92
+ concurrent-ruby (~> 1.0)
21
93
  json (2.6.2)
94
+ loofah (2.19.0)
95
+ crass (~> 1.0.2)
96
+ nokogiri (>= 1.5.9)
97
+ mail (2.7.1)
98
+ mini_mime (>= 0.1.1)
99
+ marcel (1.0.2)
22
100
  method_source (1.0.0)
101
+ mini_mime (1.1.2)
23
102
  minitest (5.16.3)
103
+ net-imap (0.3.1)
104
+ net-protocol
105
+ net-pop (0.1.2)
106
+ net-protocol
107
+ net-protocol (0.1.3)
108
+ timeout
109
+ net-smtp (0.3.2)
110
+ net-protocol
111
+ nio4r (2.5.8)
112
+ nokogiri (1.13.8-arm64-darwin)
113
+ racc (~> 1.4)
24
114
  parallel (1.22.1)
25
115
  parser (3.1.2.1)
26
116
  ast (~> 2.4.1)
27
117
  pry (0.14.1)
28
118
  coderay (~> 1.1)
29
119
  method_source (~> 1.0)
120
+ racc (1.6.0)
121
+ rack (2.2.4)
122
+ rack-test (2.0.2)
123
+ rack (>= 1.3)
124
+ rails (7.0.4)
125
+ actioncable (= 7.0.4)
126
+ actionmailbox (= 7.0.4)
127
+ actionmailer (= 7.0.4)
128
+ actionpack (= 7.0.4)
129
+ actiontext (= 7.0.4)
130
+ actionview (= 7.0.4)
131
+ activejob (= 7.0.4)
132
+ activemodel (= 7.0.4)
133
+ activerecord (= 7.0.4)
134
+ activestorage (= 7.0.4)
135
+ activesupport (= 7.0.4)
136
+ bundler (>= 1.15.0)
137
+ railties (= 7.0.4)
138
+ rails-dom-testing (2.0.3)
139
+ activesupport (>= 4.2.0)
140
+ nokogiri (>= 1.6)
141
+ rails-html-sanitizer (1.4.3)
142
+ loofah (~> 2.3)
143
+ railties (7.0.4)
144
+ actionpack (= 7.0.4)
145
+ activesupport (= 7.0.4)
146
+ method_source
147
+ rake (>= 12.2)
148
+ thor (~> 1.0)
149
+ zeitwerk (~> 2.5)
30
150
  rainbow (3.1.1)
31
151
  rake (13.0.6)
32
152
  regexp_parser (2.6.0)
@@ -44,18 +164,30 @@ GEM
44
164
  rubocop-ast (1.21.0)
45
165
  parser (>= 3.1.1.0)
46
166
  ruby-progressbar (1.11.0)
167
+ thor (1.2.1)
168
+ timeout (0.3.0)
169
+ tzinfo (2.0.5)
170
+ concurrent-ruby (~> 1.0)
47
171
  unicode-display_width (2.3.0)
172
+ websocket-driver (0.7.5)
173
+ websocket-extensions (>= 0.1.0)
174
+ websocket-extensions (0.1.5)
175
+ zeitwerk (2.6.1)
48
176
 
49
177
  PLATFORMS
50
178
  arm64-darwin-21
51
179
 
52
180
  DEPENDENCIES
181
+ activemodel (>= 7.0.4, >= 5.0)
182
+ activerecord (>= 7.0.4, >= 5.0)
183
+ activesupport (>= 7.0.4, >= 5.0)
53
184
  awesome_print
54
185
  dry-configurable (~> 0.15.0)
55
186
  dry-monads (~> 1.4)
56
187
  followability!
57
188
  minitest (~> 5.0)
58
189
  pry (~> 0.14.1)
190
+ rails (>= 7.0.4, >= 5.0)
59
191
  rake (~> 13.0)
60
192
  rubocop (~> 1.21)
61
193
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Gem Version](https://badge.fury.io/rb/btc_turk.svg)](https://badge.fury.io/rb/btc_turk)
1
+ [![Gem Version](https://badge.fury.io/rb/followability.svg)](https://badge.fury.io/rb/followability)
2
2
  ![test](https://github.com/nejdetkadir/followability/actions/workflows/test.yml/badge.svg?branch=main)
3
3
  ![rubocop](https://github.com/nejdetkadir/followability/actions/workflows/rubocop.yml/badge.svg?branch=main)
4
4
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
@@ -13,15 +13,228 @@ gem 'followability', github: 'nejdetkadir/followability', branch: 'main'
13
13
  ```
14
14
 
15
15
  Install the gem and add to the application's Gemfile by executing:
16
-
17
- $ bundle add followability
16
+ ```bash
17
+ $ bundle add followability
18
+ ```
18
19
 
19
20
  If bundler is not being used to manage dependencies, install the gem by executing:
21
+ ```bash
22
+ $ gem install followability
23
+ ```
20
24
 
21
- $ gem install followability
25
+ Run the generator for creating database migration and copying localization files.
26
+ ```bash
27
+ $ rails g followability:install
28
+ ```
22
29
 
23
30
  ## Usage
24
- TODO: Write usage instructions here
31
+ Simply drop in `followability` to a model:
32
+
33
+ ```ruby
34
+ class User < ActiveRecord::Base
35
+ followability
36
+ end
37
+ ```
38
+
39
+ Now, instances of `User` have followability.
40
+ ```ruby
41
+ User.followability?
42
+ # => true
43
+ ```
44
+
45
+ ### Following actions
46
+ Avaiable methods:
47
+ - decline_follow_request_of
48
+ - remove_follow_request_for
49
+ - send_follow_request_to
50
+ - following?
51
+ - mutual_following_with?
52
+ - sent_follow_request_to?
53
+
54
+ ### Usage
55
+ ```ruby
56
+ @foo = User.first
57
+ @bar = User.last
58
+
59
+ @foo.send_follow_request_to(@bar)
60
+ # => true
61
+
62
+ @foo.sent_follow_request_to?(@bar)
63
+ # => true
64
+
65
+ @bar.decline_follow_request_of(@foo)
66
+ # => true
67
+
68
+ @bar.accept_follow_request_of(@foo)
69
+ # => false
70
+
71
+ @bar.errors.full_messages
72
+ # => [...]
73
+
74
+ @foo.remove_follow_request_for(@bar)
75
+ # => false
76
+
77
+ @foo.errors.full_messages
78
+ # => [...]
79
+
80
+ @foo.mutual_following_with?(@bar)
81
+ # => false
82
+
83
+ @foo.errors.full_messages
84
+ # => [...]
85
+
86
+ @bar.following?(@foo)
87
+ # => false
88
+
89
+ @foo.errors.full_messages
90
+ # => [...]
91
+
92
+ ```
93
+
94
+ ### Blocking actions
95
+ Avaiable methods:
96
+ - block_to
97
+ - unblock_to
98
+ - blocked?
99
+ - blocked_by?
100
+
101
+ ### Usage
102
+ ```ruby
103
+ @foo.block_to(@bar)
104
+ # => true
105
+
106
+ @foo.blocked?(@bar)
107
+ # => true
108
+
109
+ @bar.blocked_by?(@foo)
110
+ # => true
111
+
112
+ @foo.unblock_to(@bar)
113
+ # => true
114
+ ```
115
+
116
+ ### Common
117
+ Avaiable methods:
118
+ - myself?
119
+
120
+ ### Usage
121
+ ```ruby
122
+ class User < ActiveRecord::Base
123
+ followability
124
+
125
+ def follow_request_removed_by_someone(record)
126
+ unless myself?(record)
127
+ # Do something
128
+ end
129
+ end
130
+ end
131
+ ```
132
+
133
+ ### Relations
134
+ Avaiable methods:
135
+ - follow_requests
136
+ - pending_requests
137
+ - followerable_relationships
138
+ - followable_relationships
139
+ - followers
140
+ - following
141
+ - blocks
142
+
143
+ ### Usage
144
+ ```ruby
145
+ @foo.follow_requests
146
+ # => [#<Followability::Relationship ...>]
147
+
148
+ @foo.pending_requests
149
+ # => [#<Followability::Relationship ...>]
150
+
151
+ @foo.followerable_relationships
152
+ # => [#<Followability::Relationship ...>]
153
+
154
+ @foo.followable_relationships
155
+ # => [#<Followability::Relationship ...>]
156
+
157
+ @foo.followers
158
+ # => [#<User ...>]
159
+
160
+ @foo.following
161
+ # => [#<User ...>]
162
+
163
+ @foo.blocks
164
+ # => [#<User ...>]
165
+ ```
166
+
167
+ ### Callback Methods
168
+ Available methods:
169
+ - follow_request_sent_to_me
170
+ - follow_request_sent_to_someone
171
+ - follow_request_accepted_by_me
172
+ - follow_request_accepted_by_someone
173
+ - follow_request_declined_by_me
174
+ - follow_request_declined_by_someone
175
+ - follow_request_removed_by_me
176
+ - follow_request_removed_by_someone
177
+ - followable_blocked_by_me
178
+ - followable_blocked_by_someone
179
+ - followable_unblocked_by_me
180
+ - followable_unblocked_by_someone
181
+ - followability_triggered
182
+
183
+ ### Usage
184
+ ```ruby
185
+ class User < ActiveRecord::Base
186
+ followability
187
+
188
+ def follow_request_sent_to_me(record)
189
+ Notifications::FollowRequestSentToMeJob.perform_later(from_id: record.id)
190
+ end
191
+ def follow_request_sent_to_someone(record); end
192
+ def follow_request_accepted_by_me(record); end
193
+ def follow_request_accepted_by_someone(record); end
194
+ def follow_request_declined_by_me(record); end
195
+ def follow_request_declined_by_someone(record); end
196
+ def follow_request_removed_by_me(record); end
197
+ def follow_request_removed_by_someone(record); end
198
+ def followable_blocked_by_me(record); end
199
+ def followable_blocked_by_someone(record); end
200
+ def followable_unblocked_by_me(record); end
201
+ def followable_unblocked_by_someone(record); end
202
+ def followability_triggered(record, callback_name); end
203
+ end
204
+ ```
205
+
206
+ ## I18n
207
+ ```yml
208
+ ---
209
+ ---
210
+ en:
211
+ followability:
212
+ errors:
213
+ block:
214
+ unblock_to:
215
+ myself: 'You can not run this action for yourself'
216
+ block_to:
217
+ myself: 'You can not run this action for yourself'
218
+ blocked_by: 'You can not block to who blocked to you'
219
+ already_blocked: '%{klass} already blocked'
220
+ not_blocked_for_blocking: 'You can not unblock to %{klass} because was not blocked'
221
+ follow:
222
+ decline_follow_request_of:
223
+ myself: 'You can not run this action for yourself'
224
+ empty_relation: 'You can not decline follow request of %{klass} because was not sent'
225
+ accept_follow_request_of:
226
+ myself: 'You can not run this action for yourself'
227
+ empty_relation: 'You can not accept follow request of %{klass} because was not sent'
228
+ remove_follow_request_for:
229
+ empty_relation: 'You can not remove follow request of %{klass} because was not sent'
230
+ myself: 'You can not run this action for yourself'
231
+ send_follow_request_to:
232
+ myself: 'You can not run this action for yourself'
233
+ blocked_by: 'You can not send follow request to who blocked to you'
234
+ following: 'You are already following to %{klass}'
235
+ already_sent: 'You are already sent follow request'
236
+ blocked: 'You can not send follow request to blocked %{klass}'
237
+ ```
25
238
 
26
239
  ## Development
27
240
 
@@ -0,0 +1,28 @@
1
+ ---
2
+ en:
3
+ followability:
4
+ errors:
5
+ block:
6
+ unblock_to:
7
+ myself: 'You can not run this action for yourself'
8
+ block_to:
9
+ myself: 'You can not run this action for yourself'
10
+ blocked_by: 'You can not block to who blocked to you'
11
+ already_blocked: '%{klass} already blocked'
12
+ not_blocked_for_blocking: 'You can not unblock to %{klass} because was not blocked'
13
+ follow:
14
+ decline_follow_request_of:
15
+ myself: 'You can not run this action for yourself'
16
+ empty_relation: 'You can not decline follow request of %{klass} because was not sent'
17
+ accept_follow_request_of:
18
+ myself: 'You can not run this action for yourself'
19
+ empty_relation: 'You can not accept follow request of %{klass} because was not sent'
20
+ remove_follow_request_for:
21
+ empty_relation: 'You can not remove follow request of %{klass} because was not sent'
22
+ myself: 'You can not run this action for yourself'
23
+ send_follow_request_to:
24
+ myself: 'You can not run this action for yourself'
25
+ blocked_by: 'You can not send follow request to who blocked to you'
26
+ following: 'You are already following to %{klass}'
27
+ already_sent: 'You are already sent follow request'
28
+ blocked: 'You can not send follow request to blocked %{klass}'
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Followability
4
+ module Followable
5
+ module Actions
6
+ module Block
7
+ I18N_SCOPE = 'followability.errors.block'
8
+
9
+ def block_to(record)
10
+ if myself?(record)
11
+ errors.add(:base, I18n.t('block_to.myself', scope: I18N_SCOPE, klass: record.class))
12
+
13
+ return false
14
+ end
15
+
16
+ if blocked_by?(record)
17
+ errors.add(:base, I18n.t('block_to.blocked_by', scope: I18N_SCOPE, klass: record.class))
18
+
19
+ return false
20
+ end
21
+
22
+ if blocked?(record)
23
+ errors.add(:base, I18n.t('block.already_blocked', scope: I18N_SCOPE, klass: record.class))
24
+
25
+ return false
26
+ end
27
+
28
+ relation = followerable_relationships.blocked.new(followable: record,
29
+ status: Followability::Relationship.statuses[:blocked])
30
+
31
+ if relation.save
32
+ run_callback(self, affected: record, callback: :followable_blocked_by_me)
33
+ run_callback(record, affected: self, callback: :followable_blocked_by_someone)
34
+
35
+ true
36
+ else
37
+ errors.add(:base, relation.errors.full_messages.to_sentence)
38
+
39
+ false
40
+ end
41
+ end
42
+
43
+ def unblock_to(record)
44
+ if myself?(record)
45
+ errors.add(:base, I18n.t('unblock_to.myself', scope: I18N_SCOPE, klass: record.class))
46
+
47
+ return false
48
+ end
49
+
50
+ unless blocked?(record)
51
+ errors.add(:base, I18n.t(:not_blocked_for_blocking, scope: I18N_SCOPE, klass: record.class))
52
+
53
+ return false
54
+ end
55
+
56
+ relation = followerable_relationships.blocked.find_by(followable: record)
57
+
58
+ if relation.destroy
59
+ run_callback(self, affected: record, callback: :followable_unblocked_by_me)
60
+ run_callback(record, affected: self, callback: :followable_unblocked_by_someone)
61
+
62
+ true
63
+ else
64
+ errors.add(:base, relation.errors.full_messages.to_sentence)
65
+
66
+ false
67
+ end
68
+ end
69
+
70
+ def blocked?(record)
71
+ followerable_relationships.blocked.exists?(followable: record)
72
+ end
73
+
74
+ def blocked_by?(record)
75
+ record.followerable_relationships.blocked.exists?(followable: self)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Followability
4
+ module Followable
5
+ module Actions
6
+ module Common
7
+ def myself?(record)
8
+ id == record.id
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/ModuleLength
4
+ module Followability
5
+ module Followable
6
+ module Actions
7
+ module Follow
8
+ I18N_SCOPE = 'followability.errors.follow'
9
+
10
+ def decline_follow_request_of(record)
11
+ if myself?(record)
12
+ errors.add(:base, I18n.t('decline_follow_request_of.myself', scope: I18N_SCOPE, klass: record.class))
13
+
14
+ return false
15
+ end
16
+
17
+ relation = follow_requests.find_by(followerable_id: record.id, followerable_type: record.class.name)
18
+
19
+ if relation.blank?
20
+ errors.add(:base,
21
+ I18n.t('decline_follow_request_of.empty_relation', scope: I18N_SCOPE, klass: record.class.name))
22
+
23
+ false
24
+ elsif relation.destroy
25
+ run_callback(self, affected: record, callback: :follow_request_declined_by_me)
26
+ run_callback(record, affected: self, callback: :follow_request_declined_by_someone)
27
+
28
+ true
29
+ else
30
+ errors.add(:base, relation.errors.full_messages.to_sentence)
31
+
32
+ false
33
+ end
34
+ end
35
+
36
+ def accept_follow_request_of(record)
37
+ if myself?(record)
38
+ errors.add(:base, I18n.t('accept_follow_request_of.myself', scope: I18N_SCOPE, klass: record.class))
39
+
40
+ return false
41
+ end
42
+
43
+ relation = follow_requests.find_by(followerable_id: record.id, followerable_type: record.class.name)
44
+
45
+ if relation.blank?
46
+ errors.add(:base,
47
+ I18n.t('accept_follow_request_of.empty_relation', scope: I18N_SCOPE, klass: record.class.name))
48
+
49
+ false
50
+ elsif relation.update(status: Followability::Relationship.statuses[:following])
51
+ run_callback(record, affected: record, callback: :follow_request_accepted_by_someone)
52
+ run_callback(self, affected: self, callback: :follow_request_accepted_by_me)
53
+
54
+ true
55
+ else
56
+ errors.add(:base, relation.errors.full_messages.to_sentence)
57
+
58
+ false
59
+ end
60
+ end
61
+
62
+ def remove_follow_request_for(record)
63
+ if myself?(record)
64
+ errors.add(:base, I18n.t('remove_follow_request_for.myself', scope: I18N_SCOPE, klass: record.class))
65
+
66
+ return false
67
+ end
68
+
69
+ relation = pending_requests.find_by(followable_id: record.id, followable_type: record.class.name)
70
+
71
+ if relation.blank?
72
+ errors.add(:base,
73
+ I18n.t('remove_follow_request_for.empty_relation', scope: I18N_SCOPE, klass: record.class.name))
74
+
75
+ false
76
+ elsif relation.destroy
77
+ run_callback(self, affected: record, callback: :follow_request_removed_by_me)
78
+ run_callback(record, affected: self, callback: :follow_request_removed_by_someone)
79
+
80
+ true
81
+ else
82
+ errors.add(:base, relation.errors.full_messages.to_sentence)
83
+
84
+ false
85
+ end
86
+ end
87
+
88
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
89
+ def send_follow_request_to(record)
90
+ if myself?(record)
91
+ errors.add(:base, I18n.t('send_follow_request_to.myself', scope: I18N_SCOPE, klass: record.class))
92
+
93
+ return false
94
+ end
95
+
96
+ if blocked_by?(record)
97
+ errors.add(:base, I18n.t('send_follow_request_to.blocked_by', scope: I18N_SCOPE, klass: record.class.name))
98
+
99
+ return false
100
+ end
101
+
102
+ if following?(record)
103
+ errors.add(:base,
104
+ I18n.t('send_follow_request_to.following', scope: I18N_SCOPE,
105
+ klass: pluralize(record.class.name)))
106
+
107
+ return false
108
+ end
109
+
110
+ if sent_follow_request_to?(record)
111
+ errors.add(:base,
112
+ I18n.t('send_follow_request_to.already_sent', scope: I18N_SCOPE, klass: record.class.name))
113
+
114
+ return false
115
+ end
116
+
117
+ if blocked?(record)
118
+ errors.add(:base, I18n.t('send_follow_request_to.blocked', scope: I18N_SCOPE, klass: record.class.name))
119
+
120
+ return false
121
+ end
122
+
123
+ relation = pending_requests.new(followable: record,
124
+ status: Followability::Relationship.statuses[:requested])
125
+
126
+ if relation.save
127
+ run_callback(self, affected: record, callback: :follow_request_sent_to_someone)
128
+ run_callback(record, affected: self, callback: :follow_request_sent_to_me)
129
+
130
+ true
131
+ else
132
+ errors.add(:base, relation.errors.full_messages.to_sentence)
133
+
134
+ false
135
+ end
136
+ end
137
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
138
+
139
+ def following?(record)
140
+ following.exists?(id: record.id)
141
+ end
142
+
143
+ def mutual_following_with?(record)
144
+ following.exists?(id: record.id) && followers.exists?(id: record.id)
145
+ end
146
+
147
+ def sent_follow_request_to?(record)
148
+ record.follow_requests.exists?(followerable_id: id, followerable_type: self.class.name)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ # rubocop:enable Metrics/ModuleLength
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Followability
4
+ module Followable
5
+ module Associations
6
+ def follow_requests
7
+ followable_relationships.requested
8
+ end
9
+
10
+ def pending_requests
11
+ followerable_relationships.requested
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Followability
4
+ module Followable
5
+ module Callbacks
6
+ METHOD_NAMES = %i[
7
+ follow_request_sent_to_someone
8
+ follow_request_sent_to_me
9
+ follow_request_accepted_by_me
10
+ follow_request_accepted_by_someone
11
+ follow_request_declined_by_me
12
+ follow_request_declined_by_someone
13
+ follow_request_removed_by_me
14
+ follow_request_removed_by_someone
15
+ followable_blocked_by_me
16
+ followable_blocked_by_someone
17
+ followable_unblocked_by_me
18
+ followable_unblocked_by_someone
19
+ followability_triggered
20
+ ].freeze
21
+
22
+ def run_callback(record, callback:, affected:)
23
+ raise ArgumentError if METHOD_NAMES.exclude?(callback) || callback.eql?(:followability_triggered)
24
+
25
+ [callback, :followability_triggered].each do |cb_name|
26
+ record.send(cb_name, affected, cb_name) if record.respond_to?(cb_name)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Followability
4
+ module Followable
5
+ def followability?
6
+ false
7
+ end
8
+
9
+ # rubocop:disable Metrics/MethodLength
10
+ def followability
11
+ class_eval do
12
+ def self.followability?
13
+ true
14
+ end
15
+
16
+ has_many :followerable_relationships,
17
+ as: :followerable,
18
+ class_name: 'Followability::Relationship',
19
+ dependent: :destroy
20
+
21
+ has_many :followable_relationships,
22
+ as: :followable,
23
+ class_name: 'Followability::Relationship',
24
+ dependent: :destroy
25
+
26
+ has_many :followers,
27
+ -> { Followability::Relationship.following },
28
+ through: :followable_relationships,
29
+ source: :followerable,
30
+ class_name: name,
31
+ source_type: name
32
+
33
+ has_many :following,
34
+ -> { Followability::Relationship.following },
35
+ through: :followerable_relationships,
36
+ source: :followable,
37
+ class_name: name,
38
+ source_type: name
39
+
40
+ has_many :blocks,
41
+ -> { Followability::Relationship.blocked },
42
+ through: :followerable_relationships,
43
+ source: :followable,
44
+ class_name: name,
45
+ source_type: name
46
+ end
47
+
48
+ include Followability::Followable::Associations
49
+ include Followability::Followable::Callbacks
50
+ include Followability::Followable::Actions::Common
51
+ include Followability::Followable::Actions::Follow
52
+ include Followability::Followable::Actions::Block
53
+ end
54
+ # rubocop:enable Metrics/MethodLength
55
+ end
56
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module Followability
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ def create_migration
11
+ invoke 'migration', migration_args
12
+ copy_file locale_source, locale_destination
13
+ end
14
+
15
+ def fields_command
16
+ %w[
17
+ followerable:belongs_to{polymorphic}
18
+ followable:belongs_to{polymorphic}
19
+ status:integer
20
+ ]
21
+ end
22
+
23
+ def locale_source
24
+ File.expand_path('../../../config/locales/en.yml', __dir__)
25
+ end
26
+
27
+ def locale_destination
28
+ 'config/locales/followability.en.yml'
29
+ end
30
+
31
+ def migration_name
32
+ 'CreateFollowabilityRelationships'
33
+ end
34
+
35
+ def migration_args
36
+ [migration_name].concat(fields_command)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Followability
4
+ class Relationship < ActiveRecord::Base
5
+ STATUSES = %i[requested blocked following].freeze
6
+
7
+ enum status: STATUSES
8
+
9
+ validates :status, presence: true
10
+
11
+ belongs_to :followerable, polymorphic: true, optional: false
12
+ belongs_to :followable, polymorphic: true, optional: false
13
+
14
+ def self.table_name_prefix
15
+ 'followability_'
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Followability
4
- VERSION = '0.1.0'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/followability.rb CHANGED
@@ -1,8 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'followability/version'
4
+ require 'active_support'
5
+ require 'active_record'
6
+ require 'active_model'
4
7
 
5
8
  module Followability
6
- class Error < StandardError; end
7
- # Your code goes here...
9
+ extend ActiveSupport::Autoload
10
+
11
+ require_relative 'followability/followable/associations'
12
+ require_relative 'followability/followable/callbacks'
13
+ require_relative 'followability/followable/actions/common'
14
+ require_relative 'followability/followable/actions/follow'
15
+ require_relative 'followability/followable/actions/block'
16
+ require_relative 'followability/followable'
17
+ require_relative 'followability/relationship'
18
+ require_relative 'followability/generators/install_generator'
19
+ end
20
+
21
+ ActiveSupport.on_load(:active_record) do
22
+ extend Followability::Followable
8
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: followability
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nejdetkadir
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-14 00:00:00.000000000 Z
11
+ date: 2022-10-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Implements the social network followable functionality for your Active
14
14
  Record models.
@@ -27,8 +27,17 @@ files:
27
27
  - LICENSE
28
28
  - README.md
29
29
  - Rakefile
30
+ - config/locales/en.yml
30
31
  - followability.gemspec
31
32
  - lib/followability.rb
33
+ - lib/followability/followable.rb
34
+ - lib/followability/followable/actions/block.rb
35
+ - lib/followability/followable/actions/common.rb
36
+ - lib/followability/followable/actions/follow.rb
37
+ - lib/followability/followable/associations.rb
38
+ - lib/followability/followable/callbacks.rb
39
+ - lib/followability/generators/install_generator.rb
40
+ - lib/followability/relationship.rb
32
41
  - lib/followability/version.rb
33
42
  - sig/followability.rbs
34
43
  homepage: https://github.com/nejdetkadir/followability