acts-as-taggable-on-mongoid 6.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +63 -0
- data/.gitignore +54 -0
- data/.reek.yml +8 -0
- data/.rspec +2 -0
- data/.rubocop.yml +59 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +203 -0
- data/LICENSE.txt +21 -0
- data/PULL_REQUEST_TEMPLATE.md +11 -0
- data/README.md +741 -0
- data/Rakefile +8 -0
- data/acts-as-taggable-on-mongoid.gemspec +54 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/codecov.yml +3 -0
- data/config/pronto-circleci.yml +7 -0
- data/lib/acts-as-taggable-on-mongoid.rb +80 -0
- data/lib/acts_as_taggable_on_mongoid/configuration.rb +94 -0
- data/lib/acts_as_taggable_on_mongoid/default_parser.rb +120 -0
- data/lib/acts_as_taggable_on_mongoid/errors/duplicate_tag_error.rb +9 -0
- data/lib/acts_as_taggable_on_mongoid/generic_parser.rb +44 -0
- data/lib/acts_as_taggable_on_mongoid/models/tag.rb +103 -0
- data/lib/acts_as_taggable_on_mongoid/models/tagging.rb +80 -0
- data/lib/acts_as_taggable_on_mongoid/tag_list.rb +169 -0
- data/lib/acts_as_taggable_on_mongoid/taggable.rb +131 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/changeable.rb +71 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/core.rb +219 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/list_tags.rb +45 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition.rb +189 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/attributes.rb +77 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/changeable.rb +140 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/tag_type_definition/names.rb +39 -0
- data/lib/acts_as_taggable_on_mongoid/taggable/utils/tag_list_diff.rb +121 -0
- data/lib/acts_as_taggable_on_mongoid/version.rb +5 -0
- metadata +352 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 CardTapp
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,741 @@
|
|
1
|
+
# ActsAsTaggableOnMongoid
|
2
|
+
|
3
|
+
[ActsAsTaggableOn](https://github.com/mbleigh/acts-as-taggable-on) is the clear leader in tagging
|
4
|
+
solutions in Rails. Unfortunately it does not appear to work well with Mongoid. For Mongo the
|
5
|
+
clear leader for tagging solutions is to include an indexed array of strings as tags. There are
|
6
|
+
several solutions that use this mechanism. Unfortunately, sometimes you actually do need a
|
7
|
+
many-to-many table solution even in Mongo which happens to be the situation I somehow have found
|
8
|
+
myself in.
|
9
|
+
|
10
|
+
Therefore, we are building a new solution to implement an `ActsLikeTaggableOn` like solution using
|
11
|
+
Mongo. The general goal is to mimic the features and interface of ActsLikeTaggableOn as much as
|
12
|
+
feasible/possible.
|
13
|
+
|
14
|
+
This is not a direct port of `ActsLikeTaggableOn` at this time for several reason, the main one being
|
15
|
+
time. Mongoid and ActiveRecord are enough different that the complications that would arise from forking
|
16
|
+
and trying to modify it to work with Mongoid do not seem insignificant.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'acts-as-taggable-on-mongoid'
|
24
|
+
```
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install acts-as-taggable-on-mongoid
|
33
|
+
|
34
|
+
## Architecture and design concepts
|
35
|
+
|
36
|
+
### Termonology.
|
37
|
+
|
38
|
+
* **tag_list** - a generic term for the list field in a `Taggable` object that is
|
39
|
+
defined using the `acts_as_taggable_on` method. Each tag_list is definied
|
40
|
+
independentally and can have separate settings.
|
41
|
+
* **tag_type, context** - For whatever reason, these two terms are both used and
|
42
|
+
are synonymous with each other. `context` is used in the Tag and Taggable
|
43
|
+
tables to differentiate that a record is associated with a particular tag_list
|
44
|
+
* **Taggable object** - A database model object which can have tag_lists associated with
|
45
|
+
it. All taggable objects can have multiple tag_lists in it.
|
46
|
+
* **Tagging table** - A Table for storing the which Tags are associated with a Taggable
|
47
|
+
object. The Tag details are denormalized into the Tagging table to allow efficient
|
48
|
+
querying of Taggable objects without having to go through the Taggings table.
|
49
|
+
* **Tag table** - A Table for storing the tags. Tags are used primarily to maintain
|
50
|
+
a usage counter as the tag details are denormalized into the Tagging tagle
|
51
|
+
|
52
|
+
**NOTE**: Unlike the `ActsAsTaggableOn` gem, I group Tags by context. Tags with the same
|
53
|
+
name for different contexts keep separate counts and are considered different Tags.
|
54
|
+
It is simpler to combine Tags and their counts by name then it is to split them out.
|
55
|
+
|
56
|
+
The database structure is:
|
57
|
+
```
|
58
|
+
+----------+ +---------+ +-----+
|
59
|
+
| Taggable | -> | Tagging | <- | Tag |
|
60
|
+
+----------+ +---------+ +-----+
|
61
|
+
```
|
62
|
+
|
63
|
+
## Usage
|
64
|
+
|
65
|
+
Setup
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class User
|
69
|
+
include ::Mongoid::Document
|
70
|
+
|
71
|
+
acts_as_taggable # Alias for acts_as_taggable_on :tags
|
72
|
+
acts_as_taggable_on :skills, :interests
|
73
|
+
end
|
74
|
+
|
75
|
+
class UsersController < ApplicationController
|
76
|
+
def user_params
|
77
|
+
params.require(:user).permit(:name, :tag_list) ## Rails 4 strong params usage
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@user = User.new(:name => "Bobby")
|
82
|
+
```
|
83
|
+
|
84
|
+
Add and remove a single tag
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
@user.tag_list.add("awesome") # add a single tag. alias for <<
|
88
|
+
@user.tag_list.remove("awesome") # remove a single tag
|
89
|
+
@user.save # save to persist tag_list
|
90
|
+
```
|
91
|
+
|
92
|
+
Add and remove multiple tags in an array
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
@user.tag_list.add("awesome", "slick")
|
96
|
+
@user.tag_list.remove("awesome", "slick")
|
97
|
+
@user.save
|
98
|
+
```
|
99
|
+
|
100
|
+
You can also add and remove tags in format of String. This would
|
101
|
+
be convenient in some cases such as handling tag input param in a String.
|
102
|
+
|
103
|
+
Pay attention you need to add `parse: true` as option in this case.
|
104
|
+
|
105
|
+
You may also want to take a look at delimiter in the string. The default
|
106
|
+
is comma `,` so you don't need to do anything here. However, if you want to
|
107
|
+
use a different delimiter you will have to change the delmiter on the
|
108
|
+
`DefaultParser` or create a new Parser which splits the string using the
|
109
|
+
algorithm or method you want/need. See the `GenericParser` class for details
|
110
|
+
on creating a new custom parser of your own design.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
@user.tag_list.add("awesome, slick", parse: true)
|
114
|
+
@user.tag_list.remove("awesome, slick", parse: true)
|
115
|
+
```
|
116
|
+
|
117
|
+
You can also add and remove tags by direct assignment. By default, direct
|
118
|
+
assignment will parse values passed into it. Note this will
|
119
|
+
remove existing tags so use it with attention.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
@user.tag_list = "awesome, slick, hefty"
|
123
|
+
@user.save
|
124
|
+
@user.reload
|
125
|
+
@user.tags
|
126
|
+
=> [#<ActsAsTaggableOnMongoid::Models::Tag id: 1, name: "awesome", taggings_count: 1>,
|
127
|
+
#<ActsAsTaggableOnMongoid::Models::Tag id: 2, name: "slick", taggings_count: 1>,
|
128
|
+
#<ActsAsTaggableOnMongoid::Models::Tag id: 3, name: "hefty", taggings_count: 1>]
|
129
|
+
```
|
130
|
+
|
131
|
+
With the defined context in model, you have multiple new methods at disposal
|
132
|
+
to manage and view the tags in the context. For example, with `:skill` context
|
133
|
+
these methods are added to the model: `skill_list`(and `skill_list.add`, `skill_list.remove`
|
134
|
+
`skill_list=`), `skills`(plural), `skill_counts`.
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
@user.skill_list = "joking, clowning, boxing"
|
138
|
+
@user.save
|
139
|
+
@user.reload
|
140
|
+
@user.skills
|
141
|
+
=> [#<ActsAsTaggableOnMongoid::Models::Tag id: 1, name: "joking", taggings_count: 1>,
|
142
|
+
#<ActsAsTaggableOnMongoid::Models::Tag id: 2, name: "clowning", taggings_count: 1>,
|
143
|
+
#<ActsAsTaggableOnMongoid::Models::Tag id: 3, name: "boxing", taggings_count: 1>]
|
144
|
+
|
145
|
+
@user.skill_list.add("coding")
|
146
|
+
|
147
|
+
@user.skill_list
|
148
|
+
# => ["joking", "clowning", "boxing", "coding"]
|
149
|
+
|
150
|
+
@another_user = User.new(:name => "Alice")
|
151
|
+
@another_user.skill_list.add("clowning")
|
152
|
+
@another_user.save
|
153
|
+
|
154
|
+
User.skill_counts
|
155
|
+
=> [#<ActsAsTaggableOnMongoid::Models::Tag id: 1, name: "joking", taggings_count: 1>,
|
156
|
+
#<ActsAsTaggableOnMongoid::Models::Tag id: 2, name: "clowning", taggings_count: 2>,
|
157
|
+
#<ActsAsTaggableOnMongoid::Models::Tag id: 3, name: "boxing", taggings_count: 1>]
|
158
|
+
```
|
159
|
+
|
160
|
+
To preserve the order in which tags are created use `acts_as_ordered_taggable`:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class User < ActiveRecord::Base
|
164
|
+
# Alias for acts_as_ordered_taggable_on :tags
|
165
|
+
acts_as_ordered_taggable
|
166
|
+
acts_as_ordered_taggable_on :skills, :interests
|
167
|
+
end
|
168
|
+
|
169
|
+
@user = User.new(:name => "Bobby")
|
170
|
+
@user.tag_list = "east, south"
|
171
|
+
@user.save
|
172
|
+
|
173
|
+
@user.tag_list = "north, east, south, west"
|
174
|
+
@user.save
|
175
|
+
|
176
|
+
@user.reload
|
177
|
+
@user.tag_list # => ["north", "east", "south", "west"]
|
178
|
+
```
|
179
|
+
|
180
|
+
#### `acts_as_taggable_on` documentation details
|
181
|
+
|
182
|
+
```Ruby
|
183
|
+
class MyTaggable
|
184
|
+
include ::Mongoid::Document
|
185
|
+
|
186
|
+
acts_as_taggable_on :my_tags,
|
187
|
+
:your_tags,
|
188
|
+
:other_tags,
|
189
|
+
:etc_tags,
|
190
|
+
parser: MyCustomParser,
|
191
|
+
preserve_tag_order: true,
|
192
|
+
cached_in_model: true,
|
193
|
+
force_lowercase: true,
|
194
|
+
force_parameterize: true,
|
195
|
+
remove_unused_tags: true,
|
196
|
+
tags_table: CustomTag,
|
197
|
+
taggings_table: CusomTagging,
|
198
|
+
default: "defalut, values"
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
202
|
+
`acts_as_taggable_on` will define the following methods and relationships based on the values passed in
|
203
|
+
(only methods for the `my_tags` tag_list are shown, but methods for each of the other tag_lists will be
|
204
|
+
created also.)
|
205
|
+
|
206
|
+
* `custom_taggings` - a relationship on a `MyTaggable` object which will return all `CustomTagging`s across
|
207
|
+
all contexts/tag_types for a particular tagging. Usage:
|
208
|
+
```Ruby
|
209
|
+
my_taggable = MyTaggable.find(taggable_id)
|
210
|
+
my_taggable.custom_taggings.to_a
|
211
|
+
|
212
|
+
=> [#<CustomTagging context: "my_tags", taggable_id: my_taggable.id>,
|
213
|
+
#<CustomTagging context: "your_tags", taggable_id: my_taggable.id>, ...]
|
214
|
+
```
|
215
|
+
* `base_custom_tags` - Returns a `Criteria` for all `CustomTag`s which are associated with any `MyTaggable`.
|
216
|
+
Usage:
|
217
|
+
```Ruby
|
218
|
+
my_taggable = MyTaggable.find(taggable_id)
|
219
|
+
my_taggable.base_custom_tags.to_a
|
220
|
+
|
221
|
+
=> [#<CustomTag context: "my_tags", taggable_type: "MyTaggable">,
|
222
|
+
#<CustomTag context: "your_tags", taggable_type: "MyTaggable">, ...]
|
223
|
+
```
|
224
|
+
* `my_tag_custom_taggings` - Returns all `CustomTagging`s for a particular `MyTaggable` object for the
|
225
|
+
named context in the appropriate sorted order for that context (based on if `preserve_tag_order` is true.)
|
226
|
+
Usage:
|
227
|
+
```Ruby
|
228
|
+
my_taggable = MyTaggable.find(taggable_id)
|
229
|
+
my_taggable.my_tag_custom_taggings.to_a
|
230
|
+
|
231
|
+
=> [#<CustomTagging context: "my_tags", taggable_id: my_taggable.id>,
|
232
|
+
#<CustomTagging context: "my_tags", taggable_id: my_taggable.id>, ...]
|
233
|
+
```
|
234
|
+
* `my_tags` - Returns all `CustomTag`s for a particular `MyTaggable` object for the named context sorted in the order
|
235
|
+
that the tags were added to that object if `preserve_tag_order` is true.
|
236
|
+
NOTE: This is done through a mapping from the `custom_taggings` relationship and is therefore not very efficient.
|
237
|
+
Usage:
|
238
|
+
```Ruby
|
239
|
+
my_taggable = MyTaggable.find(taggable_id)
|
240
|
+
my_taggable.my_tags.to_a
|
241
|
+
|
242
|
+
=> [#<CustomTag context: "my_tags", taggable_type: "MyTaggable">,
|
243
|
+
#<CustomTag context: "my_tags", taggable_type: "MyTaggable">, ...]
|
244
|
+
```
|
245
|
+
* `my_tag_list` - Returns an array of all tag values that are associated with a context for a `MyTaggable` object.
|
246
|
+
The array will be a simple array of strings that are sorted in the appropriate order based on the
|
247
|
+
value of `preserve_tag_order`.
|
248
|
+
Usage:
|
249
|
+
```Ruby
|
250
|
+
my_taggable = MyTaggable.find(taggable_id)
|
251
|
+
my_taggable.my_tag_list
|
252
|
+
|
253
|
+
=> ["tag value 1", "tag value 2", "tag value 3", ...]
|
254
|
+
# This list will be sorted in the order that the values were added to the list if `preserve_tag_order` is true.
|
255
|
+
```
|
256
|
+
* `my_tag_list=` - Sets the list of tags for that context for a particular `MyTaggable` object. Setting the value
|
257
|
+
in this way will automatically parse the string. You can force the string to not be parsed or to use a particular
|
258
|
+
parser by passing an array with the appropriate options (`parse` and `parser`). If `preserve_tag_order` is true
|
259
|
+
then values will be removed and re-added as necessary to ensure that the tag order is preserved for the set.
|
260
|
+
Changes to the `my_tag_list` will not be persisted until after the object is saved just like any other field.
|
261
|
+
Usage:
|
262
|
+
```Ruby
|
263
|
+
# Set list and save - Sets the list of tags for the list
|
264
|
+
my_taggable = MyTaggable.find(taggable_id)
|
265
|
+
|
266
|
+
# preserve_tag_order = false
|
267
|
+
my_taggable.my_tag_list = "tag value 3, tag value 2, tag value 1"
|
268
|
+
my_taggable.save!
|
269
|
+
my_taggable.reload.my_tag_list
|
270
|
+
|
271
|
+
=> ["tag value 3", "tag value 2", "tag value 1"]
|
272
|
+
```
|
273
|
+
```Ruby
|
274
|
+
# Use update_attributes - Sets the list of tags for the list as if assigned (i.e. parsing is assumed)
|
275
|
+
# preserve_tag_order = false
|
276
|
+
my_taggable.update_attributes! my_tag_list: "tag value 2, tag value 1, tag value 3"
|
277
|
+
my_taggable.reload.my_tag_list
|
278
|
+
|
279
|
+
=> ["tag value 3", "tag value 2", "tag value 1"]
|
280
|
+
```
|
281
|
+
```Ruby
|
282
|
+
# Use update_attributes - Sets the list of tags for the list - keep the passed in order.
|
283
|
+
# preserve_tag_order = true
|
284
|
+
my_taggable.update_attributes! my_tag_list: "tag value 2, tag value 1, tag value 3"
|
285
|
+
my_taggable.reload.my_tag_list
|
286
|
+
|
287
|
+
=> ["tag value 2", "tag value 1", "tag value 3"]
|
288
|
+
```
|
289
|
+
```Ruby
|
290
|
+
# Set list and save - disabling parsing. The string passed in i used as-is
|
291
|
+
# preserve_tag_order = false
|
292
|
+
my_taggable.my_tag_list = ["tag value 3, tag value 2, tag value 1", parse: false]
|
293
|
+
my_taggable.save!
|
294
|
+
my_taggable.reload.my_tag_list
|
295
|
+
|
296
|
+
=> ["tag value 3, tag value 2, tag value 1"]
|
297
|
+
```
|
298
|
+
```Ruby
|
299
|
+
# Set the list and save using a custom parser:
|
300
|
+
# preserve_tag_order = false
|
301
|
+
my_taggable.my_tag_list = ["tag value 2;tag value 1;tag value 3", parser: SemiColonParser]
|
302
|
+
my_taggable.save!
|
303
|
+
my_taggable.reload.my_tag_list
|
304
|
+
|
305
|
+
=> ["tag value 2", "tag value 1", "tag value 3"]
|
306
|
+
```
|
307
|
+
```Ruby
|
308
|
+
# Use add, remove, concat and << to change the list and save.
|
309
|
+
# preserve_tag_order = false
|
310
|
+
# parse is ONLY assumed true for assignment and default values.
|
311
|
+
my_taggable.my_tag_list.add "1, 2", "3, 4", "5", parse: true
|
312
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "2", "3", "4", "5"]
|
313
|
+
my_taggable.my_tag_list.add "6, 7", "8, 9", "10"
|
314
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "2", "3", "4", "5", "6, 7", "8, 9", "10"]
|
315
|
+
my_taggable.my_tag_list.remove "2, 3", "4, 9", parse: true
|
316
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "5", "6, 7", "8, 9", "10"]
|
317
|
+
my_taggable.my_tag_list.remove "6, 7", "10", "not in list"
|
318
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "5", "8, 9"]
|
319
|
+
my_taggable.my_tag_list << ["11, 12", "13, 14", parse: true]
|
320
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "5", "8, 9", "11", "12", "13", "14"]
|
321
|
+
my_taggable.my_tag_list << ["15, 16", "17, 18"]
|
322
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "5", "8, 9", "11", "12", "13", "14", "15, 16", "17, 18"]
|
323
|
+
my_taggable.my_tag_list << "19"
|
324
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "5", "8, 9", "11", "12", "13", "14", "15, 16", "17, 18", "19"]
|
325
|
+
|
326
|
+
# NOTE: Unlike the other methods, passing options for concat is not supported.
|
327
|
+
my_taggable.my_tag_list.concat ["20", "21"]
|
328
|
+
# my_tag_list => ["tag value 2", "tag value 1", "tag value 3", "1", "5", "8, 9", "11", "12", "13", "14", "15, 16", "17, 18", "19", "20", "21"]
|
329
|
+
|
330
|
+
# assignment will apply preferences:
|
331
|
+
# force_lowercase = true
|
332
|
+
my_taggable.my_tag_list = "Tag Value 1, Tag Value 2, Tag Value 3"
|
333
|
+
# my_tag_list => ["tag value 3", "tag value 2", "tag value 1"]
|
334
|
+
my_taggable.my_tag_list.remove "TAG VALUE 1"
|
335
|
+
# my_tag_list => ["tag value 3", "tag value 2"]
|
336
|
+
|
337
|
+
# force_parameterize = true
|
338
|
+
my_taggable.my_tag_list = "tag value 1, tag value 2, tag value 3"
|
339
|
+
# my_tag_list => ["tag-value-1", "tag-value-2", "tag-value-3"]
|
340
|
+
my_taggable.my_tag_list.remove "tag value 1"
|
341
|
+
# my_tag_list => ["tag-value-2", "tag-value-3"]
|
342
|
+
```
|
343
|
+
```Ruby
|
344
|
+
# Values are not stored until save is called.
|
345
|
+
# preserve_tag_order = false
|
346
|
+
my_taggable.my_tag_list = "tag value 3, tag value 2, tag value 1"
|
347
|
+
my_taggable.reload.my_tag_list
|
348
|
+
|
349
|
+
=> []
|
350
|
+
```
|
351
|
+
* `all_my_tags_list` - Returns all `CustomTag`s for the given context for any `MyTaggable` object.
|
352
|
+
NOTE: Unlike `ActsAsTaggableOn`, the Tags returned represent all Tags that have ever been used for that context
|
353
|
+
for the `MyTaggable` context. There is no guarantee that the `CustomTag`s returned are currently being used
|
354
|
+
by any `MyTaggable` object.
|
355
|
+
Usage:
|
356
|
+
```Ruby
|
357
|
+
my_taggable = MyTaggable.find(taggable_id)
|
358
|
+
my_taggable.all_my_tags_list.to_a
|
359
|
+
|
360
|
+
=> [#<CustomTag context: "my_tags", taggable_type: "MyTaggable">,
|
361
|
+
#<CustomTag context: "my_tags", taggable_type: "MyTaggable">, ...]
|
362
|
+
```
|
363
|
+
* `my_tag_list?` - Returns true if the list has been set to a value.
|
364
|
+
Usage:
|
365
|
+
```Ruby
|
366
|
+
my_taggable = MyTaggable.find(taggable_id)
|
367
|
+
my_taggable.my_tag_list?
|
368
|
+
=> false
|
369
|
+
|
370
|
+
my_taggable.my_tag_list = "tag value 1, tag_value 2"
|
371
|
+
my_taggable.my_tag_list?
|
372
|
+
=> true
|
373
|
+
```
|
374
|
+
* `my_tag_list_change` - Returns the old and new values for a changed list.
|
375
|
+
Usage:
|
376
|
+
```Ruby
|
377
|
+
my_taggable = MyTaggable.create!(my_tag_list: "tag value 1, tag_value 2")
|
378
|
+
my_taggable.my_tag_list = "tag value 2, tag value 3"
|
379
|
+
my_taggable.my_tag_list_change
|
380
|
+
=> [["tag value 1", "tag value 2"], ["tag value 2", "tag value 2"]]
|
381
|
+
```
|
382
|
+
* `my_tag_list_changed?` - Returns the old and new values for a changed list.
|
383
|
+
Usage:
|
384
|
+
```Ruby
|
385
|
+
my_taggable = MyTaggable.create!(my_tag_list: "tag value 1, tag_value 2")
|
386
|
+
my_taggable.my_tag_list_changed?
|
387
|
+
=> false
|
388
|
+
|
389
|
+
my_taggable.my_tag_list = "tag value 2, tag value 3"
|
390
|
+
my_taggable.my_tag_list_changed?
|
391
|
+
=> true
|
392
|
+
```
|
393
|
+
* `my_tag_list_will_change` - Tells the model that the `my_tag_list` field will be changing.
|
394
|
+
This method is primarily intended for internal use.
|
395
|
+
Usage:
|
396
|
+
```Ruby
|
397
|
+
my_taggable = MyTaggable.create!(my_tag_list: "tag value 1, tag_value 2")
|
398
|
+
my_taggable.my_tag_list_will_change
|
399
|
+
```
|
400
|
+
* `my_tag_list_changed_from_default?` - Returns true if the value does not match the default for the field
|
401
|
+
Usage:
|
402
|
+
```Ruby
|
403
|
+
my_taggable = MyTaggable.create!
|
404
|
+
my_taggable.my_tag_list_changed_from_default?
|
405
|
+
=> false
|
406
|
+
|
407
|
+
my_taggable.my_tag_list = "tag value 2, tag value 3"
|
408
|
+
my_taggable.my_tag_list_changed_from_default?
|
409
|
+
=> true
|
410
|
+
```
|
411
|
+
* `my_tag_list_was` - Returns the old values for a changed list.
|
412
|
+
Usage:
|
413
|
+
```Ruby
|
414
|
+
my_taggable = MyTaggable.create!(my_tag_list: "tag value 1, tag_value 2")
|
415
|
+
my_taggable.my_tag_list = "tag value 2, tag value 3"
|
416
|
+
my_taggable.my_tag_list_was
|
417
|
+
=> ["tag value 1", "tag value 2"]
|
418
|
+
```
|
419
|
+
* `reset_my_tag_list!` - Resets a changed value back to what it was before
|
420
|
+
Usage:
|
421
|
+
```Ruby
|
422
|
+
my_taggable = MyTaggable.create!(my_tag_list: "tag value 1, tag_value 2")
|
423
|
+
my_taggable.my_tag_list = "tag value 2, tag value 3"
|
424
|
+
my_taggable.reset_my_tag_list!
|
425
|
+
=> ["tag value 1", "tag value 2"]
|
426
|
+
```
|
427
|
+
* `reset_my_tag_list_to_default!` - Resets a changed value back to the default values.
|
428
|
+
Usage:
|
429
|
+
```Ruby
|
430
|
+
my_taggable = MyTaggable.create!(my_tag_list: "tag value 1, tag_value 2")
|
431
|
+
my_taggable.my_tag_list = "tag value 2, tag value 3"
|
432
|
+
my_taggable.reset_my_tag_list_to_default!
|
433
|
+
=> ["default", "values"]
|
434
|
+
```
|
435
|
+
|
436
|
+
### Finding most or least used tags
|
437
|
+
|
438
|
+
You can find the most or least used tags by using:
|
439
|
+
|
440
|
+
```ruby
|
441
|
+
ActsAsTaggableOnMongoid::Models::Tag.most_used
|
442
|
+
ActsAsTaggableOnMongoid::Models::Tag.least_used
|
443
|
+
```
|
444
|
+
|
445
|
+
You can also filter the results by passing the method a limit, however the default limit is 20.
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
ActsAsTaggableOnMongoid::Models::Tag.most_used(10)
|
449
|
+
ActsAsTaggableOnMongoid::Models::Tag.least_used(10)
|
450
|
+
```
|
451
|
+
|
452
|
+
### ~~Finding Tagged Objects~~ Not implimented yet
|
453
|
+
|
454
|
+
Acts As Taggable On uses scopes to create an association for tags.
|
455
|
+
This way you can mix and match to filter down your results.
|
456
|
+
|
457
|
+
```ruby
|
458
|
+
class User < ActiveRecord::Base
|
459
|
+
acts_as_taggable_on :tags, :skills
|
460
|
+
scope :by_join_date, order("created_at DESC")
|
461
|
+
end
|
462
|
+
|
463
|
+
User.tagged_with("awesome").by_join_date
|
464
|
+
User.tagged_with("awesome").by_join_date.paginate(:page => params[:page], :per_page => 20)
|
465
|
+
|
466
|
+
# Find users that matches all given tags:
|
467
|
+
# NOTE: This only matches users that have the exact set of specified tags. If a user has additional tags, they are not returned.
|
468
|
+
User.tagged_with(["awesome", "cool"], :match_all => true)
|
469
|
+
|
470
|
+
# Find users with any of the specified tags:
|
471
|
+
User.tagged_with(["awesome", "cool"], :any => true)
|
472
|
+
|
473
|
+
# Find users that have not been tagged with awesome or cool:
|
474
|
+
User.tagged_with(["awesome", "cool"], :exclude => true)
|
475
|
+
|
476
|
+
# Find users with any of the tags based on context:
|
477
|
+
User.tagged_with(['awesome', 'cool'], :on => :tags, :any => true).tagged_with(['smart', 'shy'], :on => :skills, :any => true)
|
478
|
+
```
|
479
|
+
|
480
|
+
You can also use `:wild => true` option along with `:any` or `:exclude` option. It will be looking for `%awesome%` and `%cool%` in SQL.
|
481
|
+
|
482
|
+
__Tip:__ `User.tagged_with([])` or `User.tagged_with('')` will return `[]`, an empty set of records.
|
483
|
+
|
484
|
+
|
485
|
+
### ~~Relationships~~ Not implimented yet
|
486
|
+
|
487
|
+
You can find objects of the same type based on similar tags on certain contexts.
|
488
|
+
Also, objects will be returned in descending order based on the total number of
|
489
|
+
matched tags.
|
490
|
+
|
491
|
+
```ruby
|
492
|
+
@bobby = User.find_by_name("Bobby")
|
493
|
+
@bobby.skill_list # => ["jogging", "diving"]
|
494
|
+
|
495
|
+
@frankie = User.find_by_name("Frankie")
|
496
|
+
@frankie.skill_list # => ["hacking"]
|
497
|
+
|
498
|
+
@tom = User.find_by_name("Tom")
|
499
|
+
@tom.skill_list # => ["hacking", "jogging", "diving"]
|
500
|
+
|
501
|
+
@tom.find_related_skills # => [<User name="Bobby">, <User name="Frankie">]
|
502
|
+
@bobby.find_related_skills # => [<User name="Tom">]
|
503
|
+
@frankie.find_related_skills # => [<User name="Tom">]
|
504
|
+
```
|
505
|
+
|
506
|
+
### ~~Dynamic Tag Contexts~~ Not implimented yet
|
507
|
+
|
508
|
+
In addition to the generated tag contexts in the definition, it is also possible
|
509
|
+
to allow for dynamic tag contexts (this could be user generated tag contexts!)
|
510
|
+
|
511
|
+
```ruby
|
512
|
+
@user = User.new(:name => "Bobby")
|
513
|
+
@user.set_tag_list_on(:customs, "same, as, tag, list")
|
514
|
+
@user.tag_list_on(:customs) # => ["same", "as", "tag", "list"]
|
515
|
+
@user.save
|
516
|
+
@user.tags_on(:customs) # => [<Tag name='same'>,...]
|
517
|
+
@user.tag_counts_on(:customs)
|
518
|
+
User.tagged_with("same", :on => :customs) # => [@user]
|
519
|
+
```
|
520
|
+
|
521
|
+
### Tag Parsers
|
522
|
+
|
523
|
+
If you want to change how tags are parsed, you can define your own implementation:
|
524
|
+
|
525
|
+
```ruby
|
526
|
+
class MyParser < ActsAsTaggableOnMongoid::GenericParser
|
527
|
+
def parse
|
528
|
+
tags.each_with_object do |tag, tag_list|
|
529
|
+
tag_list.add tag.split('|')
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
def to_s
|
534
|
+
tags.join("|")
|
535
|
+
end
|
536
|
+
end
|
537
|
+
```
|
538
|
+
|
539
|
+
Now you can use this parser, passing it as parameter:
|
540
|
+
|
541
|
+
```ruby
|
542
|
+
@user = User.new(:name => "Bobby")
|
543
|
+
@user.tag_list = "east, south"
|
544
|
+
@user.tag_list.add("north|west", parser: MyParser)
|
545
|
+
@user.tag_list # => ["north", "east", "south", "west"]
|
546
|
+
|
547
|
+
# Or also:
|
548
|
+
@user.tag_list.parser = MyParser
|
549
|
+
@user.tag_list.add("north|west")
|
550
|
+
@user.tag_list # => ["north", "east", "south", "west"]
|
551
|
+
```
|
552
|
+
|
553
|
+
Or change it globally:
|
554
|
+
|
555
|
+
```ruby
|
556
|
+
ActsAsTaggableOnMongoid.default_parser = MyParser
|
557
|
+
@user = User.new(:name => "Bobby")
|
558
|
+
@user.tag_list = "east|south"
|
559
|
+
@user.tag_list # => ["east", "south"]
|
560
|
+
```
|
561
|
+
|
562
|
+
### ~~Tag Ownership~~ Not implimented yet
|
563
|
+
|
564
|
+
Tags can have owners:
|
565
|
+
|
566
|
+
```ruby
|
567
|
+
class User < ActiveRecord::Base
|
568
|
+
acts_as_tagger
|
569
|
+
end
|
570
|
+
|
571
|
+
class Photo < ActiveRecord::Base
|
572
|
+
acts_as_taggable_on :locations
|
573
|
+
end
|
574
|
+
|
575
|
+
@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
|
576
|
+
@some_user.owned_taggings
|
577
|
+
@some_user.owned_tags
|
578
|
+
Photo.tagged_with("paris", :on => :locations, :owned_by => @some_user)
|
579
|
+
@some_photo.locations_from(@some_user) # => ["paris", "normandy"]
|
580
|
+
@some_photo.owner_tags_on(@some_user, :locations) # => [#<ActsAsTaggableOnMongoid::Models::Tag id: 1, name: "paris">...]
|
581
|
+
@some_photo.owner_tags_on(nil, :locations) # => Ownerships equivalent to saying @some_photo.locations
|
582
|
+
@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations, :skip_save => true) #won't save @some_photo object
|
583
|
+
```
|
584
|
+
|
585
|
+
#### Working with Owned Tags
|
586
|
+
Note that `tag_list` only returns tags whose taggings do not have an owner. Continuing from the above example:
|
587
|
+
```ruby
|
588
|
+
@some_photo.tag_list # => []
|
589
|
+
```
|
590
|
+
To retrieve all tags of an object (regardless of ownership) or if only one owner can tag the object, use `all_tags_list`.
|
591
|
+
|
592
|
+
##### Adding owned tags
|
593
|
+
Note that **owned tags** are added all at once, in the form of ***comma seperated tags*** in string.
|
594
|
+
Also, when you try to add **owned tags** again, it simply overwrites the previous set of **owned tags**.
|
595
|
+
So to append tags in previously existing **owned tags** list, go as follows:
|
596
|
+
```ruby
|
597
|
+
def add_owned_tag
|
598
|
+
@some_item = Item.find(params[:id])
|
599
|
+
owned_tag_list = @some_item.all_tags_list - @some_item.tag_list
|
600
|
+
owned_tag_list += [(params[:tag])]
|
601
|
+
@tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
|
602
|
+
@some_item.save
|
603
|
+
end
|
604
|
+
|
605
|
+
def stringify(tag_list)
|
606
|
+
tag_list.inject('') { |memo, tag| memo += (tag + ',') }[0..-1]
|
607
|
+
end
|
608
|
+
```
|
609
|
+
##### Removing owned tags
|
610
|
+
Similarly as above, removing will be as follows:
|
611
|
+
```ruby
|
612
|
+
def remove_owned_tag
|
613
|
+
@some_item = Item.find(params[:id])
|
614
|
+
owned_tag_list = @some_item.all_tags_list - @some_item.tag_list
|
615
|
+
owned_tag_list -= [(params[:tag])]
|
616
|
+
@tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
|
617
|
+
@some_item.save
|
618
|
+
end
|
619
|
+
```
|
620
|
+
|
621
|
+
### Dirty objects
|
622
|
+
|
623
|
+
```ruby
|
624
|
+
@bobby = User.find_by_name("Bobby")
|
625
|
+
@bobby.skill_list # => ["jogging", "diving"]
|
626
|
+
|
627
|
+
@bobby.skill_list_changed? #=> false
|
628
|
+
@bobby.changes #=> {}
|
629
|
+
|
630
|
+
@bobby.skill_list = "swimming"
|
631
|
+
@bobby.changes.should == {"skill_list"=>["jogging, diving", ["swimming"]]}
|
632
|
+
@bobby.skill_list_changed? #=> true
|
633
|
+
|
634
|
+
@bobby.skill_list_change.should == ["jogging, diving", ["swimming"]]
|
635
|
+
```
|
636
|
+
|
637
|
+
### ~~Tag cloud calculations~~ Not implimented yet
|
638
|
+
|
639
|
+
To construct tag clouds, the frequency of each tag needs to be calculated.
|
640
|
+
Because we specified `acts_as_taggable_on` on the `User` class, we can
|
641
|
+
get a calculation of all the tag counts by using `User.tag_counts_on(:customs)`. But what if we wanted a tag count for
|
642
|
+
a single user's posts? To achieve this we call tag_counts on the association:
|
643
|
+
|
644
|
+
```ruby
|
645
|
+
User.find(:first).posts.tag_counts_on(:tags)
|
646
|
+
```
|
647
|
+
|
648
|
+
A helper is included to assist with generating tag clouds.
|
649
|
+
|
650
|
+
Here is an example that generates a tag cloud.
|
651
|
+
|
652
|
+
Helper:
|
653
|
+
|
654
|
+
```ruby
|
655
|
+
module PostsHelper
|
656
|
+
include ActsAsTaggableOnMongoid::TagsHelper
|
657
|
+
end
|
658
|
+
```
|
659
|
+
|
660
|
+
Controller:
|
661
|
+
|
662
|
+
```ruby
|
663
|
+
class PostController < ApplicationController
|
664
|
+
def tag_cloud
|
665
|
+
@tags = Post.tag_counts_on(:tags)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
```
|
669
|
+
|
670
|
+
View:
|
671
|
+
|
672
|
+
```erb
|
673
|
+
<% tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| %>
|
674
|
+
<%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
|
675
|
+
<% end %>
|
676
|
+
```
|
677
|
+
|
678
|
+
CSS:
|
679
|
+
|
680
|
+
```css
|
681
|
+
.css1 { font-size: 1.0em; }
|
682
|
+
.css2 { font-size: 1.2em; }
|
683
|
+
.css3 { font-size: 1.4em; }
|
684
|
+
.css4 { font-size: 1.6em; }
|
685
|
+
```
|
686
|
+
|
687
|
+
## Configuration
|
688
|
+
|
689
|
+
Configurations set on the `ActsAsTaggableOnMongoid` (also `ActsAsTaggableOnMongoid.configuration` or
|
690
|
+
`ActsAsTaggableOnMongoid.configure { |config| }`) set global defaults for these settings. Individual
|
691
|
+
tags can override the values to whatever they want. Custom contexts will be created using the configuration
|
692
|
+
defaults.
|
693
|
+
|
694
|
+
If you would like to remove unused tag objects after removing taggings, add:
|
695
|
+
|
696
|
+
```ruby
|
697
|
+
ActsAsTaggableOnMongoid.remove_unused_tags = true
|
698
|
+
```
|
699
|
+
|
700
|
+
If you want force tags to be saved downcased:
|
701
|
+
|
702
|
+
```ruby
|
703
|
+
ActsAsTaggableOnMongoid.force_lowercase = true
|
704
|
+
```
|
705
|
+
|
706
|
+
If you want tags to be saved parametrized (you can redefine to_param as well):
|
707
|
+
|
708
|
+
```ruby
|
709
|
+
ActsAsTaggableOnMongoid.force_parameterize = true
|
710
|
+
```
|
711
|
+
|
712
|
+
If you would like tags to be case-sensitive and not use LIKE queries for creation:
|
713
|
+
|
714
|
+
```ruby
|
715
|
+
ActsAsTaggableOnMongoid.tags_table = AatoTags
|
716
|
+
ActsAsTaggableOnMongoid.taggings_table = AatoTaggings
|
717
|
+
```
|
718
|
+
|
719
|
+
If you want to change the default delimiter (it defaults to ','). You can also pass in an array of delimiters such as ([',', '|']):
|
720
|
+
|
721
|
+
```ruby
|
722
|
+
ActsAsTaggableOnMongoid::DefaultParser.delimiter = ','
|
723
|
+
```
|
724
|
+
|
725
|
+
## Development
|
726
|
+
|
727
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
728
|
+
|
729
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
730
|
+
|
731
|
+
## Contributing
|
732
|
+
|
733
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/acts-as-taggable-on-mongoid. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
734
|
+
|
735
|
+
## License
|
736
|
+
|
737
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
738
|
+
|
739
|
+
## Code of Conduct
|
740
|
+
|
741
|
+
Everyone interacting in the ActsAsTaggableOnMongoid project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/acts-as-taggable-on-mongoid/blob/master/CODE_OF_CONDUCT.md).
|