social_connections 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README +25 -0
- data/Rakefile +26 -0
- data/db/keep-this-dir.txt +1 -0
- data/db/social_connections_plugin.sqlite3 +0 -0
- data/init.rb +3 -0
- data/install.rb +1 -0
- data/lib/app/mailers/digest_mailer.rb +17 -0
- data/lib/app/models/social_activity.rb +37 -0
- data/lib/app/models/social_connection.rb +6 -0
- data/lib/app/views/digest_mailer/digest.html.erb +1 -0
- data/lib/db/migrate/20110516181115_create_social_connections.rb +13 -0
- data/lib/social_connections/acts_as_connectable.rb +58 -0
- data/lib/social_connections/social_aggregator.rb +14 -0
- data/lib/social_connections/version.rb +3 -0
- data/lib/social_connections.rb +12 -0
- data/lib/tasks/social_connections.rake +14 -0
- data/social_connections.gemspec +22 -0
- data/spec/activities_aggregator_spec.rb +41 -0
- data/spec/social_activities_spec.rb +67 -0
- data/test/activities_test.rb +9 -0
- data/test/acts_as_connectable_test.rb +33 -0
- data/test/database.yml +4 -0
- data/test/debug.log +9140 -0
- data/test/schema.rb +25 -0
- data/test/social_connection_test.rb +10 -0
- data/test/social_connections_test.rb +8 -0
- data/test/test_helper.rb +47 -0
- data/uninstall.rb +1 -0
- metadata +96 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 [name of plugin creator]
|
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
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
SocialConnections
|
2
|
+
=================
|
3
|
+
|
4
|
+
Introduction goes here.
|
5
|
+
|
6
|
+
|
7
|
+
Example
|
8
|
+
=======
|
9
|
+
|
10
|
+
Example goes here.
|
11
|
+
|
12
|
+
|
13
|
+
How to run the Tests
|
14
|
+
====================
|
15
|
+
|
16
|
+
Run test in ./test:
|
17
|
+
|
18
|
+
rake
|
19
|
+
|
20
|
+
Run specs in ./spec:
|
21
|
+
|
22
|
+
rspec spec
|
23
|
+
|
24
|
+
|
25
|
+
Copyright (c) 2011 [name of plugin creator], released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
Bundler::GemHelper.install_tasks
|
7
|
+
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
desc 'Test the social_connections plugin.'
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'lib'
|
14
|
+
t.libs << 'test'
|
15
|
+
t.pattern = 'test/**/*_test.rb'
|
16
|
+
t.verbose = true
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'Generate documentation for the social_connections plugin.'
|
20
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
21
|
+
rdoc.rdoc_dir = 'rdoc'
|
22
|
+
rdoc.title = 'SocialConnections'
|
23
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
24
|
+
rdoc.rdoc_files.include('README')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
keep this dir so that tests work.
|
Binary file
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class DigestMailer < ActionMailer::Base
|
2
|
+
|
3
|
+
default :from => 'from@example.com'
|
4
|
+
|
5
|
+
append_view_path(File.dirname(__FILE__) + '../../views')
|
6
|
+
|
7
|
+
def digest(feed, email)
|
8
|
+
|
9
|
+
@feed = feed
|
10
|
+
@email = email
|
11
|
+
mail(:to => @email, :subject => 'Digest') do |format|
|
12
|
+
format.html
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class SocialActivity < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :owner, :polymorphic => true
|
4
|
+
belongs_to :subject, :polymorphic => true
|
5
|
+
belongs_to :target, :polymorphic => true
|
6
|
+
|
7
|
+
def options
|
8
|
+
JSON.parse options_as_json
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_activities(subject, verb, object, options = {})
|
12
|
+
# TODO: we may want to delay this
|
13
|
+
activities = []
|
14
|
+
[ subject, object ].each do |owner| # one activity for the subject, and one for the object
|
15
|
+
activities << SocialActivity.create(:owner => owner,
|
16
|
+
:subject => subject,
|
17
|
+
:verb => verb,
|
18
|
+
:target => object,
|
19
|
+
:options_as_json => options.to_json)
|
20
|
+
end
|
21
|
+
[ subject, object ].each do |who| # for both subject and object...
|
22
|
+
who.incoming_social_connections.each do |connection|
|
23
|
+
activities << SocialActivity.create(:owner => connection.source, # ... an activity for each one connected
|
24
|
+
:subject => subject,
|
25
|
+
:verb => verb,
|
26
|
+
:target => object,
|
27
|
+
:options_as_json => options.to_json)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
activities
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.unseen_activities(for_whom)
|
34
|
+
SocialActivity.where('owner_id = ? and owner_type = ? and unseen = ?', for_whom.id, for_whom.class.name, true)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<p>Hi, <%= @email %></p>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateSocialConnections < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :social_connections do |t|
|
4
|
+
t.integer :source_id
|
5
|
+
t.string :source_type
|
6
|
+
t.integer :target_id
|
7
|
+
t.string :target_type
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def self.down
|
11
|
+
drop_table :social_connections
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SocialConnections
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.send :extend, ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def acts_as_connectable(options = {})
|
9
|
+
cattr_accessor :acts_as_connectable_options
|
10
|
+
send :acts_as_connectable_options=, options
|
11
|
+
send :include, InstanceMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
|
17
|
+
def outgoing_social_connections
|
18
|
+
SocialConnection.where("source_id = ? and source_type = ?", self.id, self.class.name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def incoming_social_connections
|
22
|
+
SocialConnection.where("target_id = ? and target_type = ?", self.id, self.class.name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect_to(other)
|
26
|
+
raise ArgumentError.new("other cannot be nil") if other.nil?
|
27
|
+
c = SocialConnection.new
|
28
|
+
c.target = other
|
29
|
+
c.source = self
|
30
|
+
c
|
31
|
+
end
|
32
|
+
|
33
|
+
def acts_as_connectable_verbs
|
34
|
+
acts_as_connectable_options[:verbs] || [ :likes ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(name, *args)
|
38
|
+
if acts_as_connectable_verbs.include? name
|
39
|
+
verb = name
|
40
|
+
object = args[0]
|
41
|
+
options = args[1] || {}
|
42
|
+
create_activity(verb, object, options)
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def create_activity(verb, object, options = {})
|
51
|
+
SocialActivity.create_activities(self, verb, object, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
ActiveRecord::Base.send :include, SocialConnections
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# SocialConnections
|
2
|
+
|
3
|
+
require 'social_connections/acts_as_connectable'
|
4
|
+
require 'social_connections/social_aggregator'
|
5
|
+
|
6
|
+
%w{ models mailers }.each do |dir|
|
7
|
+
path = File.join(File.dirname(__FILE__), 'app', dir)
|
8
|
+
$LOAD_PATH << path
|
9
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
10
|
+
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
namespace :db do
|
2
|
+
namespace :migrate do
|
3
|
+
description = "migrate the db in vendor/plugins/social_connections/lib/db/migrate"
|
4
|
+
|
5
|
+
puts "migration?"
|
6
|
+
|
7
|
+
desc description
|
8
|
+
task :social_connections => :environment do
|
9
|
+
ActiveRecord::Migration.verbose = true
|
10
|
+
ActiveRecord::Migrator.migrate("vendor/plugins/social_connections/lib/db/migrate/", ENV['VERSION'] ? ENV['VERSION'].to_i : nil)
|
11
|
+
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "social_connections/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "social_connections"
|
7
|
+
s.version = SocialConnections::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Chris"]
|
10
|
+
s.email = ["christian.oloff+gem@gmail.com"]
|
11
|
+
s.homepage = "https://sites.google.com/site/socialconnections7/"
|
12
|
+
s.summary = %q{Social connections for ActiveRecord.}
|
13
|
+
s.description = %q{The idea is to provide pluggable social connections, activities and a method to digest those activities (e.g. in daily emails).}
|
14
|
+
|
15
|
+
s.rubyforge_project = "social_connections"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
# s.test_files.reject! { |fn| fn.include? ".swp" }
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test/test_helper'
|
2
|
+
|
3
|
+
describe "Aggregation" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
load_schema
|
7
|
+
@tim = Connectable.create(:name => 'Tim', :email => 'tim@test.com')
|
8
|
+
@tom = Connectable.create(:name => 'Tom', :email => 'tom@test.com')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "aggregates activities into feeds for specific users" do
|
12
|
+
@tim.likes(@tom)
|
13
|
+
@tim.comments(@tom, :comment => "This is cool")
|
14
|
+
feed_tim = SocialConnections.aggregate(@tim)
|
15
|
+
feed_tim.activities.count.should be_>(1)
|
16
|
+
feed_tim.activities.each {|a| puts "For Tim: #{a.subject} #{a.verb} #{a.target}, options=#{a.options}"}
|
17
|
+
feed_tom = SocialConnections.aggregate(@tom)
|
18
|
+
feed_tom.activities.each {|a| puts "For Tom: #{a.subject} #{a.verb} #{a.target}, options=#{a.options}"}
|
19
|
+
feed_tom.activities.count.should be_>(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe DigestMailer do
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
@tim.likes(@tom)
|
26
|
+
@tim.comments(@tom, :comment => "This is boring...")
|
27
|
+
@feed_tim = SocialConnections.aggregate(@tim)
|
28
|
+
@mail = DigestMailer.digest(@feed_tim, @tim.email)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "delivers an email which references all unseen activities ..." do
|
32
|
+
@mail.subject.should eq("Digest")
|
33
|
+
@mail.to.should eq(["tim@test.com"])
|
34
|
+
@mail.from.should eq(["from@example.com"])
|
35
|
+
@mail.body.encoded.should match("Hi")
|
36
|
+
pending
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test/test_helper'
|
2
|
+
|
3
|
+
describe "Subjects" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
load_schema
|
7
|
+
@sub = Connectable.create(:name => 'Tim')
|
8
|
+
@obj = Connectable.create(:name => 'Tom')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can emit activities" do
|
12
|
+
activities = @sub.likes(@obj) # what about the 'owner'? everybody 'connected' to the subject *and*
|
13
|
+
# the object becomes an owner!
|
14
|
+
activities.should_not be_nil
|
15
|
+
activities[0].verb.should eql(:likes)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows the verbs given, but no other verbs" do
|
19
|
+
@sub.likes(@obj)
|
20
|
+
@sub.recommends(@obj)
|
21
|
+
lambda { @sub.suggests(@obj) }.should raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "allows options on activities (e.g. a comment)" do
|
25
|
+
activities = @sub.comments(@obj, :comment => 'This is a silly comment on Tom')
|
26
|
+
activities[0].options['comment'].should eql('This is a silly comment on Tom')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "creates an activity for both subject and object" do
|
30
|
+
activities = @sub.likes(@obj)
|
31
|
+
activities.select {|a| a.owner == @sub}.should_not be_empty # one activity owned by the subject
|
32
|
+
activities.select {|a| a.owner == @obj}.should_not be_empty # ... and one owned by the object
|
33
|
+
end
|
34
|
+
|
35
|
+
it "creates activities for those connected to the subject" do
|
36
|
+
mary = Connectable.create(:name => 'Mary')
|
37
|
+
mary.connect_to(@sub).save # mary is now connected to Tim
|
38
|
+
activities = @sub.likes(@obj)
|
39
|
+
activities.select {|a| a.owner == mary}.should_not be_empty # Mary gets an activity, because she's connected to Tim
|
40
|
+
end
|
41
|
+
|
42
|
+
it "does *not* create activities for those the subject is connected to" do
|
43
|
+
mary = Connectable.create(:name => 'Mary')
|
44
|
+
@sub.connect_to(mary).save # Tim is now connected to Mary
|
45
|
+
activities = @sub.likes(@obj)
|
46
|
+
# Mary does not get an activity,
|
47
|
+
# because she's *not* connected to Tim (Tim is connected to Mary, though)
|
48
|
+
activities.select {|a| a.owner == mary}.should be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
it "creates activities for those connected to the object" do
|
52
|
+
anne = Connectable.create(:name => 'Anne')
|
53
|
+
anne.connect_to(@obj).save
|
54
|
+
activities = @sub.likes(@obj)
|
55
|
+
# Anne gets an activity
|
56
|
+
activities.select {|a| a.owner == anne}.should_not be_empty
|
57
|
+
end
|
58
|
+
|
59
|
+
it "does *not* create an activity for those the subject is connected to" do
|
60
|
+
anne = Connectable.create(:name => 'Anne')
|
61
|
+
@obj.connect_to(anne).save
|
62
|
+
activities = @sub.likes(@obj)
|
63
|
+
# Anne does not get an activity
|
64
|
+
activities.select {|a| a.owner == anne}.should be_empty
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# subject, verb, object
|
2
|
+
# both subject and object are 'acts_as_connectable'
|
3
|
+
# examples:
|
4
|
+
# - tim likes idea x
|
5
|
+
# - tom comments "this is awesome" on idea y
|
6
|
+
# - ben bought deal "25% off on xyz"
|
7
|
+
#
|
8
|
+
# how to indicate that someone has seen an activity already?
|
9
|
+
# by having "owner, subject, verb, object", there could be a flag on the activity?
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class ActsAsConnectableTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
load_schema
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@connectable1 = Connectable.create(:name => 'Anne')
|
9
|
+
@connectable2 = Connectable.create(:name => 'Bob')
|
10
|
+
end
|
11
|
+
|
12
|
+
test "should define collection accessors" do
|
13
|
+
c = Connectable.create(:name => 'Tim', :email => '2@test.com')
|
14
|
+
assert c.respond_to?(:outgoing_social_connections) == true, "does not respond to outgoing connections getter."
|
15
|
+
assert c.respond_to?(:incoming_social_connections) == true, "does not respond to incoming connections getter."
|
16
|
+
# etc.
|
17
|
+
end
|
18
|
+
|
19
|
+
test "should not allow nil as 'other'" do
|
20
|
+
assert_raise ArgumentError do
|
21
|
+
@connectable1.connect_to(nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should allow adding connections" do
|
26
|
+
@connectable1.connect_to(@connectable2).save
|
27
|
+
assert @connectable1.outgoing_social_connections.count == 1
|
28
|
+
assert @connectable1.incoming_social_connections.count == 0
|
29
|
+
assert @connectable2.outgoing_social_connections.count == 0
|
30
|
+
assert @connectable2.incoming_social_connections.count == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/test/database.yml
ADDED