acts_in_relation 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/README.md +151 -0
- data/Rakefile +3 -17
- data/acts_in_relation.gemspec +24 -0
- data/lib/acts_in_relation.rb +21 -6
- data/lib/acts_in_relation/core.rb +55 -36
- data/lib/acts_in_relation/railtie.rb +1 -3
- data/lib/acts_in_relation/roles/action.rb +15 -0
- data/lib/acts_in_relation/roles/base.rb +46 -0
- data/lib/acts_in_relation/roles/source.rb +35 -0
- data/lib/acts_in_relation/roles/target.rb +24 -0
- data/lib/acts_in_relation/supports/verb.rb +8 -9
- data/lib/acts_in_relation/version.rb +1 -1
- data/spec/dummy/application.rb +86 -0
- data/spec/dummy/bin/rails +5 -0
- data/spec/dummy/config.ru +1 -0
- data/spec/models/post_spec.rb +34 -0
- data/spec/models/user_spec.rb +122 -0
- data/spec/spec_helper.rb +1 -0
- metadata +25 -16
- data/lib/acts_in_relation/action.rb +0 -10
- data/lib/acts_in_relation/source.rb +0 -35
- data/lib/acts_in_relation/target.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98631806c248ec6a232438391598ca3c3b6d1429
|
4
|
+
data.tar.gz: 15a8894cde8f8da9d753d647be4220c4c373d0f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cab9fb68c574f05868c3abc17c5cd26638ab901041f6008cc8996e231398bc8087d3002201ead3e2f9ba60cbfd98e9d87e4ab6b965ec040c523233f97f8cf297
|
7
|
+
data.tar.gz: 2b754e9fb4eacba5be95acbdb2dc7eff4adf15220ab32afd96bd87008b90ba4110356b404db0564727cc30d5c5611d359ea722bd992bd2991dff94b76c0f9ce3
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# acts_in_relation
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/kami30k/acts_in_relation.svg)](https://travis-ci.org/kami30k/acts_in_relation)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/acts_in_relation.svg)](http://badge.fury.io/rb/acts_in_relation)
|
5
|
+
|
6
|
+
acts_in_relation adds relational feature to Rails application, such as follow, block, like and so on.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'acts_in_relation'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
```
|
19
|
+
$ bundle
|
20
|
+
```
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
acts_in_relation supports two way to add relational feature.
|
25
|
+
|
26
|
+
1. Add feature to oneself
|
27
|
+
2. Add feature to between two models
|
28
|
+
|
29
|
+
Following example shows about User model, however, you can apply to any models.
|
30
|
+
|
31
|
+
### 1. Add follow feature to User
|
32
|
+
|
33
|
+
This case adds follow feature to User model.
|
34
|
+
|
35
|
+
At first, generate User and Follow model:
|
36
|
+
|
37
|
+
```
|
38
|
+
$ bin/rails g model User
|
39
|
+
$ bin/rails g model Follow user_id:integer target_user_id:integer
|
40
|
+
```
|
41
|
+
|
42
|
+
Then migrate:
|
43
|
+
|
44
|
+
```
|
45
|
+
$ bin/rake db:migrate
|
46
|
+
```
|
47
|
+
|
48
|
+
At last, add `acts_in_relation` method to each models:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class User < ActiveRecord::Base
|
52
|
+
acts_in_relation role: :self, action: :follow
|
53
|
+
end
|
54
|
+
|
55
|
+
class Follow < ActiveRecord::Base
|
56
|
+
acts_in_relation role: :action, self: :user
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
That's it.
|
61
|
+
User instance has been added following methods:
|
62
|
+
|
63
|
+
- user.follow(other_user)
|
64
|
+
- user.unfollow(other_user)
|
65
|
+
- user.following?(other_user)
|
66
|
+
- user.following
|
67
|
+
- other_user.followed_by?(user)
|
68
|
+
- other_user.followers
|
69
|
+
|
70
|
+
Example:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
user = User.create
|
74
|
+
other_user = User.create
|
75
|
+
|
76
|
+
# Follow
|
77
|
+
user.follow other_user
|
78
|
+
user.following?(other_user) #=> true
|
79
|
+
user.following #=> <ActiveRecord::Associations::CollectionProxy [#<User id: 2, created_at: "2015-01-10 01:57:52", updated_at: "2015-01-10 01:57:52">]>
|
80
|
+
other_user.followed_by?(user) #=> true
|
81
|
+
other_user.followers #=> <ActiveRecord::Associations::CollectionProxy [#<User id: 1, created_at: "2015-01-10 01:57:42", updated_at: "2015-01-10 01:57:42">]>
|
82
|
+
|
83
|
+
# Unfollow
|
84
|
+
user.unfollow other_user
|
85
|
+
user.following?(other_user) #=> false
|
86
|
+
user.following #=> <ActiveRecord::Associations::CollectionProxy []>
|
87
|
+
other_user.followed_by?(user) #=> false
|
88
|
+
other_user.followers #=> <ActiveRecord::Associations::CollectionProxy []>
|
89
|
+
```
|
90
|
+
|
91
|
+
At the same time, `:action` is able to be passed some actions:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class User < ActiveRecord::Base
|
95
|
+
acts_in_relation role: :self, action: [:follow, :block, :mute]
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
### 2. Add like feature to User and Post
|
100
|
+
|
101
|
+
This case adds like feature to User and Post model.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
class User < ActiveRecord::Base
|
105
|
+
acts_in_relation role: :source, target: :post, action: :like
|
106
|
+
end
|
107
|
+
|
108
|
+
class Post < ActiveRecord::Base
|
109
|
+
acts_in_relation role: :target, source: :user, action: :like
|
110
|
+
end
|
111
|
+
|
112
|
+
class Like < ActiveRecord::Base
|
113
|
+
acts_in_relation role: :action, source: :user, target: :post
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
User and Post instance has been added following methods:
|
118
|
+
|
119
|
+
- user.like(post)
|
120
|
+
- user.unlike(post)
|
121
|
+
- user.liking?(post)
|
122
|
+
- user.liking
|
123
|
+
- post.liked_by?(user)
|
124
|
+
- post.likers
|
125
|
+
|
126
|
+
At the same time, some `acts_in_relation` methods are able to be defined:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class User < ActiveRecord::Base
|
130
|
+
acts_in_relation role: :self, action: :follow
|
131
|
+
acts_in_relation role: :source, target: :post, action: :like
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
## Roles
|
136
|
+
|
137
|
+
acts_in_relation has three roles: source, target and action.
|
138
|
+
|
139
|
+
| Role | Outline | (1) | (2) |
|
140
|
+
| --- | --- | --- | --- |
|
141
|
+
| source | The model that performs the action. | User | User |
|
142
|
+
| target | The model that receives the action. | User | Post |
|
143
|
+
| action | The action performs between two models. | Follow | Like |
|
144
|
+
|
145
|
+
## Contributing
|
146
|
+
|
147
|
+
1. Fork it ( https://github.com/kami30k/acts_in_relation/fork )
|
148
|
+
2. Create your feature branch (git checkout -b my-new-feature)
|
149
|
+
3. Commit your changes (git commit -am 'Add some feature')
|
150
|
+
4. Push to the branch (git push origin my-new-feature)
|
151
|
+
5. Create a new Pull Request
|
data/Rakefile
CHANGED
@@ -1,21 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
rescue LoadError
|
4
|
-
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
-
end
|
6
|
-
|
7
|
-
require 'rdoc/task'
|
1
|
+
require 'bundler'
|
2
|
+
require 'rspec/core/rake_task'
|
8
3
|
|
9
|
-
|
10
|
-
rdoc.rdoc_dir = 'rdoc'
|
11
|
-
rdoc.title = 'ActsInRelation'
|
12
|
-
rdoc.options << '--line-numbers'
|
13
|
-
rdoc.rdoc_files.include('README.rdoc')
|
14
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
-
end
|
4
|
+
Bundler::GemHelper.install_tasks
|
16
5
|
|
17
|
-
require 'rspec/core/rake_task'
|
18
6
|
RSpec::Core::RakeTask.new(:spec)
|
19
7
|
task default: :spec
|
20
|
-
|
21
|
-
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'acts_in_relation/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'acts_in_relation'
|
7
|
+
s.version = ActsInRelation::VERSION
|
8
|
+
s.authors = 'kami'
|
9
|
+
s.email = 'kami30k@gmail.com'
|
10
|
+
|
11
|
+
s.summary = 'Add relational feature to Rails (e.g. follow, block and like).'
|
12
|
+
s.description = 'Add relational feature to Rails (e.g. follow, block and like).'
|
13
|
+
s.homepage = 'https://github.com/kami30k/acts_in_relation'
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.files = `git ls-files -z`.split("\x0")
|
17
|
+
|
18
|
+
s.add_dependency 'rails'
|
19
|
+
s.add_dependency 'verbs'
|
20
|
+
s.add_dependency 'caller_class'
|
21
|
+
|
22
|
+
s.add_development_dependency 'sqlite3'
|
23
|
+
s.add_development_dependency 'rspec-rails'
|
24
|
+
end
|
data/lib/acts_in_relation.rb
CHANGED
@@ -1,14 +1,29 @@
|
|
1
1
|
require 'acts_in_relation/version'
|
2
|
+
require 'acts_in_relation/railtie' if defined?(Rails)
|
2
3
|
|
3
4
|
module ActsInRelation
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
class MissingRoleError < StandardError; end
|
6
|
+
|
7
|
+
class UnknownRoleError < StandardError
|
8
|
+
def initialize(role)
|
9
|
+
@role = role
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
":role should be one of :source, :target, :action or :self (#{@role} is passed)"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
autoload :Core, 'acts_in_relation/core'
|
18
|
+
|
19
|
+
module Roles
|
20
|
+
autoload :Base, 'acts_in_relation/roles/base'
|
21
|
+
autoload :Source, 'acts_in_relation/roles/source'
|
22
|
+
autoload :Target, 'acts_in_relation/roles/target'
|
23
|
+
autoload :Action, 'acts_in_relation/roles/action'
|
24
|
+
end
|
8
25
|
|
9
26
|
module Supports
|
10
27
|
autoload :Verb, 'acts_in_relation/supports/verb'
|
11
28
|
end
|
12
|
-
|
13
|
-
require 'acts_in_relation/railtie' if defined?(Rails)
|
14
29
|
end
|
@@ -1,54 +1,73 @@
|
|
1
|
-
require 'caller_class'
|
2
|
-
|
3
1
|
module ActsInRelation
|
4
2
|
module Core
|
5
|
-
|
6
|
-
base
|
7
|
-
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
include CallerClass
|
11
|
-
|
12
|
-
def acts_in_relation(position = :self, params)
|
13
|
-
relation = Relation.new(position, params, caller_class.downcase)
|
14
|
-
relation.define
|
3
|
+
class << self
|
4
|
+
def included(base)
|
5
|
+
base.extend ClassMethods
|
15
6
|
end
|
16
7
|
end
|
17
8
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
module ClassMethods
|
10
|
+
# DSL called to a subclass of ActiveRecord::Base
|
11
|
+
#
|
12
|
+
# @param [Hash] args Define relation with :role, :action, :source, :target or :self.
|
13
|
+
#
|
14
|
+
# @example Define self relation
|
15
|
+
# class User < ActiveRecord::Base
|
16
|
+
# acts_in_relation role: :self, action: [:follow, :block]
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# class Follow < ActiveRecord::Base
|
20
|
+
# acts_in_relation role: :action, self: :user
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# class Block < ActiveRecord::Base
|
24
|
+
# acts_in_relation role: :action, self: :user
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @example Define relation of each models
|
28
|
+
# class User < ActiveRecord::Base
|
29
|
+
# acts_in_relation role: :source, target: :post, action: :like
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# class Post < ActiveRecord::Base
|
33
|
+
# acts_in_relation role: :target, source: :user, action: :like
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# class Like < ActiveRecord::Base
|
37
|
+
# acts_in_relation role: :action, source: :user, target: :post
|
38
|
+
# end
|
39
|
+
def acts_in_relation(**args)
|
40
|
+
@args = args
|
24
41
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
42
|
+
case @args[:role]
|
43
|
+
when nil
|
44
|
+
raise ActsInRelation::MissingRoleError
|
45
|
+
when :source
|
46
|
+
define_source
|
47
|
+
when :target
|
48
|
+
define_target
|
49
|
+
when :action
|
50
|
+
define_action
|
51
|
+
when :self
|
52
|
+
define_source
|
53
|
+
define_target
|
54
|
+
else
|
55
|
+
raise ActsInRelation::UnknownRoleError, @args[:role]
|
30
56
|
end
|
31
57
|
end
|
32
58
|
|
33
59
|
private
|
34
60
|
|
35
|
-
def
|
36
|
-
@
|
37
|
-
end
|
38
|
-
|
39
|
-
def target
|
40
|
-
@target ||= @params[:target] || @class_name
|
61
|
+
def define_source
|
62
|
+
ActsInRelation::Roles::Source.new(@args).define
|
41
63
|
end
|
42
64
|
|
43
|
-
|
44
|
-
|
45
|
-
with = @params[:with]
|
46
|
-
with = with.kind_of?(Array) ? with : [with]
|
47
|
-
with.map(&:to_s)
|
65
|
+
def define_target
|
66
|
+
ActsInRelation::Roles::Target.new(@args).define
|
48
67
|
end
|
49
68
|
|
50
|
-
def
|
51
|
-
@
|
69
|
+
def define_action
|
70
|
+
ActsInRelation::Roles::Action.new(@args).define
|
52
71
|
end
|
53
72
|
end
|
54
73
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActsInRelation
|
2
|
+
module Roles
|
3
|
+
class Action < Base
|
4
|
+
def define
|
5
|
+
@class.class_eval <<-RUBY
|
6
|
+
belongs_to :"#{source}"
|
7
|
+
|
8
|
+
belongs_to :"target_#{target}",
|
9
|
+
class_name: "#{target.capitalize}",
|
10
|
+
foreign_key: :"target_#{target}_id"
|
11
|
+
RUBY
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'caller_class'
|
2
|
+
|
3
|
+
module ActsInRelation
|
4
|
+
module Roles
|
5
|
+
class Base
|
6
|
+
include CallerClass
|
7
|
+
include ActsInRelation::Supports::Verb
|
8
|
+
|
9
|
+
def initialize(args)
|
10
|
+
@class = caller_class.constantize
|
11
|
+
@args = recursive_to_s(args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def source
|
15
|
+
@source ||= @args[:source] || @args[:self] || @class.to_s.downcase
|
16
|
+
end
|
17
|
+
|
18
|
+
def target
|
19
|
+
@target ||= @args[:target] || @args[:self] || @class.to_s.downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def actions
|
23
|
+
@actions ||= [@args[:action]].flatten
|
24
|
+
end
|
25
|
+
|
26
|
+
def define
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def recursive_to_s(object)
|
33
|
+
case object
|
34
|
+
when Hash
|
35
|
+
object.each do |k, v|
|
36
|
+
object[k] = recursive_to_s(v)
|
37
|
+
end
|
38
|
+
when Array
|
39
|
+
object.map { |o| recursive_to_s(o) }
|
40
|
+
else
|
41
|
+
object.to_s unless object.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActsInRelation
|
2
|
+
module Roles
|
3
|
+
class Source < Base
|
4
|
+
def define
|
5
|
+
actions.each do |action|
|
6
|
+
@class.class_eval <<-RUBY
|
7
|
+
has_many :"#{action.pluralize}",
|
8
|
+
foreign_key: :"#{source}_id",
|
9
|
+
dependent: :destroy
|
10
|
+
|
11
|
+
has_many :"#{progressize(action)}",
|
12
|
+
through: :"#{action.pluralize}",
|
13
|
+
source: :"target_#{target}"
|
14
|
+
|
15
|
+
def #{action}(target)
|
16
|
+
return if #{progressize(action)}?(target) || (self == target)
|
17
|
+
|
18
|
+
#{action.pluralize}.create(target_#{target}_id: target.id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def un#{action}(target)
|
22
|
+
return unless #{progressize(action)}?(target)
|
23
|
+
|
24
|
+
#{action.pluralize}.find_by(target_#{target}_id: target.id).destroy
|
25
|
+
end
|
26
|
+
|
27
|
+
def #{progressize(action)}?(target)
|
28
|
+
#{action.pluralize}.exists?(target_#{target}_id: target.id)
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActsInRelation
|
2
|
+
module Roles
|
3
|
+
class Target < Base
|
4
|
+
def define
|
5
|
+
actions.each do |action|
|
6
|
+
@class.class_eval <<-RUBY
|
7
|
+
has_many :"#{action.pluralize}_as_target",
|
8
|
+
foreign_key: :"target_#{target}_id",
|
9
|
+
class_name: action.capitalize,
|
10
|
+
dependent: :destroy
|
11
|
+
|
12
|
+
has_many :"#{peoplize(action)}",
|
13
|
+
through: :"#{action.pluralize}_as_target",
|
14
|
+
source: :"#{source}"
|
15
|
+
|
16
|
+
def #{pastize(action)}_by?(source)
|
17
|
+
source.#{action.pluralize}.exists?(#{source}_id: source.id)
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -7,20 +7,19 @@ module ActsInRelation
|
|
7
7
|
'follow' => 'following'
|
8
8
|
}
|
9
9
|
|
10
|
-
def pastize
|
11
|
-
verb.conjugate(tense: :past).split(' ').last
|
10
|
+
def pastize(object)
|
11
|
+
object.verb.conjugate(tense: :past).split(' ').last
|
12
12
|
end
|
13
13
|
|
14
|
-
def progressize
|
15
|
-
return PATCHES[
|
14
|
+
def progressize(object)
|
15
|
+
return PATCHES[object] if PATCHES.has_key?(object)
|
16
16
|
|
17
|
-
verb.conjugate(aspect: :progressive).split(' ').last
|
17
|
+
object.verb.conjugate(aspect: :progressive).split(' ').last
|
18
18
|
end
|
19
19
|
|
20
|
-
#
|
21
|
-
def peoplize
|
22
|
-
|
23
|
-
action + 'ers'
|
20
|
+
# @todo Implement more logically
|
21
|
+
def peoplize(object)
|
22
|
+
(object.last == 'e' ? object.chop : object) + 'ers'
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'action_controller/railtie'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
require 'acts_in_relation'
|
5
|
+
|
6
|
+
module Dummy
|
7
|
+
class Application < Rails::Application
|
8
|
+
config.secret_token = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
9
|
+
config.session_store :cookie_store, key: '_dummy_session'
|
10
|
+
config.eager_load = false
|
11
|
+
config.active_support.deprecation = :log
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Dummy::Application.initialize!
|
16
|
+
|
17
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
18
|
+
|
19
|
+
#
|
20
|
+
# Models
|
21
|
+
#
|
22
|
+
|
23
|
+
class User < ActiveRecord::Base
|
24
|
+
acts_in_relation role: :self, action: [:follow, :block, :mute]
|
25
|
+
|
26
|
+
acts_in_relation role: :source, target: :post, action: :like
|
27
|
+
end
|
28
|
+
|
29
|
+
class Post < ActiveRecord::Base
|
30
|
+
acts_in_relation role: :target, source: :user, action: :like
|
31
|
+
end
|
32
|
+
|
33
|
+
class Follow < ActiveRecord::Base
|
34
|
+
acts_in_relation role: :action, self: :user
|
35
|
+
end
|
36
|
+
|
37
|
+
class Like < ActiveRecord::Base
|
38
|
+
acts_in_relation role: :action, source: :user, target: :post
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Migrates
|
43
|
+
#
|
44
|
+
|
45
|
+
class CreateUsers < ActiveRecord::Migration
|
46
|
+
def change
|
47
|
+
create_table :users do |t|
|
48
|
+
t.timestamps null: false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class CreatePosts < ActiveRecord::Migration
|
54
|
+
def change
|
55
|
+
create_table :posts do |t|
|
56
|
+
t.timestamps null: false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class CreateFollows < ActiveRecord::Migration
|
62
|
+
def change
|
63
|
+
create_table :follows do |t|
|
64
|
+
t.integer :user_id
|
65
|
+
t.integer :target_user_id
|
66
|
+
|
67
|
+
t.timestamps null: false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class CreateLikes < ActiveRecord::Migration
|
73
|
+
def change
|
74
|
+
create_table :likes do |t|
|
75
|
+
t.integer :user_id
|
76
|
+
t.integer :target_post_id
|
77
|
+
|
78
|
+
t.timestamps null: false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
CreateUsers.new.change
|
84
|
+
CreatePosts.new.change
|
85
|
+
CreateFollows.new.change
|
86
|
+
CreateLikes.new.change
|
@@ -0,0 +1 @@
|
|
1
|
+
run Rails.application
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Post, type: :model do
|
4
|
+
let(:user) { User.create }
|
5
|
+
let(:post) { Post.create }
|
6
|
+
|
7
|
+
describe 'instance methods' do
|
8
|
+
it 'should be defined' do
|
9
|
+
# source: :user, action: :like
|
10
|
+
expect(post).not_to respond_to(:like)
|
11
|
+
expect(post).not_to respond_to(:unlike)
|
12
|
+
expect(post).not_to respond_to(:liking?)
|
13
|
+
expect(post).not_to respond_to(:liking)
|
14
|
+
expect(post).to respond_to(:liked_by?)
|
15
|
+
expect(post).to respond_to(:likers)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#liked_by?' do
|
20
|
+
before { user.like post }
|
21
|
+
|
22
|
+
it 'should be liked by user' do
|
23
|
+
expect(post).to be_liked_by(user)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#likers' do
|
28
|
+
before { user.like post }
|
29
|
+
|
30
|
+
it 'should be included user' do
|
31
|
+
expect(post.likers).to be_exists(id: user.id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe User, type: :model do
|
4
|
+
let(:user) { User.create }
|
5
|
+
let(:other_user) { User.create }
|
6
|
+
|
7
|
+
describe 'instance methods' do
|
8
|
+
it 'should be defined' do
|
9
|
+
# action: :follow
|
10
|
+
expect(user).to respond_to(:follow)
|
11
|
+
expect(user).to respond_to(:unfollow)
|
12
|
+
expect(user).to respond_to(:following?)
|
13
|
+
expect(user).to respond_to(:following)
|
14
|
+
expect(user).to respond_to(:followed_by?)
|
15
|
+
expect(user).to respond_to(:followers)
|
16
|
+
|
17
|
+
# action: :block
|
18
|
+
expect(user).to respond_to(:block)
|
19
|
+
expect(user).to respond_to(:unblock)
|
20
|
+
expect(user).to respond_to(:blocking?)
|
21
|
+
expect(user).to respond_to(:blocking)
|
22
|
+
expect(user).to respond_to(:blocked_by?)
|
23
|
+
expect(user).to respond_to(:blockers)
|
24
|
+
|
25
|
+
# action: :mute
|
26
|
+
expect(user).to respond_to(:mute)
|
27
|
+
expect(user).to respond_to(:unmute)
|
28
|
+
expect(user).to respond_to(:muting?)
|
29
|
+
expect(user).to respond_to(:muting)
|
30
|
+
expect(user).to respond_to(:muted_by?)
|
31
|
+
expect(user).to respond_to(:muters)
|
32
|
+
|
33
|
+
# target: :post, action: :like
|
34
|
+
expect(user).to respond_to(:like)
|
35
|
+
expect(user).to respond_to(:unlike)
|
36
|
+
expect(user).to respond_to(:liking?)
|
37
|
+
expect(user).to respond_to(:liking)
|
38
|
+
expect(user).not_to respond_to(:liked_by?)
|
39
|
+
expect(user).not_to respond_to(:likers)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#follow' do
|
44
|
+
context 'unfollow user' do
|
45
|
+
before { user.follow other_user }
|
46
|
+
|
47
|
+
it 'should be followed' do
|
48
|
+
expect(user.follows).to be_exists(target_user_id: other_user.id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'following user' do
|
53
|
+
before do
|
54
|
+
user.follow other_user
|
55
|
+
user.follow other_user
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should be followed only once' do
|
59
|
+
expect(user.follows.count).to eq(1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'user-self' do
|
64
|
+
before { user.follow user }
|
65
|
+
|
66
|
+
it 'should not be followed' do
|
67
|
+
expect(user.follows).not_to be_exists(target_user_id: user.id)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#unfollow' do
|
73
|
+
context 'following user' do
|
74
|
+
before do
|
75
|
+
user.follow other_user
|
76
|
+
user.unfollow other_user
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should be unfollowed' do
|
80
|
+
expect(user.follows).not_to be_exists(target_user_id: other_user.id)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'unfollow user' do
|
85
|
+
it 'should not raise error' do
|
86
|
+
expect { user.unfollow other_user }.not_to raise_error
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#following?' do
|
92
|
+
before { user.follow other_user }
|
93
|
+
|
94
|
+
it 'should be followed' do
|
95
|
+
expect(user).to be_following(other_user)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#following' do
|
100
|
+
before { user.follow other_user }
|
101
|
+
|
102
|
+
it 'should be included other user' do
|
103
|
+
expect(user.following).to be_exists(other_user.id)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#followed_by?' do
|
108
|
+
before { other_user.follow user }
|
109
|
+
|
110
|
+
it 'should be followed by other user' do
|
111
|
+
expect(user).to be_followed_by(other_user)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#followers' do
|
116
|
+
before { other_user.follow user }
|
117
|
+
|
118
|
+
it 'should be included other user' do
|
119
|
+
expect(user.followers).to be_exists(other_user.id)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'dummy/application'
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_in_relation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kami
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: verbs
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,24 +80,34 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
84
|
-
|
85
|
-
email:
|
86
|
-
- kami30k@gmail.com
|
83
|
+
description: Add relational feature to Rails (e.g. follow, block and like).
|
84
|
+
email: kami30k@gmail.com
|
87
85
|
executables: []
|
88
86
|
extensions: []
|
89
87
|
extra_rdoc_files: []
|
90
88
|
files:
|
89
|
+
- ".gitignore"
|
90
|
+
- ".travis.yml"
|
91
|
+
- Gemfile
|
91
92
|
- MIT-LICENSE
|
93
|
+
- README.md
|
92
94
|
- Rakefile
|
95
|
+
- acts_in_relation.gemspec
|
93
96
|
- lib/acts_in_relation.rb
|
94
|
-
- lib/acts_in_relation/action.rb
|
95
97
|
- lib/acts_in_relation/core.rb
|
96
98
|
- lib/acts_in_relation/railtie.rb
|
97
|
-
- lib/acts_in_relation/
|
99
|
+
- lib/acts_in_relation/roles/action.rb
|
100
|
+
- lib/acts_in_relation/roles/base.rb
|
101
|
+
- lib/acts_in_relation/roles/source.rb
|
102
|
+
- lib/acts_in_relation/roles/target.rb
|
98
103
|
- lib/acts_in_relation/supports/verb.rb
|
99
|
-
- lib/acts_in_relation/target.rb
|
100
104
|
- lib/acts_in_relation/version.rb
|
105
|
+
- spec/dummy/application.rb
|
106
|
+
- spec/dummy/bin/rails
|
107
|
+
- spec/dummy/config.ru
|
108
|
+
- spec/models/post_spec.rb
|
109
|
+
- spec/models/user_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
101
111
|
homepage: https://github.com/kami30k/acts_in_relation
|
102
112
|
licenses:
|
103
113
|
- MIT
|
@@ -118,9 +128,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
128
|
version: '0'
|
119
129
|
requirements: []
|
120
130
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.
|
131
|
+
rubygems_version: 2.4.6
|
122
132
|
signing_key:
|
123
133
|
specification_version: 4
|
124
|
-
summary:
|
125
|
-
mute, or like and so on.
|
134
|
+
summary: Add relational feature to Rails (e.g. follow, block and like).
|
126
135
|
test_files: []
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module ActsInRelation
|
2
|
-
module Source
|
3
|
-
def define
|
4
|
-
with.each do |action|
|
5
|
-
action.extend ActsInRelation::Supports::Verb
|
6
|
-
|
7
|
-
class_object.class_eval <<-RUBY
|
8
|
-
has_many :"#{action.pluralize}",
|
9
|
-
foreign_key: :"#{source}_id",
|
10
|
-
dependent: :destroy
|
11
|
-
|
12
|
-
has_many :"#{action.progressize}",
|
13
|
-
through: :"#{action.pluralize}",
|
14
|
-
source: :"target_#{target}"
|
15
|
-
|
16
|
-
def #{action}(target)
|
17
|
-
return if #{action.progressize}?(target) || (self == target)
|
18
|
-
|
19
|
-
#{action.pluralize}.create(target_#{target}_id: target.id)
|
20
|
-
end
|
21
|
-
|
22
|
-
def un#{action}(target)
|
23
|
-
return unless #{action.progressize}?(target)
|
24
|
-
|
25
|
-
#{action.pluralize}.find_by(target_#{target}_id: target.id).destroy
|
26
|
-
end
|
27
|
-
|
28
|
-
def #{action.progressize}?(target)
|
29
|
-
#{action.pluralize}.exists?(target_#{target}_id: target.id)
|
30
|
-
end
|
31
|
-
RUBY
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module ActsInRelation
|
2
|
-
module Target
|
3
|
-
def define
|
4
|
-
with.each do |action|
|
5
|
-
action.extend ActsInRelation::Supports::Verb
|
6
|
-
|
7
|
-
class_object.class_eval <<-RUBY
|
8
|
-
has_many :"#{action.pluralize}_as_target",
|
9
|
-
foreign_key: :"target_#{target}_id",
|
10
|
-
class_name: action.capitalize,
|
11
|
-
dependent: :destroy
|
12
|
-
|
13
|
-
has_many :"#{action.peoplize}",
|
14
|
-
through: :"#{action.pluralize}_as_target",
|
15
|
-
source: :"#{source}"
|
16
|
-
|
17
|
-
def #{action.pastize}_by?(source)
|
18
|
-
source.#{action.pluralize}.exists?(#{source}_id: source.id)
|
19
|
-
end
|
20
|
-
RUBY
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|