mongo_taggable 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +70 -0
- data/Rakefile +2 -0
- data/lib/app/models/model_tag.rb +84 -0
- data/lib/app/models/tag.rb +107 -0
- data/lib/app/models/tagging.rb +6 -0
- data/lib/mongo_taggable.rb +210 -0
- data/lib/mongo_taggable/version.rb +3 -0
- data/mongo_taggable.gemspec +27 -0
- data/spec/mongo_taggable_spec.rb +77 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/test_helper.rb +63 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3758c89d443f91ef1f35c5590a2468be9a6a38e2
|
4
|
+
data.tar.gz: cf8bddb5855929fa982415b23f3421ef4b73f3a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ba706ac8dd684f7a4cd447999b42279afe44cc59a4130b47a6fbeef325589232e3e38a3c20a598578e628a181784b807661d5a7ced85edc41cab231c6f5ddba
|
7
|
+
data.tar.gz: 62ad8b7e3460f9ee6051182f624b431ae489055fc4d9175c7a1f5e28cf30d59d0535fdff3ad1b13d5b4c7cf01a4024e21d46fbf11e74f180987cf93fb3be6862
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 dkulhari
|
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,70 @@
|
|
1
|
+
# MongoTaggable
|
2
|
+
Extensions for gem https://github.com/mepatterson/acts_as_mongo_taggable with rails 4 and ruby 2.1.1
|
3
|
+
|
4
|
+
## Requirements
|
5
|
+
MongoDB
|
6
|
+
MongoMapper gem
|
7
|
+
Expects you to have a User model that includes MongoMapper::Document
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'mongo_taggable'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install mongo_taggable
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
class User
|
28
|
+
include MongoMapper::Document
|
29
|
+
end
|
30
|
+
|
31
|
+
class Widget
|
32
|
+
include MongoMapper::Document
|
33
|
+
include MongoTaggable
|
34
|
+
end
|
35
|
+
To rate it:
|
36
|
+
|
37
|
+
widget.tag(word_or_words, user)
|
38
|
+
word_or_words can be a string, a string of comma-delimited words, or an array
|
39
|
+
user is the User who is tagging this widget
|
40
|
+
Basic search:
|
41
|
+
|
42
|
+
Widget.find_with_tag('vampires')
|
43
|
+
... will return the first Widget object that has been tagged with that phrase
|
44
|
+
|
45
|
+
Widget.find_all_with_tag('vampires')
|
46
|
+
... will return an array of Widget objects, all of which have been tagged with that phrase
|
47
|
+
|
48
|
+
Widget.most_tagged_with('vampires')
|
49
|
+
... will return the Widget object that has been tagged the most times with that phrase
|
50
|
+
|
51
|
+
Making tag clouds:
|
52
|
+
|
53
|
+
Widget.all_tags_with_counts
|
54
|
+
... will return a nice array of arrays, a la [["rails", 8],["ruby", 12], ["php", 6], ["java", 2]] Use this to make yourself a tag cloud for now. (maybe I'll implement a tag cloud view helper someday.)
|
55
|
+
|
56
|
+
Statistics on Tags:
|
57
|
+
|
58
|
+
Tag.top_25
|
59
|
+
... returns the top 25 most used tags across all taggable object classes in the system
|
60
|
+
|
61
|
+
Tag.top_25("Widget")
|
62
|
+
... returns the top 25 most used tags for Widget objects
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it ( https://github.com/[my-github-username]/mongo_taggable/fork )
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
class ModelTag
|
2
|
+
include MongoMapper::EmbeddedDocument
|
3
|
+
|
4
|
+
key :word, String, :required => true
|
5
|
+
key :tagging_count, Integer
|
6
|
+
key :user_ids, Array
|
7
|
+
|
8
|
+
def users
|
9
|
+
UserProxy.new(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
belongs_to :tag
|
13
|
+
|
14
|
+
before_save :set_tagging_count
|
15
|
+
|
16
|
+
def save_or_destroy
|
17
|
+
user_ids.empty? ? destroy : save
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def set_tagging_count
|
22
|
+
self.tagging_count = user_ids.count
|
23
|
+
end
|
24
|
+
|
25
|
+
class UserProxy
|
26
|
+
attr_accessor :model_tag
|
27
|
+
|
28
|
+
def initialize(model_tag)
|
29
|
+
@model_tag = model_tag
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_a
|
33
|
+
fetch_all.to_a
|
34
|
+
end
|
35
|
+
|
36
|
+
def count
|
37
|
+
model_tag.user_ids.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def all(opts = {})
|
41
|
+
fetch_all
|
42
|
+
end
|
43
|
+
|
44
|
+
def each(&block)
|
45
|
+
fetch_all.each {|user| yield user}
|
46
|
+
end
|
47
|
+
|
48
|
+
def find(id)
|
49
|
+
return nil unless model_tag.user_ids.include?(id)
|
50
|
+
User.find(id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def first(opts = {})
|
54
|
+
return @first ||= User.find(model_tag.user_ids.first) if opts.empty?
|
55
|
+
User.first(opts.merge(:_id.in => model_tag.user_ids))
|
56
|
+
end
|
57
|
+
|
58
|
+
def last(opts = {})
|
59
|
+
return @last ||= User.find(model_tag.user_ids.last) if opts.empty?
|
60
|
+
User.last(opts.merge(:_id.in => model_tag.user_ids))
|
61
|
+
end
|
62
|
+
|
63
|
+
alias :size :count
|
64
|
+
|
65
|
+
def << (user)
|
66
|
+
model_tag.user_ids << user.id
|
67
|
+
model_tag.send(:set_tagging_count)
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete(user)
|
71
|
+
model_tag.user_ids.delete user.id
|
72
|
+
model_tag.send(:set_tagging_count)
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
all.inspect
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def fetch_all
|
81
|
+
@fetch ||= User.find(model_tag.user_ids)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class Tag
|
2
|
+
include MongoMapper::Document
|
3
|
+
key :word, String, :required => true, :index => true
|
4
|
+
key :taggings_count, Integer, :index => true
|
5
|
+
|
6
|
+
many :taggings do
|
7
|
+
def << (tagging)
|
8
|
+
super << tagging
|
9
|
+
tagging._parent_document.send(:increment_counts, tagging)
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete(tagging)
|
13
|
+
target.delete tagging
|
14
|
+
tagging._parent_document.send(:decrement_counts, tagging)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
ensure_index 'taggings.user_id'
|
19
|
+
ensure_index 'taggings.taggable_type'
|
20
|
+
ensure_index 'taggings.taggable_id'
|
21
|
+
|
22
|
+
before_save :set_tagging_counts
|
23
|
+
|
24
|
+
def self.register_taggable_type(type)
|
25
|
+
key taggings_count_key_for(type), Integer, :index => true
|
26
|
+
end
|
27
|
+
|
28
|
+
# == Various Class Methods
|
29
|
+
|
30
|
+
# takes a string and produces an array of words from the db that are 'like' this one
|
31
|
+
# great for those oh-so-fancy autocomplete/suggestion text fields
|
32
|
+
def self.like(string, klass = nil)
|
33
|
+
opts = {:word => /^#{string}/}
|
34
|
+
opts['taggings.taggable_type'] = klass.to_s if klass
|
35
|
+
all(opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.all_for_class(klass, opts = {})
|
39
|
+
all(opts.merge('taggings.taggable_type' => klass.to_s))
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.most_tagged(klass = nil, opts = {})
|
43
|
+
order = klass ? "#{taggings_count_key_for(klass)} desc" : 'taggings_count desc'
|
44
|
+
lo = opts.merge(:order => order)
|
45
|
+
lo['taggings.taggable_type'] = klass.to_s if klass
|
46
|
+
all(lo)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.top_25(klass = nil)
|
50
|
+
most_tagged(klass, :limit => 25)
|
51
|
+
end
|
52
|
+
|
53
|
+
def count_for(klass = nil)
|
54
|
+
klass ? send(taggings_count_key_for(klass)) : taggings_count
|
55
|
+
end
|
56
|
+
|
57
|
+
#Called when removing taggings. If no taggings left, destroy, otherwise save
|
58
|
+
def save_or_destroy
|
59
|
+
taggings.empty? ? destroy : save
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def set_tagging_counts
|
64
|
+
self.taggings_count = self.taggings.size
|
65
|
+
|
66
|
+
count_hash = self.taggings.inject({}) do |hash, tagging|
|
67
|
+
key = taggings_count_key_for(tagging.taggable_type)
|
68
|
+
hash[key] ||= 0
|
69
|
+
hash[key] += 1
|
70
|
+
hash
|
71
|
+
end
|
72
|
+
count_hash.each{|key, count| self.send("#{key}=", count)}
|
73
|
+
end
|
74
|
+
|
75
|
+
def increment_counts(tagging)
|
76
|
+
safe_increment_count(:taggings_count)
|
77
|
+
safe_increment_count(taggings_count_key_for(tagging.taggable_type))
|
78
|
+
end
|
79
|
+
|
80
|
+
def decrement_counts(tagging)
|
81
|
+
safe_decrement_count(:taggings_count)
|
82
|
+
safe_decrement_count(taggings_count_key_for(tagging.taggable_type))
|
83
|
+
end
|
84
|
+
|
85
|
+
def taggings_count_key_for(type)
|
86
|
+
Tag.taggings_count_key_for(type)
|
87
|
+
end
|
88
|
+
|
89
|
+
def safe_increment_count(key)
|
90
|
+
val = self.send(key) || 0
|
91
|
+
self.send("#{key}=", val + 1)
|
92
|
+
end
|
93
|
+
|
94
|
+
def safe_decrement_count(key)
|
95
|
+
self.send("#{key}=", self.send("#{key}") - 1) if self.send("#{key}")
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.taggings_count_key_for(type)
|
99
|
+
type = type.constantize if type.is_a? String
|
100
|
+
#check for inheritance, get most superclass with include
|
101
|
+
while type.superclass && type.superclass.include?(MongoTaggable)
|
102
|
+
type = type.superclass
|
103
|
+
end
|
104
|
+
type = type.name
|
105
|
+
:"#{type.underscore}_taggings_count"
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require "mongo_taggable/version"
|
2
|
+
|
3
|
+
module MongoTaggable
|
4
|
+
module ClassMethods
|
5
|
+
def all_tags_with_counts
|
6
|
+
Tag.most_tagged(self).map{|tag| [tag.word, tag.count_for(self)]}
|
7
|
+
end
|
8
|
+
|
9
|
+
# returns the _first_ widget with this tag, a la ActiveRecord find()
|
10
|
+
# note: case-insensitive unless you specify otherwise with :case_sensitive=>true
|
11
|
+
def first_with_tag(phrase, opts={})
|
12
|
+
lo = opts.clone
|
13
|
+
case_sensitive = lo.delete :case_sensitive
|
14
|
+
phrase = phrase.downcase unless case_sensitive
|
15
|
+
first(lo.merge(:tag_words => phrase))
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def all_with_tag(phrase, opts={})
|
20
|
+
lo = opts.clone
|
21
|
+
case_sensitive = lo.delete :case_sensitive
|
22
|
+
phrase = phrase.downcase unless case_sensitive
|
23
|
+
all(lo.merge(:tag_words => phrase))
|
24
|
+
end
|
25
|
+
|
26
|
+
def most_tagged_with(phrase, opts={})
|
27
|
+
lo = opts.clone
|
28
|
+
case_sensitive = lo.delete :case_sensitive
|
29
|
+
phrase = phrase.downcase unless case_sensitive
|
30
|
+
|
31
|
+
#Doesn't work :(
|
32
|
+
#first(lo.merge('model_tags.word' => phrase, :order => 'model_tags.tagging_count desc'))
|
33
|
+
|
34
|
+
all_with_tag(phrase, lo).sort do |a, b|
|
35
|
+
b.model_tag(phrase, opts).tagging_count <=> a.model_tag(phrase, opts).tagging_count
|
36
|
+
end.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def top_25_tags
|
40
|
+
Tag.top_25(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module InstanceMethods
|
45
|
+
|
46
|
+
def delete_all_tags
|
47
|
+
Tag.find(tag_ids).each do |tag|
|
48
|
+
model_taggings = tag.taggings.select{|tagging| tagging.taggable_type == self.class.name && tagging.taggable_id == self.id}
|
49
|
+
model_taggings.each{|tagging| tag.taggings.delete tagging}
|
50
|
+
tag.save_or_destroy
|
51
|
+
end
|
52
|
+
update_attributes({ :model_tags => [], :tag_words => [] })
|
53
|
+
end
|
54
|
+
|
55
|
+
def _tags
|
56
|
+
Tag.find(:all, tag_ids)
|
57
|
+
end
|
58
|
+
|
59
|
+
# returns array of tags and counts:
|
60
|
+
# [["matt", 3], ["bob", 2], ["bill", 1], ["joe", 1], ["frank", 1]]
|
61
|
+
def tags_with_counts
|
62
|
+
counts = model_tags.map{|tag| [tag.word, tag.tagging_count]}
|
63
|
+
counts.sort{|a, b| b[1] <=> a[1]}
|
64
|
+
end
|
65
|
+
|
66
|
+
# returns an array of ids and user_ids
|
67
|
+
def tags_with_user_ids
|
68
|
+
model_tags.inject([]) do |arr, tag|
|
69
|
+
tag.user_ids.each{|user_id| arr << [tag.id, user_id]}
|
70
|
+
arr
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def tag_words_by_user(user)
|
75
|
+
tags_by_user(user).map(&:word)
|
76
|
+
end
|
77
|
+
|
78
|
+
def tags_by_user(user)
|
79
|
+
model_tags.select{|tag| tag.user_ids.include? user.id}
|
80
|
+
end
|
81
|
+
|
82
|
+
# returns only the tag words, sorted by frequency; optionally can be limited
|
83
|
+
def tags(limit=nil)
|
84
|
+
array = tags_with_counts
|
85
|
+
limit ||= array.size
|
86
|
+
array[0,limit].map{|t| t[0]}
|
87
|
+
end
|
88
|
+
|
89
|
+
def model_tag(phrase, opts = {})
|
90
|
+
phrase = phrase.downcase unless opts[:case_sensitive]
|
91
|
+
model_tags.detect{|tag| tag.word == phrase}
|
92
|
+
end
|
93
|
+
|
94
|
+
def tag_ids
|
95
|
+
model_tags.map(&:tag_id)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def delete_tags_by_user(user)
|
100
|
+
return false unless user
|
101
|
+
return 0 if model_tags.blank?
|
102
|
+
user_tags = tags_by_user(user)
|
103
|
+
|
104
|
+
Tag.find(user_tags.map(&:tag_id)).each do |tag|
|
105
|
+
user_taggings = tag.taggings.select{|tagging| tagging.user_id == user.id && tagging.taggable_type == self.class.name && tagging.taggable_id == self.id}
|
106
|
+
user_taggings.each{|tagging| tag.taggings.delete tagging}
|
107
|
+
tag.save_or_destroy
|
108
|
+
end
|
109
|
+
|
110
|
+
user_tags.each do |tag|
|
111
|
+
tag.users.delete user
|
112
|
+
destroy_if_empty(tag)
|
113
|
+
end
|
114
|
+
save
|
115
|
+
reload
|
116
|
+
end
|
117
|
+
|
118
|
+
def arr_of_words(words)
|
119
|
+
raise "Passed an invalid data type to tag()" unless words.is_a?(String) || words.is_a?(Array)
|
120
|
+
if words.is_a?(String)
|
121
|
+
words.squish.split(',').map{|w| w.squish}
|
122
|
+
else
|
123
|
+
words.map{|w| w.squish}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# returns my current tag word list; raises exception if user tries to multi-tag with same word
|
128
|
+
def tag!(word_or_words, user)
|
129
|
+
arr_of_words(word_or_words).each do |word|
|
130
|
+
raise StandardError if tag_words_by_user(user).include?(word)
|
131
|
+
|
132
|
+
#First add Tag/Tagging
|
133
|
+
t = Tag.first(:word => word) || Tag.create!(:word => word)
|
134
|
+
t.taggings << Tagging.new(:user => user, :taggable => self)
|
135
|
+
t.save!
|
136
|
+
|
137
|
+
#Now add ModelTag/User/tag_word
|
138
|
+
model_tag = model_tags.detect{|tag| tag.word == word}
|
139
|
+
unless model_tag
|
140
|
+
model_tag = ModelTag.new(:word => word, :tag => t)
|
141
|
+
self.model_tags << model_tag
|
142
|
+
self.tag_words << word
|
143
|
+
end
|
144
|
+
model_tag.users << user
|
145
|
+
end
|
146
|
+
save!
|
147
|
+
tags
|
148
|
+
end
|
149
|
+
|
150
|
+
# tags, but silently ignores if user tries to multi-tag with same word
|
151
|
+
# NOTE: automatically downcases each word unless you manually specify :case_sensitive=>true
|
152
|
+
def tag(word_or_words, user, opts={})
|
153
|
+
arr_of_words(word_or_words).each do |word|
|
154
|
+
word = word.downcase unless opts[:case_sensitive] == true
|
155
|
+
unless tag_words_by_user(user).include?(word)
|
156
|
+
#First add Tag/Tagging
|
157
|
+
t = Tag.first(:word => word) || Tag.create!(:word => word)
|
158
|
+
t.taggings << Tagging.new(:user => user, :taggable => self)
|
159
|
+
t.save
|
160
|
+
|
161
|
+
#Now add ModelTag/User/tag_word
|
162
|
+
model_tag = model_tags.detect{|tag| tag.word == word}
|
163
|
+
unless model_tag
|
164
|
+
model_tag = ModelTag.new(:word => word, :tag => t)
|
165
|
+
self.model_tags << model_tag
|
166
|
+
self.tag_words << word
|
167
|
+
end
|
168
|
+
model_tag.users << user
|
169
|
+
end
|
170
|
+
end
|
171
|
+
save
|
172
|
+
tags
|
173
|
+
end
|
174
|
+
|
175
|
+
# returns the Rating object found if user has rated this project, else returns nil
|
176
|
+
def tagged_by_user?(user)
|
177
|
+
!(model_tags.detect{|tag| tag.user_ids.include?(user.id)}.nil?)
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.included(receiver)
|
181
|
+
receiver.class_eval do
|
182
|
+
key :tag_words, Array, :index => true
|
183
|
+
many :model_tags
|
184
|
+
|
185
|
+
ensure_index 'model_tags.word'
|
186
|
+
ensure_index 'model_tags.tagging_count'
|
187
|
+
end
|
188
|
+
receiver.extend ClassMethods
|
189
|
+
receiver.send :include, InstanceMethods
|
190
|
+
|
191
|
+
Tag.register_taggable_type receiver
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
def destroy_if_empty(tag)
|
196
|
+
if tag.user_ids.empty?
|
197
|
+
tag_words.delete(tag.word)
|
198
|
+
model_tags.delete tag
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
%w{ models observers }.each do |dir|
|
205
|
+
path = File.join(File.dirname(__FILE__), 'app', dir)
|
206
|
+
$LOAD_PATH << path
|
207
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
208
|
+
end
|
209
|
+
|
210
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mongo_taggable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mongo_taggable"
|
8
|
+
spec.version = MongoTaggable::VERSION
|
9
|
+
spec.authors = ["deepak Shinde"]
|
10
|
+
spec.email = ["deepak2726@coriolis.co.in"]
|
11
|
+
spec.summary = %q{A ruby gem for acts_as_taggable to mongo}
|
12
|
+
spec.description = %q{A ruby gem for acts_as_taggable to mongo}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.99"
|
24
|
+
spec.add_development_dependency "mongo_mapper", "~> 0.13.0"
|
25
|
+
spec.add_development_dependency "faker", "~> 1.1.2"
|
26
|
+
spec.add_development_dependency "pry-byebug"
|
27
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe "it mongo acts as taggable" do
|
4
|
+
|
5
|
+
it "widget tagged with the same word multiple times should not have dupes in tag_words" do
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
it "ensure we can actually tag two different object types without collisions" do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
it "search and find all widgets containing a specified tag" do
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
it "find first widget that matches specified tag" do
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
it "all_with_tag and first_with_tag are case-insensitive" do
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
it "case-sensitive mode" do
|
26
|
+
end
|
27
|
+
|
28
|
+
it "we get an empty array if we ask for all tags with counts and there are none" do
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
it "we can build an array of tags and counts across an entire tagged object space" do
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
it "most_tagged_with returns the proper widget" do
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
it "tag is created when project tagged" do
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it "tagged_by_user? returns true when this object tagged by user" do
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
it "tagged_by_user? returns false when this object not tagged but some other object is" do
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
it "tag object can be retrieved after project tagged" do
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
it "widget returns correct array with multiple tags with varying counts" do
|
57
|
+
end
|
58
|
+
|
59
|
+
it "tags_with_counts returns the right tags and counts" do
|
60
|
+
end
|
61
|
+
|
62
|
+
it "same user cannot multi-tag with same word using tag!()" do
|
63
|
+
end
|
64
|
+
|
65
|
+
it "delete only tags by certain users" do
|
66
|
+
end
|
67
|
+
|
68
|
+
it "silently ignore multi-tag by single user with same word using tag()" do
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
it "widget with multiple tags can be cleared" do
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/test_helper.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/test_case'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
|
7
|
+
env_rb = File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
|
8
|
+
|
9
|
+
if File.exists? env_rb
|
10
|
+
require env_rb
|
11
|
+
else
|
12
|
+
require 'mongo_mapper'
|
13
|
+
require File.dirname(__FILE__) + '/../lib/acts_as_mongo_taggable'
|
14
|
+
config = {'test' => {'database' => 'aamt-test'}}
|
15
|
+
MongoMapper.setup(config, 'test')
|
16
|
+
end
|
17
|
+
|
18
|
+
class ActiveSupport::TestCase
|
19
|
+
# Drop all columns after each test case.
|
20
|
+
def teardown
|
21
|
+
MongoMapper.database.collections.each do |coll|
|
22
|
+
coll.drop
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Make sure that each test case has a teardown
|
27
|
+
# method to clear the db after each test.
|
28
|
+
def inherited(base)
|
29
|
+
base.define_method teardown do
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# kinda weird, but we have to do this so we can ignore the app's User class and use our own for testing
|
36
|
+
Object.send(:remove_const, :User) if Object.const_defined?(:User)
|
37
|
+
|
38
|
+
class User
|
39
|
+
include MongoMapper::Document
|
40
|
+
key :name, String
|
41
|
+
has_many :widgets
|
42
|
+
has_many :dongles
|
43
|
+
end
|
44
|
+
|
45
|
+
class Widget
|
46
|
+
include MongoMapper::Document
|
47
|
+
include ActsAsMongoTaggable
|
48
|
+
|
49
|
+
belongs_to :user
|
50
|
+
|
51
|
+
key :user_id, ObjectId
|
52
|
+
key :name, String
|
53
|
+
end
|
54
|
+
|
55
|
+
class Dongle
|
56
|
+
include MongoMapper::Document
|
57
|
+
include ActsAsMongoTaggable
|
58
|
+
|
59
|
+
belongs_to :user
|
60
|
+
|
61
|
+
key :user_id, ObjectId
|
62
|
+
key :name, String
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongo_taggable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- deepak Shinde
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.99'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.99'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mongo_mapper
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.13.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.13.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faker
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.1.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.1.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: A ruby gem for acts_as_taggable to mongo
|
98
|
+
email:
|
99
|
+
- deepak2726@coriolis.co.in
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- lib/app/models/model_tag.rb
|
111
|
+
- lib/app/models/tag.rb
|
112
|
+
- lib/app/models/tagging.rb
|
113
|
+
- lib/mongo_taggable.rb
|
114
|
+
- lib/mongo_taggable/version.rb
|
115
|
+
- mongo_taggable.gemspec
|
116
|
+
- spec/mongo_taggable_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- spec/test_helper.rb
|
119
|
+
homepage: ''
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.2.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: A ruby gem for acts_as_taggable to mongo
|
143
|
+
test_files:
|
144
|
+
- spec/mongo_taggable_spec.rb
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
- spec/test_helper.rb
|
147
|
+
has_rdoc:
|