socialization 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/.gitignore +9 -0
  2. data/Appraisals +11 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +126 -0
  6. data/Rakefile +16 -0
  7. data/demo/demo_app/.gitignore +15 -0
  8. data/demo/demo_app/Gemfile +35 -0
  9. data/demo/demo_app/README.rdoc +261 -0
  10. data/demo/demo_app/Rakefile +7 -0
  11. data/demo/demo_app/app/assets/images/rails.png +0 -0
  12. data/demo/demo_app/app/assets/javascripts/application.js +15 -0
  13. data/demo/demo_app/app/assets/stylesheets/application.css +13 -0
  14. data/demo/demo_app/app/controllers/application_controller.rb +3 -0
  15. data/demo/demo_app/app/helpers/application_helper.rb +2 -0
  16. data/demo/demo_app/app/mailers/.gitkeep +0 -0
  17. data/demo/demo_app/app/models/.gitkeep +0 -0
  18. data/demo/demo_app/app/models/celebrity.rb +4 -0
  19. data/demo/demo_app/app/models/follow.rb +3 -0
  20. data/demo/demo_app/app/models/like.rb +3 -0
  21. data/demo/demo_app/app/models/movie.rb +3 -0
  22. data/demo/demo_app/app/models/user.rb +6 -0
  23. data/demo/demo_app/app/views/layouts/application.html.erb +14 -0
  24. data/demo/demo_app/config.ru +4 -0
  25. data/demo/demo_app/config/application.rb +59 -0
  26. data/demo/demo_app/config/boot.rb +6 -0
  27. data/demo/demo_app/config/database.yml +25 -0
  28. data/demo/demo_app/config/environment.rb +5 -0
  29. data/demo/demo_app/config/environments/development.rb +37 -0
  30. data/demo/demo_app/config/environments/production.rb +67 -0
  31. data/demo/demo_app/config/environments/test.rb +37 -0
  32. data/demo/demo_app/config/initializers/backtrace_silencers.rb +7 -0
  33. data/demo/demo_app/config/initializers/inflections.rb +15 -0
  34. data/demo/demo_app/config/initializers/mime_types.rb +5 -0
  35. data/demo/demo_app/config/initializers/secret_token.rb +7 -0
  36. data/demo/demo_app/config/initializers/session_store.rb +8 -0
  37. data/demo/demo_app/config/initializers/wrap_parameters.rb +14 -0
  38. data/demo/demo_app/config/locales/en.yml +5 -0
  39. data/demo/demo_app/config/routes.rb +58 -0
  40. data/demo/demo_app/db/migrate/20120115051222_create_users.rb +9 -0
  41. data/demo/demo_app/db/migrate/20120115051234_create_movies.rb +9 -0
  42. data/demo/demo_app/db/migrate/20120115051255_create_celebrities.rb +9 -0
  43. data/demo/demo_app/db/migrate/20120115054646_create_follows.rb +14 -0
  44. data/demo/demo_app/db/migrate/20120115054647_create_likes.rb +14 -0
  45. data/demo/demo_app/db/schema.rb +56 -0
  46. data/demo/demo_app/db/seeds.rb +14 -0
  47. data/demo/demo_app/doc/README_FOR_APP +2 -0
  48. data/demo/demo_app/lib/assets/.gitkeep +0 -0
  49. data/demo/demo_app/lib/tasks/.gitkeep +0 -0
  50. data/demo/demo_app/public/404.html +26 -0
  51. data/demo/demo_app/public/422.html +26 -0
  52. data/demo/demo_app/public/500.html +25 -0
  53. data/demo/demo_app/public/favicon.ico +0 -0
  54. data/demo/demo_app/public/index.html +204 -0
  55. data/demo/demo_app/public/robots.txt +5 -0
  56. data/demo/demo_app/script/rails +6 -0
  57. data/demo/demo_app/test/fixtures/.gitkeep +0 -0
  58. data/demo/demo_app/test/fixtures/celebrities.yml +7 -0
  59. data/demo/demo_app/test/fixtures/movies.yml +7 -0
  60. data/demo/demo_app/test/fixtures/users.yml +7 -0
  61. data/demo/demo_app/test/functional/.gitkeep +0 -0
  62. data/demo/demo_app/test/integration/.gitkeep +0 -0
  63. data/demo/demo_app/test/performance/browsing_test.rb +12 -0
  64. data/demo/demo_app/test/test_helper.rb +13 -0
  65. data/demo/demo_app/test/unit/.gitkeep +0 -0
  66. data/demo/demo_app/test/unit/celebrity_test.rb +7 -0
  67. data/demo/demo_app/test/unit/movie_test.rb +7 -0
  68. data/demo/demo_app/test/unit/user_test.rb +7 -0
  69. data/demo/demo_app/vendor/assets/javascripts/.gitkeep +0 -0
  70. data/demo/demo_app/vendor/assets/stylesheets/.gitkeep +0 -0
  71. data/demo/demo_app/vendor/plugins/.gitkeep +0 -0
  72. data/generators/socialization/USAGE +1 -0
  73. data/generators/socialization/socialization_generator.rb +11 -0
  74. data/generators/socialization/templates/migration_follows.rb +14 -0
  75. data/generators/socialization/templates/migration_likes.rb +14 -0
  76. data/generators/socialization/templates/model_follow.rb +3 -0
  77. data/generators/socialization/templates/model_like.rb +3 -0
  78. data/init.rb +1 -0
  79. data/lib/generators/socialization/socialization_generator.rb +23 -0
  80. data/lib/socialization.rb +6 -0
  81. data/lib/socialization/follow_store.rb +14 -0
  82. data/lib/socialization/followable.rb +23 -0
  83. data/lib/socialization/follower.rb +31 -0
  84. data/lib/socialization/hello.rb +37 -0
  85. data/lib/socialization/like_store.rb +14 -0
  86. data/lib/socialization/likeable.rb +23 -0
  87. data/lib/socialization/liker.rb +31 -0
  88. data/lib/socialization/version.rb +3 -0
  89. data/socialization.gemspec +29 -0
  90. data/test/follow_test.rb +37 -0
  91. data/test/like_test.rb +46 -0
  92. data/test/test_helper.rb +67 -0
  93. metadata +200 -0
