interest 0.0.1

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +329 -0
  7. data/Rakefile +2 -0
  8. data/interest.gemspec +29 -0
  9. data/lib/generators/interest_generator.rb +18 -0
  10. data/lib/generators/templates/migrations/blockings.rb +14 -0
  11. data/lib/generators/templates/migrations/followings.rb +15 -0
  12. data/lib/generators/templates/models/blocking.rb +3 -0
  13. data/lib/generators/templates/models/following.rb +4 -0
  14. data/lib/interest.rb +49 -0
  15. data/lib/interest/base.rb +49 -0
  16. data/lib/interest/blockable.rb +25 -0
  17. data/lib/interest/blockable/blockee.rb +40 -0
  18. data/lib/interest/blockable/blocker.rb +69 -0
  19. data/lib/interest/blockable/blocking.rb +48 -0
  20. data/lib/interest/blockable/exceptions.rb +12 -0
  21. data/lib/interest/definition.rb +45 -0
  22. data/lib/interest/exception.rb +10 -0
  23. data/lib/interest/follow_requestable.rb +25 -0
  24. data/lib/interest/follow_requestable/exceptions.rb +12 -0
  25. data/lib/interest/follow_requestable/follow_request.rb +45 -0
  26. data/lib/interest/follow_requestable/follow_requestee.rb +45 -0
  27. data/lib/interest/follow_requestable/follow_requester.rb +92 -0
  28. data/lib/interest/followable.rb +25 -0
  29. data/lib/interest/followable/exceptions.rb +12 -0
  30. data/lib/interest/followable/followee.rb +41 -0
  31. data/lib/interest/followable/follower.rb +68 -0
  32. data/lib/interest/followable/following.rb +62 -0
  33. data/lib/interest/utils.rb +29 -0
  34. data/lib/interest/version.rb +3 -0
  35. data/spec/blockable_spec.rb +127 -0
  36. data/spec/database.yml.example +3 -0
  37. data/spec/follow_requestable_spec.rb +126 -0
  38. data/spec/followable_spec.rb +171 -0
  39. data/spec/models/blocking_spec.rb +42 -0
  40. data/spec/models/following_spec.rb +120 -0
  41. data/spec/spec_helper.rb +103 -0
  42. data/spec/support/database.rb +10 -0
  43. data/spec/support/models/blockable_user.rb +8 -0
  44. data/spec/support/models/blocking.rb +4 -0
  45. data/spec/support/models/collection.rb +6 -0
  46. data/spec/support/models/follow_requestable_and_blockable_user.rb +8 -0
  47. data/spec/support/models/follow_requestable_user.rb +9 -0
  48. data/spec/support/models/followable_and_blockable_user.rb +9 -0
  49. data/spec/support/models/followable_user.rb +8 -0
  50. data/spec/support/models/following.rb +4 -0
  51. data/spec/support/models/stuff.rb +6 -0
  52. data/spec/support/schema.rb +26 -0
  53. metadata +211 -0
