acts-as-joinable 0.0.1.5 → 0.0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +28 -1
- data/Rakefile +1 -1
- data/lib/acts-as-joinable.rb +23 -136
- data/lib/acts_as_joinable/core.rb +153 -0
- data/test/{lib/_database.rb → database.rb} +5 -0
- data/test/lib/asset.rb +1 -0
- data/test/lib/page.rb +6 -0
- data/test/lib/post.rb +2 -1
- data/test/test_acts_as_joinable.rb +67 -6
- data/test/test_helper.rb +20 -2
- metadata +6 -4
data/README.markdown
CHANGED
@@ -51,4 +51,31 @@ It looks like this:
|
|
51
51
|
|
52
52
|
## Alternatives
|
53
53
|
|
54
|
-
- [ActsAsRelationable](http://github.com/winton/acts_as_relationable)
|
54
|
+
- [ActsAsRelationable](http://github.com/winton/acts_as_relationable)
|
55
|
+
|
56
|
+
## Examples
|
57
|
+
|
58
|
+
If you would like to define accessors that scope the relationship based on the `context` attribute of the `Relationship` model, you can do this:
|
59
|
+
|
60
|
+
class Post < ActiveRecord::Base
|
61
|
+
acts_as_joinable_on :assets, :scopes => [:featured, :thumb]
|
62
|
+
end
|
63
|
+
|
64
|
+
@post = Post.new
|
65
|
+
@post.featured_assets << Asset.first
|
66
|
+
|
67
|
+
If you need more fine-grained control over each relationship scope, you can use a block:
|
68
|
+
|
69
|
+
class Post < ActiveRecord::Base
|
70
|
+
acts_as_joinable_on :assets do
|
71
|
+
has_one :featured_image, where(:...)
|
72
|
+
has_many :thumbnails
|
73
|
+
end
|
74
|
+
acts_as_joinable_on :tags
|
75
|
+
end
|
76
|
+
|
77
|
+
@post = Post.new
|
78
|
+
@post.featured_image = Image.first
|
79
|
+
@post.thumbnails = Image.all
|
80
|
+
|
81
|
+
The goal of this is to make it so you never have to create migrations or new classes, or rewrite the same code over and over again. Instead, you can just define `scopes` for cherry-picking the habtm items you'd like.
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
|
|
5
5
|
spec = Gem::Specification.new do |s|
|
6
6
|
s.name = "acts-as-joinable"
|
7
7
|
s.authors = ["Lance Pollard"]
|
8
|
-
s.version = "0.0.1.
|
8
|
+
s.version = "0.0.1.6"
|
9
9
|
s.summary = "ActsAsJoinable: DRYing up Many-to-Many Relationships in ActiveRecord"
|
10
10
|
s.homepage = "http://github.com/viatropos/cockpit"
|
11
11
|
s.email = "lancejpollard@gmail.com"
|
data/lib/acts-as-joinable.rb
CHANGED
@@ -13,160 +13,47 @@ module ActsAsJoinable
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module ClassMethods
|
16
|
+
|
17
|
+
def joinable?
|
18
|
+
false
|
19
|
+
end
|
16
20
|
# the parent in the relationship, so to speak
|
17
21
|
def acts_as_joinable_on(*args, &block)
|
18
|
-
if
|
19
|
-
|
20
|
-
belongs_to :child, :polymorphic => true
|
21
|
-
|
22
|
-
ActsAsJoinable.models.each do |m|
|
23
|
-
belongs_to "parent_#{m}".intern, :foreign_key => 'parent_id', :class_name => m.camelize
|
24
|
-
belongs_to "child_#{m}".intern, :foreign_key => 'child_id', :class_name => m.camelize
|
25
|
-
end
|
22
|
+
if joinable?
|
23
|
+
write_inheritable_attribute(:acts_as_joinable_config, args)
|
26
24
|
else
|
27
|
-
|
28
|
-
|
29
|
-
table = options[:table]
|
30
|
-
fields = options[:fields] || []
|
31
|
-
fields = [ fields ] unless fields.respond_to?(:flatten)
|
25
|
+
write_inheritable_attribute(:acts_as_joinable_config, args)
|
26
|
+
class_inheritable_reader(:acts_as_joinable_config)
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
args.each do |type|
|
37
|
-
type = type.to_s
|
38
|
-
table = table || type
|
39
|
-
select = "#{table}.*, relationships.id AS relationship_id#{fields.empty? ? '' : ', '}" + fields.collect { |f| "relationships.#{f}" }.join(', ')
|
40
|
-
opts = {
|
41
|
-
:select => select,
|
42
|
-
:conditions => sql,
|
43
|
-
:through => :parent_relationships,
|
44
|
-
:source => :parent,
|
45
|
-
:class_name => type.classify,
|
46
|
-
:source_type => table.classify
|
47
|
-
}
|
48
|
-
|
49
|
-
has_many "parent_#{type}", opts do
|
50
|
-
fields.each do |field|
|
51
|
-
define_method field.to_s.pluralize do |*args|
|
52
|
-
value = args[0] || 1
|
53
|
-
scoped :conditions => [ "relationships.#{field} = ?", value ]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
opts = {
|
59
|
-
:select => select,
|
60
|
-
:conditions => sql,
|
61
|
-
:through => :child_relationships,
|
62
|
-
:source => :child,
|
63
|
-
:class_name => type.classify,
|
64
|
-
:source_type => table.classify
|
65
|
-
}
|
28
|
+
class_eval do
|
29
|
+
has_many :parent_relationships, :class_name => 'ActsAsJoinable::Relationship', :as => :child
|
30
|
+
has_many :child_relationships, :class_name => 'ActsAsJoinable::Relationship', :as => :parent
|
66
31
|
|
67
|
-
|
68
|
-
|
69
|
-
define_method field.to_s.pluralize do |*args|
|
70
|
-
value = args[0] || 1
|
71
|
-
scoped :conditions => [ "relationships.#{field} = ?", value ]
|
72
|
-
end
|
73
|
-
end
|
32
|
+
def self.joinable?
|
33
|
+
true
|
74
34
|
end
|
75
35
|
|
76
|
-
|
77
|
-
# Records reader
|
78
|
-
define_method type do |*args|
|
79
|
-
if (read_attribute(:type) || self.class.to_s) < (args.empty? ? type.classify : args[0].to_s)
|
80
|
-
eval "self.child_#{type}"
|
81
|
-
else
|
82
|
-
eval "self.parent_#{type}"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
fields.each do |field|
|
88
|
-
# Relationship field writer
|
89
|
-
self.class_eval do
|
90
|
-
define_method field.to_s + '=' do |value|
|
91
|
-
modified = read_attribute(:modified_relationship_fields) || []
|
92
|
-
modified << field
|
93
|
-
write_attribute :modified_relationship_fields, modified.uniq
|
94
|
-
write_attribute field, value
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
unless included_modules.include?(InstanceMethods)
|
100
|
-
extend ClassMethods
|
101
|
-
include InstanceMethods
|
102
|
-
before_save :save_relationship_fields
|
36
|
+
include ActsAsJoinable::Core
|
103
37
|
end
|
104
38
|
end
|
105
39
|
end
|
106
|
-
|
107
40
|
# the child in the relationship, so to speak
|
108
41
|
def acts_as_joinable(*args, &block)
|
109
42
|
acts_as_joinable_on(*args, &block)
|
110
43
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
r[field] = self[field]
|
120
|
-
end
|
121
|
-
r.save
|
122
|
-
write_attribute :modified_relationship_fields, nil
|
123
|
-
end
|
124
|
-
|
125
|
-
def get_joining(type, context)
|
126
|
-
get_joinings(type, context).first
|
127
|
-
end
|
128
|
-
|
129
|
-
def get_joinings(type, context)
|
130
|
-
self.joinings.select do |joining|
|
131
|
-
joining.context == context.to_s
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def get_joined(type, context)
|
136
|
-
get_joineds(type, context).first
|
137
|
-
end
|
138
|
-
|
139
|
-
def get_joineds(type, context)
|
140
|
-
return [] unless self.joineds and !self.joineds.empty?
|
141
|
-
|
142
|
-
get_joinings(context).collect do |joining|
|
143
|
-
joining.joined
|
44
|
+
|
45
|
+
def acts_as_relationship
|
46
|
+
belongs_to :parent, :polymorphic => true
|
47
|
+
belongs_to :child, :polymorphic => true
|
48
|
+
|
49
|
+
ActsAsJoinable.models.each do |m|
|
50
|
+
belongs_to "parent_#{m}".intern, :foreign_key => 'parent_id', :class_name => m.camelize
|
51
|
+
belongs_to "child_#{m}".intern, :foreign_key => 'child_id', :class_name => m.camelize
|
144
52
|
end
|
145
53
|
end
|
146
|
-
|
147
|
-
def set_joined(type, context, value)
|
148
|
-
joining = get_joining(context) || Joining.new
|
149
|
-
clazz = get_join_class(type)
|
150
|
-
joining.joined = value.is_a?(clazz) ? value : clazz.find(value)
|
151
|
-
joining.joining = self
|
152
|
-
joining.context = context.to_s
|
153
|
-
joining.save
|
154
|
-
self.send("#{context.to_s}_#{type.to_s}")
|
155
|
-
end
|
156
|
-
|
157
|
-
private
|
158
|
-
def get_join_class(type)
|
159
|
-
if type.is_a?(String) || type.is_a?(Symbol)
|
160
|
-
type.to_s.camelize.constantize
|
161
|
-
elsif type.is_a?(Class)
|
162
|
-
type
|
163
|
-
else
|
164
|
-
type.class
|
165
|
-
end
|
166
|
-
end
|
167
54
|
end
|
168
55
|
end
|
169
56
|
|
170
57
|
ActiveRecord::Base.send(:include, ActsAsJoinable) if defined?(ActiveRecord::Base)
|
171
58
|
|
172
|
-
Dir["#{File.dirname(__FILE__)}
|
59
|
+
Dir["#{File.dirname(__FILE__)}/acts_as_joinable/*"].each { |c| require c if File.extname(c) == ".rb" }
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module ActsAsJoinable
|
2
|
+
module Core
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, ActsAsJoinable::Core::InstanceMethods
|
5
|
+
base.extend ActsAsJoinable::Core::ClassMethods
|
6
|
+
|
7
|
+
base.initialize_acts_as_joinable_on_core
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def initialize_acts_as_joinable_on_core
|
12
|
+
args = acts_as_joinable_config.dup
|
13
|
+
options = args.extract_options!
|
14
|
+
if args.empty?
|
15
|
+
args = ActsAsJoinable.models
|
16
|
+
as = :child
|
17
|
+
else
|
18
|
+
as = options[:as] || :parent
|
19
|
+
end
|
20
|
+
|
21
|
+
contexts = options[:contexts] || []
|
22
|
+
contexts = contexts.map(&:to_s).inject({}) {|hash, i| hash[i] = i.empty? ? i : "#{i}_"; hash}
|
23
|
+
|
24
|
+
sql = options[:conditions]
|
25
|
+
|
26
|
+
joins = args.inject({}) { |hash, i| hash[i.to_s.pluralize] = as; hash }
|
27
|
+
|
28
|
+
fields = options[:fields] || []
|
29
|
+
fields = [fields] unless fields.respond_to?(:flatten)
|
30
|
+
|
31
|
+
has_many :parent_relationships, :class_name => 'ActsAsJoinable::Relationship', :as => :child
|
32
|
+
has_many :child_relationships, :class_name => 'ActsAsJoinable::Relationship', :as => :parent
|
33
|
+
|
34
|
+
joins.each do |type, as|
|
35
|
+
type = type.to_s
|
36
|
+
singular_type = type.singularize
|
37
|
+
relationship_type = "#{singular_type}_relationships".to_sym
|
38
|
+
select = "#{type}.*, relationships.id AS relationship_id#{fields.empty? ? '' : ', '}" + fields.collect { |f| "relationships.#{f}" }.join(', ')
|
39
|
+
|
40
|
+
role = opposite_for(as).to_sym
|
41
|
+
joined_options = {
|
42
|
+
:select => select,
|
43
|
+
:conditions => sql,
|
44
|
+
:through => relationship_type,
|
45
|
+
:source => role,
|
46
|
+
:class_name => type.classify,
|
47
|
+
:source_type => type.classify
|
48
|
+
}
|
49
|
+
|
50
|
+
relationship_options = {
|
51
|
+
:class_name => 'ActsAsJoinable::Relationship',
|
52
|
+
:as => as
|
53
|
+
}
|
54
|
+
|
55
|
+
type = type.to_sym
|
56
|
+
|
57
|
+
# so we don't override
|
58
|
+
unless self.reflect_on_all_associations.detect {|association| association.name.to_s == type.to_s}
|
59
|
+
has_many type, joined_options
|
60
|
+
has_many relationship_type, relationship_options
|
61
|
+
end
|
62
|
+
|
63
|
+
contexts.each do |context, context_prefix|
|
64
|
+
context_type = "#{context_prefix}#{type}".to_sym
|
65
|
+
relationship_type = "#{context_prefix}#{singular_type}_relationships".to_sym
|
66
|
+
has_many relationship_type, relationship_options.merge(
|
67
|
+
:conditions => ["#{ActsAsJoinable::Relationship.table_name}.context = ?", context.to_s]
|
68
|
+
)
|
69
|
+
|
70
|
+
has_many context_type, joined_options.merge(
|
71
|
+
:through => relationship_type,
|
72
|
+
:before_add => lambda do |parent, child|
|
73
|
+
parent.set_joined(role, type, context, child)
|
74
|
+
end
|
75
|
+
)
|
76
|
+
|
77
|
+
# define_method context_type do
|
78
|
+
# joins_for(role, type, context)
|
79
|
+
# end
|
80
|
+
|
81
|
+
# define_method "add_#{context_type.to_s.singularize}" do |value|
|
82
|
+
# value = [value] unless value.is_a?(Array)
|
83
|
+
# value.each do |item|
|
84
|
+
# set_joined(role, type, context, item)
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def acts_as_joinable_on(*args)
|
93
|
+
super(*args)
|
94
|
+
initialize_acts_as_joinable_on_core
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def opposite_for(role)
|
99
|
+
role.to_s == "parent" ? "child" : "parent"
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
module InstanceMethods
|
105
|
+
|
106
|
+
def relationships_for(role, type, context = nil, options = {})
|
107
|
+
conditions = {
|
108
|
+
"#{role}_type" => type.to_s.classify,
|
109
|
+
}
|
110
|
+
conditions[:context] = context.to_s unless context.blank?
|
111
|
+
options.merge!(:conditions => conditions)
|
112
|
+
|
113
|
+
self.send("#{type.to_s.singularize}_relationships").all(options)
|
114
|
+
end
|
115
|
+
|
116
|
+
def relationship_for(role, type, context, value, options = {})
|
117
|
+
relationships_for(role, type, context).detect do |relationship|
|
118
|
+
relationship.send(role).id == value.id
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def joins_for(role, type, context)
|
123
|
+
relationships_for(role, type, context, :include => opposite_for(role)).map(&role)
|
124
|
+
end
|
125
|
+
|
126
|
+
def set_joined(role, type, context, value)
|
127
|
+
relationship = relationship_for(role, type, context, value) || ActsAsJoinable::Relationship.new
|
128
|
+
clazz = get_join_class(type)
|
129
|
+
relationship.send("#{role}=", value.is_a?(clazz) ? value : clazz.find(value))
|
130
|
+
relationship.send("#{opposite_for(role)}=", self)
|
131
|
+
relationship.context = context.to_s
|
132
|
+
relationship.save
|
133
|
+
self.send("#{context.to_s}_#{type.to_s.pluralize}")
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
def get_join_class(type)
|
138
|
+
if type.is_a?(String) || type.is_a?(Symbol)
|
139
|
+
type.to_s.singularize.camelize.constantize
|
140
|
+
elsif type.is_a?(Class)
|
141
|
+
type
|
142
|
+
else
|
143
|
+
type.class
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def opposite_for(role)
|
148
|
+
role.to_s == "parent" ? "child" : "parent"
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -23,6 +23,11 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
23
23
|
t.timestamps
|
24
24
|
end
|
25
25
|
|
26
|
+
create_table :pages, :force => true do |t|
|
27
|
+
t.string :title
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
26
31
|
create_table :relationships do |t|
|
27
32
|
t.references :parent, :polymorphic => true
|
28
33
|
t.references :child, :polymorphic => true
|
data/test/lib/asset.rb
CHANGED
data/test/lib/page.rb
ADDED
data/test/lib/post.rb
CHANGED
@@ -14,24 +14,85 @@ class ActsAsJoinableTest < ActiveSupport::TestCase
|
|
14
14
|
assert_equal 2, Asset.count
|
15
15
|
end
|
16
16
|
|
17
|
+
should "have the correct number of Relationship models" do
|
18
|
+
result = [ActsAsJoinable::Relationship.find_all_by_parent_type("Post")]
|
19
|
+
result << ActsAsJoinable::Relationship.find_all_by_parent_type("Asset")
|
20
|
+
result << ActsAsJoinable::Relationship.find_all_by_parent_type("Tag")
|
21
|
+
result.each do |kind|
|
22
|
+
kind.sort {|a, b| b.child_type <=> a.child_type}.each do |relationship|
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
17
28
|
context "correct generated methods" do
|
18
29
|
setup do
|
19
|
-
@post
|
30
|
+
@post = Post.first
|
20
31
|
@asset = Asset.first
|
21
|
-
@tag
|
32
|
+
@tag = Tag.first
|
22
33
|
end
|
23
34
|
|
24
35
|
should "Post should respond_to?(:tags) and respond_to?(:assets)" do
|
25
|
-
puts @post.tags.inspect + " !!"
|
26
36
|
assert @post.respond_to?(:tags)
|
27
37
|
assert @post.respond_to?(:assets)
|
28
38
|
end
|
39
|
+
|
40
|
+
should "Tag should respond_to?(:posts) and respond_to?(:assets)" do
|
41
|
+
assert @tag.respond_to?(:posts)
|
42
|
+
assert @tag.respond_to?(:assets)
|
43
|
+
end
|
44
|
+
|
45
|
+
should "Post have tags and assets" do
|
46
|
+
assert_equal 2, @post.tags.count
|
47
|
+
assert_equal 2, @post.assets.count
|
48
|
+
end
|
49
|
+
|
50
|
+
should "Tag have Post and Asset" do
|
51
|
+
assert_equal 1, @tag.posts.count
|
52
|
+
assert_equal 1, @tag.assets.count
|
53
|
+
assert_equal 1, Tag.first.posts.count
|
54
|
+
assert_equal 1, Tag.first.assets.count
|
55
|
+
end
|
56
|
+
|
57
|
+
should "Asset have Post and Tags" do
|
58
|
+
assert_equal 1, @asset.tags.count
|
59
|
+
assert_equal 1, @asset.posts.count
|
60
|
+
assert_equal 1, Asset.first.tags.count
|
61
|
+
assert_equal 1, Asset.first.posts.count
|
62
|
+
end
|
63
|
+
|
64
|
+
context "custom scopes" do
|
65
|
+
|
66
|
+
should "have a 'featured_assets' scope on Post" do
|
67
|
+
@post = Post.first
|
68
|
+
assert @post.respond_to?(:featured_assets)
|
69
|
+
@featured = Asset.first
|
70
|
+
@post.featured_assets << @featured
|
71
|
+
#@post.add_featured_asset(@featured)
|
72
|
+
@post = Post.first
|
73
|
+
assert_equal @post.featured_assets.first, @featured
|
74
|
+
assert @post.assets.include?(@featured)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "manipulating posts" do
|
81
|
+
|
82
|
+
setup do
|
83
|
+
@post = Post.create!(:title => "testing")
|
84
|
+
end
|
85
|
+
|
86
|
+
should "be able to add and remove objects" do
|
87
|
+
@post.assets << Asset.all
|
88
|
+
@post.featured_assets << Asset.all
|
89
|
+
@post = Post.last
|
90
|
+
end
|
91
|
+
|
29
92
|
end
|
30
93
|
|
31
94
|
teardown do
|
32
|
-
|
33
|
-
Tag.detonate
|
34
|
-
Asset.detonate
|
95
|
+
destroy_models
|
35
96
|
end
|
36
97
|
|
37
98
|
end
|
data/test/test_helper.rb
CHANGED
@@ -11,9 +11,14 @@ require 'shoulda/active_record'
|
|
11
11
|
|
12
12
|
this = File.expand_path(File.dirname(__FILE__))
|
13
13
|
|
14
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
15
|
+
|
14
16
|
require File.expand_path(File.join(this, '/../lib/acts-as-joinable'))
|
17
|
+
require File.join(this, "database")
|
18
|
+
|
19
|
+
ActsAsJoinable.models = Dir["#{this}/lib/*.rb"].collect { |f| File.basename f, '.rb' }
|
15
20
|
|
16
|
-
|
21
|
+
Dir["#{File.dirname(__FILE__)}/../app/models/*"].each { |c| require c if File.extname(c) == ".rb" }
|
17
22
|
|
18
23
|
Dir["#{this}/lib/*"].each { |c| require c if File.extname(c) == ".rb" }
|
19
24
|
|
@@ -25,14 +30,27 @@ end
|
|
25
30
|
|
26
31
|
ActiveSupport::TestCase.class_eval do
|
27
32
|
|
33
|
+
|
28
34
|
def create_models(parent = 1, child = 2)
|
35
|
+
return unless Post.all.empty?
|
29
36
|
parent.times do |i|
|
30
37
|
post = Post.create!(:title => "title-#{i.to_s}")
|
31
38
|
child.times do |j|
|
32
39
|
position = (i + 1) * (j + 1)
|
33
40
|
asset = Asset.create!(:title => "asset-title-#{position.to_s}")
|
34
41
|
tag = Tag.create!(:name => "tag-name-#{position.to_s}")
|
42
|
+
asset.tags << tag
|
43
|
+
post.tags << tag
|
44
|
+
post.assets << asset
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
38
|
-
|
48
|
+
|
49
|
+
def destroy_models
|
50
|
+
Post.detonate
|
51
|
+
Tag.detonate
|
52
|
+
Asset.detonate
|
53
|
+
Page.detonate
|
54
|
+
ActsAsJoinable::Relationship.detonate
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts-as-joinable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 71
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
9
|
- 1
|
10
|
-
-
|
11
|
-
version: 0.0.1.
|
10
|
+
- 6
|
11
|
+
version: 0.0.1.6
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Lance Pollard
|
@@ -34,9 +34,11 @@ files:
|
|
34
34
|
- init.rb
|
35
35
|
- MIT-LICENSE
|
36
36
|
- lib/acts-as-joinable.rb
|
37
|
+
- lib/acts_as_joinable/core.rb
|
37
38
|
- rails/init.rb
|
38
|
-
- test/
|
39
|
+
- test/database.rb
|
39
40
|
- test/lib/asset.rb
|
41
|
+
- test/lib/page.rb
|
40
42
|
- test/lib/post.rb
|
41
43
|
- test/lib/tag.rb
|
42
44
|
- test/test_acts_as_joinable.rb
|