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