@@ -0,0 +1,14 @@
1
+ class InterestBlockingsMigration < ActiveRecord::Migration
2
+ def change
3
+ create_table :blockings do |t|
4
+ t.belongs_to :blocker, polymorphic: true, null: false
5
+ t.belongs_to :blockee, polymorphic: true, null: false
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :blockings, [:blocker_id, :blocker_type], name: :index_blockings_blocker
11
+ add_index :blockings, [:blockee_id, :blockee_type], name: :index_blockings_blockee
12
+ add_index :blockings, [:blocker_id, :blocker_type, :blockee_id, :blockee_type], unique: true, name: :index_blockings_uniqueness
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ class InterestFollowingsMigration < ActiveRecord::Migration
2
+ def change
3
+ create_table :followings do |t|
4
+ t.belongs_to :follower, polymorphic: true, null: false
5
+ t.belongs_to :followee, polymorphic: true, null: false
6
+ t.string :status, null: false
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :followings, [:follower_id, :follower_type], name: :index_followings_follower
12
+ add_index :followings, [:followee_id, :followee_type], name: :index_followings_followee
13
+ add_index :followings, [:follower_id, :follower_type, :followee_id, :followee_type], unique: true, name: :index_followings_uniqueness
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ class Blocking < ActiveRecord::Base
2
+ include Interest::Blockable::Blocking
3
+ end
@@ -0,0 +1,4 @@
1
+ class Following < ActiveRecord::Base
2
+ include Interest::Followable::Following
3
+ include Interest::FollowRequestable::FollowRequest
4
+ end
data/lib/interest.rb ADDED
@@ -0,0 +1,49 @@
1
+ require "interest/version"
2
+ require "interest/base"
3
+ require "interest/followable"
4
+ require "interest/follow_requestable"
5
+ require "interest/blockable"
6
+ require "active_support"
7
+ require "active_support/core_ext/string/inflections"
8
+ require "active_record"
9
+
10
+ module Interest
11
+ module Extension
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ include Base
16
+ extend Followable::Extension
17
+ extend FollowRequestable::Extension
18
+ extend Blockable::Extension
19
+ end
20
+
21
+ module ClassMethods
22
+ def interest
23
+ acts_as_followable
24
+ acts_as_follow_requestable
25
+ acts_as_blockable
26
+ end
27
+ end
28
+ end
29
+
30
+ class << self
31
+ def following_class_name
32
+ "::Following"
33
+ end
34
+
35
+ def following_class
36
+ @following_class ||= following_class_name.constantize
37
+ end
38
+
39
+ def blocking_class_name
40
+ "::Blocking"
41
+ end
42
+
43
+ def blocking_class
44
+ @blocking_class ||= blocking_class_name.constantize
45
+ end
46
+ end
47
+ end
48
+
49
+ ActiveRecord::Base.__send__ :include, Interest::Extension
@@ -0,0 +1,49 @@
1
+ require "active_support"
2
+ require "interest/followable"
3
+ require "interest/follow_requestable"
4
+ require "interest/blockable"
5
+
6
+ module Interest
7
+ module Base
8
+ extend ActiveSupport::Concern
9
+
10
+ def followable?(other)
11
+ is_a?(Interest::Followable::Follower) and other.is_a?(Interest::Followable::Followee)
12
+ end
13
+
14
+ def follow_requestable?(other)
15
+ followable?(other) and is_a?(Interest::FollowRequestable::FollowRequester) and other.is_a?(Interest::FollowRequestable::FollowRequestee)
16
+ end
17
+
18
+ def blockable?(other)
19
+ is_a?(Interest::Blockable::Blocker) and other.is_a?(Interest::Blockable::Blockee)
20
+ end
21
+
22
+ def follower?
23
+ is_a? Interest::Followable::Follower
24
+ end
25
+
26
+ def followee?
27
+ is_a? Interest::Followable::Followee
28
+ end
29
+
30
+ def follow_requester?
31
+ is_a? Interest::FollowRequestable::FollowRequester
32
+ end
33
+
34
+ def follow_requestee?
35
+ is_a? Interest::FollowRequestable::FollowRequestee
36
+ end
37
+
38
+ def blocker?
39
+ is_a? Interest::Blockable::Blocker
40
+ end
41
+
42
+ def blockee?
43
+ is_a? Interest::Blockable::Blockee
44
+ end
45
+
46
+ module ClassMethods
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ require "interest/blockable/exceptions"
2
+ require "interest/blockable/blocking"
3
+ require "interest/blockable/blocker"
4
+ require "interest/blockable/blockee"
5
+
6
+ module Interest
7
+ module Blockable
8
+ module Extension
9
+ def acts_as_blocker
10
+ include Blocker
11
+ define_blocker_association_methods
12
+ end
13
+
14
+ def acts_as_blockee
15
+ include Blockee
16
+ define_blockee_association_methods
17
+ end
18
+
19
+ def acts_as_blockable
20
+ acts_as_blocker
21
+ acts_as_blockee
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ require "active_support"
2
+ require "interest/definition"
3
+
4
+ module Interest
5
+ module Blockable
6
+ module Blockee
7
+ extend ActiveSupport::Concern
8
+
9
+ include Interest::Definition.instance_methods_for(:blockee, :blocker)
10
+
11
+ def blocked_by?(blocker)
12
+ blockee_collection_for(blockee).include? blocker
13
+ end
14
+
15
+ module ClassMethods
16
+ include Interest::Definition.class_methods_for(:blockee, :blocker)
17
+
18
+ def define_blockee_association_methods
19
+ has_many :blocker_relationships,
20
+ -> { uniq },
21
+ as: :blockee,
22
+ dependent: :destroy,
23
+ class_name: Interest.blocking_class_name do
24
+ include Interest::Definition.collection_methods_for(:blocker)
25
+ end
26
+ end
27
+
28
+ def define_blockee_association_method(source_type)
29
+ association_method_name = blockee_association_method_name_for source_type
30
+
31
+ has_many association_method_name,
32
+ -> { uniq },
33
+ through: :blocker_relationships,
34
+ source: :blocker,
35
+ source_type: source_type
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,69 @@
1
+ require "active_support"
2
+ require "active_record"
3
+ require "interest/definition"
4
+ require "interest/blockable/exceptions"
5
+ require "interest/blockable/blockee"
6
+
7
+ module Interest
8
+ module Blockable
9
+ module Blocker
10
+ extend ActiveSupport::Concern
11
+
12
+ include Interest::Definition.instance_methods_for(:blocker, :blocking)
13
+
14
+ def followable?(other)
15
+ super and not (other.blockee? and blocking?(other)) and not (blockee? and other.blocker? and other.blocking? self)
16
+ end
17
+
18
+ def blocking?(blockee)
19
+ blocker_collection_for(blockee).include? blockee
20
+ end
21
+
22
+ def block(blockee, raise_record_invalid = false)
23
+ blocking_relationships.create!(blockee: blockee)
24
+ rescue ActiveRecord::RecordInvalid => exception
25
+ raise_record_invalid ? (raise exception) : nil
26
+ rescue ActiveRecord::RecordNotUnique
27
+ blocking_relationships.find_by(blockee: blockee)
28
+ end
29
+
30
+ def block!(blockee)
31
+ block(blockee, true)
32
+ rescue ActiveRecord::RecordInvalid => exception
33
+ raise Interest::Blockable::Rejected.new(exception)
34
+ end
35
+
36
+ def unblock(blockee)
37
+ blocker_collection_for(blockee).delete blockee
38
+ end
39
+
40
+ def valid_blocking_for?(blockee)
41
+ not (self == blockee or not blockable?(blockee))
42
+ end
43
+
44
+ module ClassMethods
45
+ include Interest::Definition.class_methods_for(:blocker, :blocking)
46
+
47
+ def define_blocker_association_methods
48
+ has_many :blocking_relationships,
49
+ -> { uniq },
50
+ as: :blocker,
51
+ dependent: :destroy,
52
+ class_name: Interest.blocking_class_name do
53
+ include Interest::Definition.collection_methods_for(:blockee)
54
+ end
55
+ end
56
+
57
+ def define_blocker_association_method(source_type)
58
+ association_method_name = blocker_association_method_name_for source_type
59
+
60
+ has_many association_method_name,
61
+ -> { uniq },
62
+ through: :blocking_relationships,
63
+ source: :blockee,
64
+ source_type: source_type
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,48 @@
1
+ require "active_support"
2
+
3
+ module Interest
4
+ module Blockable
5
+ module Blocking
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ belongs_to :blocker, polymorphic: true
10
+ belongs_to :blockee, polymorphic: true
11
+
12
+ validates :blocker, presence: true
13
+ validates :blockee, presence: true
14
+
15
+ validate :validate_blocking_relationships, if: :should_validate_blocking_relationships?
16
+
17
+ scope :between, ->(a, b) {
18
+ a_to_b = where(blocker: a, blockee: b).where_values.reduce(:and)
19
+ b_to_a = where(blocker: b, blockee: a).where_values.reduce(:and)
20
+
21
+ where a_to_b.or(b_to_a)
22
+ }
23
+
24
+ after_create :destroy_following_relationships
25
+ end
26
+
27
+ def destroy_following_relationships
28
+ Interest.following_class.destroy_relationships_between blocker, blockee
29
+ end
30
+
31
+ def should_validate_blocking_relationships?
32
+ blocker.is_a?(ActiveRecord::Base) and blockee.is_a?(ActiveRecord::Base)
33
+ end
34
+
35
+ def validate_blocking_relationships
36
+ errors.add :blocker, :invalid unless blocker.blocker?
37
+ errors.add :blockee, :invalid unless blockee.blockee?
38
+ errors.add :blockee, :rejected if blocker.blocker? and not blocker.valid_blocking_for?(blockee)
39
+ end
40
+
41
+ module ClassMethods
42
+ def destroy_relationships_between(a, b)
43
+ between(a, b).destroy_all
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ require "interest/exception"
2
+
3
+ module Interest
4
+ module Blockable
5
+ module Exceptions
6
+ class Exception < Interest::Exception; end
7
+ class Rejected < Exception; end
8
+ end
9
+
10
+ include Exceptions
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require "active_support"
2
+ require "active_support/core_ext/string/inflections"
3
+ require "interest/utils"
4
+
5
+ module Interest
6
+ module Definition
7
+ class << self
8
+ def instance_methods_for(source, target)
9
+ Module.new do
10
+ define_method :"#{source}_collection_for" do |record|
11
+ __send__ self.class.__send__(:"#{source}_association_method_name_for", record)
12
+ end
13
+
14
+ define_method :method_missing do |name, *args, &block|
15
+ return super(name, *args, &block) unless matches = /\A#{target}_(?<type>.+)\Z/.match(name.to_s)
16
+
17
+ self.class.__send__ :"define_#{source}_association_method", Interest::Utils.source_type_of(matches[:type])
18
+
19
+ __send__ name, *args, &block
20
+ end
21
+
22
+ define_method :respond_to_missing? do |name, include_private = false|
23
+ !! (super(name, include_private) or /\A#{target}_.+\Z/ =~ name.to_s)
24
+ end
25
+ end
26
+ end
27
+
28
+ def class_methods_for(source, target)
29
+ Module.new do
30
+ define_method :"#{source}_association_method_name_for" do |record|
31
+ :"#{target}_#{Interest::Utils.symbolic_name_of record}"
32
+ end
33
+ end
34
+ end
35
+
36
+ def collection_methods_for(target)
37
+ Module.new do
38
+ define_method :of do |*args|
39
+ where :"#{target}_type" => args.map(&Interest::Utils.method(:source_type_of))
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,10 @@
1
+ module Interest
2
+ class Exception < StandardError
3
+ attr_accessor :original_exception
4
+
5
+ def initialize(original_exception)
6
+ self.original_exception = original_exception
7
+ super()
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ require "interest/follow_requestable/exceptions"
2
+ require "interest/follow_requestable/follow_requester"
3
+ require "interest/follow_requestable/follow_requestee"
4
+ require "interest/follow_requestable/follow_request"
5
+
6
+ module Interest
7
+ module FollowRequestable
8
+ module Extension
9
+ def acts_as_follow_requester
10
+ include FollowRequester
11
+ define_follow_requester_association_methods
12
+ end
13
+
14
+ def acts_as_follow_requestee
15
+ include FollowRequestee
16
+ define_follow_requestee_association_methods
17
+ end
18
+
19
+ def acts_as_follow_requestable
20
+ acts_as_follow_requester
21
+ acts_as_follow_requestee
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ require "interest/exception"
2
+
3
+ module Interest
4
+ module FollowRequestable
5
+ module Exceptions
6
+ class Exception < Interest::Exception; end
7
+ class Rejected < Exception; end
8
+ end
9
+
10
+ include Exceptions
11
+ end
12
+ end