mongoid_taggable_with_context 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +67 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +133 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/mongoid_taggable_with_context.rb +249 -0
- data/mongoid_taggable_with_context.gemspec +88 -0
- data/spec/mongoid_taggable_with_context_spec.rb +272 -0
- data/spec/spec_helper.rb +21 -0
- metadata +271 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'mongoid', '~> 2.0.0.beta.20'
|
4
|
+
|
5
|
+
# Add dependencies to develop your gem here.
|
6
|
+
# Include everything needed to run rake, tests, features, etc.
|
7
|
+
group :development do
|
8
|
+
gem 'database_cleaner'
|
9
|
+
gem 'bson', '~> 1.2.1'
|
10
|
+
gem 'bson_ext', '~> 1.2.1'
|
11
|
+
gem 'rspec', '~> 2.3.0'
|
12
|
+
gem 'yard', '~> 0.6.0'
|
13
|
+
gem 'bundler', '~> 1.0.0'
|
14
|
+
gem 'jeweler', '~> 1.5.2'
|
15
|
+
gem 'rcov', '>= 0'
|
16
|
+
gem 'reek', '~> 1.2.8'
|
17
|
+
gem 'roodi', '~> 2.1.0'
|
18
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (3.0.4)
|
5
|
+
activesupport (= 3.0.4)
|
6
|
+
builder (~> 2.1.2)
|
7
|
+
i18n (~> 0.4)
|
8
|
+
activesupport (3.0.4)
|
9
|
+
bson (1.2.1)
|
10
|
+
bson_ext (1.2.1)
|
11
|
+
builder (2.1.2)
|
12
|
+
database_cleaner (0.6.0)
|
13
|
+
diff-lcs (1.1.2)
|
14
|
+
git (1.2.5)
|
15
|
+
i18n (0.5.0)
|
16
|
+
jeweler (1.5.2)
|
17
|
+
bundler (~> 1.0.0)
|
18
|
+
git (>= 1.2.5)
|
19
|
+
rake
|
20
|
+
mongo (1.2.1)
|
21
|
+
bson (>= 1.2.1)
|
22
|
+
mongoid (2.0.0.rc.7)
|
23
|
+
activemodel (~> 3.0)
|
24
|
+
mongo (~> 1.2)
|
25
|
+
tzinfo (~> 0.3.22)
|
26
|
+
will_paginate (~> 3.0.pre)
|
27
|
+
rake (0.8.7)
|
28
|
+
rcov (0.9.9)
|
29
|
+
reek (1.2.8)
|
30
|
+
ruby2ruby (~> 1.2)
|
31
|
+
ruby_parser (~> 2.0)
|
32
|
+
sexp_processor (~> 3.0)
|
33
|
+
roodi (2.1.0)
|
34
|
+
ruby_parser
|
35
|
+
rspec (2.3.0)
|
36
|
+
rspec-core (~> 2.3.0)
|
37
|
+
rspec-expectations (~> 2.3.0)
|
38
|
+
rspec-mocks (~> 2.3.0)
|
39
|
+
rspec-core (2.3.1)
|
40
|
+
rspec-expectations (2.3.0)
|
41
|
+
diff-lcs (~> 1.1.2)
|
42
|
+
rspec-mocks (2.3.0)
|
43
|
+
ruby2ruby (1.2.5)
|
44
|
+
ruby_parser (~> 2.0)
|
45
|
+
sexp_processor (~> 3.0)
|
46
|
+
ruby_parser (2.0.5)
|
47
|
+
sexp_processor (~> 3.0)
|
48
|
+
sexp_processor (3.0.5)
|
49
|
+
tzinfo (0.3.24)
|
50
|
+
will_paginate (3.0.pre2)
|
51
|
+
yard (0.6.4)
|
52
|
+
|
53
|
+
PLATFORMS
|
54
|
+
ruby
|
55
|
+
|
56
|
+
DEPENDENCIES
|
57
|
+
bson (~> 1.2.1)
|
58
|
+
bson_ext (~> 1.2.1)
|
59
|
+
bundler (~> 1.0.0)
|
60
|
+
database_cleaner
|
61
|
+
jeweler (~> 1.5.2)
|
62
|
+
mongoid (~> 2.0.0.beta.20)
|
63
|
+
rcov
|
64
|
+
reek (~> 1.2.8)
|
65
|
+
roodi (~> 2.1.0)
|
66
|
+
rspec (~> 2.3.0)
|
67
|
+
yard (~> 0.6.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Aaron Qian
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
== Mongoid Taggable With Context
|
2
|
+
|
3
|
+
A tagging plugin for Mongoid that allows for custom tagging along dynamic contexts. This plugin was originally based on Mongoid Taggable by Wilker Lúcio and Ches Martin. It has evolved substantially since that point, but all credit goes to them for the initial tagging functionality.
|
4
|
+
|
5
|
+
For instance, in a social network, a user might have tags that are called skills, interests, sports, and more. There is no real way to differentiate between tags and so an implementation of this type is not possible with Mongoid Taggable.
|
6
|
+
|
7
|
+
Another example, aggregation such as counting tag occurrences was achieved by map-reduce with Mongoid Taggable. It was ok for small amount of tags, but when the amount of tags and documents grow, the original Mongoid Taggable won't be able to scale to real-time statistics demand.
|
8
|
+
|
9
|
+
Enter Mongoid Taggable With Context. Rather than tying functionality to a specific keyword (namely "tags"), Mongoid Taggable With Context allows you to specify an arbitrary number of tag "contexts" that can be used locally or in combination in the same way Mongoid Taggable was used.
|
10
|
+
|
11
|
+
Mongoid Taggable With Context implements tag aggregation without map-reduce, instead it quickly adjusts the aggregation collection whenever tags are inserted or removed with $inc operator. So performance won't be impacted as the number of tags and documents grow.
|
12
|
+
|
13
|
+
== Installation
|
14
|
+
|
15
|
+
You can simply install from rubygems:
|
16
|
+
|
17
|
+
gem install mongoid_taggable_with_context
|
18
|
+
|
19
|
+
or in Gemfile:
|
20
|
+
|
21
|
+
gem 'mongoid_taggable_with_context'
|
22
|
+
|
23
|
+
== Basic Usage
|
24
|
+
|
25
|
+
To make a document taggable you need to include Mongoid::TaggableOnContext into your document and call the *taggable* macro with optional arguments:
|
26
|
+
|
27
|
+
class Post
|
28
|
+
include Mongoid::Document
|
29
|
+
include Mongoid::TaggableWithContext
|
30
|
+
|
31
|
+
field :title
|
32
|
+
field :content
|
33
|
+
|
34
|
+
# default context is 'tags'.
|
35
|
+
# This creates #tags, #tags=, #tags_array, #tags_array= instance methods
|
36
|
+
# separator is " " by default
|
37
|
+
# #tags method returns space separated string
|
38
|
+
# #tags= methods accepts space separated string
|
39
|
+
# #tags_array method returns an array of tags
|
40
|
+
# #tags_array= method accepts an array of tags
|
41
|
+
# #tags and #tags_array are automatically synched.
|
42
|
+
taggable
|
43
|
+
|
44
|
+
# tagging for 'interests' context.
|
45
|
+
# This creates #interests, #interests=, #interests_array, #interests_array= instance methods
|
46
|
+
taggable :interests
|
47
|
+
|
48
|
+
# tagging for 'skills' context.
|
49
|
+
# This creates #skills, #skills=, #skills_array, #skills_array= instance methods
|
50
|
+
# changing tag separator to "," (Default is " ")
|
51
|
+
taggable :skills, :separator => ','
|
52
|
+
end
|
53
|
+
|
54
|
+
Then in your form, for example:
|
55
|
+
|
56
|
+
<% form_for @post do |f| %>
|
57
|
+
<p>
|
58
|
+
<%= f.label :title %><br />
|
59
|
+
<%= f.text_field :title %>
|
60
|
+
</p>
|
61
|
+
<p>
|
62
|
+
<%= f.label :content %><br />
|
63
|
+
<%= f.text_area :content %>
|
64
|
+
</p>
|
65
|
+
<p>
|
66
|
+
<%= f.label :tags %><br />
|
67
|
+
<%= text_field_tag 'post[tags]' %>
|
68
|
+
</p>
|
69
|
+
<p>
|
70
|
+
<%= f.label :interests %><br />
|
71
|
+
<%= text_field_tag 'post[interests]' %>
|
72
|
+
</p>
|
73
|
+
<p>
|
74
|
+
<%= f.label :skills %><br />
|
75
|
+
<%= text_field_tag 'post[skills]' %>
|
76
|
+
</p>
|
77
|
+
<p>
|
78
|
+
<button type="submit">Send</button>
|
79
|
+
</p>
|
80
|
+
<% end %>
|
81
|
+
|
82
|
+
|
83
|
+
== Tag Aggregation with Counts
|
84
|
+
|
85
|
+
This lib can automatically create aggregate collections of contexts and their counts for you. Each aggregate collection is scoped to the document collection type and context.
|
86
|
+
|
87
|
+
Post.create!(:tags => "food,ant,bee")
|
88
|
+
Post.create!(:tags => "juice,food,bee,zip")
|
89
|
+
Post.create!(:tags => "honey,strip,food")
|
90
|
+
|
91
|
+
Post.tags # will retrieve ["ant", "bee", "food", "honey", "juice", "strip", "zip"]
|
92
|
+
Post.tags_with_weight # will retrieve:
|
93
|
+
# [
|
94
|
+
# ['ant', 1],
|
95
|
+
# ['bee', 2],
|
96
|
+
# ['food', 3],
|
97
|
+
# ['honey', 1],
|
98
|
+
# ['juice', 1],
|
99
|
+
# ['strip', 1],
|
100
|
+
# ['zip', 1]
|
101
|
+
# ]
|
102
|
+
|
103
|
+
== Changing default separator
|
104
|
+
|
105
|
+
To change the default separator you may pass a *separator* argument to the macro:
|
106
|
+
|
107
|
+
class Post
|
108
|
+
include Mongoid::Document
|
109
|
+
include Mongoid::Taggable
|
110
|
+
|
111
|
+
field :title
|
112
|
+
field :content
|
113
|
+
|
114
|
+
taggable :separator => ',' # tags will be delineated by comma instead of space
|
115
|
+
end
|
116
|
+
|
117
|
+
== Development and Testing
|
118
|
+
|
119
|
+
Mongoid Taggable With Context uses Bundler for easy installation of development dependent gems.
|
120
|
+
To get all dependent gems for development, while inside mongoid_taggable_with_context directory:
|
121
|
+
|
122
|
+
bundle install
|
123
|
+
|
124
|
+
|
125
|
+
Mongoid Taggable With Context uses RSpec for its test coverage.
|
126
|
+
Inside mongoid_taggable_with_context directory, you can run the specs with:
|
127
|
+
|
128
|
+
rake spec
|
129
|
+
|
130
|
+
To see the test coverage, you need to be on ruby 1.8.7 (since latest rcov doesn't work 1.9)
|
131
|
+
and run the following command:
|
132
|
+
|
133
|
+
rake rcov
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
gem.name = "mongoid_taggable_with_context"
|
15
|
+
gem.homepage = "http://github.com/aq1018/mongoid_taggable_with_context"
|
16
|
+
gem.license = "MIT"
|
17
|
+
gem.summary = %Q{Mongoid taggable behaviour}
|
18
|
+
gem.description = %Q{It provides some helpers to create taggable documents with context.}
|
19
|
+
gem.email = "aq1018@gmail.com"
|
20
|
+
gem.authors = ["Aaron Qian"]
|
21
|
+
gem.add_runtime_dependency 'mongoid', '~> 2.0.0.beta.20'
|
22
|
+
end
|
23
|
+
Jeweler::RubygemsDotOrgTasks.new
|
24
|
+
|
25
|
+
require 'rspec/core'
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
28
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
29
|
+
spec.rspec_opts = "--color --format progress"
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.rcov = true
|
35
|
+
spec.rcov_opts = "--exclude ~\/.rvm,spec"
|
36
|
+
end
|
37
|
+
|
38
|
+
require 'reek/rake/task'
|
39
|
+
Reek::Rake::Task.new do |t|
|
40
|
+
t.fail_on_error = true
|
41
|
+
t.verbose = false
|
42
|
+
t.source_files = 'lib/**/*.rb'
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'roodi'
|
46
|
+
require 'roodi_task'
|
47
|
+
RoodiTask.new do |t|
|
48
|
+
t.verbose = false
|
49
|
+
end
|
50
|
+
|
51
|
+
task :default => :spec
|
52
|
+
|
53
|
+
require 'yard'
|
54
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'mongoid/taggable_with_context'
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# Copyright (c) 2010 Wilker Lúcio <wilkerlucio@gmail.com>
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongoid::TaggableWithContext
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
|
18
|
+
included do
|
19
|
+
class_inheritable_reader :taggable_with_context_options
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# Macro to declare a document class as taggable, specify field name
|
24
|
+
# for tags, and set options for tagging behavior.
|
25
|
+
#
|
26
|
+
# @example Define a taggable document.
|
27
|
+
#
|
28
|
+
# class Article
|
29
|
+
# include Mongoid::Document
|
30
|
+
# include Mongoid::Taggable
|
31
|
+
# taggable :keywords, :separator => ' ', :aggregation => true, :default_type => "seo"
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# @param [ Symbol ] field The name of the field for tags.
|
35
|
+
# @param [ Hash ] options Options for taggable behavior.
|
36
|
+
#
|
37
|
+
# @option options [ String ] :separator The tag separator to
|
38
|
+
# convert from; defaults to ','
|
39
|
+
# @option options [ true, false ] :aggregation Whether or not to
|
40
|
+
# aggregate counts of tags within the document collection using
|
41
|
+
# map/reduce; defaults to false
|
42
|
+
# @option options [ String ] :default_type The default type of the tag.
|
43
|
+
# Each tag can optionally have a tag type. The default type is nil
|
44
|
+
def taggable(*args)
|
45
|
+
# init variables
|
46
|
+
options = args.extract_options!
|
47
|
+
tags_field = (args.blank? ? :tags : args.shift).to_sym
|
48
|
+
options.reverse_merge!(
|
49
|
+
:separator => ' ',
|
50
|
+
:array_field => "#{tags_field}_array".to_sym
|
51
|
+
)
|
52
|
+
tags_array_field = options[:array_field]
|
53
|
+
first_invoke = taggable_with_context_options.nil?
|
54
|
+
|
55
|
+
# register / update settings
|
56
|
+
class_options = taggable_with_context_options || {}
|
57
|
+
class_options[tags_field] = options
|
58
|
+
write_inheritable_attribute(:taggable_with_context_options, class_options)
|
59
|
+
|
60
|
+
# setup fields & indexes
|
61
|
+
field tags_field, :default => ""
|
62
|
+
field tags_array_field, :type => Array, :default => []
|
63
|
+
index tags_array_field
|
64
|
+
|
65
|
+
if first_invoke
|
66
|
+
delegate "convert_string_to_array", :to => 'self.class'
|
67
|
+
delegate "convert_array_to_string", :to => 'self.class'
|
68
|
+
delegate "get_tag_separator_for", :to => 'self.class'
|
69
|
+
delegate "tag_contexts", :to => 'self.class'
|
70
|
+
delegate "aggregation_collection_for", :to => 'self.class'
|
71
|
+
delegate "tag_options_for", :to => 'self.class'
|
72
|
+
|
73
|
+
set_callback :create, :after, :increment_tags_agregation
|
74
|
+
set_callback :save, :after, :update_tags_aggregation
|
75
|
+
set_callback :destroy, :after, :decrement_tags_aggregation
|
76
|
+
end
|
77
|
+
|
78
|
+
extend SingletonMethods
|
79
|
+
include InstanceMethods
|
80
|
+
|
81
|
+
# singleton methods
|
82
|
+
class_eval <<-END
|
83
|
+
class << self
|
84
|
+
def #{tags_field}_aggregation_collection
|
85
|
+
@#{tags_field}_aggregation_collection ||= aggregation_collection_for(:"#{tags_field}")
|
86
|
+
end
|
87
|
+
|
88
|
+
def #{tags_field}
|
89
|
+
tags_for(:"#{tags_field}")
|
90
|
+
end
|
91
|
+
|
92
|
+
def #{tags_field}_with_weight
|
93
|
+
tags_with_weight_for(:"#{tags_field}")
|
94
|
+
end
|
95
|
+
|
96
|
+
def #{tags_field}_separator
|
97
|
+
get_tag_separator_for(:"#{tags_field}")
|
98
|
+
end
|
99
|
+
|
100
|
+
def #{tags_field}_separator=(value)
|
101
|
+
set_tag_separator_for(:"#{tags_field}", value)
|
102
|
+
end
|
103
|
+
|
104
|
+
def #{tags_field}_tagged_with(tags)
|
105
|
+
tagged_with(:"#{tags_field}", tags)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
END
|
109
|
+
|
110
|
+
# instance methods
|
111
|
+
class_eval <<-END
|
112
|
+
def #{tags_field}=(s)
|
113
|
+
super
|
114
|
+
write_attribute(:#{tags_array_field}, convert_string_to_array(s, get_tag_separator_for(:"#{tags_field}")))
|
115
|
+
end
|
116
|
+
|
117
|
+
def #{tags_array_field}=(a)
|
118
|
+
super
|
119
|
+
write_attribute(:#{tags_field}, convert_array_to_string(a, get_tag_separator_for(:"#{tags_field}")))
|
120
|
+
end
|
121
|
+
END
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module SingletonMethods
|
126
|
+
def tag_contexts
|
127
|
+
taggable_with_context_options.keys
|
128
|
+
end
|
129
|
+
|
130
|
+
def tag_options_for(context)
|
131
|
+
taggable_with_context_options[context]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Collection name for storing results of tag count aggregation
|
135
|
+
def aggregation_collection_for(context)
|
136
|
+
"#{collection_name}_#{context}_aggregation"
|
137
|
+
end
|
138
|
+
|
139
|
+
def tags_for(context, conditions={})
|
140
|
+
conditions = {:sort => '_id'}.merge(conditions)
|
141
|
+
db.collection(aggregation_collection_for(context)).find({:value => {"$gt" => 0 }}, conditions).to_a.map{ |t| t["_id"] }
|
142
|
+
end
|
143
|
+
|
144
|
+
# retrieve the list of tag with weight(count), this is useful for
|
145
|
+
# creating tag clouds
|
146
|
+
def tags_with_weight_for(context, conditions={})
|
147
|
+
conditions = {:sort => '_id'}.merge(conditions)
|
148
|
+
db.collection(aggregation_collection_for(context)).find({:value => {"$gt" => 0 }}, conditions).to_a.map{ |t| [t["_id"], t["value"]] }
|
149
|
+
end
|
150
|
+
|
151
|
+
def get_tag_separator_for(context)
|
152
|
+
taggable_with_context_options[context][:separator]
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_tag_separator_for(context, value)
|
156
|
+
taggable_with_context_options[context][:separator] = value.nil? ? " " : value.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
# Find documents tagged with all tags passed as a parameter, given
|
160
|
+
# as an Array or a String using the configured separator.
|
161
|
+
#
|
162
|
+
# @example Find matching all tags in an Array.
|
163
|
+
# Article.tagged_with(['ruby', 'mongodb'])
|
164
|
+
# @example Find matching all tags in a String.
|
165
|
+
# Article.tagged_with('ruby, mongodb')
|
166
|
+
#
|
167
|
+
# @param [ String ] :field The field name of the tag.
|
168
|
+
# @param [ Array<String, Symbol>, String ] :tags Tags to match.
|
169
|
+
# @return [ Criteria ] A new criteria.
|
170
|
+
def tagged_with(context, tags)
|
171
|
+
tags = convert_string_to_array(tags, get_tag_separator_for(context)) if tags.is_a? String
|
172
|
+
array_field = tag_options_for(context)[:array_field]
|
173
|
+
all_in(array_field => tags)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Helper method to convert a String to an Array based on the
|
177
|
+
# configured tag separator.
|
178
|
+
def convert_string_to_array(str = "", seperator = " ")
|
179
|
+
str.split(seperator).map(&:strip).uniq.compact
|
180
|
+
end
|
181
|
+
|
182
|
+
def convert_array_to_string(ary = [], seperator = " ")
|
183
|
+
ary.uniq.compact.join(seperator)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
module InstanceMethods
|
188
|
+
def need_update_tags_aggregation?
|
189
|
+
!changed_contexts.empty?
|
190
|
+
end
|
191
|
+
|
192
|
+
def changed_contexts
|
193
|
+
tag_contexts & previous_changes.keys.map(&:to_sym)
|
194
|
+
end
|
195
|
+
|
196
|
+
def increment_tags_agregation
|
197
|
+
# if document is created by using MyDocument.new
|
198
|
+
# and attributes are individually assigned
|
199
|
+
# #previous_changes won't be empty and aggregation
|
200
|
+
# is updated in after_save, so we simply skip it.
|
201
|
+
return unless previous_changes.empty?
|
202
|
+
|
203
|
+
# if the document is created by using MyDocument.create(:tags => "tag1 tag2")
|
204
|
+
# #previous_changes hash is empty and we have to update aggregation here
|
205
|
+
tag_contexts.each do |context|
|
206
|
+
coll = self.class.db.collection(self.class.aggregation_collection_for(context))
|
207
|
+
field_name = self.class.tag_options_for(context)[:array_field]
|
208
|
+
tags = self.send field_name || []
|
209
|
+
tags.each do |t|
|
210
|
+
coll.update({:_id => t}, {'$inc' => {:value => 1}}, :upsert => true)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def decrement_tags_aggregation
|
216
|
+
tag_contexts.each do |context|
|
217
|
+
coll = self.class.db.collection(self.class.aggregation_collection_for(context))
|
218
|
+
field_name = self.class.tag_options_for(context)[:array_field]
|
219
|
+
tags = self.send field_name || []
|
220
|
+
tags.each do |t|
|
221
|
+
coll.update({:_id => t}, {'$inc' => {:value => -1}}, :upsert => true)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def update_tags_aggregation
|
227
|
+
return unless need_update_tags_aggregation?
|
228
|
+
|
229
|
+
changed_contexts.each do |context|
|
230
|
+
coll = self.class.db.collection(self.class.aggregation_collection_for(context))
|
231
|
+
field_name = self.class.tag_options_for(context)[:array_field]
|
232
|
+
old_tags, new_tags = previous_changes["#{field_name}"]
|
233
|
+
old_tags ||= []
|
234
|
+
new_tags ||= []
|
235
|
+
unchanged_tags = old_tags & new_tags
|
236
|
+
tags_removed = old_tags - unchanged_tags
|
237
|
+
tags_added = new_tags - unchanged_tags
|
238
|
+
|
239
|
+
tags_removed.each do |t|
|
240
|
+
coll.update({:_id => t}, {'$inc' => {:value => -1}}, :upsert => true)
|
241
|
+
end
|
242
|
+
|
243
|
+
tags_added.each do |t|
|
244
|
+
coll.update({:_id => t}, {'$inc' => {:value => 1}}, :upsert => true)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{mongoid_taggable_with_context}
|
8
|
+
s.version = "0.5.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Aaron Qian"]
|
12
|
+
s.date = %q{2011-02-12}
|
13
|
+
s.description = %q{It provides some helpers to create taggable documents with context.}
|
14
|
+
s.email = %q{aq1018@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"init.rb",
|
28
|
+
"lib/mongoid_taggable_with_context.rb",
|
29
|
+
"mongoid_taggable_with_context.gemspec",
|
30
|
+
"spec/mongoid_taggable_with_context_spec.rb",
|
31
|
+
"spec/spec_helper.rb"
|
32
|
+
]
|
33
|
+
s.homepage = %q{http://github.com/aq1018/mongoid_taggable_with_context}
|
34
|
+
s.licenses = ["MIT"]
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
s.rubygems_version = %q{1.5.2}
|
37
|
+
s.summary = %q{Mongoid taggable behaviour}
|
38
|
+
s.test_files = [
|
39
|
+
"spec/mongoid_taggable_with_context_spec.rb",
|
40
|
+
"spec/spec_helper.rb"
|
41
|
+
]
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<mongoid>, ["~> 2.0.0.beta.20"])
|
48
|
+
s.add_development_dependency(%q<database_cleaner>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<bson>, ["~> 1.2.1"])
|
50
|
+
s.add_development_dependency(%q<bson_ext>, ["~> 1.2.1"])
|
51
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
52
|
+
s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
|
53
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
54
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
55
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
|
57
|
+
s.add_development_dependency(%q<roodi>, ["~> 2.1.0"])
|
58
|
+
s.add_runtime_dependency(%q<mongoid>, ["~> 2.0.0.beta.20"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<mongoid>, ["~> 2.0.0.beta.20"])
|
61
|
+
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
62
|
+
s.add_dependency(%q<bson>, ["~> 1.2.1"])
|
63
|
+
s.add_dependency(%q<bson_ext>, ["~> 1.2.1"])
|
64
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
65
|
+
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
66
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
67
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
68
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
69
|
+
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
70
|
+
s.add_dependency(%q<roodi>, ["~> 2.1.0"])
|
71
|
+
s.add_dependency(%q<mongoid>, ["~> 2.0.0.beta.20"])
|
72
|
+
end
|
73
|
+
else
|
74
|
+
s.add_dependency(%q<mongoid>, ["~> 2.0.0.beta.20"])
|
75
|
+
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
76
|
+
s.add_dependency(%q<bson>, ["~> 1.2.1"])
|
77
|
+
s.add_dependency(%q<bson_ext>, ["~> 1.2.1"])
|
78
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
79
|
+
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
80
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
81
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
82
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
83
|
+
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
84
|
+
s.add_dependency(%q<roodi>, ["~> 2.1.0"])
|
85
|
+
s.add_dependency(%q<mongoid>, ["~> 2.0.0.beta.20"])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class MyModel
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongoid::TaggableWithContext
|
6
|
+
taggable
|
7
|
+
taggable :artists
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Mongoid::TaggableWithContext do
|
11
|
+
context "saving tags from plain text" do
|
12
|
+
before :each do
|
13
|
+
@m = MyModel.new
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should set tags array from string" do
|
17
|
+
@m.tags = "some new tag"
|
18
|
+
@m.tags_array.should == %w[some new tag]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should set artists array from string" do
|
22
|
+
@m.artists = "some new tag"
|
23
|
+
@m.artists_array.should == %w[some new tag]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should retrieve tags string from array" do
|
27
|
+
@m.tags_array = %w[some new tags]
|
28
|
+
@m.tags.should == "some new tags"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should retrieve artists string from array" do
|
32
|
+
@m.artists_array = %w[some new tags]
|
33
|
+
@m.artists.should == "some new tags"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should strip tags before put in array" do
|
37
|
+
@m.tags = "now with some spaces in places "
|
38
|
+
@m.tags_array.should == %w[now with some spaces in places]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should remove repeated tags from string" do
|
42
|
+
@m.tags = "some new tags some new tags"
|
43
|
+
@m.tags_array.should == %w[some new tags]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should remove repeated tags from array" do
|
47
|
+
@m.tags_array = %w[some new tags some new tags]
|
48
|
+
@m.tags.should == "some new tags"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should remove nil tags from array" do
|
52
|
+
@m.tags_array = ["some", nil, "new", nil, "tags"]
|
53
|
+
@m.tags.should == "some new tags"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "changing separator" do
|
58
|
+
before :all do
|
59
|
+
MyModel.tags_separator = ";"
|
60
|
+
end
|
61
|
+
|
62
|
+
after :all do
|
63
|
+
MyModel.tags_separator = " "
|
64
|
+
end
|
65
|
+
|
66
|
+
before :each do
|
67
|
+
@m = MyModel.new
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should split with custom separator" do
|
71
|
+
@m.tags = "some;other;separator"
|
72
|
+
@m.tags_array.should == %w[some other separator]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should join with custom separator" do
|
76
|
+
@m.tags_array = %w[some other sep]
|
77
|
+
@m.tags.should == "some;other;sep"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "indexing tags" do
|
82
|
+
it "should generate the tags aggregation collection name correctly" do
|
83
|
+
MyModel.tags_aggregation_collection.should == "my_models_tags_aggregation"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should generate the artists aggregation collection name correctly" do
|
87
|
+
MyModel.artists_aggregation_collection.should == "my_models_artists_aggregation"
|
88
|
+
end
|
89
|
+
|
90
|
+
context "retriving index" do
|
91
|
+
context "on create directly" do
|
92
|
+
before :each do
|
93
|
+
MyModel.create!(:tags => "food ant bee", :artists => "jeff greg mandy aaron andy")
|
94
|
+
MyModel.create!(:tags => "juice food bee zip", :artists => "grant andrew andy")
|
95
|
+
MyModel.create!(:tags => "honey strip food", :artists => "mandy aaron andy")
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should retrieve the list of all saved tags distinct and ordered" do
|
99
|
+
MyModel.tags.should == %w[ant bee food honey juice strip zip]
|
100
|
+
MyModel.artists.should == %w[aaron andrew andy grant greg jeff mandy]
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should retrieve a list of tags with weight" do
|
104
|
+
MyModel.tags_with_weight.should == [
|
105
|
+
['ant', 1],
|
106
|
+
['bee', 2],
|
107
|
+
['food', 3],
|
108
|
+
['honey', 1],
|
109
|
+
['juice', 1],
|
110
|
+
['strip', 1],
|
111
|
+
['zip', 1]
|
112
|
+
]
|
113
|
+
|
114
|
+
MyModel.artists_with_weight.should == [
|
115
|
+
['aaron', 2],
|
116
|
+
['andrew', 1],
|
117
|
+
['andy', 3],
|
118
|
+
['grant', 1],
|
119
|
+
['greg', 1],
|
120
|
+
['jeff', 1],
|
121
|
+
['mandy', 2]
|
122
|
+
]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "on new then change attributes directly" do
|
127
|
+
before :each do
|
128
|
+
m = MyModel.new
|
129
|
+
m.tags = "food ant bee"
|
130
|
+
m.artists = "jeff greg mandy aaron andy"
|
131
|
+
m.save!
|
132
|
+
|
133
|
+
m = MyModel.new
|
134
|
+
m.tags = "juice food bee zip"
|
135
|
+
m.artists = "grant andrew andy"
|
136
|
+
m.save!
|
137
|
+
|
138
|
+
m = MyModel.new
|
139
|
+
m.tags = "honey strip food"
|
140
|
+
m.artists = "mandy aaron andy"
|
141
|
+
m.save!
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should retrieve the list of all saved tags distinct and ordered" do
|
145
|
+
MyModel.tags.should == %w[ant bee food honey juice strip zip]
|
146
|
+
MyModel.artists.should == %w[aaron andrew andy grant greg jeff mandy]
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should retrieve a list of tags with weight" do
|
150
|
+
MyModel.tags_with_weight.should == [
|
151
|
+
['ant', 1],
|
152
|
+
['bee', 2],
|
153
|
+
['food', 3],
|
154
|
+
['honey', 1],
|
155
|
+
['juice', 1],
|
156
|
+
['strip', 1],
|
157
|
+
['zip', 1]
|
158
|
+
]
|
159
|
+
|
160
|
+
MyModel.artists_with_weight.should == [
|
161
|
+
['aaron', 2],
|
162
|
+
['andrew', 1],
|
163
|
+
['andy', 3],
|
164
|
+
['grant', 1],
|
165
|
+
['greg', 1],
|
166
|
+
['jeff', 1],
|
167
|
+
['mandy', 2]
|
168
|
+
]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "on create then update" do
|
173
|
+
before :each do
|
174
|
+
m1 = MyModel.create!(:tags => "food ant bee", :artists => "jeff greg mandy aaron andy")
|
175
|
+
m2 = MyModel.create!(:tags => "juice food bee zip", :artists => "grant andrew andy")
|
176
|
+
m3 = MyModel.create!(:tags => "honey strip food", :artists => "mandy aaron andy")
|
177
|
+
|
178
|
+
m1.tags_array = m1.tags_array + %w[honey strip shoe]
|
179
|
+
m1.save!
|
180
|
+
|
181
|
+
m3.artists_array = m3.artists_array + %w[grant greg gory]
|
182
|
+
m3.save!
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should retrieve the list of all saved tags distinct and ordered" do
|
186
|
+
MyModel.tags.should == %w[ant bee food honey juice shoe strip zip]
|
187
|
+
MyModel.artists.should == %w[aaron andrew andy gory grant greg jeff mandy]
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should retrieve a list of tags with weight" do
|
191
|
+
MyModel.tags_with_weight.should == [
|
192
|
+
['ant', 1],
|
193
|
+
['bee', 2],
|
194
|
+
['food', 3],
|
195
|
+
['honey', 2],
|
196
|
+
['juice', 1],
|
197
|
+
['shoe', 1],
|
198
|
+
['strip', 2],
|
199
|
+
['zip', 1]
|
200
|
+
]
|
201
|
+
|
202
|
+
MyModel.artists_with_weight.should == [
|
203
|
+
['aaron', 2],
|
204
|
+
['andrew', 1],
|
205
|
+
['andy', 3],
|
206
|
+
['gory', 1],
|
207
|
+
['grant', 2],
|
208
|
+
['greg', 2],
|
209
|
+
['jeff', 1],
|
210
|
+
['mandy', 2]
|
211
|
+
]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context "on create, update, then destroy" do
|
216
|
+
before :each do
|
217
|
+
m1 = MyModel.create!(:tags => "food ant bee", :artists => "jeff greg mandy aaron andy")
|
218
|
+
m2 = MyModel.create!(:tags => "juice food bee zip", :artists => "grant andrew andy")
|
219
|
+
m3 = MyModel.create!(:tags => "honey strip food", :artists => "mandy aaron andy")
|
220
|
+
|
221
|
+
m1.tags_array = m1.tags_array + %w[honey strip shoe] - %w[food]
|
222
|
+
m1.save!
|
223
|
+
|
224
|
+
m3.artists_array = m3.artists_array + %w[grant greg gory] - %w[andy]
|
225
|
+
m3.save!
|
226
|
+
|
227
|
+
m2.destroy
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should retrieve the list of all saved tags distinct and ordered" do
|
231
|
+
MyModel.tags.should == %w[ant bee food honey shoe strip]
|
232
|
+
MyModel.artists.should == %w[aaron andy gory grant greg jeff mandy]
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should retrieve a list of tags with weight" do
|
236
|
+
MyModel.tags_with_weight.should == [
|
237
|
+
['ant', 1],
|
238
|
+
['bee', 1],
|
239
|
+
['food', 1],
|
240
|
+
['honey', 2],
|
241
|
+
['shoe', 1],
|
242
|
+
['strip', 2]
|
243
|
+
]
|
244
|
+
|
245
|
+
MyModel.artists_with_weight.should == [
|
246
|
+
['aaron', 2],
|
247
|
+
['andy', 1],
|
248
|
+
['gory', 1],
|
249
|
+
['grant', 1],
|
250
|
+
['greg', 2],
|
251
|
+
['jeff', 1],
|
252
|
+
['mandy', 2]
|
253
|
+
]
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
context "tagged_with" do
|
260
|
+
before :each do
|
261
|
+
@m1 = MyModel.create!(:tags => "food ant bee", :artists => "jeff greg mandy aaron andy")
|
262
|
+
@m2 = MyModel.create!(:tags => "juice food bee zip", :artists => "grant andrew andy")
|
263
|
+
@m3 = MyModel.create!(:tags => "honey strip food", :artists => "mandy aaron andy")
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should retrieve a list of documents" do
|
267
|
+
(MyModel.tags_tagged_with("food").to_a - [@m1, @m2, @m3]).should be_empty
|
268
|
+
(MyModel.artists_tagged_with("aaron").to_a - [@m1, @m3]).should be_empty
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'mongoid'
|
6
|
+
require 'mongoid_taggable_with_context.rb'
|
7
|
+
require 'database_cleaner'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.before(:suite) do
|
11
|
+
DatabaseCleaner.strategy = :truncation
|
12
|
+
end
|
13
|
+
|
14
|
+
config.after(:each) do
|
15
|
+
DatabaseCleaner.clean
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Mongoid.configure do |config|
|
20
|
+
config.master = Mongo::Connection.new.db("mongoid_taggable_with_context_test")
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongoid_taggable_with_context
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 11
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Aaron Qian
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-12 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 62196427
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
- beta
|
34
|
+
- 20
|
35
|
+
version: 2.0.0.beta.20
|
36
|
+
name: mongoid
|
37
|
+
version_requirements: *id001
|
38
|
+
prerelease: false
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
type: :development
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
name: database_cleaner
|
51
|
+
version_requirements: *id002
|
52
|
+
prerelease: false
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
type: :development
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 29
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 2
|
64
|
+
- 1
|
65
|
+
version: 1.2.1
|
66
|
+
name: bson
|
67
|
+
version_requirements: *id003
|
68
|
+
prerelease: false
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
type: :development
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 29
|
77
|
+
segments:
|
78
|
+
- 1
|
79
|
+
- 2
|
80
|
+
- 1
|
81
|
+
version: 1.2.1
|
82
|
+
name: bson_ext
|
83
|
+
version_requirements: *id004
|
84
|
+
prerelease: false
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
type: :development
|
87
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 2
|
95
|
+
- 3
|
96
|
+
- 0
|
97
|
+
version: 2.3.0
|
98
|
+
name: rspec
|
99
|
+
version_requirements: *id005
|
100
|
+
prerelease: false
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
type: :development
|
103
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ~>
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 7
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
- 6
|
112
|
+
- 0
|
113
|
+
version: 0.6.0
|
114
|
+
name: yard
|
115
|
+
version_requirements: *id006
|
116
|
+
prerelease: false
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
type: :development
|
119
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
hash: 23
|
125
|
+
segments:
|
126
|
+
- 1
|
127
|
+
- 0
|
128
|
+
- 0
|
129
|
+
version: 1.0.0
|
130
|
+
name: bundler
|
131
|
+
version_requirements: *id007
|
132
|
+
prerelease: false
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
type: :development
|
135
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ~>
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
hash: 7
|
141
|
+
segments:
|
142
|
+
- 1
|
143
|
+
- 5
|
144
|
+
- 2
|
145
|
+
version: 1.5.2
|
146
|
+
name: jeweler
|
147
|
+
version_requirements: *id008
|
148
|
+
prerelease: false
|
149
|
+
- !ruby/object:Gem::Dependency
|
150
|
+
type: :development
|
151
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
hash: 3
|
157
|
+
segments:
|
158
|
+
- 0
|
159
|
+
version: "0"
|
160
|
+
name: rcov
|
161
|
+
version_requirements: *id009
|
162
|
+
prerelease: false
|
163
|
+
- !ruby/object:Gem::Dependency
|
164
|
+
type: :development
|
165
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
166
|
+
none: false
|
167
|
+
requirements:
|
168
|
+
- - ~>
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
hash: 15
|
171
|
+
segments:
|
172
|
+
- 1
|
173
|
+
- 2
|
174
|
+
- 8
|
175
|
+
version: 1.2.8
|
176
|
+
name: reek
|
177
|
+
version_requirements: *id010
|
178
|
+
prerelease: false
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
type: :development
|
181
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
182
|
+
none: false
|
183
|
+
requirements:
|
184
|
+
- - ~>
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
hash: 11
|
187
|
+
segments:
|
188
|
+
- 2
|
189
|
+
- 1
|
190
|
+
- 0
|
191
|
+
version: 2.1.0
|
192
|
+
name: roodi
|
193
|
+
version_requirements: *id011
|
194
|
+
prerelease: false
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
type: :runtime
|
197
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
198
|
+
none: false
|
199
|
+
requirements:
|
200
|
+
- - ~>
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
hash: 62196427
|
203
|
+
segments:
|
204
|
+
- 2
|
205
|
+
- 0
|
206
|
+
- 0
|
207
|
+
- beta
|
208
|
+
- 20
|
209
|
+
version: 2.0.0.beta.20
|
210
|
+
name: mongoid
|
211
|
+
version_requirements: *id012
|
212
|
+
prerelease: false
|
213
|
+
description: It provides some helpers to create taggable documents with context.
|
214
|
+
email: aq1018@gmail.com
|
215
|
+
executables: []
|
216
|
+
|
217
|
+
extensions: []
|
218
|
+
|
219
|
+
extra_rdoc_files:
|
220
|
+
- LICENSE.txt
|
221
|
+
- README.rdoc
|
222
|
+
files:
|
223
|
+
- .document
|
224
|
+
- Gemfile
|
225
|
+
- Gemfile.lock
|
226
|
+
- LICENSE.txt
|
227
|
+
- README.rdoc
|
228
|
+
- Rakefile
|
229
|
+
- VERSION
|
230
|
+
- init.rb
|
231
|
+
- lib/mongoid_taggable_with_context.rb
|
232
|
+
- mongoid_taggable_with_context.gemspec
|
233
|
+
- spec/mongoid_taggable_with_context_spec.rb
|
234
|
+
- spec/spec_helper.rb
|
235
|
+
has_rdoc: true
|
236
|
+
homepage: http://github.com/aq1018/mongoid_taggable_with_context
|
237
|
+
licenses:
|
238
|
+
- MIT
|
239
|
+
post_install_message:
|
240
|
+
rdoc_options: []
|
241
|
+
|
242
|
+
require_paths:
|
243
|
+
- lib
|
244
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
245
|
+
none: false
|
246
|
+
requirements:
|
247
|
+
- - ">="
|
248
|
+
- !ruby/object:Gem::Version
|
249
|
+
hash: 3
|
250
|
+
segments:
|
251
|
+
- 0
|
252
|
+
version: "0"
|
253
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
254
|
+
none: false
|
255
|
+
requirements:
|
256
|
+
- - ">="
|
257
|
+
- !ruby/object:Gem::Version
|
258
|
+
hash: 3
|
259
|
+
segments:
|
260
|
+
- 0
|
261
|
+
version: "0"
|
262
|
+
requirements: []
|
263
|
+
|
264
|
+
rubyforge_project:
|
265
|
+
rubygems_version: 1.5.2
|
266
|
+
signing_key:
|
267
|
+
specification_version: 3
|
268
|
+
summary: Mongoid taggable behaviour
|
269
|
+
test_files:
|
270
|
+
- spec/mongoid_taggable_with_context_spec.rb
|
271
|
+
- spec/spec_helper.rb
|