File without changes
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2
+
3
+ one:
4
+ name: MyString
5
+
6
+ two:
7
+ name: MyString
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2
+
3
+ one:
4
+ name: MyString
5
+
6
+ two:
7
+ name: MyString
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2
+
3
+ one:
4
+ name: MyString
5
+
6
+ two:
7
+ name: MyString
File without changes
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+ require 'rails/performance_test_help'
3
+
4
+ class BrowsingTest < ActionDispatch::PerformanceTest
5
+ # Refer to the documentation for all available options
6
+ # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
7
+ # :output => 'tmp/performance', :formats => [:flat] }
8
+
9
+ def test_homepage
10
+ get '/'
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path('../../config/environment', __FILE__)
3
+ require 'rails/test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
7
+ #
8
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
9
+ # -- they do not yet inherit this setting
10
+ fixtures :all
11
+
12
+ # Add more helper methods to be used by all tests here...
13
+ end
File without changes
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class CelebrityTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class MovieTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class UserTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
File without changes
@@ -0,0 +1 @@
1
+ Generates both the Follow model class and the migration to create its table.
@@ -0,0 +1,11 @@
1
+ class SocializationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.template 'model_follow.rb', 'app/models/follow.rb'
5
+ m.template 'model_like.rb', 'app/models/like.rb'
6
+ m.migration_template 'migration_follows.rb', 'db/migrate', :migration_file_name => 'create_follows'
7
+ sleep 1 # force unique migration timestamp
8
+ m.migration_template 'migration_likes.rb', 'db/migrate', :migration_file_name => 'create_likes'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreateFollows < ActiveRecord::Migration
2
+ def change
3
+ create_table :follows do |t|
4
+ t.string :follower_type
5
+ t.integer :follower_id
6
+ t.string :followable_type
7
+ t.integer :followable_id
8
+ t.datetime :created_at
9
+ end
10
+
11
+ add_index :follows, ["follower_id", "follower_type"], :name => "fk_follows"
12
+ add_index :follows, ["followable_id", "followable_type"], :name => "fk_followables"
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ class CreateLikes < ActiveRecord::Migration
2
+ def change
3
+ create_table :likes do |t|
4
+ t.string :liker_type
5
+ t.integer :liker_id
6
+ t.string :likeable_type
7
+ t.integer :likeable_id
8
+ t.datetime :created_at
9
+ end
10
+
11
+ add_index :likes, ["liker_id", "liker_type"], :name => "fk_likes"
12
+ add_index :likes, ["likeable_id", "likeable_type"], :name => "fk_likeables"
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ class Follow < ActiveRecord::Base
2
+ acts_as_follow_store
3
+ end
@@ -0,0 +1,3 @@
1
+ class Like < ActiveRecord::Base
2
+ acts_as_like_store
3
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'socialization'
@@ -0,0 +1,23 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class SocializationGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+ source_root File.expand_path(File.join('..', '..', '..', 'generators', 'socialization', 'templates'), File.dirname(__FILE__))
7
+
8
+ def self.next_migration_number(dirname)
9
+ if ActiveRecord::Base.timestamped_migrations
10
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
11
+ else
12
+ "%.3d" % (current_migration_number(dirname) + 1)
13
+ end
14
+ end
15
+
16
+ def create_migration_file
17
+ copy_file 'model_follow.rb', 'app/models/follow.rb'
18
+ copy_file 'model_like.rb', 'app/models/like.rb'
19
+ migration_template 'migration_follows.rb', 'db/migrate/create_follows.rb'
20
+ sleep 1 # wait a second to have a unique migration timestamp
21
+ migration_template 'migration_likes.rb', 'db/migrate/create_likes.rb'
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ require 'socialization/hello'
2
+
3
+ module Socialization
4
+ end
5
+
6
+ ActiveRecord::Base.send :include, Socialization::Hello
@@ -0,0 +1,14 @@
1
+ module Socialization
2
+ module FollowStore
3
+ def self.included(base)
4
+ base.class_eval do
5
+ belongs_to :follower, :polymorphic => true
6
+ belongs_to :followable, :polymorphic => true
7
+
8
+ validates_uniqueness_of :followable_type, :scope => [:followable_id, :follower_type, :follower_id], :message => 'You cannot follow the same thing twice.'
9
+
10
+ def self.human_attribute_name(*args); ''; end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Socialization
2
+ module Followable
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # A following is the Follow record of the follower following self.
6
+ has_many :followings, :as => :followable, :dependent => :destroy, :class_name => 'Follow'
7
+
8
+ def is_followable?
9
+ true
10
+ end
11
+
12
+ def followed_by?(follower)
13
+ raise ArgumentError, "#{follower} is not a follower!" unless follower.respond_to?(:is_follower?) && follower.is_follower?
14
+ !self.followings.where(:follower_type => follower.class.to_s, :follower_id => follower.id).pluck("1").empty?
15
+ end
16
+
17
+ def followers
18
+ self.followings.map { |f| f.follower }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ module Socialization
2
+ module Follower
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # A follow is the Follow record of self following a followable record.
6
+ has_many :follows, :as => :follower, :dependent => :destroy, :class_name => 'Follow'
7
+
8
+ def is_follower?
9
+ true
10
+ end
11
+
12
+ def follow!(followable)
13
+ raise ArgumentError, "#{followable} is not followable!" unless followable.respond_to?(:is_followable?)
14
+ Follow.create! follower: self, followable: followable
15
+ end
16
+
17
+ def unfollow!(followable)
18
+ followable.followers.where(:follower => self).each do |f|
19
+ f.destroy
20
+ end
21
+ end
22
+
23
+ def follows?(followable)
24
+ raise ArgumentError, "#{followable} is not followable!" unless followable.respond_to?(:is_followable?) && followable.is_followable?
25
+ !self.follows.where(:followable_type => followable.class.to_s, :followable_id => followable.id).pluck("1").empty?
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ %w{followable follower follow_store likeable liker like_store}.each do |f|
2
+ require "#{File.dirname(__FILE__)}/#{f}"
3
+ end
4
+
5
+ module Socialization
6
+ module Hello
7
+ def self.included(klass)
8
+ klass.send(:extend, ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def acts_as_follower(opts = nil)
13
+ include Socialization::Follower
14
+ end
15
+
16
+ def acts_as_followable(opts = nil)
17
+ include Socialization::Followable
18
+ end
19
+
20
+ def acts_as_follow_store(opts = nil)
21
+ include Socialization::FollowStore
22
+ end
23
+
24
+ def acts_as_liker(opts = nil)
25
+ include Socialization::Liker
26
+ end
27
+
28
+ def acts_as_likeable(opts = nil)
29
+ include Socialization::Likeable
30
+ end
31
+
32
+ def acts_as_like_store(opts = nil)
33
+ include Socialization::LikeStore
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module Socialization
2
+ module LikeStore
3
+ def self.included(base)
4
+ base.class_eval do
5
+ belongs_to :liker, :polymorphic => true
6
+ belongs_to :likeable, :polymorphic => true
7
+
8
+ validates_uniqueness_of :likeable_type, :scope => [:likeable_id, :liker_type, :liker_id], :message => 'You cannot like the same thing twice.'
9
+
10
+ def self.human_attribute_name(*args); ''; end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Socialization
2
+ module Likeable
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # A liking is the Like record of the liker liking self.
6
+ has_many :likings, :as => :likeable, :dependent => :destroy, :class_name => 'Like'
7
+
8
+ def is_likeable?
9
+ true
10
+ end
11
+
12
+ def liked_by?(liker)
13
+ raise ArgumentError, "#{liker} is not a liker!" unless liker.respond_to?(:is_liker?) && liker.is_liker?
14
+ !self.likings.where(:liker_type => liker.class.to_s, :liker_id => liker.id).pluck("1").empty?
15
+ end
16
+
17
+ def likers
18
+ self.likings.map { |l| l.liker }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ module Socialization
2
+ module Liker
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # A like is the Like record of self liking a likeable record.
6
+ has_many :likes, :as => :liker, :dependent => :destroy, :class_name => 'Like'
7
+
8
+ def is_liker?
9
+ true
10
+ end
11
+
12
+ def like!(likeable)
13
+ raise ArgumentError, "#{likeable} is not likeable!" unless likeable.respond_to?(:is_likeable?)
14
+ Like.create! liker: self, likeable: likeable
15
+ end
16
+
17
+ def unlike!(likeable)
18
+ likeable.likers.where(:liker => self).each do |l|
19
+ l.destroy
20
+ end
21
+ end
22
+
23
+ def likes?(likeable)
24
+ raise ArgumentError, "#{likeable} is not likeable!" unless likeable.respond_to?(:is_likeable?) && likeable.is_likeable?
25
+ !self.likes.where(:likeable_type => likeable.class.to_s, :likeable_id => likeable.id).pluck("1").empty?
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Socialization
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "socialization/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "socialization"
7
+ s.version = Socialization::VERSION
8
+ s.authors = ["Carl Mercier"]
9
+ s.email = "carl@carlmercier.com"
10
+ s.homepage = "https://github.com/cmer/socialization"
11
+ s.summary = "Easily socialize your app with Likes and Follows"
12
+ s.description = "Socialization allow any model to Follow and/or Like any other model. This is accomplished through a double polymorphic relationship on the Follow and Like models. But you don't need to know that since all the complexity is hidden from you."
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README.rdoc"]
21
+ s.rdoc_options = ["--charset=UTF-8"]
22
+
23
+ s.add_runtime_dependency "activerecord"
24
+
25
+ s.add_development_dependency "appraisal"
26
+ s.add_development_dependency "logger"
27
+ s.add_development_dependency "mocha"
28
+ s.add_development_dependency "sqlite3"
29
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__)+'/test_helper'
2
+
3
+ class FollowTest < Test::Unit::TestCase
4
+ def setup
5
+ @u_john = User.create name: 'John Doe'
6
+ @u_jane = User.create name: 'Jane Doe'
7
+ @c_chuck = Celebrity.create name: 'Chuck Norris'
8
+ @c_uma = Celebrity.create name: 'Uma Thurman'
9
+ @c_rick = Celebrity.create name: 'Rick Astley'
10
+ end
11
+
12
+ def test_the_world
13
+ assert @u_john.is_follower?
14
+ assert @c_chuck.is_followable?
15
+
16
+ assert @u_john.follow!(@c_rick)
17
+ assert @u_john.follow!(@c_chuck)
18
+ assert @u_jane.follow!(@c_rick)
19
+
20
+ assert_equal true, @u_john.follows?(@c_chuck)
21
+ assert_equal true, @u_john.follows?(@c_rick)
22
+ assert_equal true, @c_chuck.followed_by?(@u_john)
23
+
24
+ assert @c_uma.followers.empty?
25
+
26
+ # can't have duplicate follows
27
+ assert_raise ActiveRecord::RecordInvalid do
28
+ @u_john.follow!(@c_rick)
29
+ end
30
+ end
31
+
32
+ def test_user_following_user
33
+ @u_john.follow!(@u_jane)
34
+ assert_equal true, @u_john.follows?(@u_jane)
35
+ assert_equal false, @u_jane.follows?(@u_john)
36
+ end
37
+ end