resonance 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1706b436453caa501e2d7970f4eea6d0f39dd2a6
4
+ data.tar.gz: ef87ae52d6523a1f13fff3b917de503e95f0de71
5
+ SHA512:
6
+ metadata.gz: e3727b7ef17bb964c388afc8ef8075aefa07f0cd63c14564def3e69161d8e07a5aabc8d62ee4d0dcfca7f184e36aaed2a5d9220f42e5d420ff02dd229f035a8e
7
+ data.tar.gz: f448efaf6e41d23c696761a8fcf6af51ea882025d56ae9ca4c82b1479b09a6622ebceb13d62f982a43c55607c8bd7d66e111e6352f6f362e5deeb358756a935d
@@ -0,0 +1,6 @@
1
+ .bundle/
2
+ pkg/
3
+ spec/dummy/db
4
+ spec/dummy/log
5
+ spec/dummy/tmp/
6
+ Gemfile.lock
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright 2015-2016 kami
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,159 @@
1
+ # Resonance
2
+
3
+ [![Build Status](https://travis-ci.org/kami-zh/resonance.svg)](https://travis-ci.org/kami-zh/resonance)
4
+ [![Gem Version](https://badge.fury.io/rb/resonance.svg)](http://badge.fury.io/rb/resonance)
5
+
6
+ Resonance provides a relational feature to your Rails application, such as follow, like, and so on.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'resonance'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ ```
19
+ $ bundle
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Resonance supports two way to add relational feature.
25
+
26
+ 1. Add a feature to itself
27
+ 2. Add features to 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
+ And migrate:
43
+
44
+ ```
45
+ $ bin/rake db:migrate
46
+ ```
47
+
48
+ Then, define `Resonatable` module to `app/models/concerns/resonatable.rb`:
49
+
50
+ ```ruby
51
+ module Resonatable
52
+ include Resonance
53
+
54
+ resonate :user, with: :user, by: :follow
55
+ end
56
+ ```
57
+
58
+ At last, include this module from each models:
59
+
60
+ ```ruby
61
+ class User < ActiveRecord::Base
62
+ include Resonatable
63
+ end
64
+
65
+ class Follow < ActiveRecord::Base
66
+ include Resonatable
67
+ end
68
+ ```
69
+
70
+ That's it.
71
+ User instance has been added following methods:
72
+
73
+ - user.follow(other_user)
74
+ - user.unfollow(other_user)
75
+ - user.following?(other_user)
76
+ - user.following
77
+ - other_user.followed_by?(user)
78
+ - other_user.followers
79
+
80
+ Example:
81
+
82
+ ```ruby
83
+ user = User.create
84
+ other_user = User.create
85
+
86
+ # Follow
87
+ user.follow other_user
88
+ user.following?(other_user) #=> true
89
+ user.following #=> <ActiveRecord::Associations::CollectionProxy [#<User id: 2, created_at: "2015-01-10 01:57:52", updated_at: "2015-01-10 01:57:52">]>
90
+ other_user.followed_by?(user) #=> true
91
+ 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">]>
92
+
93
+ # Unfollow
94
+ user.unfollow other_user
95
+ user.following?(other_user) #=> false
96
+ user.following #=> <ActiveRecord::Associations::CollectionProxy []>
97
+ other_user.followed_by?(user) #=> false
98
+ other_user.followers #=> <ActiveRecord::Associations::CollectionProxy []>
99
+ ```
100
+
101
+ ### 2. Add like feature to User and Post
102
+
103
+ This case adds like feature to User and Post model.
104
+
105
+ ```ruby
106
+ module Resonatable
107
+ include Resonance
108
+
109
+ resonate :user, with: :post, by: :like
110
+ end
111
+
112
+ class User < ActiveRecord::Base
113
+ include Resonatable
114
+ end
115
+
116
+ class Post < ActiveRecord::Base
117
+ include Resonatable
118
+ end
119
+
120
+ class Like < ActiveRecord::Base
121
+ include Resonatable
122
+ end
123
+ ```
124
+
125
+ User and Post instance has been added following methods:
126
+
127
+ - user.like(post)
128
+ - user.unlike(post)
129
+ - user.liking?(post)
130
+ - user.liking
131
+ - post.liked_by?(user)
132
+ - post.likers
133
+
134
+ At the same time, some `resonate` methods are able to be defined:
135
+
136
+ ```ruby
137
+ module Resonatable
138
+ include Resonance
139
+
140
+ resonate :user, with: :user, by: :follow
141
+ resonate :user, with: :post, by: :like
142
+ end
143
+ ```
144
+
145
+ ## Customization
146
+
147
+ If you want to use other foreign key name, you can define it by `:foreign_key` option.
148
+
149
+ ```ruby
150
+ resonate :user, with: :post, by: :like, foreign_key: :post_id # Default is `:target_post_id`
151
+ ```
152
+
153
+ ## Contributing
154
+
155
+ 1. Fork it ( https://github.com/kami-zh/resonance/fork )
156
+ 2. Create your feature branch (git checkout -b my-new-feature)
157
+ 3. Commit your changes (git commit -am 'Add some feature')
158
+ 4. Push to the branch (git push origin my-new-feature)
159
+ 5. Create a new Pull Request
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ task default: :spec
@@ -0,0 +1,94 @@
1
+ require 'resonance/errors/argument_error'
2
+ require 'resonance/supports/converter'
3
+
4
+ module Resonance
5
+ class << self
6
+ def included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ include Resonance::Supports::Converter
13
+
14
+ def resonate(subject, with: nil, by: nil, **options)
15
+ @roles = { source: subject, target: with, action: by }
16
+ @options = options
17
+
18
+ if @roles.values.any?(&:nil?)
19
+ raise Resonance::Errors::ArgumentError, 'Passed argument is not a valid'
20
+ end
21
+
22
+ classify(source).class_eval <<-EOS
23
+ has_many :#{pluralize(action)},
24
+ foreign_key: :#{source}_id,
25
+ dependent: :destroy
26
+
27
+ has_many :#{progressize(action)},
28
+ through: :#{pluralize(action)},
29
+ source: :target_#{target}
30
+
31
+ def #{action}(target)
32
+ if #{progressize(action)}?(target) || (self == target)
33
+ return
34
+ end
35
+
36
+ #{pluralize(action)}.create(#{foreign_key}: target.id)
37
+ end
38
+
39
+ def un#{action}(target)
40
+ unless #{progressize(action)}?(target)
41
+ return
42
+ end
43
+
44
+ #{pluralize(action)}.find_by(#{foreign_key}: target.id).destroy
45
+ end
46
+
47
+ def #{progressize(action)}?(target)
48
+ #{pluralize(action)}.exists?(#{foreign_key}: target.id)
49
+ end
50
+ EOS
51
+
52
+ classify(target).class_eval <<-EOS
53
+ has_many :#{pluralize(action)}_as_target,
54
+ foreign_key: :#{foreign_key},
55
+ class_name: '#{classify(action)}',
56
+ dependent: :destroy
57
+
58
+ has_many :#{peoplize(action)},
59
+ through: :#{pluralize(action)}_as_target,
60
+ source: :#{source}
61
+
62
+ def #{pastize(action)}_by?(source)
63
+ source.#{pluralize(action)}.exists?(#{foreign_key}: id)
64
+ end
65
+ EOS
66
+
67
+ classify(action).class_eval <<-EOS
68
+ belongs_to :#{source}
69
+
70
+ belongs_to :target_#{target},
71
+ class_name: '#{classify(target)}',
72
+ foreign_key: :#{foreign_key}
73
+ EOS
74
+ end
75
+
76
+ private
77
+
78
+ def source
79
+ @roles[:source].to_s
80
+ end
81
+
82
+ def target
83
+ @roles[:target].to_s
84
+ end
85
+
86
+ def action
87
+ @roles[:action].to_s
88
+ end
89
+
90
+ def foreign_key
91
+ @options[:foreign_key] || "target_#{target}_id"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,5 @@
1
+ module Resonance
2
+ module Errors
3
+ class ArgumentError < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'verbs'
2
+
3
+ module Resonance
4
+ module Supports
5
+ module Converter
6
+ private
7
+
8
+ def classify(str)
9
+ str.capitalize.constantize
10
+ end
11
+
12
+ def pluralize(str)
13
+ str.pluralize
14
+ end
15
+
16
+ def progressize(str)
17
+ if patches['progressize'].key?(str)
18
+ return patches['progressize'][str]
19
+ end
20
+
21
+ str.verb.conjugate(aspect: :progressive).split(' ').last
22
+ end
23
+
24
+ def peoplize(str)
25
+ str = (str.last == 'e') ? str.chop : str
26
+ str + 'ers'
27
+ end
28
+
29
+ def pastize(str)
30
+ str.verb.conjugate(tense: :past).split(' ').last
31
+ end
32
+
33
+ def patches
34
+ @patches ||= YAML.load_file(patches_path)['patches']
35
+ end
36
+
37
+ def patches_path
38
+ File.expand_path('../patches.yml', __FILE__)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ patches:
2
+ progressize:
3
+ follow: following
@@ -0,0 +1,3 @@
1
+ module Resonance
2
+ VERSION = '0.3.2'
3
+ end
@@ -0,0 +1,25 @@
1
+ $:.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ require 'resonance/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'resonance'
7
+ s.version = Resonance::VERSION
8
+ s.authors = 'kami'
9
+ s.email = 'hiroki.zenigami@gmail.com'
10
+
11
+ s.summary = 'Provides a relational feature to your Rails application.'
12
+ s.description = 'Provides a relational feature to your Rails application.'
13
+ s.homepage = 'https://github.com/kami-zh/resonance'
14
+ s.license = 'MIT'
15
+
16
+ s.files = `git ls-files -z`.split("\x0")
17
+
18
+ s.add_dependency 'activesupport'
19
+ s.add_dependency 'verbs'
20
+
21
+ s.add_development_dependency 'rspec-rails'
22
+ s.add_development_dependency 'actionpack'
23
+ s.add_development_dependency 'activerecord'
24
+ s.add_development_dependency 'sqlite3'
25
+ end
@@ -0,0 +1,97 @@
1
+ $:.unshift File.expand_path('../../../lib', __FILE__)
2
+
3
+ require 'action_controller/railtie'
4
+ require 'active_record'
5
+ require 'resonance'
6
+
7
+ module Dummy
8
+ class Application < Rails::Application
9
+ config.secret_token = 'abcdefghijklmnopqrstuvwxyz0123456789'
10
+ config.session_store :cookie_store, key: '_dummy_session'
11
+ config.eager_load = false
12
+ config.active_support.deprecation = :log
13
+ end
14
+ end
15
+
16
+ Dummy::Application.initialize!
17
+
18
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
19
+
20
+ #
21
+ # Models
22
+ #
23
+
24
+ class User < ActiveRecord::Base; end
25
+ class Post < ActiveRecord::Base; end
26
+ class Follow < ActiveRecord::Base; end
27
+ class Like < ActiveRecord::Base; end
28
+
29
+ module Resonatable
30
+ include Resonance
31
+
32
+ resonate :user, with: :user, by: :follow
33
+ resonate :user, with: :post, by: :like, foreign_key: :post_id
34
+ end
35
+
36
+ class User
37
+ include Resonatable
38
+ end
39
+
40
+ class Post
41
+ include Resonatable
42
+ end
43
+
44
+ class Follow
45
+ include Resonatable
46
+ end
47
+
48
+ class Like
49
+ include Resonatable
50
+ end
51
+
52
+ #
53
+ # Migrates
54
+ #
55
+
56
+ class CreateUsers < ActiveRecord::Migration
57
+ def change
58
+ create_table :users do |t|
59
+ t.timestamps null: false
60
+ end
61
+ end
62
+ end
63
+
64
+ class CreatePosts < ActiveRecord::Migration
65
+ def change
66
+ create_table :posts do |t|
67
+ t.timestamps null: false
68
+ end
69
+ end
70
+ end
71
+
72
+ class CreateFollows < ActiveRecord::Migration
73
+ def change
74
+ create_table :follows do |t|
75
+ t.integer :user_id
76
+ t.integer :target_user_id
77
+
78
+ t.timestamps null: false
79
+ end
80
+ end
81
+ end
82
+
83
+ class CreateLikes < ActiveRecord::Migration
84
+ def change
85
+ create_table :likes do |t|
86
+ t.integer :user_id
87
+ t.integer :post_id
88
+
89
+ t.timestamps null: false
90
+ end
91
+ end
92
+ end
93
+
94
+ CreateUsers.new.change
95
+ CreatePosts.new.change
96
+ CreateFollows.new.change
97
+ CreateLikes.new.change
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ APP_PATH = File.expand_path('../../application', __FILE__)
4
+
5
+ require 'rails/commands'
@@ -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
+ # :user resonates with :post by :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,106 @@
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
+ # :user resonates with :user by :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
+ # :user resonates with :post by :like
18
+ expect(user).to respond_to(:like)
19
+ expect(user).to respond_to(:unlike)
20
+ expect(user).to respond_to(:liking?)
21
+ expect(user).to respond_to(:liking)
22
+ expect(user).not_to respond_to(:liked_by?)
23
+ expect(user).not_to respond_to(:likers)
24
+ end
25
+ end
26
+
27
+ describe '#follow' do
28
+ context 'unfollow user' do
29
+ before { user.follow other_user }
30
+
31
+ it 'should be followed' do
32
+ expect(user.follows).to be_exists(target_user_id: other_user.id)
33
+ end
34
+ end
35
+
36
+ context 'following user' do
37
+ before do
38
+ user.follow other_user
39
+ user.follow other_user
40
+ end
41
+
42
+ it 'should be followed only once' do
43
+ expect(user.follows.count).to eq(1)
44
+ end
45
+ end
46
+
47
+ context 'user-self' do
48
+ before { user.follow user }
49
+
50
+ it 'should not be followed' do
51
+ expect(user.follows).not_to be_exists(target_user_id: user.id)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#unfollow' do
57
+ context 'following user' do
58
+ before do
59
+ user.follow other_user
60
+ user.unfollow other_user
61
+ end
62
+
63
+ it 'should be unfollowed' do
64
+ expect(user.follows).not_to be_exists(target_user_id: other_user.id)
65
+ end
66
+ end
67
+
68
+ context 'unfollow user' do
69
+ it 'should not raise error' do
70
+ expect { user.unfollow other_user }.not_to raise_error
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#following?' do
76
+ before { user.follow other_user }
77
+
78
+ it 'should be followed' do
79
+ expect(user).to be_following(other_user)
80
+ end
81
+ end
82
+
83
+ describe '#following' do
84
+ before { user.follow other_user }
85
+
86
+ it 'should be included other user' do
87
+ expect(user.following).to be_exists(other_user.id)
88
+ end
89
+ end
90
+
91
+ describe '#followed_by?' do
92
+ before { other_user.follow user }
93
+
94
+ it 'should be followed by other user' do
95
+ expect(user).to be_followed_by(other_user)
96
+ end
97
+ end
98
+
99
+ describe '#followers' do
100
+ before { other_user.follow user }
101
+
102
+ it 'should be included other user' do
103
+ expect(user.followers).to be_exists(other_user.id)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1 @@
1
+ require 'dummy/application'
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resonance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - kami
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: verbs
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
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: actionpack
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: activerecord
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
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Provides a relational feature to your Rails application.
98
+ email: hiroki.zenigami@gmail.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - ".gitignore"
104
+ - ".travis.yml"
105
+ - Gemfile
106
+ - MIT-LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - lib/resonance.rb
110
+ - lib/resonance/errors/argument_error.rb
111
+ - lib/resonance/supports/converter.rb
112
+ - lib/resonance/supports/patches.yml
113
+ - lib/resonance/version.rb
114
+ - resonance.gemspec
115
+ - spec/dummy/application.rb
116
+ - spec/dummy/bin/rails
117
+ - spec/dummy/config.ru
118
+ - spec/models/post_spec.rb
119
+ - spec/models/user_spec.rb
120
+ - spec/spec_helper.rb
121
+ homepage: https://github.com/kami-zh/resonance
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.5.1
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Provides a relational feature to your Rails application.
145
+ test_files: []