acts-as-taggable-on-mongoid 6.0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.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).
|