interest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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