mongoid-tag-collectible 0.1.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.
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ services:
5
+ - mongodb
@@ -0,0 +1,3 @@
1
+ ### 0.1.0 (06/26/2013)
2
+
3
+ * Initial public release, written on a plane from SF to NYC - [@dblock](https://github.com/dblock).
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rspec"
6
+ gem "rake"
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2013 Daniel Doubrovkine.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ Mongoid::TagCollectible
2
+ =======================
3
+
4
+ [![Build Status](https://travis-ci.org/dblock/mongoid-tag-collectible.png)](https://travis-ci.org/dblock/mongoid-tag-collectible)
5
+
6
+ Easily maintain a collection of `Tag` instances with aggregate counts from your model's `tags`.
7
+
8
+ ### Install
9
+
10
+ Add `mongoid-tag-collectible` to your Gemfile.
11
+
12
+ ```
13
+ gem 'mongoid-tag-collectible'
14
+ ```
15
+
16
+ ### Use
17
+
18
+ ``` ruby
19
+ class Thing
20
+ include Mongoid::Document
21
+ include Mongoid::TagCollectible::Tagged
22
+ end
23
+
24
+ thing1 = Thing.create!(tags: [ 'funny', 'red' ])
25
+ thing2 = Thing.create!(tags: [ 'funny', 'yellow' ])
26
+
27
+ funny_tag = ThingTag.find('funny') # find by tag
28
+ funny_tag.name # "funny"
29
+ funny_tag.count # 2, not a database query
30
+ funny_tag.tagged # thing1 and thing2
31
+ ```
32
+
33
+ #### Renaming Tags
34
+
35
+ You can rename a tag, which causes all the tags in your model's `tags` collection to be renamed.
36
+
37
+ ``` ruby
38
+ ThingTag.find('funny').update_attributes!(name: 'sad')
39
+
40
+ Thing.first.tags # [ 'sad', 'red' ]
41
+ ```
42
+
43
+ #### Destroying Tags
44
+
45
+ You can destroy a tag, which also removes it from your model's `tags` collection.
46
+
47
+ ``` ruby
48
+ ThingTag.find('red').destroy
49
+ Thing.first.tags # [ 'sad' ]
50
+ ```
51
+
52
+ #### Case-Sensitive
53
+
54
+ Tags are case-sensitive. Transform your tags in `before_validation` if you don't want this behavior.
55
+
56
+ ``` ruby
57
+ class Thing
58
+ before_validation :downcase_tags
59
+
60
+ private
61
+
62
+ def downcase_tags
63
+ tags = tags.map(&:downcase) if tags
64
+ end
65
+ end
66
+ ```
67
+
68
+ ### Contribute
69
+
70
+ You're encouraged to contribute to this gem.
71
+
72
+ * Fork this project.
73
+ * Make changes, write tests.
74
+ * Updated CHANGELOG.
75
+ * Make a pull request, bonus points for topic branches.
76
+
77
+ ### Copyright and License
78
+
79
+ Copyright Daniel Doubrovkine and Contributors, Artsy Inc., 2013
80
+
81
+ [MIT License](LICENSE.md)
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler/gem_tasks'
3
+
4
+ Bundler.setup :default, :development
5
+
6
+ require 'rspec/core'
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec) do |spec|
10
+ spec.pattern = FileList["spec/**/*_spec.rb"]
11
+ end
12
+
13
+ task :default => :spec
@@ -0,0 +1,39 @@
1
+ require 'bundler'
2
+ Bundler.setup(:default, :development)
3
+
4
+ require 'mongoid-tag-collectible'
5
+
6
+ Mongoid.configure do |config|
7
+ config.connect_to('mongoid_tag_collectible_example')
8
+ end
9
+
10
+ class Thing
11
+ include Mongoid::Document
12
+ include Mongoid::TagCollectible::Tagged
13
+
14
+ before_validation :downcase_tags
15
+
16
+ private
17
+
18
+ def downcase_tags
19
+ tags = tags.map(&:downcase) if tags
20
+ end
21
+ end
22
+
23
+ thing1 = Thing.create!(tags: [ 'funny', 'red' ])
24
+ thing2 = Thing.create!(tags: [ 'funny', 'yellow' ])
25
+
26
+ funny_tag = ThingTag.where(name: 'funny').first
27
+ puts funny_tag.name # funny
28
+ puts funny_tag.count # 2
29
+ p funny_tag.tagged.to_a # thing1 and thing2
30
+
31
+ # rename a tag
32
+ ThingTag.find('funny').update_attributes!(name: 'sad')
33
+ p Thing.first.tags # [ 'sad', 'red' ]
34
+
35
+ # delete a tag
36
+ ThingTag.find('red').destroy
37
+ p Thing.first.tags # [ 'sad' ]
38
+
39
+ Mongoid.default_session.drop
@@ -0,0 +1,6 @@
1
+ require 'mongoid'
2
+ require 'active_support'
3
+
4
+ require 'mongoid-tag-collectible/version'
5
+ require 'mongoid-tag-collectible/tag.rb'
6
+ require 'mongoid-tag-collectible/tagged.rb'
@@ -0,0 +1,51 @@
1
+ module Mongoid
2
+ module TagCollectible
3
+ class Tag
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+
7
+ field :name, type: String
8
+ index({ name: 1 }, { unique: true })
9
+
10
+ field :count, type: Integer, default: 0
11
+ index({ count: -1 })
12
+
13
+ before_destroy :_remove_tags!
14
+ before_update :_rename_tag!
15
+ attr_accessor :renaming
16
+
17
+ def renaming?
18
+ !! renaming
19
+ end
20
+
21
+ def tagged
22
+ tagged_class.where(tags: self.name)
23
+ end
24
+
25
+ def _remove_tags!
26
+ tagged_class.remove_tag!(self[:name]) unless renaming?
27
+ end
28
+
29
+ def self.find(value)
30
+ if Moped::BSON::ObjectId.legal?(value)
31
+ super(value)
32
+ else
33
+ where(name: value).first
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def _rename_tag!
40
+ if ! new_record? && name_changed?
41
+ self.class.where(name: name).each do |tag|
42
+ tag.renaming = true
43
+ tag.destroy
44
+ end
45
+ tagged_class.rename_tag!(name_was, name)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,78 @@
1
+ module Mongoid
2
+ module TagCollectible
3
+ module Tagged
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ field :tags, type: Array, default: []
8
+ index({ tags: 1 })
9
+ scope :tagged, where({ :tags.nin => [ nil, [] ], :tags.ne => nil })
10
+ after_save :_update_tags!
11
+ after_destroy :_destroy_tags!
12
+ cattr_accessor :tag_class
13
+
14
+ klass = Class.new(Mongoid::TagCollectible::Tag) do
15
+ cattr_accessor :tagged_class
16
+ end
17
+ klass.tagged_class = self
18
+ klass.store_in collection: "#{self.name.underscore}_tags"
19
+ Object.const_set "#{self.name}Tag", klass
20
+ self.tag_class = "#{self.name}Tag".constantize
21
+ end
22
+
23
+ module ClassMethods
24
+
25
+ def rename_tag!(old_tag, new_tag)
26
+ self.collection.where({ tags: old_tag }).update({ '$addToSet' => { tags: new_tag }}, multi: true)
27
+ self.remove_tag!(old_tag)
28
+ end
29
+
30
+ def remove_tag!(tag_name)
31
+ self.collection.where({ tags: tag_name }).update({'$pull' => { tags: tag_name}}, multi: true)
32
+ end
33
+
34
+ end
35
+
36
+ private
37
+
38
+ def _update_tags!
39
+ return unless tags_changed?
40
+ before = (tags_was || [])
41
+ after = (tags || [])
42
+ # added tags
43
+ (after - before).each do |tag|
44
+ next unless tag && tag.length > 0
45
+ self.tag_class.collection.find(
46
+ name: tag,
47
+ _type: self.tag_class.name
48
+ ).upsert(
49
+ "$inc" => { count: 1 }
50
+ )
51
+ end
52
+ # removed tags
53
+ (before - after).each do |tag|
54
+ next unless tag && tag.length > 0
55
+ self.tag_class.collection.find(
56
+ name: tag,
57
+ _type: self.tag_class.name
58
+ ).upsert(
59
+ "$inc" => { count: -1 }
60
+ )
61
+ end
62
+ end
63
+
64
+ def _destroy_tags!
65
+ tags.each do |tag|
66
+ next unless tag && tag.length > 0
67
+ self.tag_class.collection.find(
68
+ name: tag,
69
+ _type: self.tag_class.name
70
+ ).upsert(
71
+ "$inc" => { count: -1 }
72
+ )
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module TagCollectible
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'mongoid-tag-collectible'
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'mongoid-tag-collectible/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "mongoid-tag-collectible"
6
+ s.version = Mongoid::TagCollectible::VERSION
7
+ s.authors = [ "Daniel Doubrovkine" ]
8
+ s.email = "dblock@dblock.org"
9
+ s.platform = Gem::Platform::RUBY
10
+ s.required_rubygems_version = '>= 1.3.6'
11
+ s.files = `git ls-files`.split("\n")
12
+ s.require_paths = [ "lib" ]
13
+ s.homepage = "http://github.com/dblock/mongoid-tag-collectible"
14
+ s.licenses = [ "MIT" ]
15
+ s.summary = "Easily maintain a collection of Tag instances with aggregate counts from your model's tags."
16
+ s.add_dependency "mongoid", ">= 3.0.0"
17
+ s.add_dependency "activesupport"
18
+ end
@@ -0,0 +1,9 @@
1
+ test:
2
+ sessions:
3
+ default:
4
+ database: mongoid-tag-collectible
5
+ hosts:
6
+ - localhost:27017
7
+ options:
8
+ raise_not_found_error: false
9
+ safe: true
@@ -0,0 +1,126 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Mongoid::TagCollectible::Tag do
5
+ describe "tag" do
6
+ it "stores tags in a collection name defined by the class" do
7
+ TestTaggedTag.collection.name.should == "test_tagged_tags"
8
+ end
9
+ end
10
+ describe "without tags" do
11
+ it "generates no entries in 'tags' when tags is nil" do
12
+ TestTagged.create!
13
+ TestTaggedTag.all.size.should == 0
14
+ end
15
+ it "generates no entries in 'tags' when tags is empty" do
16
+ TestTagged.create!(tags: [])
17
+ TestTaggedTag.all.size.should == 0
18
+ end
19
+ it "generates no entries in 'tags' when tags contains an empty or nil value" do
20
+ TestTagged.create!(tags: ["", nil])
21
+ TestTaggedTag.all.size.should == 0
22
+ end
23
+ end
24
+ describe "with tags" do
25
+ it "find by id" do
26
+ TestTagged.create!(tags: ['one'])
27
+ TestTaggedTag.find(TestTaggedTag.first.id).should be_a TestTaggedTag
28
+ end
29
+ it "find by tag" do
30
+ TestTagged.create!(tags: ['one'])
31
+ TestTaggedTag.find('one').should be_a TestTaggedTag
32
+ end
33
+ it "generates a tags collection that is case-sensitive" do
34
+ TestTagged.create!(tags: ['one'])
35
+ TestTagged.create!(tags: ['One'])
36
+ TestTagged.create!(name: "whatever")
37
+ TestTaggedTag.count.should == 2
38
+ end
39
+ it "generates a tags collection with the tag with several tags" do
40
+ TestTagged.create!(tags: ["one"])
41
+ TestTagged.create!(tags: ["one", "two"])
42
+ tags = TestTaggedTag.all.desc(:count)
43
+ tags.size.should == 2
44
+ tags[0].name.should == "one"
45
+ tags[0].count.should == 2
46
+ tags[1].name.should == "two"
47
+ tags[1].count.should == 1
48
+ end
49
+ it "prevents duplicates" do
50
+ TestTaggedTag.create!(name: "one")
51
+ expect { TestTaggedTag.create!(name: "one") }.to raise_error Moped::Errors::OperationFailure, /duplicate key error/
52
+ end
53
+ end
54
+ describe "incrementally" do
55
+ before(:each) do
56
+ @instance1 = TestTagged.create!(tags: ["one"])
57
+ @instance2 = TestTagged.create!(tags: ["one", "two"])
58
+ @tags_before = TestTaggedTag.all.desc(:count)
59
+ end
60
+ it "increments an existing tag by 1 when a tagged instance is added" do
61
+ TestTagged.create!(tags: ["one", "two", "three"])
62
+ tags_after = TestTaggedTag.all.desc(:count)
63
+ tags_after.size.should == 3
64
+ # 'one'
65
+ tags_after[0].id.should == @tags_before[0].id
66
+ tags_after[0].name.should == "one"
67
+ tags_after[0].count.should == 3
68
+ # 'two'
69
+ tags_after[1].id.should == @tags_before[1].id
70
+ tags_after[1].name.should == "two"
71
+ tags_after[1].count.should == 2
72
+ # 'three'
73
+ tags_after[2].name.should == "three"
74
+ tags_after[2].count.should == 1
75
+ end
76
+ it "decrements an existing tag by 1 and removes tags with zero when a tagged instance is removed" do
77
+ @instance2.destroy
78
+ tags_after = TestTaggedTag.all.desc(:count)
79
+ tags_after.size.should == 2
80
+ tags_after[0].id.should == @tags_before[0].id
81
+ tags_after[0].name.should == "one"
82
+ tags_after[0].count.should == 1
83
+ tags_after[1].id.should == @tags_before[1].id
84
+ tags_after[1].name.should == "two"
85
+ tags_after[1].count.should == 0
86
+ end
87
+ end
88
+ describe "renaming" do
89
+ it "renames all instances of tag" do
90
+ instance = TestTagged.create!(tags: ['one'])
91
+ TestTaggedTag.where(name: 'one').first.update_attributes!(name: 'two')
92
+ instance.reload.tags.should == ['two']
93
+ TestTaggedTag.count.should == 1
94
+ end
95
+ it "avoids duplicate tags when renaming to an existing tag" do
96
+ instance = TestTagged.create!(tags: ['one', 'two'])
97
+ TestTaggedTag.where(name: 'one').first.update_attributes!(name: 'two')
98
+ TestTaggedTag.count.should == 1
99
+ instance.reload.tags.should == ['two']
100
+ end
101
+ it "preserves renamed tags when TestTaggedTag.update! is called" do
102
+ instance1 = TestTagged.create!(tags: ['one', 'two'])
103
+ instance2 = TestTagged.create!(tags: ['two'])
104
+ instance3 = TestTagged.create!(tags: ['one'])
105
+ TestTaggedTag.where(name: 'one').first.update_attributes!(name: 'two')
106
+ [instance1, instance2, instance3].each{ |a| a.reload.tags.should == ['two'] }
107
+ TestTaggedTag.where(name: 'two').count.should == 1
108
+ TestTaggedTag.where(name: 'one').count.should == 0
109
+ [instance1, instance2, instance3].each{ |a| a.reload.tags.should == ['two'] }
110
+ TestTaggedTag.where(name: 'two').count.should == 1
111
+ TestTaggedTag.where(name: 'one').count.should == 0
112
+ end
113
+ end
114
+ describe "instances" do
115
+ it "returns all matching tagged instances" do
116
+ TestTagged.create!(tags: ['one'])
117
+ TestTagged.create!(tags: ['one'])
118
+ tag = TestTaggedTag.first
119
+ tag.tagged.count.should == 2
120
+ tag.tagged.each { |a| a.should be_a TestTagged }
121
+ end
122
+ it "returns a non-nil result if there are no matching tagged instances" do
123
+ TestTaggedTag.new.tagged.count.should == 0
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::TagCollectible::Tagged do
4
+ let(:instance) { TestTagged.create! }
5
+ describe "tag_class" do
6
+ it "defines tag_class" do
7
+ instance.class.tag_class.should == TestTaggedTag
8
+ end
9
+ end
10
+ describe "rename_tag" do
11
+ context "doesn't match an existing tag" do
12
+ it "is the same" do
13
+ old_tags = instance.class.all.map{ |a| a.tags }
14
+ instance.class.rename_tag! 'Yellow', 'yellow'
15
+ instance.class.all.map{ |a| a.tags }.should eq old_tags
16
+ end
17
+ end
18
+ context "matches an existing tag" do
19
+ it "is different" do
20
+ instance.tags = [ 'Yellow', 'Mellow' ]
21
+ instance.save!
22
+ old_tags = instance.class.all.map{ |a| a.tags }
23
+ instance.class.rename_tag! 'Yellow', 'Blue'
24
+ instance.class.all.map{ |a| a.tags }.should_not eq old_tags
25
+ instance.reload.tags.should include 'Blue'
26
+ instance.tags.should include 'Mellow'
27
+ instance.tags.should_not include 'Yellow'
28
+ end
29
+ end
30
+ end
31
+ describe "remove_tag!" do
32
+ it "deletes tag" do
33
+ instance.tags = [ 'Yellow', 'Mellow' ]
34
+ instance.save!
35
+ instance.class.remove_tag!('Yellow')
36
+ instance.class.find(instance.id).tags.should == [ 'Mellow' ]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::TagCollectible do
4
+ it "has a version" do
5
+ Mongoid::TagCollectible::VERSION.should_not be_nil
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+ require 'mongoid-tag-collectible'
6
+
7
+ require 'support/mongoid'
8
+ require 'support/test_tagged'
@@ -0,0 +1,16 @@
1
+ ENV["MONGOID_ENV"] = "test"
2
+
3
+ Mongoid.load! "spec/config/mongoid.yml"
4
+
5
+ RSpec.configure do |config|
6
+ config.before(:each) do
7
+ Mongoid.purge!
8
+ TestTagged.tag_class.index_options.each_pair do |name, options|
9
+ TestTagged.tag_class.collection.indexes.create(name, options)
10
+ end
11
+ end
12
+ config.after(:all) do
13
+ Mongoid.default_session.drop
14
+ end
15
+ end
16
+
@@ -0,0 +1,5 @@
1
+ class TestTagged
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Mongoid::TagCollectible::Tagged
5
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-tag-collectible
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Doubrovkine
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description:
47
+ email: dblock@dblock.org
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - .gitignore
53
+ - .rspec
54
+ - .travis.yml
55
+ - CHANGELOG.md
56
+ - Gemfile
57
+ - LICENSE.md
58
+ - README.md
59
+ - Rakefile
60
+ - examples/readme.rb
61
+ - lib/mongoid-tag-collectible.rb
62
+ - lib/mongoid-tag-collectible/tag.rb
63
+ - lib/mongoid-tag-collectible/tagged.rb
64
+ - lib/mongoid-tag-collectible/version.rb
65
+ - lib/mongoid_tag_collectible.rb
66
+ - mongoid-tag-collectible.gemspec
67
+ - spec/config/mongoid.yml
68
+ - spec/mongoid-tag-collectible/tag_spec.rb
69
+ - spec/mongoid-tag-collectible/tagged_spec.rb
70
+ - spec/mongoid-tag-collectible/version_spec.rb
71
+ - spec/spec_helper.rb
72
+ - spec/support/mongoid.rb
73
+ - spec/support/test_tagged.rb
74
+ homepage: http://github.com/dblock/mongoid-tag-collectible
75
+ licenses:
76
+ - MIT
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ segments:
88
+ - 0
89
+ hash: -3407508302417476120
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: 1.3.6
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.25
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Easily maintain a collection of Tag instances with aggregate counts from
102
+ your model's tags.
103
+ test_files: []