tagalong 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/debug.log
19
+ *.sqlite3
data/.rvmrc ADDED
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Check to see if brew is installed
4
+ function check_for_brew() {
5
+ printf "Checking for brew - "
6
+ which brew > /dev/null
7
+ if [ $? -ne 0 ]; then
8
+ echo "NOT found"
9
+ echo "brew, could not be found!"
10
+ echo "Please install it using the instructions found on their site (http://mxcl.github.com/homebrew/).\n"
11
+ return 1
12
+ else
13
+ echo "found"
14
+ fi
15
+ return 0
16
+ }
17
+
18
+ # Given a brew package as the first argument check if that package has already been installed.
19
+ function check_for_brew_package() {
20
+ printf "Checking for $1 - "
21
+ brew list | grep $1 > /dev/null
22
+ if [ $? -ne 0 ]; then
23
+ echo "NOT found"
24
+ echo "$1, could not be found."
25
+ echo "Please run brew update && brew install $1 to install it.\n"
26
+ return 1
27
+ else
28
+ echo "found"
29
+ fi
30
+ return 0
31
+ }
32
+
33
+ # This method takes two arguments. The first argument is the name of the gem to check for and install. The second argument is
34
+ # the version of the gem to check for and install. Both arguments are required. If the gem is already installed an exit status
35
+ # of 0 is set, if an install is required an exit status of 1 is set.
36
+ function install_gem_if_not_found() {
37
+ printf "Checking for gem $1 ($2) - "
38
+ gem list | grep "$1.*$2.*" > /dev/null
39
+ if [ $? -ne 0 ]; then
40
+ echo "NOT found, installing for you"
41
+ gem install $1 -v $2
42
+ return 1
43
+ else
44
+ echo "found"
45
+ fi
46
+ return 0
47
+ }
48
+
49
+ function load_rvm_as_a_function() {
50
+ # Here I load rvm as a bash function rather than a binary. I do
51
+ # this because the binary version is limited and won't properly
52
+ # create or switch to gemsets. This was added specifically to
53
+ # handle the case where .rvmrc is loaded via rvm rvmrc load in
54
+ # a Capistrano deploy. However, it doesn't seem to hurt the
55
+ # normal dev workflow so I am just leaving it here. For more info
56
+ # on binary/function mode of RVM refer to the following url:
57
+ # http://beginrescueend.com/workflow/scripting/
58
+ if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
59
+ # First try to load from a user install
60
+ source "$HOME/.rvm/scripts/rvm"
61
+ elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
62
+ # Then try to load from a root install
63
+ source "/usr/local/rvm/scripts/rvm"
64
+ else
65
+ printf "ERROR: An RVM installation was not found.\n"
66
+ return 1
67
+ fi
68
+ return 0
69
+ }
70
+
71
+ # Attempt to load the specified RVM environment where the RVM environment
72
+ # is the first argument passed to this method when called in the form of
73
+ # "ruby-1.9.3-p0@rpp_web_app"
74
+ function load_rvm_environment() {
75
+ #
76
+ # First we attempt to load the desired environment directly from the environment
77
+ # file. This is very fast and efficient compared to running through the entire
78
+ # CLI and selector. If you want feedback on which environment was used then
79
+ # insert the word 'use' after --create as this triggers verbose mode.
80
+ #
81
+
82
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" && -s "${rvm_path:-$HOME/.rvm}/environments/$1" ]]
83
+ then
84
+ source "${rvm_path:-$HOME/.rvm}/environments/$1"
85
+
86
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
87
+ then
88
+ source "${rvm_path:-$HOME/.rvm}/hooks/after_use"
89
+ fi
90
+ return 0
91
+ else
92
+ #If the environment file has not yet been created, use the RVM CLI to select.
93
+ if ! rvm --create "$1"
94
+ then
95
+ echo "Failed to create RVM environment '$1'."
96
+ return 1
97
+ fi
98
+ return 0
99
+ fi
100
+
101
+ #
102
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
103
+ # it be automatically loaded. Uncomment the following and adjust the filename if
104
+ # necessary.
105
+ #
106
+ # filename=".gems"
107
+ # if [[ -s "$filename" ]]
108
+ # then
109
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
110
+ # fi
111
+ }
112
+
113
+ # if [ `uname -s` == "Darwin" ]; then # we assume we are on a Mac OS X dev box
114
+ # missing_brew=0
115
+ # missing_brew_packages=0
116
+
117
+ # check_for_brew; if [ $? -ne 0 ]; then missing_brew=1; fi
118
+ # check_for_brew_package "readline"; if [ $? -ne 0 ]; then missing_brew_packages=1; fi
119
+ # check_for_brew_package "imagemagick"; if [ $? -ne 0 ]; then missing_brew_packages=1; fi
120
+ # check_for_brew_package "qt"; if [ $? -ne 0 ]; then missing_brew_packages=1; fi
121
+ # check_for_brew_package "wkhtmltopdf"; if [ $? -ne 0 ]; then missing_brew_packages=1; fi
122
+ # check_for_brew_package "mysql"; if [ $? -ne 0 ]; then missing_brew_packages=1; fi
123
+ # check_for_brew_package "redis"; if [ $? -ne 0 ]; then missing_brew_packages=1; fi
124
+
125
+ # if [ $missing_brew_packages -ne 0 ] || [ $missing_brew -ne 0 ]; then
126
+ # echo "YOU ARE MISSING THE ABOVE PACKAGES! YOU ARE NOT IN THE CORRECT RVM RUBY VERSION GEMSET COMBO!"
127
+ # cur_rvm=$(rvm current)
128
+ # echo "The rvm you are currently using is: $cur_rvm\n"
129
+
130
+ # echo "Please install them using the provided instructions above."
131
+ # echo "Once completed cd out of this directory and back into this directory to have this project specific .rvmrc script run again."
132
+ # return 1;
133
+ # fi
134
+ # fi
135
+
136
+ load_rvm_as_a_function; if [ $? -ne 0 ]; then return 1; fi
137
+ #
138
+ # Note: In order for this script to work, specifically the creation
139
+ # of the gemset below with the rvm --create call, the user executing
140
+ # this needs to have the following in their ~/.rvmrc:
141
+ #
142
+ # export rvm_is_not_a_shell_function=0
143
+ #
144
+ # This is only really needed for when it is being run via Capistrano
145
+ # so it only needs to be in the deployment users ~/.rvmrc on
146
+ # boxes you will be deploying to. This with the function loading
147
+ # above is the magic that makes the rvm --create work in this script
148
+ # when it is loaded via Capistrano.
149
+
150
+ #
151
+ # Note: We also use the following options in our deployment users ~/.rvmrc
152
+ # files handle trusting, auto install of new ruby versions, auto creation
153
+ # of gemsets, etc. The example of the full ~/.rvmrc file is as follows:
154
+ #
155
+ # export rvm_path="/home/rpi/.rvm"
156
+ # export rvm_is_not_a_shell_function=0
157
+ # rvm_trust_rvmrcs_flag=1
158
+ # rvm_install_on_use_flag=1
159
+ # rvm_gemset_create_on_use_flag=1
160
+ #
161
+
162
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
163
+ # development environment upon cd'ing into the directory
164
+
165
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
166
+ environment_id="ruby-1.9.3-p125@tagalong"
167
+
168
+ load_rvm_environment $environment_id; if [ $? -ne 0 ]; then return 1; fi
169
+
170
+ # At this point we should have the proper RVM environment and gemset loaded
171
+ cur_rvm=$(rvm current)
172
+ echo "Entered RVM environment '$cur_rvm'"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tagalong.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Andrew De Ponte
2
+
3
+ MIT License
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.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # Tagalong
2
+
3
+ Tagalong is a Rails plugin that is intended to be a clean, efficient, and simple. I have tried very hard to have the API make sense in terms of OOP as I have seen many other tagging libraries that I don't think do a great job of this.
4
+
5
+ The other key differentiation between Tagalong and many of the other tagging libraries out there is the relational database structure behind the scenes. This allows us to differentiate this tagging plugin in the following ways:
6
+
7
+ * clean object oriented API
8
+ * does NOT require saving of the model being tagged
9
+ * keeps history of tags Taggers have used
10
+ * allows defining multiple Taggers and Taggables
11
+ * tracks number of times tags are used
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'tagalong'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install tagalong
26
+
27
+ ## Usage
28
+
29
+ ### Migration Setup
30
+
31
+ In order to use `tagalong` you have to generate the proper migrations so that the tags can be stored in the database. You can do this with the following command:
32
+
33
+ rails generate tagalong:migration
34
+
35
+ The above will generate the migration and place it appropriately in the db/migrate/ project path so that the next time you `rake db:migrate` it will make the changes to the database.
36
+
37
+ ### Declaring Taggers and Taggables
38
+
39
+ In order to use `tagalong` you need to first declare at least one Tagger and at least one Taggable. You do this as follows:
40
+
41
+ class Contact < ActiveRecord::Base
42
+ tagalong_taggable
43
+ end
44
+
45
+ class User < ActiveRecord::Base
46
+ tagalong_tagger
47
+ end
48
+
49
+ ### Tag things
50
+
51
+ To tag things you must use the Tagger object and hand it a persisted Taggable object with the given tag that you want to apply as follows:
52
+
53
+ @user.tag(@contact, "sometag")
54
+
55
+ ### Untag things
56
+
57
+ To untag things you must use the Tagger object and hand it a persisted Taggable object with the given tag that you want to untag as follows:
58
+
59
+ @user.untag(@contact, "sometag")
60
+
61
+ ### List tags
62
+
63
+ You can get the list of tags for either a Tagger or a Taggable.
64
+
65
+ When you get the tags from a Tagger you are getting a list of all tags that tagger has ever used. This can be done as follows:
66
+
67
+ @user.tags
68
+ # => ['some_tag', 'another_tag', 'woot_tag']
69
+
70
+ When you get the tags from a Taggable you are getting a list of all the tags that taggable currently has applied. This can be done as follows:
71
+
72
+ @contact.tags
73
+ # => ['some_tag', 'woot_tag']
74
+
75
+ Tags are returned ordered by how often the tags are used.
76
+
77
+ ### List tags with usage info
78
+
79
+ Passing a taggable object to the tags method on the Tagger will return a list of hash objects containing the tag (`tag`), a boolean representing if the tag is applied to the passed taggable (`used`), and the number of applications of that tag by the Tagger (`number_of_references`).
80
+
81
+ @user.tags(@contact)
82
+ # => [
83
+ { tag: 'some_tag', :used => true, :number_of_references => 23 },
84
+ { tag: 'another_tag', :used => false, :number_of_references => 42 }
85
+ ]
86
+
87
+ ### List taggables that have a tag
88
+
89
+ You can aquire an array of taggable objects that have a given tag using the `taggables_with` method on the Tagger object as follows:
90
+
91
+ @user.taggables_with('some_tag')
92
+ # => [Taggable Object, Taggable Object] (in this case Taggable Objects would be Contacts)
93
+
94
+ ## Credits
95
+
96
+ I just wanted to thank all of the other open source Rails tagging plugins out there. Especially, acts-as-taggable-on, I learned a lot from you all. Thanks!
97
+
98
+ ## Contributing
99
+
100
+ If you are interested in contributing code please follow the process below and please include tests. Also, please fill out issues if you have discovered a bug or simply want to request a feature on our GitHub issues page.
101
+
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,39 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Tagalong
5
+ class MigrationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ desc "Generates migration for TagalongTag and TagalongTagging models"
9
+
10
+ def self.orm
11
+ Rails::Generators.options[:rails][:orm]
12
+ end
13
+
14
+ #source_root File.expand_path('../templates', __FILE__)
15
+ def self.source_root
16
+ File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)))
17
+ end
18
+
19
+ def self.orm_has_migration?
20
+ [:active_record].include? orm
21
+ end
22
+
23
+ def self.next_migration_number(dirname)
24
+ if ActiveRecord::Base.timestamped_migrations
25
+ migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
26
+ migration_number += 1
27
+ migration_number.to_s
28
+ else
29
+ "%.3d" % (current_migration_number(dirname) + 1)
30
+ end
31
+ end
32
+
33
+ def create_migration_file
34
+ if self.class.orm_has_migration?
35
+ migration_template 'migration.rb', 'db/migrate/tagalong_migration'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ class TagalongMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tagalong_tags do |t|
4
+ t.string :name
5
+ t.integer :number_of_references
6
+ t.integer :tagger_id
7
+ t.string :tagger_type
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :tagalong_tags, :tagger_id
13
+ add_index :tagalong_tags, :tagger_type
14
+ add_index :tagalong_tags, [:tagger_id, :tagger_type]
15
+
16
+ create_table :tagalong_taggings do |t|
17
+ t.integer :tagalong_tag_id
18
+ t.integer :taggable_id
19
+ t.string :taggable_type
20
+
21
+ t.timestamps
22
+ end
23
+
24
+ add_index :tagalong_taggings, :tagalong_tag_id
25
+ add_index :tagalong_taggings, :taggable_id
26
+ add_index :tagalong_taggings, :taggable_type
27
+ add_index :tagalong_taggings, [:taggable_id, :taggable_type]
28
+ end
29
+
30
+ def self.down
31
+ drop_table :tagalong_tags
32
+ drop_table :tagalong_taggings
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ module Tagalong
2
+ class TaggableNotPersisted < StandardError
3
+ end
4
+ end
@@ -0,0 +1,63 @@
1
+ module Tagalong
2
+ class TagManager
3
+ def initialize(taggable, tagger)
4
+ @taggable = taggable
5
+ @tagger = tagger
6
+ end
7
+
8
+ def add_tag(name)
9
+ tagger_tag = tagger_used_tag?(name)
10
+ if (tagger_tag != nil)
11
+ if !taggable_has_tag?(name)
12
+ associate_tag_with_taggable(tagger_tag, @taggable)
13
+ end
14
+ else
15
+ tagger_tag = create_tag_for_tagger(name, @tagger)
16
+ associate_tag_with_taggable(tagger_tag, @taggable)
17
+ end
18
+ end
19
+
20
+ def remove_tag(name)
21
+ tagger_tag = tagger_used_tag?(name)
22
+ if tagger_tag && taggable_has_tag?(name)
23
+ disassociate_tag_from_taggable(tagger_tag, @taggable)
24
+ end
25
+ end
26
+
27
+ def tagger_used_tag?(name)
28
+ @tagger.tagalong_tags.find_by_name(name)
29
+ end
30
+
31
+ def taggable_has_tag?(name)
32
+ @taggable.tagalong_tags.find_by_name(name)
33
+ end
34
+
35
+ private
36
+
37
+ def create_tag_for_tagger(name, tagger)
38
+ return TagalongTag.create!(:tagger_id => tagger.id, :tagger_type => tagger.class.to_s, :name => name)
39
+ end
40
+
41
+ def associate_tag_with_taggable(tag, taggable)
42
+ TagalongTagging.create!(:taggable_id => taggable.id, :taggable_type => taggable.class.to_s, :tagalong_tag_id => tag.id)
43
+ increment_tag_number_of_references(tag)
44
+ end
45
+
46
+ def disassociate_tag_from_taggable(tag, taggable)
47
+ taggable_tagging = TagalongTagging.find_by_tagalong_tag_id_and_taggable_id(tag.id, taggable.id)
48
+ TagalongTagging.destroy(taggable_tagging.id)
49
+ taggable.tagalong_tags(true)
50
+ decrement_tag_number_of_references(tag)
51
+ end
52
+
53
+ def increment_tag_number_of_references(tag)
54
+ tag.number_of_references = (tag.number_of_references || 0) + 1
55
+ tag.save!
56
+ end
57
+
58
+ def decrement_tag_number_of_references(tag)
59
+ tag.number_of_references = tag.number_of_references ? tag.number_of_references - 1 : 0
60
+ tag.save!
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ module Tagalong
2
+ class TagalongTag < ::ActiveRecord::Base
3
+ has_many :tagalong_taggings, :dependent => :destroy, :class_name => 'Tagalong::TagalongTagging'
4
+ belongs_to :tagger, :polymorphic => true
5
+
6
+ validates_presence_of :name
7
+ validates_uniqueness_of :name, :scope => [ :tagger_id, :tagger_type ]
8
+
9
+ def taggables
10
+ tagalong_taggings.map { |tagging| tagging.taggable }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Tagalong
2
+ class TagalongTagging < ::ActiveRecord::Base
3
+ belongs_to :tagalong_tag, :class_name => 'Tagalong::TagalongTag'
4
+ belongs_to :taggable, :polymorphic => true
5
+
6
+ validates_presence_of :tagalong_tag_id
7
+ validates_presence_of :tagalong_tag_id, :scope => [ :taggable_type, :taggable_id ]
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ module Tagalong
2
+ module Taggable
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def tagalong_taggable
9
+ class_eval do
10
+ has_many :tagalong_taggings, :class_name => 'Tagalong::TagalongTagging', :as => :taggable
11
+ has_many :tagalong_tags, :class_name => 'Tagalong::TagalongTag', :through => :tagalong_taggings
12
+ include Tagalong::Taggable::InstanceMethods
13
+ end
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def has_tag?(name)
19
+ return self.tagalong_tags.map { |r| r.name }.include?(name)
20
+ end
21
+
22
+ def tags
23
+ return self.tagalong_tags.order("number_of_references DESC").map { |r| r.name }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,44 @@
1
+ module Tagalong
2
+ module Tagger
3
+ def self.included(base)
4
+ base.extend Tagalong::Tagger::ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def tagalong_tagger
9
+ class_eval do
10
+ has_many :tagalong_tags, :class_name => 'Tagalong::TagalongTag', :as => :tagger
11
+ include Tagalong::Tagger::InstanceMethods
12
+ end
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def tag(taggable_obj, tag_name)
18
+ raise Tagalong::TaggableNotPersisted, "Taggable must be persisted to tag it." if !taggable_obj.persisted?
19
+ tag_manager = Tagalong::TagManager.new(taggable_obj, self)
20
+ tag_manager.add_tag(tag_name)
21
+ end
22
+
23
+ def untag(taggable_obj, tag_name)
24
+ raise Tagalong::TaggableNotPersisted, "Taggable must be persisted to untag it." if !taggable_obj.persisted?
25
+ tag_manager = Tagalong::TagManager.new(taggable_obj, self)
26
+ tag_manager.remove_tag(tag_name)
27
+ end
28
+
29
+ def tags(taggable = nil)
30
+ if taggable == nil
31
+ return self.tagalong_tags.order("number_of_references DESC").map { |r| r.name }
32
+ else
33
+ return self.tagalong_tags.joins("LEFT OUTER JOIN tagalong_taggings ON tagalong_taggings.tagalong_tag_id = tagalong_tags.id").select("tagalong_tags.id, tagalong_tags.name, tagalong_tags.number_of_references, tagalong_taggings.id as used").order("number_of_references DESC").map { |r| { :tag => r.name, :used => !r.used.nil?, :number_of_references => r.number_of_references } }
34
+ end
35
+ end
36
+
37
+ def taggables_with(name)
38
+ self.tagalong_tags.where(:name => name).each do |t|
39
+ return t.taggables
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Tagalong
2
+ VERSION = "0.0.1"
3
+ end
data/lib/tagalong.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "active_record"
2
+
3
+ require "tagalong/version"
4
+ require "tagalong/exceptions"
5
+ require "tagalong/tagalong_tag"
6
+ require "tagalong/tagalong_tagging"
7
+ require "tagalong/tag_manager"
8
+ require "tagalong/taggable"
9
+ require "tagalong/tagger"
10
+
11
+ if defined?(ActiveRecord::Base)
12
+ class ActiveRecord::Base
13
+ include Tagalong::Taggable
14
+ include Tagalong::Tagger
15
+ end
16
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,4 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: tagalong.sqlite3
4
+
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe "TagManager Integration" do
4
+ before(:each) do
5
+ @contact = Contact.create!(:name => "Bob Bobster")
6
+ @user = User.create!(:name => "My User")
7
+ @contact_tag_manager = Tagalong::TagManager.new(@contact, @user)
8
+ end
9
+
10
+ describe "#add_tag" do
11
+ it "add new tag to contact" do
12
+ @contact_tag_manager.add_tag("fool")
13
+ @contact.tagalong_tags.map { |r| r.name }.should include("fool")
14
+ end
15
+
16
+ it "add an existing tag to contact" do
17
+ @contact_tag_manager.add_tag("bar")
18
+ @contact_tag_manager.add_tag("bar")
19
+ end
20
+ end
21
+
22
+ describe "#remove_tag" do
23
+ it "remove tag from contact" do
24
+ cm_contact_tag = @user.tagalong_tags.create!(:name => "crazy")
25
+ @contact.tagalong_taggings.create!(:tagalong_tag_id => cm_contact_tag.id)
26
+ @contact_tag_manager.remove_tag("crazy")
27
+ @contact.tagalong_tags.map { |r| r.name }.should_not include("crazy")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+
3
+ # class TagalongTag; end
4
+ # class TagalongTagging; end
5
+
6
+ describe Tagalong::TagManager do
7
+ before(:each) do
8
+ @taggable = stub('taggable', :id => 58, :class => "Contact", :reload => nil)
9
+ @tagger = stub('tagger', :id => 23, :class => "User")
10
+ @tag_manager = Tagalong::TagManager.new(@taggable, @tagger)
11
+ end
12
+
13
+ describe "#add_tag" do
14
+ context "tagger does NOT already have the tag" do
15
+ before(:each) do
16
+ @tag_manager.stub(:tagger_used_tag?).and_return(nil)
17
+ end
18
+
19
+ it "creates a tag for the tagger" do
20
+ @tag_manager.should_receive(:create_tag_for_tagger).with("foo_tag", @tagger)
21
+ @tag_manager.stub(:associate_tag_with_taggable)
22
+ @tag_manager.add_tag("foo_tag")
23
+ end
24
+
25
+ it "associates the created tagger tag with the taggable" do
26
+ tag = stub('tag', :id => 98)
27
+ @tag_manager.stub(:create_tag_for_tagger).and_return(tag)
28
+ @tag_manager.should_receive(:associate_tag_with_taggable).with(tag, @taggable)
29
+ @tag_manager.add_tag("foo_tag")
30
+ end
31
+ end
32
+
33
+ context "tagger already has the tag" do
34
+ before(:each) do
35
+ @tag = stub('tag', :id => 108)
36
+ @tag_manager.stub(:tagger_used_tag?).and_return(@tag)
37
+ end
38
+
39
+ it "does NOT create a new tag for the tagger" do
40
+ @tag_manager.stub(:taggable_has_tag?).and_return(@tag)
41
+ @tag_manager.should_not_receive(:create_tag_for_tagger)
42
+ @tag_manager.add_tag("foo_tag")
43
+ end
44
+
45
+ context "tag is already associated to the taggable" do
46
+ before(:each) do
47
+ @tag_manager.stub(:taggable_has_tag?).and_return(@tag)
48
+ end
49
+
50
+ it "does NOT associated the matching tag record with taggable" do
51
+ @tag_manager.should_not_receive(:associate_tag_with_taggable)
52
+ @tag_manager.add_tag("foo_tag")
53
+ end
54
+ end
55
+
56
+ context "tag is NOT associated to the taggable" do
57
+ before(:each) do
58
+ @tag_manager.stub(:taggable_has_tag?).and_return(nil)
59
+ end
60
+
61
+ it "associates the tagger matched tag record with the taggable" do
62
+ @tag_manager.should_receive(:associate_tag_with_taggable).with(@tag, @taggable)
63
+ @tag_manager.add_tag("foo_tag")
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#remove_tag" do
70
+ it "disassociates the tag from the taggable if the tag belongs to tagger" do
71
+ tag = stub('tag')
72
+ @tag_manager.stub(:tagger_used_tag?).and_return(tag)
73
+ @tag_manager.stub(:taggable_has_tag?).and_return(tag)
74
+ @tag_manager.should_receive(:disassociate_tag_from_taggable).with(tag, @taggable)
75
+ @tag_manager.remove_tag("foo_tag")
76
+ end
77
+
78
+ it "should NOT dissassociate the tag from the taggable if it does NOT belong to the tagger" do
79
+ @tag_manager.stub(:tagger_used_tag?).and_return(stub('tag'))
80
+ @tag_manager.stub(:taggable_has_tag?).and_return(nil)
81
+ @tag_manager.should_not_receive(:disassociate_tag_from_taggable)
82
+ @tag_manager.remove_tag("foo_tag")
83
+ end
84
+ end
85
+
86
+ describe "#tagger_used_tag?" do
87
+ it "returns the matching TagalongTag if the tagger has the tag" do
88
+ tag = stub('tag')
89
+ @tagger.stub_chain(:tagalong_tags, :find_by_name).and_return(tag)
90
+ @tag_manager.tagger_used_tag?("foo_tag").should == tag
91
+ end
92
+
93
+ it "returns nil if the tagger does NOT have the tag" do
94
+ @tagger.stub_chain(:tagalong_tags, :find_by_name).and_return(nil)
95
+ @tag_manager.tagger_used_tag?("foo_tag").should be_nil
96
+ end
97
+ end
98
+
99
+ describe "#taggable_has_tag?" do
100
+ it "returns matching TagalongTag if the taggable is already associated with the tag" do
101
+ tag = stub('tag')
102
+ @taggable.stub_chain(:tagalong_tags, :find_by_name).and_return(tag)
103
+ @tag_manager.taggable_has_tag?("foo_tag").should == tag
104
+ end
105
+
106
+ it "returns nil, if the tag is NOT already associated with the taggable" do
107
+ @taggable.stub_chain(:tagalong_tags, :find_by_name).and_return(nil)
108
+ @tag_manager.taggable_has_tag?("foo_tag").should be_nil
109
+ end
110
+ end
111
+
112
+ describe "#create_tag_for_tagger" do
113
+ it "creates a TagalongTag record with the given name, associated with the tagger object" do
114
+ Tagalong::TagalongTag.should_receive(:create!).with({ :tagger_id => @tagger.id, :tagger_type => "User", :name => "hoopty" })
115
+ @tag_manager.send(:create_tag_for_tagger, "hoopty", @tagger)
116
+ end
117
+ end
118
+
119
+ describe "#associate_tag_with_taggable" do
120
+ it "creates a TagalongTagging record associated with the given taggable and tag" do
121
+ tag = stub('tag', :id => 214)
122
+ @tag_manager.stub(:increment_tag_number_of_references)
123
+ Tagalong::TagalongTagging.should_receive(:create!).with({ :taggable_id => @taggable.id, :taggable_type => "Contact", :tagalong_tag_id => 214 })
124
+ @tag_manager.send(:associate_tag_with_taggable, tag, @taggable)
125
+ end
126
+
127
+ it "increments the reference count for the tag" do
128
+ tag = stub('tag', :id => stub)
129
+ Tagalong::TagalongTagging.stub(:create!)
130
+ @tag_manager.should_receive(:increment_tag_number_of_references).with(tag)
131
+ @tag_manager.send(:associate_tag_with_taggable, tag, @taggable)
132
+ end
133
+ end
134
+
135
+ describe "#disassociate_tag_from_taggable" do
136
+ it "destroys the TagalongTagging record that associates the given tag with the given taggable" do
137
+ tag = stub('tag', :id => 111)
138
+ tagging = mock('tagging', :id => 283)
139
+ @taggable.stub(:tagalong_tags)
140
+ Tagalong::TagalongTagging.stub(:find_by_tagalong_tag_id_and_taggable_id).with(111, @taggable.id).and_return(tagging)
141
+ @tag_manager.stub(:decrement_tag_number_of_references)
142
+ Tagalong::TagalongTagging.should_receive(:destroy).with(283)
143
+ @tag_manager.send(:disassociate_tag_from_taggable, tag, @taggable)
144
+ end
145
+
146
+ it "decrements the reference count of the tag" do
147
+ tag = stub('tag', :id => 111)
148
+ tagging = stub('tagging', :id => 283)
149
+ @taggable.stub(:tagalong_tags)
150
+ Tagalong::TagalongTagging.stub(:find_by_tagalong_tag_id_and_taggable_id).and_return(tagging)
151
+ Tagalong::TagalongTagging.stub(:destroy)
152
+ @tag_manager.should_receive(:decrement_tag_number_of_references).with(tag)
153
+ @tag_manager.send(:disassociate_tag_from_taggable, tag, @taggable)
154
+ end
155
+ end
156
+
157
+ describe "#increment_tag_number_of_references" do
158
+ it "increments the number of references for the tag" do
159
+ tag = mock('tag', :number_of_references => 13, :save! => nil)
160
+ tag.should_receive(:number_of_references=).with(14)
161
+ @tag_manager.send(:increment_tag_number_of_references, tag)
162
+ end
163
+
164
+ it "initializes number of referencs to 1 if number of references is nil" do
165
+ tag = mock('tag', :number_of_references => nil, :save! => nil)
166
+ tag.should_receive(:number_of_references=).with(1)
167
+ @tag_manager.send(:increment_tag_number_of_references, tag)
168
+ end
169
+
170
+ it "saves the changes to the database" do
171
+ tag = mock('tag', :number_of_references => 13, :number_of_references= => nil)
172
+ tag.should_receive(:save!)
173
+ @tag_manager.send(:increment_tag_number_of_references, tag)
174
+ end
175
+ end
176
+
177
+ describe "#decrement_tag_number_of_references" do
178
+ it "decrements the number of references for the tag" do
179
+ tag = mock('tag', :number_of_references => 13, :save! => nil)
180
+ tag.should_receive(:number_of_references=).with(12)
181
+ @tag_manager.send(:decrement_tag_number_of_references, tag)
182
+ end
183
+
184
+ it "initializes number of references to zero if number of references is nil" do
185
+ tag = mock('tag', :number_of_references => nil, :save! => nil)
186
+ tag.should_receive(:number_of_references=).with(0)
187
+ @tag_manager.send(:decrement_tag_number_of_references, tag)
188
+ end
189
+
190
+ it "saves the changes to the database" do
191
+ tag = mock('tag', :number_of_references => 13, :number_of_references= => nil)
192
+ tag.should_receive(:save!)
193
+ @tag_manager.send(:decrement_tag_number_of_references, tag)
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ # The Contact class which is used in this as our taggable model
4
+ # is defined in the spec/models.rb file.
5
+
6
+ describe "Taggable" do
7
+ before(:each) do
8
+ @user = User.create!(:name => "My Owner")
9
+ @contact = Contact.create!(:name => "My Taggable")
10
+ end
11
+
12
+ describe "Integration" do
13
+ describe "#has_tag?" do
14
+ it "returns true if the taggable has the given tag" do
15
+ tag = @user.tagalong_tags.create!(:name => "foo")
16
+ @contact.tagalong_taggings.create!(:tagalong_tag_id => tag.id)
17
+ @contact.has_tag?("foo").should be_true
18
+ end
19
+
20
+ it "returns false if the taggable does NOT have the given tag" do
21
+ @contact.has_tag?("bar").should be_false
22
+ end
23
+ end
24
+
25
+ describe "#tags" do
26
+ it "returns list of tags currently applied to this taggable" do
27
+ @contact.tagalong_tags.create!(:name => "foo")
28
+ @contact.tagalong_tags.create!(:name => "bar")
29
+ @contact.tagalong_tags.create!(:name => "car")
30
+ @contact.tags.should == ["foo", "bar", "car"]
31
+ end
32
+
33
+ it "returns list of tags currently applied in descending order of references" do
34
+ @contact.tagalong_tags.create!(:name => "hoopty", :number_of_references => 5)
35
+ @contact.tagalong_tags.create!(:name => "doopty", :number_of_references => 99)
36
+ @contact.tagalong_tags.create!(:name => "toopty", :number_of_references => 4)
37
+ @contact.tags.should == ["doopty", "hoopty", "toopty"]
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "Isolation" do
43
+ end
44
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ # The User class which is used in this spec as our tagger
4
+ # model, it is defined in the spec/models.rb file.
5
+
6
+ describe "Tagger" do
7
+ before(:each) do
8
+ @user = User.create!(:name => "My Owner")
9
+ @contact = Contact.create!(:name => "My Taggable")
10
+ end
11
+
12
+ describe "Integration" do
13
+ describe "#tag" do
14
+ it "tags the given taggable object with the given tag name" do
15
+ @user.tag(@contact, "foo")
16
+ @contact.tagalong_tags.map { |r| r.name }.should include("foo")
17
+ end
18
+ end
19
+
20
+ describe "#untag" do
21
+ it "untags the tag from the given taggable object for the tagger" do
22
+ @contact.tagalong_tags.create!(:name => "bar", :tagger_id => @user.id, :tagger_type => @user.class.to_s)
23
+ @user.untag(@contact, "bar")
24
+ @contact.tagalong_tags.map { |r| r.name }.should_not include("bar")
25
+ end
26
+ end
27
+
28
+ describe "#tags" do
29
+ context "without a taggable" do
30
+ it "returns list of tags the tagger has used" do
31
+ @user.tagalong_tags.create!(:name => "foo")
32
+ @user.tagalong_tags.create!(:name => "bar")
33
+ @user.tagalong_tags.create!(:name => "car")
34
+ @user.tags.should == ["foo", "bar", "car"]
35
+ end
36
+
37
+ it "returns the list of tags in descending order of number of references" do
38
+ @user.tagalong_tags.create!(:name => "foo", :number_of_references => 20)
39
+ @user.tagalong_tags.create!(:name => "bar", :number_of_references => 100)
40
+ @user.tagalong_tags.create!(:name => "car", :number_of_references => 8)
41
+ @user.tags.should == ["bar", "foo", "car"]
42
+ end
43
+ end
44
+
45
+ context "with a taggable" do
46
+ it "returns a hash of the tags with usage information about the passed taggable" do
47
+ tag = @user.tagalong_tags.create!(:name => "foo", :number_of_references => 1)
48
+ @contact.tagalong_taggings.create!(:tagalong_tag_id => tag.id)
49
+ @user.tagalong_tags.create!(:name => "bar", :number_of_references => 0)
50
+ tag = @user.tagalong_tags.create!(:name => "car", :number_of_references => 1)
51
+ @contact.tagalong_taggings.create!(:tagalong_tag_id => tag.id)
52
+ @user.tags(@contact).should == [
53
+ { :tag => "foo", :used => true, :number_of_references => 1 },
54
+ { :tag => "car", :used => true, :number_of_references => 1 },
55
+ { :tag => "bar", :used => false, :number_of_references => 0 }
56
+ ]
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "#taggables_with" do
62
+ it "returns a collection of the taggables tagged with the given tag" do
63
+ @user.tag(@contact, "jackson")
64
+ @user.taggables_with("jackson").should == [@contact]
65
+ end
66
+
67
+ it "returns an empty array if it has no matching taggables" do
68
+ @user.taggables_with("jackson_five").should == []
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "Isolation" do
74
+ describe "#tag" do
75
+ it "creates an instance of the tag manager" do
76
+ tag_manager = stub('tag_manager', :add_tag => nil)
77
+ Tagalong::TagManager.should_receive(:new).with(@contact, @user).and_return(tag_manager)
78
+ @user.tag(@contact, "foo")
79
+ end
80
+
81
+ it "tells the tag manager instance to tag the given taggable for tagger (self)" do
82
+ tag_manager = mock('tag_manager')
83
+ Tagalong::TagManager.stub(:new).with(@contact, @user).and_return(tag_manager)
84
+ tag_manager.should_receive(:add_tag).with("foo")
85
+ @user.tag(@contact, "foo")
86
+ end
87
+
88
+ it "raises taggable not persisted exception if attempting to tag a non-persisted taggable" do
89
+ new_contact = Contact.new
90
+ lambda { @user.tag(new_contact, "bar") }.should raise_error(Tagalong::TaggableNotPersisted)
91
+ end
92
+ end
93
+
94
+ describe "#untag" do
95
+ it "creates an instance of the tag manager" do
96
+ tag_manager = stub('tag_manager', :remove_tag => nil)
97
+ Tagalong::TagManager.should_receive(:new).with(@contact, @user).and_return(tag_manager)
98
+ @user.untag(@contact, "bar")
99
+ end
100
+
101
+ it "tells the tag manager instance to untag the given taggable for tagger (self)" do
102
+ tag_manager = mock('tag_manager')
103
+ Tagalong::TagManager.stub(:new).with(@contact, @user).and_return(tag_manager)
104
+ tag_manager.should_receive(:remove_tag).with("bar")
105
+ @user.untag(@contact, "bar")
106
+ end
107
+
108
+ it "raises taggable not persisted exception if attempting to untag a non-persisted taggable" do
109
+ new_contact = Contact.new
110
+ lambda { @user.untag(new_contact, "bar") }.should raise_error(Tagalong::TaggableNotPersisted)
111
+ end
112
+ end
113
+ end
114
+ end
data/spec/models.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Contact < ActiveRecord::Base
2
+ tagalong_taggable
3
+ end
4
+
5
+ class User < ActiveRecord::Base
6
+ tagalong_tagger
7
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,37 @@
1
+ ActiveRecord::Schema.define :version => 0 do
2
+ create_table "tagalong_taggings", :force => true do |t|
3
+ t.integer "tagalong_tag_id", :limit => 11
4
+ t.integer "taggable_id", :limit => 11
5
+ t.string "taggable_type"
6
+ t.datetime "created_at"
7
+ t.datetime "updated_at"
8
+ end
9
+
10
+ add_index "tagalong_taggings", ["tagalong_tag_id"], :name => "index_tagalong_taggings_on_tagalong_tag_id"
11
+ add_index "tagalong_taggings", ["taggable_id", "taggable_type"], :name => "index_tagalong_taggings_on_taggable_id_and_taggable_type"
12
+
13
+ create_table "tagalong_tags", :force => true do |t|
14
+ t.integer "tagger_id", :limit => 11
15
+ t.string "tagger_type"
16
+ t.string "name"
17
+ t.integer "number_of_references", :limit => 11
18
+ t.datetime "created_at"
19
+ t.datetime "updated_at"
20
+ end
21
+
22
+ add_index "tagalong_tags", ["tagger_id", "tagger_type"], :name => "index_tagalong_tags_on_tagger_id_and_tagger_type"
23
+
24
+ create_table "contacts", :force => true do |t|
25
+ t.string "name"
26
+ t.string "phone"
27
+ t.datetime "created_at"
28
+ t.datetime "udpated_at"
29
+ end
30
+
31
+ create_table "users", :force => true do |t|
32
+ t.string "email"
33
+ t.string "name"
34
+ t.datetime "created_at"
35
+ t.datetime "updated_at"
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ require 'logger'
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ require File.expand_path('../../lib/tagalong', __FILE__)
7
+
8
+ db_name = ENV['DB'] || 'sqlite3'
9
+ database_yml = File.expand_path('../database.yml', __FILE__)
10
+
11
+ if File.exists?(database_yml)
12
+ active_record_configuration = YAML.load_file(database_yml)
13
+
14
+ ActiveRecord::Base.configurations = active_record_configuration
15
+ config = ActiveRecord::Base.configurations[db_name]
16
+
17
+ begin
18
+ ActiveRecord::Base.establish_connection(db_name)
19
+ ActiveRecord::Base.connection
20
+ rescue
21
+ case db_name
22
+ when /mysql/
23
+ ActiveRecord::Base.establish_connection(config.merge('database' => nil))
24
+ ActiveRecord::Base.connection.create_database(config['database'], {:charset => 'utf8', :collation => 'utf8_unicode_ci'})
25
+ when 'postgresql'
26
+ ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
27
+ ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => 'utf8'))
28
+ end
29
+
30
+ ActiveRecord::Base.establish_connection(config)
31
+ end
32
+
33
+ ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
34
+ ActiveRecord::Base.default_timezone = :utc
35
+
36
+ ActiveRecord::Base.silence do
37
+ ActiveRecord::Migration.verbose = false
38
+
39
+ load(File.dirname(__FILE__) + '/schema.rb')
40
+ load(File.dirname(__FILE__) + '/models.rb')
41
+ end
42
+ else
43
+ raise "Please create #{database_yml} first to configure your database. Take a look at: #{database_yml}.sample"
44
+ end
45
+
46
+ def clean_database!
47
+ models = [Tagalong::TagalongTag, Tagalong::TagalongTagging]
48
+ models.each do |model|
49
+ ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
50
+ end
51
+ end
52
+
53
+ clean_database!
data/tagalong.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/tagalong/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Andrew De Ponte"]
6
+ gem.email = ["cyphactor@gmail.com"]
7
+ gem.description = %q{A Rails tagging plugin that makes sense.}
8
+ gem.summary = %q{A Rails tagging plugin that makes sense.}
9
+ gem.homepage = "http://github.com/cyphactor/tagalong"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "tagalong"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Tagalong::VERSION
17
+
18
+ gem.add_dependency "activerecord"
19
+ gem.add_development_dependency "sqlite3"
20
+ gem.add_development_dependency "rspec"
21
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tagalong
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew De Ponte
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70241519058460 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70241519058460
25
+ - !ruby/object:Gem::Dependency
26
+ name: sqlite3
27
+ requirement: &70241519058000 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70241519058000
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70241519057520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70241519057520
47
+ description: A Rails tagging plugin that makes sense.
48
+ email:
49
+ - cyphactor@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rvmrc
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - lib/generators/tagalong/migration/migration_generator.rb
61
+ - lib/generators/tagalong/migration/templates/active_record/migration.rb
62
+ - lib/tagalong.rb
63
+ - lib/tagalong/exceptions.rb
64
+ - lib/tagalong/tag_manager.rb
65
+ - lib/tagalong/tagalong_tag.rb
66
+ - lib/tagalong/tagalong_tagging.rb
67
+ - lib/tagalong/taggable.rb
68
+ - lib/tagalong/tagger.rb
69
+ - lib/tagalong/version.rb
70
+ - spec/database.yml
71
+ - spec/integration/tag_manager_integration_spec.rb
72
+ - spec/lib/tagalong/tag_manager_spec.rb
73
+ - spec/lib/tagalong/taggable_spec.rb
74
+ - spec/lib/tagalong/tagger_spec.rb
75
+ - spec/models.rb
76
+ - spec/schema.rb
77
+ - spec/spec_helper.rb
78
+ - tagalong.gemspec
79
+ homepage: http://github.com/cyphactor/tagalong
80
+ licenses: []
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 1.8.16
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: A Rails tagging plugin that makes sense.
103
+ test_files:
104
+ - spec/database.yml
105
+ - spec/integration/tag_manager_integration_spec.rb
106
+ - spec/lib/tagalong/tag_manager_spec.rb
107
+ - spec/lib/tagalong/taggable_spec.rb
108
+ - spec/lib/tagalong/tagger_spec.rb
109
+ - spec/models.rb
110
+ - spec/schema.rb
111
+ - spec/spec_helper.rb