glebtv_mongoid_taggable_with_context 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +16 -0
- data/.travis.yml +7 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +20 -0
- data/README.md +223 -0
- data/Rakefile +1 -0
- data/glebtv_mongoid_taggable_with_context.gemspec +27 -0
- data/init.rb +1 -0
- data/lib/mongoid/taggable_with_context.rb +321 -0
- data/lib/mongoid/taggable_with_context/aggregation_strategy/map_reduce.rb +71 -0
- data/lib/mongoid/taggable_with_context/aggregation_strategy/real_time.rb +114 -0
- data/lib/mongoid/taggable_with_context/aggregation_strategy/real_time_group_by.rb +52 -0
- data/lib/mongoid/taggable_with_context/deprecations.rb +26 -0
- data/lib/mongoid/taggable_with_context/version.rb +6 -0
- data/lib/mongoid_taggable_with_context.rb +8 -0
- data/spec/mongoid_taggable_with_context_spec.rb +469 -0
- data/spec/spec_helper.rb +18 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9e5d9045dcccb4e3ecbd7967e81ef954ec970a8d2e35e48f201b512f79a3a117
|
4
|
+
data.tar.gz: 596291220d50a02651f0a12ba148d89536c7e2cd951b90a7005fa6653d402253
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 44048b42dcb273e5ba92dc2c4cca427670e1e18c90b0a335bbc16ada5129dd6a815fe2e8d4d58425242ee943f0164781eec65ff217638009dc42e04bbd03c8e8
|
7
|
+
data.tar.gz: a675d4701c60ad10d09947bebbfe99bacbecb9dde36c995406fdf7f2d8ca188cd384b16ad887d6db72735f42afa79c935a8030aeff58ec179ea7f8830c90dd4b
|
data/.document
ADDED
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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.md
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
mongoid_taggable_with_context
|
2
|
+
=============================
|
3
|
+
|
4
|
+
[![Build Status](https://secure.travis-ci.org/lgs/mongoid_taggable_with_context.png?branch=master)](http://travis-ci.org/lgs/mongoid_taggable_with_context) [![Dependency Status](https://gemnasium.com/lgs/mongoid_taggable_with_context.png?travis)](https://gemnasium.com/lgs/mongoid_taggable_with_context)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/lgs/mongoid_taggable_with_context.png)](https://codeclimate.com/github/lgs/mongoid_taggable_with_context)
|
6
|
+
|
7
|
+
A tagging lib for Mongoid that allows for custom tagging along dynamic contexts. This gem was originally based on [mongoid_taggable](https://github.com/ches/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.
|
8
|
+
|
9
|
+
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`.
|
10
|
+
|
11
|
+
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.
|
12
|
+
|
13
|
+
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.
|
14
|
+
|
15
|
+
`mongoid_taggable_with_context` also provides flexibility on aggregation strategy. In addition to the map-reduce strategy, this gem also comes with real-time strategy. By using real-time strategy, your document can 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.
|
16
|
+
|
17
|
+
Installation
|
18
|
+
------------
|
19
|
+
|
20
|
+
You can simply install from rubygems:
|
21
|
+
|
22
|
+
```
|
23
|
+
gem install mongoid_taggable_with_context
|
24
|
+
```
|
25
|
+
|
26
|
+
or in Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'mongoid_taggable_with_context'
|
30
|
+
```
|
31
|
+
|
32
|
+
|
33
|
+
The "taggable" Macro Function
|
34
|
+
-----------------------------
|
35
|
+
|
36
|
+
Use the `taggable` macro function in your model to
|
37
|
+
declare a tags field. Specify `field name`
|
38
|
+
for tags, and `options` for tagging behavior.
|
39
|
+
|
40
|
+
Example:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class Article
|
44
|
+
include Mongoid::Document
|
45
|
+
include Mongoid::TaggableWithContext
|
46
|
+
taggable :keywords, separator: ' ', default: ['foobar']
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
* `@param [ Symbol ] field`
|
51
|
+
|
52
|
+
(Optional) The name of the field for tags. Defaults to "tags"
|
53
|
+
|
54
|
+
|
55
|
+
* `@param [ Hash ] options`
|
56
|
+
|
57
|
+
(Optional) Options for taggable behavior.
|
58
|
+
|
59
|
+
|
60
|
+
* `@option [ String ] :separator`
|
61
|
+
|
62
|
+
The delimiter used when converting the tags to and from String format. Defaults to " "
|
63
|
+
|
64
|
+
|
65
|
+
* `@option [ :Symbol ] :group_by_field`
|
66
|
+
|
67
|
+
The Mongoid field to group by when RealTimeGroupBy aggregation is used.
|
68
|
+
|
69
|
+
|
70
|
+
* `@option [ <various> ] :default, :as, :localize, etc.`
|
71
|
+
|
72
|
+
Options for Mongoid #field method will be automatically passed
|
73
|
+
to the underlying Array field (with the exception of `:type`,
|
74
|
+
which is coerced to `Array`).
|
75
|
+
|
76
|
+
|
77
|
+
Example Usage
|
78
|
+
-------------
|
79
|
+
|
80
|
+
To make a document taggable you need to include Mongoid::TaggableOnContext
|
81
|
+
into your document and call the *taggable* macro with optional arguments:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
class Post
|
85
|
+
include Mongoid::Document
|
86
|
+
include Mongoid::TaggableWithContext
|
87
|
+
|
88
|
+
field :title
|
89
|
+
field :content
|
90
|
+
|
91
|
+
# default context is 'tags'.
|
92
|
+
# This creates #tags, #tags=, #tag_string instance methods
|
93
|
+
# separator is " " by default
|
94
|
+
# #tags method returns an array of tags
|
95
|
+
# #tags= methods accepts an array of tags or a separated string
|
96
|
+
# #tag_string method returns a separated string
|
97
|
+
taggable
|
98
|
+
|
99
|
+
# tagging for 'skills' context.
|
100
|
+
# This creates #skills, #skills=, #skills_string instance methods
|
101
|
+
# changing tag separator to "," (Default is " ")
|
102
|
+
taggable :skills, separator: ','
|
103
|
+
|
104
|
+
# aliased context tagging.
|
105
|
+
# This creates #interests, #interests=, #interests_string instance methods
|
106
|
+
# The tags will be stored in a database field called 'ints'
|
107
|
+
taggable :ints, as: :interests
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
Then in your form, for example:
|
112
|
+
|
113
|
+
```rhtml
|
114
|
+
<% form_for @post do |f| %>
|
115
|
+
<p>
|
116
|
+
<%= f.label :title %><br />
|
117
|
+
<%= f.text_field :title %>
|
118
|
+
</p>
|
119
|
+
<p>
|
120
|
+
<%= f.label :content %><br />
|
121
|
+
<%= f.text_area :content %>
|
122
|
+
</p>
|
123
|
+
<p>
|
124
|
+
<%= f.label :tags %><br />
|
125
|
+
<%= text_field_tag 'post[tags]' %>
|
126
|
+
</p>
|
127
|
+
<p>
|
128
|
+
<%= f.label :interests %><br />
|
129
|
+
<%= text_field_tag 'post[interests]' %>
|
130
|
+
</p>
|
131
|
+
<p>
|
132
|
+
<%= f.label :skills %><br />
|
133
|
+
<%= text_field_tag 'post[skills]' %>
|
134
|
+
</p>
|
135
|
+
<p>
|
136
|
+
<button type="submit">Send</button>
|
137
|
+
</p>
|
138
|
+
<% end %>
|
139
|
+
```
|
140
|
+
|
141
|
+
|
142
|
+
Aggregation Strategies
|
143
|
+
----------------------
|
144
|
+
|
145
|
+
By including an aggregation strategy in your document, tag aggregations will be automatically available to you.
|
146
|
+
This lib presents the following aggregation strategies:
|
147
|
+
|
148
|
+
* MapReduce
|
149
|
+
* RealTime
|
150
|
+
* RealTimeGroupBy
|
151
|
+
|
152
|
+
The following document will automatically aggregate counts on all tag contexts.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class Post
|
156
|
+
include Mongoid::Document
|
157
|
+
include Mongoid::TaggableWithContext
|
158
|
+
|
159
|
+
# automatically adds real time aggregations to all tag contexts
|
160
|
+
include Mongoid::TaggableWithContext::AggregationStrategy::RealTime
|
161
|
+
|
162
|
+
# alternatively for map-reduce
|
163
|
+
# include Mongoid::TaggableWithContext::AggregationStrategy::MapReduce
|
164
|
+
|
165
|
+
field :title
|
166
|
+
field :content
|
167
|
+
|
168
|
+
taggable
|
169
|
+
taggable :skills, separator: ','
|
170
|
+
taggable :ints, as: interests
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
When you include an aggregation strategy, your document also gains a few extra methods to retrieve aggregation data.
|
175
|
+
In the case of previous example the following methods are included:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
Post.tags
|
179
|
+
Post.tags_with_weight
|
180
|
+
Post.interests
|
181
|
+
Post.interests_with_weight
|
182
|
+
Post.skills
|
183
|
+
Post.skills_with_weight
|
184
|
+
```
|
185
|
+
|
186
|
+
Here is how to use these methods in more detail:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
Post.create!(tags: "food,ant,bee")
|
190
|
+
Post.create!(tags: "juice,food,bee,zip")
|
191
|
+
Post.create!(tags: "honey,strip,food")
|
192
|
+
|
193
|
+
Post.tags # will retrieve ["ant", "bee", "food", "honey", "juice", "strip", "zip"]
|
194
|
+
Post.tags_with_weight # will retrieve:
|
195
|
+
# [
|
196
|
+
# ['ant', 1],
|
197
|
+
# ['bee', 2],
|
198
|
+
# ['food', 3],
|
199
|
+
# ['honey', 1],
|
200
|
+
# ['juice', 1],
|
201
|
+
# ['strip', 1],
|
202
|
+
# ['zip', 1]
|
203
|
+
# ]
|
204
|
+
```
|
205
|
+
|
206
|
+
|
207
|
+
Contributing to mongoid_taggable_with_context
|
208
|
+
-----------------------------------------------
|
209
|
+
|
210
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
211
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
212
|
+
* Fork the project
|
213
|
+
* Start a feature/bugfix branch
|
214
|
+
* Commit and push until you are happy with your contribution
|
215
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
216
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
217
|
+
|
218
|
+
|
219
|
+
Copyright
|
220
|
+
---------
|
221
|
+
|
222
|
+
Copyright (c) 2011 Aaron Qian. See LICENSE.txt for
|
223
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mongoid/taggable_with_context/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "glebtv_mongoid_taggable_with_context"
|
7
|
+
spec.version = Mongoid::TaggableWithContext::VERSION.dup
|
8
|
+
|
9
|
+
spec.homepage = "http://github.com/glebtv/mongoid_taggable_with_context"
|
10
|
+
spec.licenses = ["MIT"]
|
11
|
+
spec.summary = "Mongoid taggable behaviour"
|
12
|
+
spec.authors = ["Aaron Qian", "Luca G. Soave", "John Shields", "Wilker Lucio", "Ches Martin", "glebtv"]
|
13
|
+
|
14
|
+
spec.description = "Add multiple tag fields on Mongoid documents with aggregation capability."
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/).reject {|f| f.start_with?('mongoid') || f.start_with?('activerecord') }
|
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_runtime_dependency(%q<mongoid>, "~> 7.0.0")
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
spec.add_development_dependency(%q<rspec>)
|
26
|
+
end
|
27
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'mongoid_taggable_with_context'
|
@@ -0,0 +1,321 @@
|
|
1
|
+
module Mongoid::TaggableWithContext
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class AggregationStrategyMissing < Exception; end
|
5
|
+
class InvalidTagsFormat < Exception; end
|
6
|
+
|
7
|
+
DEFAULT_FIELD = :tags
|
8
|
+
DEFAULT_SEPARATOR = ' '
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute :taggable_with_context_options
|
12
|
+
self.taggable_with_context_options = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def tag_string_for(context)
|
16
|
+
self.read_attribute(context).join(self.class.get_tag_separator_for(context)) rescue ""
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_tag_string_for(context, value)
|
20
|
+
self.write_attribute(context, self.class.format_tags_for(context, value))
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
# Macro to declare a document class as taggable, specify field name
|
25
|
+
# for tags, and set options for tagging behavior.
|
26
|
+
#
|
27
|
+
# @example Define a taggable document.
|
28
|
+
#
|
29
|
+
# class Article
|
30
|
+
# include Mongoid::Document
|
31
|
+
# include Mongoid::TaggableWithContext
|
32
|
+
# taggable :keywords, separator: ' ', default: ['foobar']
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# @param [ Symbol ] field The name of the field for tags. Defaults to :tags if not specified.
|
36
|
+
# @param [ Hash ] options Options for taggable behavior.
|
37
|
+
#
|
38
|
+
# @option options [ String ] :separator
|
39
|
+
# The delimiter used when converting the tags to and from String format. Defaults to ' '
|
40
|
+
# @option options [ :Symbol ] :group_by_field
|
41
|
+
# The Mongoid field to group by when RealTimeGroupBy aggregation is used.
|
42
|
+
# @option options [ <various> ] :default, :as, :localize, etc.
|
43
|
+
# Options for Mongoid #field method will be automatically passed
|
44
|
+
# to the underlying Array field
|
45
|
+
def taggable(*args)
|
46
|
+
# init variables
|
47
|
+
options = args.extract_options!
|
48
|
+
field = args.present? ? args.shift.to_sym : DEFAULT_FIELD
|
49
|
+
added = add_taggable(field, options)
|
50
|
+
# TODO: test if this is needed
|
51
|
+
# descendants.each do |subclass|
|
52
|
+
# subclass.add_taggable(field, options)
|
53
|
+
# end
|
54
|
+
added
|
55
|
+
end
|
56
|
+
|
57
|
+
def tag_contexts
|
58
|
+
self.taggable_with_context_options.keys
|
59
|
+
end
|
60
|
+
|
61
|
+
def tag_database_fields
|
62
|
+
self.taggable_with_context_options.keys.map do |context|
|
63
|
+
tag_options_for(context)[:db_field]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def tag_options_for(context)
|
68
|
+
self.taggable_with_context_options[context]
|
69
|
+
end
|
70
|
+
|
71
|
+
def tags_for(context, group_by=nil, conditions={})
|
72
|
+
raise AggregationStrategyMissing
|
73
|
+
end
|
74
|
+
|
75
|
+
def tags_with_weight_for(context, group_by=nil, conditions={})
|
76
|
+
raise AggregationStrategyMissing
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_tag_separator_for(context)
|
80
|
+
self.taggable_with_context_options[context][:separator]
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_tag_group_by_field_for(context)
|
84
|
+
self.taggable_with_context_options[context][:group_by_field]
|
85
|
+
end
|
86
|
+
|
87
|
+
# Find documents tagged with all tags passed as a parameter, given
|
88
|
+
# as an Array or a String using the configured separator.
|
89
|
+
#
|
90
|
+
# @example Find matching all tags in an Array.
|
91
|
+
# Article.tagged_with(['ruby', 'mongodb'])
|
92
|
+
# @example Find matching all tags in a String.
|
93
|
+
# Article.tagged_with('ruby, mongodb')
|
94
|
+
#
|
95
|
+
# @param [ String ] :field The field name of the tag.
|
96
|
+
# @param [ Array<String, Symbol>, String ] :tags Tags to match.
|
97
|
+
# @return [ Criteria ] A new criteria.
|
98
|
+
def tagged_with(context, tags)
|
99
|
+
tags = format_tags_for(context, tags)
|
100
|
+
field = tag_options_for(context)[:field]
|
101
|
+
all_in(field => tags)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Helper method to convert a a tag input value of unknown type
|
105
|
+
# to a formatted array.
|
106
|
+
def format_tags_for(context, value)
|
107
|
+
return nil if value.nil?
|
108
|
+
# 0) Tags must be an array or a string
|
109
|
+
raise InvalidTagsFormat unless value.is_a?(Array) || value.is_a?(String)
|
110
|
+
# 1) convert String to Array
|
111
|
+
value = value.split(get_tag_separator_for(context)) if value.is_a? String
|
112
|
+
# 2) remove all nil values
|
113
|
+
# 3) strip all white spaces. Could leave blank strings (e.g. foo, , bar, baz)
|
114
|
+
# 4) remove all blank strings
|
115
|
+
# 5) remove duplicate
|
116
|
+
value.compact.map(&:strip).reject(&:blank?).uniq
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
|
121
|
+
# Adds a taggable context to the list of contexts, and creates the underlying
|
122
|
+
# Mongoid field and alias methods for the context.
|
123
|
+
#
|
124
|
+
# @param [ Symbol ] field The name of the Mongoid database field to store the taggable.
|
125
|
+
# @param [ Hash ] options The taggable options.
|
126
|
+
#
|
127
|
+
# @since 1.1.1
|
128
|
+
def add_taggable(field, options)
|
129
|
+
validate_taggable_options(options)
|
130
|
+
|
131
|
+
# db_field: the field name stored in the database
|
132
|
+
options[:db_field] = field.to_sym
|
133
|
+
# field: the field name used to identify the tags. :field will
|
134
|
+
# be identical to :db_field unless the :as option is specified
|
135
|
+
options[:field] = options[:as] || field
|
136
|
+
context = options[:field]
|
137
|
+
options.reverse_merge!(
|
138
|
+
separator: DEFAULT_SEPARATOR
|
139
|
+
)
|
140
|
+
|
141
|
+
# register / update settings
|
142
|
+
self.taggable_with_context_options[options[:field]] = options
|
143
|
+
|
144
|
+
create_taggable_mongoid_field(field, options)
|
145
|
+
create_taggable_mongoid_index(context)
|
146
|
+
define_taggable_accessors(context)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Validates the taggable options and raises errors if invalid options are detected.
|
150
|
+
#
|
151
|
+
# @param [ Hash ] options The taggable options.
|
152
|
+
#
|
153
|
+
# @since 1.1.1
|
154
|
+
def validate_taggable_options(options)
|
155
|
+
if options[:field]
|
156
|
+
raise <<-ERR
|
157
|
+
taggable :field option has been removed as of version 1.1.1. Please use the
|
158
|
+
syntax "taggable <database_name>, as: <tag_name>"
|
159
|
+
ERR
|
160
|
+
end
|
161
|
+
if options[:string_method]
|
162
|
+
raise <<-ERR
|
163
|
+
taggable :string_method option has been removed as of version 1.1.1. Please
|
164
|
+
define an alias to "<tags>_string" in your Model
|
165
|
+
ERR
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Creates the underlying Mongoid field for the tag context.
|
170
|
+
#
|
171
|
+
# @param [ Symbol ] name The name of the Mongoid field.
|
172
|
+
# @param [ Hash ] options Options for the Mongoid field.
|
173
|
+
#
|
174
|
+
# @since 1.1.1
|
175
|
+
def create_taggable_mongoid_field(name, options)
|
176
|
+
field name, mongoid_field_options(options)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Prepares valid Mongoid option keys from the taggable options. Slices
|
180
|
+
# the taggable options to include only valid options for the Mongoid #field
|
181
|
+
# method, and coerces :type to Array.
|
182
|
+
#
|
183
|
+
# @param [ Hash ] :options The taggable options hash.
|
184
|
+
# @return [ Hash ] A options hash for the Mongoid #field method.
|
185
|
+
#
|
186
|
+
# @since 1.1.1
|
187
|
+
def mongoid_field_options(options = {})
|
188
|
+
options.slice(*::Mongoid::Fields::Validators::Macro::OPTIONS).merge!(type: Array)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Creates an index for the underlying Mongoid field.
|
192
|
+
#
|
193
|
+
# @param [ Symbol ] name The name or alias name of Mongoid field.
|
194
|
+
#
|
195
|
+
# @since 1.1.1
|
196
|
+
def create_taggable_mongoid_index(name)
|
197
|
+
index({ name => 1 }, { background: true })
|
198
|
+
end
|
199
|
+
|
200
|
+
# Defines all accessor methods for the taggable context at both
|
201
|
+
# the instance and class level.
|
202
|
+
#
|
203
|
+
# @param [ Symbol ] context The name of the tag context.
|
204
|
+
#
|
205
|
+
# @since 1.1.1
|
206
|
+
def define_taggable_accessors(context)
|
207
|
+
define_class_tags_getter(context)
|
208
|
+
define_class_weighted_tags_getter(context)
|
209
|
+
define_class_separator_getter(context)
|
210
|
+
define_class_tagged_with_getter(context)
|
211
|
+
define_class_group_by_getter(context)
|
212
|
+
define_instance_tag_string_getter(context)
|
213
|
+
define_instance_tag_setter(context)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Create the singleton getter method to retrieve all tags
|
217
|
+
# of a given context for all instances of the model.
|
218
|
+
#
|
219
|
+
# @param [ Symbol ] context The name of the tag context.
|
220
|
+
#
|
221
|
+
# @since 1.1.1
|
222
|
+
def define_class_tags_getter(context)
|
223
|
+
# retrieve all tags ever created for the model
|
224
|
+
self.class.class_eval do
|
225
|
+
define_method context do |group_by = nil|
|
226
|
+
tags_for(context, group_by)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Create the singleton getter method to retrieve a weighted
|
232
|
+
# array of tags of a given context for all instances of the model.
|
233
|
+
#
|
234
|
+
# @param [ Symbol ] context The name of the tag context.
|
235
|
+
#
|
236
|
+
# @since 1.1.1
|
237
|
+
def define_class_weighted_tags_getter(context)
|
238
|
+
self.class.class_eval do
|
239
|
+
define_method :"#{context}_with_weight" do |group_by = nil|
|
240
|
+
tags_with_weight_for(context, group_by)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Create the singleton getter method to retrieve the tag separator
|
246
|
+
# for a given context for all instances of the model.
|
247
|
+
#
|
248
|
+
# @param [ Symbol ] context The name of the tag context.
|
249
|
+
#
|
250
|
+
# @since 1.1.1
|
251
|
+
def define_class_separator_getter(context)
|
252
|
+
self.class.class_eval do
|
253
|
+
define_method :"#{context}_separator" do
|
254
|
+
get_tag_separator_for(context)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Create the singleton getter method to retrieve the all
|
260
|
+
# instances of the model which contain the tag/tags for a given context.
|
261
|
+
#
|
262
|
+
# @param [ Symbol ] context The name of the tag context.
|
263
|
+
#
|
264
|
+
# @since 1.1.1
|
265
|
+
def define_class_tagged_with_getter(context)
|
266
|
+
self.class.class_eval do
|
267
|
+
define_method :"#{context}_tagged_with" do |tags|
|
268
|
+
tagged_with(context, tags)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Create the singleton getter method to retrieve the
|
274
|
+
# group_by field
|
275
|
+
#
|
276
|
+
# @param [ Symbol ] context The name of the tag context.
|
277
|
+
#
|
278
|
+
# @since 1.1.1
|
279
|
+
def define_class_group_by_getter(context)
|
280
|
+
self.class.class_eval do
|
281
|
+
define_method :"#{context}_group_by_field" do
|
282
|
+
get_tag_group_by_field_for(context)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Create the setter method for the provided taggable, using an
|
288
|
+
# alias method chain to the underlying field method.
|
289
|
+
#
|
290
|
+
# @param [ Symbol ] context The name of the tag context.
|
291
|
+
#
|
292
|
+
# @since 1.1.1
|
293
|
+
def define_instance_tag_setter(context)
|
294
|
+
generated_methods.module_eval do
|
295
|
+
alias_method "#{context}_without_taggable=", "#{context}="
|
296
|
+
re_define_method("#{context}=") do |value|
|
297
|
+
value = self.class.format_tags_for(context, value)
|
298
|
+
self.send("#{context}_without_taggable=", value)
|
299
|
+
end
|
300
|
+
#alias_method_chain "#{context}=", :taggable
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Create the getter method for the joined tags string.
|
305
|
+
#
|
306
|
+
# @param [ Symbol ] context The name of the tag context.
|
307
|
+
#
|
308
|
+
# @since 1.1.1
|
309
|
+
def define_instance_tag_string_getter(context)
|
310
|
+
generated_methods.module_eval do
|
311
|
+
re_define_method("#{context}_string") do
|
312
|
+
tag_string_for(context)
|
313
|
+
end
|
314
|
+
|
315
|
+
re_define_method("#{context}_string=") do |value|
|
316
|
+
set_tag_string_for(context, value)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|