party_boy 0.1.0

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.
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