partisan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 915b00e7ddd9b09bc6a7e260e56d475a45b8daaf
4
+ data.tar.gz: affbbc560d7a023b40b85216ac65b035490a793c
5
+ SHA512:
6
+ metadata.gz: 3d1c0843ee2e7ce9c1558490f6ea37b6fdf3911a0af79959f1343a627163ec430293951233104b67438bdd59414fe32519054684d16321a64d9aec97ddf71ef1
7
+ data.tar.gz: df350de0310c0a71e68bb752d8986afc74fa0e0f5f878aefbde51782f37933c059b11dbb6b8d797ce683a68a0c9b9ffc2bc751a70dae56ae80fd7298119b9257
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rémi Prévost
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Partisan
2
+
3
+ Partisan is a Ruby library that allows ActiveRecord records to be follower and followable
4
+
5
+ It’s heavily inspired by `acts_as_follower`.
6
+
7
+ It’s not 100% compatible with `acts_as_follower`, I removed some "features":
8
+
9
+ * block follower
10
+ * Array with all types of followers/following
11
+ * `*_count` methods
12
+
13
+ But I also added awesome new ones:
14
+
15
+ * model_follower_fields: So you can do `following_team_ids` but also `following_team_names`. It takes advantage of the `pluck` method, so it doesn’t create an instance of each follower. (go check `pluck` documentation, it’s simply awesome).
16
+
17
+ * followers/followings now returns ActiveRecord::Relation for easy chaining/scoping/paginating...
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application’s Gemfile:
22
+
23
+ ```ruby
24
+ gem 'partisan'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ ```bash
30
+ $ bundle
31
+ ```
32
+
33
+ Run the migration to add the `follows` table:
34
+
35
+ ```bash
36
+ $ rails generate partisan:install
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Create a couple of models.
42
+
43
+ ```ruby
44
+ class Fan < ActiveRecord::Base
45
+ acts_as_follower
46
+ end
47
+
48
+ class Band < ActiveRecord::Base
49
+ acts_as_followable
50
+ end
51
+ ```
52
+
53
+ And follow/unfollow other records!
54
+
55
+ ```ruby
56
+ fan = Fan.find(1)
57
+ band = Band.find(2)
58
+
59
+ fan.follow(band)
60
+ fan.following_bands
61
+ # => [<Band id=2>]
62
+
63
+ fan.following?(band)
64
+ # => true
65
+
66
+ fan.unfollow(band)
67
+ fan.following?(band)
68
+ # => false
69
+
70
+ ```
71
+
72
+ Most of the times, you would want to get a quick look at about how many bands followed a certain resource. That could be an expensive operation.
73
+
74
+ However, if the *followed* record has an `followers_count` column, Partisan will populate its value with how many bands followed that record.
75
+
76
+ ```ruby
77
+ band.follow(band)
78
+
79
+ band.followers.count
80
+ # SQL query that counts records and returns `1`
81
+
82
+ band.followers_count
83
+ # Quick lookup into the column and returns `1`
84
+ ```
85
+
86
+ The same concept applies to `followable` with a `following_count` column
87
+
88
+ ## License
89
+
90
+ `Partisan` is © 2013 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/emotions/blob/master/LICENSE.md) file.
91
+
92
+ ## About Mirego
93
+
94
+ Mirego is a team of passionate people who believe that work is a place where you can innovate and have fun.
95
+ We proudly built mobile applications for
96
+ [iPhone](http://mirego.com/en/iphone-app-development/ "iPhone application development"),
97
+ [iPad](http://mirego.com/en/ipad-app-development/ "iPad application development"),
98
+ [Android](http://mirego.com/en/android-app-development/ "Android application development"),
99
+ [Blackberry](http://mirego.com/en/blackberry-app-development/ "Blackberry application development"),
100
+ [Windows Phone](http://mirego.com/en/windows-phone-app-development/ "Windows Phone application development") and
101
+ [Windows 8](http://mirego.com/en/windows-8-app-development/ "Windows 8 application development").
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ require 'rake'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ task default: :spec
7
+
8
+ desc 'Run all specs'
9
+ RSpec::Core::RakeTask.new(:spec) do |task|
10
+ task.pattern = 'spec/**/*_spec.rb'
11
+ end
@@ -0,0 +1,3 @@
1
+ Description:
2
+ The acts_as_follower:install generator generates a migration that will
3
+ create the `follows` table in your database.
@@ -0,0 +1,25 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Partisan
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ # Implement the required interface for Rails::Generators::Migration.
11
+ # taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
12
+ def self.next_migration_number(dirname)
13
+ if ActiveRecord::Base.timestamped_migrations
14
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
15
+ else
16
+ "%.3d" % (current_migration_number(dirname) + 1)
17
+ end
18
+ end
19
+
20
+ def create_migration_file
21
+ migration_template 'migration.rb', 'db/migrate/add_follows_migration.rb'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ class AddFollowsMigration < ActiveRecord::Migration
2
+ def up
3
+ create_table :follows do |t|
4
+ t.references :followable, polymorphic: true
5
+ t.references :follower, polymorphic: true
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :follows, ["follower_id", "follower_type"], name: "fk_follows"
11
+ add_index :follows, ["followable_id", "followable_type"], name: "fk_followables"
12
+ end
13
+
14
+ def down
15
+ drop_table :follows
16
+ end
17
+ end
data/lib/partisan.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "partisan/version"
2
+
3
+ require "active_record"
4
+
5
+ require "partisan/follow"
6
+ require "partisan/follow_helper"
7
+ require "partisan/follower"
8
+ require "partisan/followable"
9
+
10
+ module Partisan
11
+
12
+ include Partisan::FollowHelper
13
+ end
14
+
15
+ require 'partisan/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
@@ -0,0 +1,24 @@
1
+ module Partisan
2
+ class Follow < ActiveRecord::Base
3
+ self.table_name = 'follows'
4
+
5
+ # Validations
6
+ validates :followable, presence: true
7
+ validates :follower, presence: true
8
+
9
+ # Associations
10
+ belongs_to :followable, polymorphic: true
11
+ belongs_to :follower, polymorphic: true
12
+
13
+ # Callbacks
14
+ after_create :update_follow_counter
15
+ after_destroy :update_follow_counter
16
+
17
+ protected
18
+
19
+ def update_follow_counter
20
+ self.follower.update_follow_counter
21
+ self.followable.update_follow_counter
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module Partisan
2
+ module FollowHelper
3
+
4
+ private
5
+
6
+ # Retrieves the parent class name if using STI.
7
+ def parent_class_name(obj)
8
+ klass = obj.class
9
+ klass = klass.superclass while klass.superclass != ActiveRecord::Base
10
+
11
+ klass.name
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,70 @@
1
+ module Partisan
2
+ module Followable
3
+ extend ActiveSupport::Concern
4
+ include Partisan::FollowHelper
5
+
6
+ included do
7
+ has_many :followings, as: :followable, class_name: 'Partisan::Follow', dependent: :destroy
8
+ end
9
+
10
+ # Return true or false if the resource is following another
11
+ #
12
+ # @example
13
+ #
14
+ # @team.followed_by?(@user)
15
+ #
16
+ # @return (Boolean)
17
+ def followed_by?(resource)
18
+ resource.following?(self)
19
+ end
20
+
21
+ # Get resource records for a specific type, used by #method_missing
22
+ # It conveniently returns an ActiveRecord::Relation for easy chaining of useful ActiveRecord methods
23
+ #
24
+ # @example
25
+ #
26
+ # @team.followers_by_type('User')
27
+ #
28
+ # @return (ActiveRecord::Relation)
29
+ def followers_by_type(follower_type)
30
+ opts = {
31
+ 'follows.followable_id' => self.id,
32
+ 'follows.followable_type' => parent_class_name(self)
33
+ }
34
+
35
+ follower_type.constantize.joins(:follows).where(opts)
36
+ end
37
+
38
+ # Get ids of resources following self
39
+ # Use #pluck for an optimized sql query
40
+ #
41
+ # @example
42
+ #
43
+ # @team.following_ids_by_type('User')
44
+ #
45
+ # @return (Array)
46
+ def follower_fields_by_type(follower_type, follower_field)
47
+ followers_by_type(follower_type).pluck("#{follower_type.tableize}.#{follower_field}")
48
+ end
49
+
50
+ # Update cache counter
51
+ # Called in after_create and after_destroy
52
+ def update_follow_counter
53
+ self.update_attribute('followers_count', self.followings.count) if self.respond_to?(:followers_count)
54
+ end
55
+
56
+ def method_missing(m, *args)
57
+ if m.to_s[/(.+)_follower_(.+)s$/]
58
+ follower_fields_by_type($1.singularize.classify, $2)
59
+ elsif m.to_s[/(.+)_followers$/]
60
+ followers_by_type($1.singularize.classify)
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def respond_to?(m, include_private = false)
67
+ super || m.to_s[/(.+)_follower_(.+)s$/] || m.to_s[/(.+)_follower/]
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,113 @@
1
+ module Partisan
2
+ module Follower
3
+ extend ActiveSupport::Concern
4
+ include Partisan::FollowHelper
5
+
6
+ included do
7
+ has_many :follows, as: :follower, class_name: 'Partisan::Follow', dependent: :destroy
8
+ end
9
+
10
+ # Add follow record if it doesn’t exist
11
+ #
12
+ # @example
13
+ #
14
+ # @user.follow(@team)
15
+ #
16
+ # @return (Partisan::Follow)
17
+ def follow(resource)
18
+ return if self == resource
19
+
20
+ fetch_follows(resource).first_or_create
21
+ end
22
+ alias_method :start_following, :follow
23
+
24
+ # Delete follow record if it exists
25
+ #
26
+ # @example
27
+ #
28
+ # @user.unfollow(@team)
29
+ #
30
+ # @return (Partisan::Follow) || nil
31
+ def unfollow(resource)
32
+ return if self == resource
33
+
34
+ record = fetch_follows(resource).first
35
+ record.try(:destroy)
36
+ end
37
+ alias_method :stop_following, :unfollow
38
+
39
+ # Return true or false if the resource is following another
40
+ #
41
+ # @example
42
+ #
43
+ # @user.following?(@team)
44
+ #
45
+ # @return (Boolean)
46
+ def following?(resource)
47
+ return false if self == resource
48
+
49
+ fetch_follows(resource).exists?
50
+ end
51
+
52
+ # Get all follows record related to a resource
53
+ #
54
+ # @example
55
+ #
56
+ # @user.fetch_follows(@team)
57
+ #
58
+ # @return [Partisan::Follow, ...]
59
+ def fetch_follows(resource)
60
+ follows.where followable_id: resource.id, followable_type: parent_class_name(resource)
61
+ end
62
+
63
+ # Get resource records for a specific type, used by #method_missing
64
+ # It conveniently returns an ActiveRecord::Relation for easy chaining of useful ActiveRecord methods
65
+ #
66
+ # @example
67
+ #
68
+ # @user.following_by_type('Team')
69
+ #
70
+ # @return (ActiveRecord::Relation)
71
+ def following_by_type(followable_type)
72
+ opts = {
73
+ 'follows.follower_id' => self.id,
74
+ 'follows.follower_type' => parent_class_name(self)
75
+ }
76
+
77
+ followable_type.constantize.joins(:followings).where(opts)
78
+ end
79
+
80
+ # Get ids of resources following self
81
+ # Use #pluck for an optimized sql query
82
+ #
83
+ # @example
84
+ #
85
+ # @user.following_ids_by_type('Team')
86
+ #
87
+ # @return (Array)
88
+ def following_fields_by_type(followable_type, followable_field)
89
+ following_by_type(followable_type).pluck("#{followable_type.tableize}.#{followable_field}")
90
+ end
91
+
92
+ # Update cache counter
93
+ # Called in after_create and after_destroy
94
+ def update_follow_counter
95
+ self.update_attribute('followings_count', self.follows.count) if self.respond_to?(:followings_count)
96
+ end
97
+
98
+ def method_missing(m, *args)
99
+ if m.to_s[/following_(.+)_(.+)s$/]
100
+ following_fields_by_type($1.singularize.classify, $2)
101
+ elsif m.to_s[/following_(.+)/]
102
+ following_by_type($1.singularize.classify)
103
+ else
104
+ super
105
+ end
106
+ end
107
+
108
+ def respond_to?(m, include_private = false)
109
+ super || m.to_s[/following_(.+)_(.+)s$/] || m.to_s[/following_(.+)/]
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,18 @@
1
+ require 'acts_as_follower'
2
+ require 'rails'
3
+
4
+ module Partisan
5
+ class Railtie < Rails::Railtie
6
+ initializer "follows.active_record" do |app|
7
+ ActiveSupport.on_load :active_record do
8
+ def self.acts_as_follower
9
+ self.send :include, Partisan::Follower
10
+ end
11
+
12
+ def self.acts_as_followable
13
+ self.send :include, Partisan::Followable
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Partisan
2
+ VERSION = "0.0.1"
3
+ end
data/partisan.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'partisan/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'partisan'
8
+ spec.version = Partisan::VERSION
9
+ spec.authors = ['Simon Prévost']
10
+ spec.email = ['sprevost@mirego.com']
11
+ spec.description = 'Partisan is a Ruby library that allows ActiveRecord records to be follower and followable, just like on popular social networks. It’s heavily inspired by the origin acts_as_follower which is no longer maintened'
12
+ spec.summary = 'Partisan is a Ruby library that allows ActiveRecord records to be follower and followable'
13
+ spec.homepage = 'https://github.com/simonprev/partisan'
14
+ spec.license = 'BSD 3-Clause'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(spec)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'rails', '>= 3.0.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'sqlite3'
27
+ end
@@ -0,0 +1,74 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+
3
+ describe Partisan::Followable do
4
+ before do
5
+ run_migration do
6
+ create_table(:fans, force: true)
7
+ create_table(:users, force: true)
8
+ create_table(:concerts, force: true)
9
+
10
+ create_table(:bands, force: true) do |t|
11
+ t.integer :followers_count, default: 0
12
+ end
13
+ end
14
+
15
+ follower 'Fan'
16
+ follower 'User'
17
+ followable 'Band'
18
+ followable 'Concert'
19
+ end
20
+
21
+ let(:band) { Band.create }
22
+ let(:user) { User.create }
23
+ let(:concert) { Concert.create }
24
+ let(:fan) { Fan.create }
25
+
26
+ describe :InstanceMethods do
27
+ before do
28
+ user.follow band
29
+
30
+ band.reload
31
+ end
32
+
33
+ describe :followed_by? do
34
+ it { expect(band.followed_by? user).to be_true }
35
+ it { expect(concert.followed_by? user).to be_false }
36
+ end
37
+
38
+ describe :followers_by_type do
39
+ it { expect(band.followers_by_type('User').count).to eq 1 }
40
+ it { expect(band.followers_by_type('User')).to be_an_instance_of(ActiveRecord::Relation) }
41
+ it { expect(band.followers_by_type('User').first).to be_an_instance_of(User) }
42
+ it { expect(band.followers_by_type('Fan').count).to eq 0 }
43
+ end
44
+
45
+ describe :following_fields_by_type do
46
+ it { expect(band.follower_fields_by_type('User', 'id').count).to eq 1 }
47
+ it { expect(band.follower_fields_by_type('User', 'id').first).to eq user.id }
48
+ it { expect(band.follower_fields_by_type('Fan', 'id').count).to eq 0 }
49
+ end
50
+
51
+ describe :followers_by_type_in_method_missing do
52
+ it { expect(band.user_followers.count).to eq 1 }
53
+ it { expect(band.user_followers).to be_an_instance_of(ActiveRecord::Relation) }
54
+ it { expect(band.user_followers.first).to be_an_instance_of(User) }
55
+ it { expect(band.fan_followers.count).to eq 0 }
56
+ end
57
+
58
+ describe :following_fields_by_type_in_method_missing do
59
+ it { expect(band.users_follower_ids.count).to eq 1 }
60
+ it { expect(band.users_follower_ids.first).to eq user.id }
61
+ it { expect(band.fans_follower_ids.count).to eq 0 }
62
+ end
63
+
64
+ describe :respond_to? do
65
+ it { expect(band.respond_to?(:user_followers)).to be_true }
66
+ it { expect(band.respond_to?(:users_follower_ids)).to be_true }
67
+ end
68
+
69
+ describe :update_follow_counter do
70
+ it { expect(band.followers_count).to eq 1 }
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,89 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+
3
+ describe Partisan::Follower do
4
+ before do
5
+ run_migration do
6
+ create_table(:users, force: true) do |t|
7
+ t.integer :followings_count, default: 0
8
+ end
9
+ create_table(:concerts, force: true)
10
+ create_table(:bands, force: true)
11
+ end
12
+
13
+ follower 'User'
14
+ followable 'Band'
15
+ followable 'Concert'
16
+ end
17
+
18
+ let(:band) { Band.create }
19
+ let(:user) { User.create }
20
+ let(:concert) { Concert.create }
21
+
22
+ describe :InstanceMethods do
23
+ before do
24
+ user.follow band
25
+
26
+ user.reload
27
+ end
28
+
29
+ describe :follow do
30
+ it { expect(Partisan::Follow.last.follower_id).to eq user.id }
31
+ it { expect(Partisan::Follow.last.followable_id).to eq band.id }
32
+ end
33
+
34
+ describe :unfollow do
35
+ it { expect{user.unfollow band}.to change{Partisan::Follow.last}.to(nil) }
36
+ end
37
+
38
+ describe :following? do
39
+ let(:band2) { Band.create }
40
+
41
+ it { expect(user.following? band).to be_true }
42
+ it { expect(user.following? band2).to be_false }
43
+ end
44
+
45
+ describe :following_by_type do
46
+ it { expect(user.following_by_type('Band').count).to eq 1 }
47
+ it { expect(user.following_by_type('Band')).to be_an_instance_of(ActiveRecord::Relation) }
48
+ it { expect(user.following_by_type('Band').first).to be_an_instance_of(Band) }
49
+ it { expect(user.following_by_type('Concert').count).to eq 0 }
50
+ end
51
+
52
+ describe :following_fields_by_type do
53
+ it { expect(user.following_fields_by_type('Band', 'id').count).to eq 1 }
54
+ it { expect(user.following_fields_by_type('Band', 'id').first).to eq band.id }
55
+ it { expect(user.following_fields_by_type('Concert', 'id').count).to eq 0 }
56
+ end
57
+
58
+ describe :following_by_type_in_method_missing do
59
+ it { expect(user.following_bands.count).to eq 1 }
60
+ it { expect(user.following_bands).to be_an_instance_of(ActiveRecord::Relation) }
61
+ it { expect(user.following_bands.first).to be_an_instance_of(Band) }
62
+
63
+ it { expect(user.following_concerts.count).to eq 0 }
64
+ it { expect(user.following_concerts).to be_an_instance_of(ActiveRecord::Relation) }
65
+ end
66
+
67
+ describe :following_fields_by_type_in_method_missing do
68
+ it { expect(user.following_band_ids.count).to eq 1 }
69
+ it { expect(user.following_band_ids.first).to eq band.id }
70
+ it { expect(user.following_concert_ids.count).to eq 0 }
71
+ end
72
+
73
+ describe :update_follow_counter do
74
+ it { expect(user.followings_count).to eq 1 }
75
+ end
76
+
77
+ describe :respond_to? do
78
+ it { expect(user.respond_to?(:following_bands)).to be_true }
79
+ it { expect(user.respond_to?(:following_band_ids)).to be_true }
80
+ end
81
+ end
82
+
83
+ describe :AliasMethods do
84
+ subject { User.create }
85
+
86
+ it { should respond_to :start_following }
87
+ it { should respond_to :stop_following }
88
+ end
89
+ end
@@ -0,0 +1,30 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
2
+
3
+ require 'rspec'
4
+ require 'sqlite3'
5
+
6
+ require 'partisan'
7
+
8
+ # Require our macros and extensions
9
+ Dir[File.expand_path('../../spec/support/macros/*.rb', __FILE__)].map(&method(:require))
10
+ Dir[File.expand_path('../../spec/support/extensions/*.rb', __FILE__)].map(&method(:require))
11
+
12
+
13
+ RSpec.configure do |config|
14
+ # Include our macros
15
+ config.include DatabaseMacros
16
+ config.include ModelMacros
17
+
18
+ config.before(:each) do
19
+ # Create the SQLite database
20
+ setup_database
21
+
22
+ # Run our migration
23
+ run_default_migration
24
+ end
25
+
26
+ config.after(:each) do
27
+ # Make sure we remove our test database file
28
+ cleanup_database
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ class ActiveRecord::Base
2
+ def self.acts_as_follower
3
+ self.send :include, Partisan::Follower
4
+ end
5
+
6
+ def self.acts_as_followable
7
+ self.send :include, Partisan::Followable
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module DatabaseMacros
2
+ # Run migrations in the test database
3
+ def run_migration(&block)
4
+ # Create a new migration class
5
+ klass = Class.new(ActiveRecord::Migration)
6
+
7
+ # Create a new `up` that executes the argument
8
+ klass.send(:define_method, :up) { self.instance_exec(&block) }
9
+
10
+ # Create a new instance of it and execute its `up` method
11
+ klass.new.up
12
+ end
13
+
14
+ def self.database_file
15
+ @database_file || File.join(File.dirname(__FILE__), 'test.db')
16
+ end
17
+
18
+ def setup_database
19
+ # Make sure the test database file is gone
20
+ cleanup_database
21
+
22
+ # Establish the connection
23
+ SQLite3::Database.new FileUtils.touch(DatabaseMacros.database_file).first
24
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: DatabaseMacros.database_file)
25
+
26
+ # Silence everything
27
+ ActiveRecord::Base.logger = ActiveRecord::Migration.verbose = false
28
+ end
29
+
30
+ def cleanup_database
31
+ FileUtils.rm(DatabaseMacros.database_file) if File.exists?(DatabaseMacros.database_file)
32
+ end
33
+
34
+ # Run the built-in migration
35
+ def run_default_migration
36
+ load File.join(File.dirname(__FILE__), '../../../lib/generators/partisan/templates/migration.rb')
37
+ AddFollowsMigration.new.up
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ module ModelMacros
2
+ # Create a new emotional model
3
+ def followable(klass_name, &block)
4
+ spawn_model klass_name, ActiveRecord::Base do
5
+ acts_as_followable
6
+ instance_exec(&block) if block
7
+ end
8
+ end
9
+
10
+ # Create a new emotive model
11
+ def follower(klass_name, &block)
12
+ spawn_model klass_name, ActiveRecord::Base do
13
+ acts_as_follower
14
+ class_eval(&block) if block
15
+ end
16
+ end
17
+
18
+ protected
19
+
20
+ # Create a new model class
21
+ def spawn_model(klass_name, parent_klass, &block)
22
+ Object.instance_eval { remove_const klass_name } if Object.const_defined?(klass_name)
23
+ Object.const_set(klass_name, Class.new(parent_klass))
24
+ Object.const_get(klass_name).class_eval(&block) if block_given?
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: partisan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Simon Prévost
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Partisan is a Ruby library that allows ActiveRecord records to be follower
84
+ and followable, just like on popular social networks. It’s heavily inspired by the
85
+ origin acts_as_follower which is no longer maintened
86
+ email:
87
+ - sprevost@mirego.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .gitignore
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/generators/partisan/USAGE
98
+ - lib/generators/partisan/install_generator.rb
99
+ - lib/generators/partisan/templates/migration.rb
100
+ - lib/partisan.rb
101
+ - lib/partisan/follow.rb
102
+ - lib/partisan/follow_helper.rb
103
+ - lib/partisan/followable.rb
104
+ - lib/partisan/follower.rb
105
+ - lib/partisan/railtie.rb
106
+ - lib/partisan/version.rb
107
+ - partisan.gemspec
108
+ - spec/followable_spec.rb
109
+ - spec/follower_spec.rb
110
+ - spec/spec_helper.rb
111
+ - spec/support/extensions/active_record.rb
112
+ - spec/support/macros/database_macros.rb
113
+ - spec/support/macros/model_macros.rb
114
+ homepage: https://github.com/simonprev/partisan
115
+ licenses:
116
+ - BSD 3-Clause
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.0.2
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Partisan is a Ruby library that allows ActiveRecord records to be follower
138
+ and followable
139
+ test_files:
140
+ - spec/followable_spec.rb
141
+ - spec/follower_spec.rb
142
+ - spec/spec_helper.rb
143
+ - spec/support/extensions/active_record.rb
144
+ - spec/support/macros/database_macros.rb
145
+ - spec/support/macros/model_macros.rb