acts_in_relation 0.1.1 → 0.2.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.
- 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
|
+
[](https://travis-ci.org/kami30k/acts_in_relation)
|
4
|
+
[](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
|