mongoid-tag-collectible 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []