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