party_boy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ spec/database.yml
23
+ *.log
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Mike Nelson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,89 @@
1
+ = party_boy
2
+
3
+ Models relationships between AR models. Allows you to follow, friend, and block other AR's. Consists of two mixins: acts_as_followable and acts_as_friend. These options allow an AR to inherit either a twitter-like follower system or a facebook-like friend system.
4
+
5
+ == Installation
6
+
7
+ Install the gem
8
+ gem install party_boy
9
+
10
+ Run the generator
11
+ script/generate party_boy
12
+
13
+ This will generate a migration file as well as the necessary Relationship class in your models folder.
14
+
15
+ == Usage
16
+
17
+ === Setup
18
+
19
+ Add the appropriate mixin to your models:
20
+ class Waterboy < ActiveRecord::Base
21
+ ...
22
+ acts_as_followable
23
+ ...
24
+ end
25
+
26
+ class Quarterback < ActiveRecord::Base
27
+ ...
28
+ acts_as_friend
29
+ ...
30
+ end
31
+
32
+ ---
33
+
34
+ === acts_as_follower
35
+
36
+ To allow a model (A) to follow another (B), add acts_as_follow to at least model A. Now, you can follow any other model in your project:
37
+ a = Waterboy.find 1
38
+ b = Quarterback.find 2
39
+ a.follow(b)
40
+
41
+ To stop following, simply just:
42
+ a.unfollow(b)
43
+
44
+ Or to block the relationship:
45
+ b.block(a)
46
+
47
+ To find out if there is a relationship between two models, use the methods:
48
+ a.following?(b)
49
+ b.followed_by?(a)
50
+
51
+ To retrieve a set of models based on the relationships, use:
52
+ a.following
53
+ b.followers
54
+
55
+ ==== STI
56
+
57
+ STI is also handled by party_boy. The relationship is always stored using the super-most class. However, relationships to inheriting classes can also be retrieved. Do so by passing in the type(s):
58
+
59
+ class Quarterback < User; end
60
+ class Cheerleader < User; end
61
+ class Waterboy < User; end
62
+
63
+ In string form
64
+ a.followers('users')
65
+ a.following(%w(users quarterbacks))
66
+
67
+ Or in class form
68
+ a.followers(User)
69
+ b.following([User, Quarterback, Cheerleader])
70
+
71
+ ==== STI - part deux
72
+
73
+ On top of accessing relationships through followers / following methods, party_boy will dynamically filter the results based on the combined class name:
74
+
75
+ a.quarterback_followers # returns all followers of type quarterback
76
+ b.cheerleader_followers # returns all followers of type cheerleader
77
+ a.following_quarterbacks # returns all the quarterbacks 'a' is following
78
+ b.following_cheerleaders # returns all the cheerleaders 'b' is following
79
+
80
+ ---
81
+
82
+ === acts_as_friend
83
+
84
+ Docs coming soon. I don't need it, do you?
85
+
86
+
87
+ == Copyright
88
+
89
+ Copyright (c) 2010 Mike Nelson. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "party_boy"
8
+ gem.summary = "Models relationships between AR models. Allows you to follow, friend, and block other AR's."
9
+ gem.description = "Models relationships between AR models. Allows you to follow, friend, and block other AR's. Consists of two acts_as: acts_as_followable and acts_as_friend. These options allow an AR to inherit either a twitter-like follower system or a facebook-like friend system."
10
+ gem.email = "mdnelson30@gmail.com"
11
+ gem.homepage = "http://github.com/mnelson/party_boy"
12
+ gem.authors = ["Mike Nelson"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "party_boy #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,14 @@
1
+ class PartyBoyGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.directory "app/models"
5
+ m.template "model.rb", "app/models/relationship.rb"
6
+
7
+ m.migration_template 'migration.rb', 'db/migrate'
8
+ end
9
+ end
10
+
11
+ def file_name
12
+ "party_boy_migration"
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ class PartyBoyMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :relationships do |t|
4
+ t.references :requestor, :polymorphic => true, :null => false
5
+ t.references :requestee, :polymorphic => true, :null => false
6
+ t.boolean :restricted, :default => true
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :relationships
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ class Relationship < ActiveRecord::Base
2
+
3
+ named_scope :restricted, :conditions => ['relationships.restricted = ?', true]
4
+ named_scope :unrestricted, :conditions => ['relationships.restricted = ?', false]
5
+
6
+ named_scope :to_type, lambda {|rtype| rtype = [rtype].flatten.compact; rtype.size > 0 && {:conditions => ['relationships.requestee_type IN (?)', [rtype].flatten]} || {}}
7
+ named_scope :from_type, lambda {|rtype| rtype = [rtype].flatten.compact; rtype.size > 0 && {:conditions => ['relationships.requestor_type IN (?)', [rtype].flatten]} || {}}
8
+
9
+ default_scope :order => 'created_at DESC'
10
+
11
+ with_options :polymorphic => true do |klazz|
12
+ klazz.belongs_to :requestor
13
+ klazz.belongs_to :requestee
14
+ end
15
+
16
+ validates_presence_of :requestor, :requestee
17
+
18
+ alias_attribute :blocked, :restricted
19
+ def accepted?
20
+ !self.restricted
21
+ end
22
+
23
+ class << self
24
+ alias_attribute :blocked, :restricted
25
+ alias_attribute :unblocked, :unrestricted
26
+
27
+ alias_attribute :unaccepted, :restricted
28
+ alias_attribute :accepted, :unrestricted
29
+ end
30
+ end
data/lib/party_boy.rb ADDED
@@ -0,0 +1,225 @@
1
+ module Party
2
+ module Boy
3
+
4
+ class IdentityTheftError < StandardError; end
5
+ class StalkerError < StandardError; end
6
+
7
+ def self.included(klazz)
8
+ klazz.extend(Party::Boy::ClassMethods)
9
+ klazz.class_eval do
10
+ include Party::Boy::RelateableInstanceMethods
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def acts_as_followable
17
+ with_options :class_name => 'Relationship', :dependent => :destroy do |klazz|
18
+ klazz.has_many :followings, :as => :requestee
19
+ klazz.has_many :follows, :as => :requestor
20
+ end
21
+
22
+ include Party::Boy::FollowableInstanceMethods
23
+ end
24
+
25
+ def acts_as_friend
26
+ with_options :class_name => 'Relationship', :dependent => :destroy do |klazz|
27
+ klazz.has_many :outgoing_friendships, :as => :requestor, :include => :requestee
28
+ klazz.has_many :incoming_friendships, :as => :requestee, :include => :requestor
29
+ end
30
+
31
+ include Party::Boy::RelateableInstanceMethods
32
+ include Party::Boy::FriendlyInstanceMethods
33
+ end
34
+
35
+ end
36
+
37
+
38
+ module RelateableInstanceMethods
39
+
40
+ private
41
+
42
+ # should be able to pass a class, string, or object and get back the super-most class (before activerecord)
43
+ def super_class_name(obj = self)
44
+ obj = (obj.class == Class && obj || obj.class == String && obj.constantize || obj.class)
45
+ if obj.superclass != ActiveRecord::Base
46
+ super_class_name(obj.superclass)
47
+ else
48
+ obj.name
49
+ end
50
+ end
51
+
52
+ def super_class_names(obj = self)
53
+ if obj.nil?
54
+ return nil
55
+ end
56
+ obj = (obj.class == Class && obj || obj.class == String && obj.constantize || obj.class)
57
+ if obj.superclass != ActiveRecord::Base
58
+ [obj.name, super_class_names(obj.superclass)].flatten
59
+ else
60
+ [obj.name]
61
+ end
62
+ end
63
+
64
+ def get_relationship_to(requestee)
65
+ requestee && Relationship.unblocked.find(:first, :conditions => ['requestor_id = ? and requestor_type = ? and requestee_id = ? and requestee_type = ?', self.id, super_class_name, requestee.id, super_class_name(requestee)]) || nil
66
+ end
67
+
68
+ def get_relationship_from(requestor)
69
+ requestor && Relationship.unblocked.find(:first, :conditions => ['requestor_id = ? and requestor_type = ? and requestee_id = ? and requestee_type = ?', requestor.id, super_class_name(requestor), self.id, super_class_name]) || nil
70
+ end
71
+
72
+ end
73
+
74
+ module FollowableInstanceMethods
75
+
76
+ def following?(something)
77
+ !!(something && Relationship.unblocked.count(:conditions => ['requestor_id = ? and requestor_type = ? and requestee_id = ? and requestee_type = ?', self.id, super_class_name, something.id, super_class_name(something)]) > 0)
78
+ end
79
+
80
+ def followed_by?(something)
81
+ !!(something && Relationship.unblocked.count(:conditions => ['requestor_id = ? and requestor_type = ? and requestee_id = ? and requestee_type = ?', something.id, super_class_name(something), self.id, super_class_name]) > 0)
82
+ end
83
+
84
+ def follow(something)
85
+ if blocked_by?(something)
86
+ raise(Party::Boy::StalkerError, "#{super_class_name} #{self.id} has been blocked by #{super_class_name(something)} #{something.id} but is trying to follow them")
87
+ else
88
+ Relationship.create(:requestor => self, :requestee => something, :restricted => false) if !(blocked_by?(something) || following?(something))
89
+ end
90
+ end
91
+
92
+ def blocked_by?(something)
93
+ !!(something && Relationship.blocked.count(:conditions => ['requestor_id = ? and requestor_type = ? and requestee_id = ? and requestee_type = ?', self.id, super_class_name, something.id, super_class_name(something)]) > 0)
94
+ end
95
+
96
+ def unfollow(something)
97
+ (rel = get_relationship_to(something)) && rel.destroy
98
+ end
99
+
100
+ def block(something)
101
+ (rel = (get_relationship_from(something) || get_relationship_to(something))) && rel.update_attribute(:restricted, true)
102
+ end
103
+
104
+ def follower_count(type = nil)
105
+ followings.unblocked.from_type(type).size
106
+ end
107
+
108
+ def following_count(type = nil)
109
+ follows.unblocked.to_type(type).size
110
+ end
111
+
112
+ def followers(type = nil)
113
+ type = [type].flatten.compact.uniq
114
+ super_class = type.last
115
+ exact_class = type.first
116
+ results = relationships_from(super_class)
117
+ if super_class != exact_class
118
+ results.collect{|r| r.requestor.class.name == exact_class && r.requestor || nil}.compact
119
+ else
120
+ results.collect{|r| r.requestor}
121
+ end
122
+
123
+ end
124
+
125
+ def following(type = nil)
126
+ type = [type].flatten.compact.uniq
127
+ super_class = type.last
128
+ exact_class = type.first
129
+ results = relationships_to(super_class)
130
+ if super_class != exact_class
131
+ results.collect{|r| r.requestee.class.name == exact_class && r.requestee || nil}.compact
132
+ else
133
+ results.collect{|r| r.requestee}
134
+ end
135
+ end
136
+
137
+ def extended_network(type = nil)
138
+ following.collect{|f| f.methods.include?('following') && f.following(type) || []}.flatten.uniq
139
+ end
140
+
141
+ def method_missing(method, *args)
142
+ case method.id2name
143
+ when /^(.+)ss_followers$/
144
+ # this is for the rare case of a class name ending in ss, like 'business'; 'business'.classify => 'Busines'
145
+ followers(super_class_names("#{$1.classify}ss"))
146
+ when /^(.+)s_followers$/, /^(.+)_followers$/
147
+ followers(super_class_names($1.classify))
148
+ when /^following_(.+)$/
149
+ following(super_class_names($1.classify))
150
+ else
151
+ super
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def relationships_to(type)
158
+ self.follows.unblocked.to_type(type).all(:include => [:requestee])
159
+ end
160
+
161
+ def relationships_from(type)
162
+ self.followings.unblocked.from_type(type).all(:include => [:requestor])
163
+ end
164
+
165
+ end
166
+
167
+ module FriendlyInstanceMethods
168
+
169
+ def friends
170
+ (outgoing_friendships.accepted + incoming_friendships.accepted).collect{|r| [r.requestor, r.requestee]}.flatten.uniq - [self]
171
+ end
172
+
173
+ def extended_network
174
+ friends.collect{|f| f.methods.include?('friends') && f.friends || []}.flatten.uniq - [self]
175
+ end
176
+
177
+ def outgoing_friend_requests
178
+ self.outgoing_friendships.unaccepted.all
179
+ end
180
+
181
+ def incoming_friend_requests
182
+ self.incoming_friendships.unaccepted.all
183
+ end
184
+
185
+ def is_friends_with?(something)
186
+ arr = something && [self.id, super_class_name, super_class_name(something), something.id]
187
+ arr && Relationship.accepted.count(:conditions => [(['(requestor_id = ? AND requestor_type = ? AND requestee_type = ? AND requestee_id = ?)']*2).join(' OR '), arr, arr.reverse].flatten) > 0
188
+ end
189
+
190
+ def pending?(something)
191
+ arr = something && [self.id, super_class_name, super_class_name(something), something.id]
192
+ arr && Relationship.unaccepted.count(:conditions => [(['(requestor_id = ? AND requestor_type = ? AND requestee_type = ? AND requestee_id = ?)']*2).join(' OR '), arr, arr.reverse].flatten) > 0
193
+ end
194
+
195
+ def friend_count
196
+ arr = [self.id, super_class_name]
197
+ Relationship.accepted.count(:conditions => ['(requestor_id = ? AND requestor_type = ?) OR (requestee_id = ? and requestee_type = ?)', arr, arr].flatten)
198
+ end
199
+
200
+ def request_friendship(friendship_or_something)
201
+ rel = relationship_from(friendship_or_something)
202
+ rel.nil? && Relationship.create(:requestor => self, :requestee => friendship_or_something, :restricted => true) || rel.update_attributes(:restricted => false)
203
+ end
204
+
205
+ def deny_friendship(friendship_or_something)
206
+ (rel = relationship_from(friendship_or_something)) && rel.destroy
207
+ end
208
+
209
+ alias_method :reject_friendship, :deny_friendship
210
+ alias_method :accept_friendship, :request_friendship
211
+
212
+ private
213
+
214
+ def relationship_from(friendship_or_something)
215
+ if friendship_or_something && friendship_or_something.class == Relationship
216
+ raise(Party::Boy::IdentityTheftError, "#{self.class.name} with id of #{self.id} tried to access Relationship #{friendship_or_something.id}") if !(friendship_or_something.requestor == self || friendship_or_something.requestee == self)
217
+ friendship_or_something
218
+ else
219
+ arr = friendship_or_something && [self.id, super_class_name, super_class_name(friendship_or_something), friendship_or_something.id]
220
+ arr && Relationship.find(:first, :conditions => [(['(requestor_id = ? AND requestor_type = ? AND requestee_type = ? AND requestee_id = ?)']*2).join(' OR '), arr, arr.reverse].flatten) || nil
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{social_lite}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Mike Nelson"]
12
+ s.date = %q{2010-01-28}
13
+ s.description = %q{Models relationships between AR models. Allows you to follow, friend, and block other AR's. Consists of two acts_as: acts_as_followable and acts_as_friend. These options allow an AR to inherit either a twitter-like follower system or a facebook-like friend system.}
14
+ s.email = %q{mdnelson30@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "generators/party_boy/party_boy_generator.rb",
27
+ "generators/party_boy/templates/migration.rb",
28
+ "generators/party_boy/templates/model.rb",
29
+ "lib/party_boy.rb",
30
+ "spec/models/follower_class.rb",
31
+ "spec/models/friend_class.rb",
32
+ "spec/party_boy_spec.rb",
33
+ "spec/schema.rb",
34
+ "spec/spec.opts",
35
+ "spec/spec_helper.rb"
36
+ ]
37
+ s.homepage = %q{http://github.com/mnelson/social_lite}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.5}
41
+ s.summary = %q{Models relationships between AR models. Allows you to follow, friend, and block other AR's.}
42
+ s.test_files = [
43
+ "spec/models/follower_class.rb",
44
+ "spec/models/friend_class.rb",
45
+ "spec/party_boy_spec.rb",
46
+ "spec/schema.rb",
47
+ "spec/spec_helper.rb"
48
+ ]
49
+
50
+ if s.respond_to? :specification_version then
51
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
55
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
56
+ else
57
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
61
+ end
62
+ end
63
+
@@ -0,0 +1,5 @@
1
+ class FollowerClass < ActiveRecord::Base
2
+ include Party::Boy
3
+
4
+ acts_as_followable
5
+ end
@@ -0,0 +1,5 @@
1
+ class FriendClass < ActiveRecord::Base
2
+ include Party::Boy
3
+
4
+ acts_as_friend
5
+ end
@@ -0,0 +1,160 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+
4
+ describe "PartyBoy" do
5
+
6
+ it "should integrate follower methods properly" do
7
+ %w(followers following following? followed_by?).each do |method|
8
+ FollowerClass.new.methods.include?(method).should be_true
9
+ end
10
+ end
11
+
12
+ it "should integrate friend methods properly" do
13
+ %w(friends outgoing_friend_requests incoming_friend_requests extended_network).each do |method|
14
+ FriendClass.new.methods.include?(method).should be_true
15
+ end
16
+ end
17
+
18
+
19
+ it "should validate relationships properly" do
20
+ a = FollowerClass.create
21
+ b = FollowerClass.create
22
+
23
+ r = Relationship.new(:requestor => a, :requestee => b, :restricted => false)
24
+ r.should be_valid
25
+
26
+ r2 = Relationship.new(:requestor => a, :restricted => false)
27
+ r2.should_not be_valid
28
+
29
+ r3 = Relationship.new(:requestee => b, :restricted => true)
30
+ r3.should_not be_valid
31
+ end
32
+
33
+ end
34
+
35
+ describe "PartyBoy -- Follower" do
36
+ it "should generate relationships and return proper counts" do
37
+ a = FollowerClass.create
38
+ b = FollowerClass.create
39
+
40
+ a.follow(b)
41
+
42
+ r = Relationship.last
43
+
44
+ r.requestor.should eql(a)
45
+ r.requestee.should eql(b)
46
+ r.blocked.should be_false
47
+
48
+ a.following_count.should eql(1)
49
+ a.follower_count.should eql(0)
50
+
51
+ b.following_count.should eql(0)
52
+ b.follower_count.should eql(1)
53
+ end
54
+
55
+ it "should generate and destroy relationships from party_boy models" do
56
+ a = FollowerClass.create
57
+ b = FollowerClass.create
58
+
59
+ a.follow(b)
60
+
61
+ a.following_count.should eql(1)
62
+
63
+ a.unfollow(b)
64
+ a.following_count.should eql(0)
65
+
66
+ end
67
+
68
+ it "should restrict relationships properly" do
69
+ a = FollowerClass.create
70
+ b = FollowerClass.create
71
+
72
+ r = a.follow(b)
73
+
74
+ b.block(a)
75
+
76
+ a.following_count.should eql(0)
77
+ b.follower_count.should eql(0)
78
+
79
+ r.reload
80
+ r.blocked.should be_true
81
+
82
+ lambda { a.follow(b) }.should raise_error(Party::Boy::StalkerError)
83
+
84
+ a.following_count.should eql(0)
85
+ b.follower_count.should eql(0)
86
+
87
+ b.follow(a)
88
+
89
+ a.follower_count.should eql(1)
90
+ b.following_count.should eql(1)
91
+
92
+ end
93
+
94
+
95
+ it "should collect relationship personnel properly" do
96
+ a = FollowerClass.create
97
+ b = FollowerClass.create
98
+ c = FollowerClass.create
99
+ d = FollowerClass.create
100
+ e = FollowerClass.create
101
+
102
+ a.follow(b)
103
+ b.follow(c)
104
+ a.follow(c)
105
+ c.follow(d)
106
+ e.follow(c)
107
+
108
+ a.followers.empty?.should be_true
109
+ a.following.size.should eql(2)
110
+ a.following.sort{|m,n| m.id <=> n.id}.should eql([b,c])
111
+
112
+ c.followers.size.should eql(3)
113
+ c.followers.sort{|m,n| m.id <=> n.id}.should eql([a,b,e])
114
+
115
+ a.extended_network.include?(d).should be_true
116
+ a.extended_network.include?(e).should be_false
117
+ end
118
+
119
+
120
+ class User < FollowerClass; end
121
+ class Business < FollowerClass; end
122
+
123
+
124
+ it "should handle STI properly" do
125
+ u = User.create
126
+ b = Business.create
127
+
128
+ u.follow(b)
129
+
130
+ b.followers.first.class.name.should eql('User')
131
+ u.following.first.class.name.should eql('Business')
132
+ end
133
+
134
+ it "should handle method_missing properly" do
135
+ u = User.create
136
+ b = Business.create
137
+ u2 = User.create
138
+ b2 = Business.create
139
+
140
+ u.follow(u2)
141
+ u.follow(b)
142
+ u.follow(b2)
143
+
144
+ u.following.size.should eql(3)
145
+ u.following_businesses.size.should eql(2)
146
+ u.following_users.size.should eql(1)
147
+
148
+ b.follow(u)
149
+ b.follow(u2)
150
+ b.follow(b2)
151
+
152
+ b2.followers.size.should eql(2)
153
+ b2.user_followers.size.should eql(1)
154
+ b2.business_followers.size.should eql(1)
155
+ b2.follower_class_followers.size.should eql(2)
156
+
157
+ end
158
+
159
+ end
160
+
data/spec/schema.rb ADDED
@@ -0,0 +1,19 @@
1
+ ActiveRecord::Schema.define :version => 0 do
2
+
3
+ create_table :relationships, :force => true do |t|
4
+ t.references :requestor, :polymorphic => true, :null => false
5
+ t.references :requestee, :polymorphic => true, :null => false
6
+ t.boolean :restricted, :default => true
7
+ t.timestamps
8
+ end
9
+
10
+ create_table :friend_classes, :force => true do |t|
11
+ t.timestamps
12
+ end
13
+
14
+ create_table :follower_classes, :force => true do |t|
15
+ t.string :type
16
+ t.timestamps
17
+ end
18
+
19
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format specdoc
3
+ --loadby mtime
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'party_boy'
5
+ require 'rubygems'
6
+ require 'spec'
7
+ require 'spec/autorun'
8
+ require 'active_record'
9
+ require "#{File.dirname(__FILE__)}/../generators/party_boy/templates/model"
10
+ require 'models/follower_class'
11
+ require 'models/friend_class'
12
+
13
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log')
14
+ ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
15
+ ActiveRecord::Base.establish_connection(ENV['DB'] || 'mysql')
16
+
17
+ load(File.dirname(__FILE__) + '/schema.rb')
18
+
19
+ Spec::Runner.configure do |config|
20
+
21
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: party_boy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Nelson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-28 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ description: "Models relationships between AR models. Allows you to follow, friend, and block other AR's. Consists of two acts_as: acts_as_followable and acts_as_friend. These options allow an AR to inherit either a twitter-like follower system or a facebook-like friend system."
26
+ email: mdnelson30@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - generators/party_boy/party_boy_generator.rb
42
+ - generators/party_boy/templates/migration.rb
43
+ - generators/party_boy/templates/model.rb
44
+ - lib/party_boy.rb
45
+ - social_lite.gemspec
46
+ - spec/models/follower_class.rb
47
+ - spec/models/friend_class.rb
48
+ - spec/party_boy_spec.rb
49
+ - spec/schema.rb
50
+ - spec/spec.opts
51
+ - spec/spec_helper.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/mnelson/party_boy
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Models relationships between AR models. Allows you to follow, friend, and block other AR's.
80
+ test_files:
81
+ - spec/models/follower_class.rb
82
+ - spec/models/friend_class.rb
83
+ - spec/party_boy_spec.rb
84
+ - spec/schema.rb
85
+ - spec/spec_helper.rb