acts-as-taggable-on 2.0.0.pre5 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/{spec/spec.opts → .rspec} +0 -0
  4. data/.travis.yml +40 -0
  5. data/Appraisals +16 -0
  6. data/CHANGELOG.md +208 -0
  7. data/CONTRIBUTING.md +44 -0
  8. data/Gemfile +10 -5
  9. data/Guardfile +5 -0
  10. data/{MIT-LICENSE → LICENSE.md} +1 -1
  11. data/README.md +477 -0
  12. data/Rakefile +14 -52
  13. data/UPGRADING.md +8 -0
  14. data/acts-as-taggable-on.gemspec +36 -0
  15. data/{lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb → db/migrate/1_acts_as_taggable_on_migration.rb} +5 -3
  16. data/db/migrate/2_add_missing_unique_indices.rb +19 -0
  17. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +14 -0
  18. data/db/migrate/4_add_missing_taggable_index.rb +9 -0
  19. data/db/migrate/5_change_collation_for_tag_names.rb +9 -0
  20. data/gemfiles/activerecord_3.2.gemfile +15 -0
  21. data/gemfiles/activerecord_4.0.gemfile +15 -0
  22. data/gemfiles/activerecord_4.1.gemfile +15 -0
  23. data/gemfiles/activerecord_4.2.gemfile +16 -0
  24. data/lib/acts-as-taggable-on.rb +117 -22
  25. data/lib/acts_as_taggable_on/compatibility.rb +35 -0
  26. data/lib/acts_as_taggable_on/default_parser.rb +79 -0
  27. data/lib/acts_as_taggable_on/engine.rb +5 -0
  28. data/lib/acts_as_taggable_on/generic_parser.rb +19 -0
  29. data/lib/acts_as_taggable_on/tag.rb +137 -61
  30. data/lib/acts_as_taggable_on/tag_list.rb +96 -75
  31. data/lib/acts_as_taggable_on/tag_list_parser.rb +21 -0
  32. data/lib/acts_as_taggable_on/taggable/cache.rb +86 -0
  33. data/lib/acts_as_taggable_on/taggable/collection.rb +178 -0
  34. data/lib/acts_as_taggable_on/taggable/core.rb +459 -0
  35. data/lib/acts_as_taggable_on/taggable/dirty.rb +36 -0
  36. data/lib/acts_as_taggable_on/taggable/ownership.rb +125 -0
  37. data/lib/acts_as_taggable_on/taggable/related.rb +71 -0
  38. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  39. data/lib/acts_as_taggable_on/tagger.rb +88 -0
  40. data/lib/acts_as_taggable_on/tagging.rb +38 -18
  41. data/lib/acts_as_taggable_on/tags_helper.rb +12 -14
  42. data/lib/acts_as_taggable_on/utils.rb +38 -0
  43. data/lib/acts_as_taggable_on/version.rb +4 -0
  44. data/lib/acts_as_taggable_on.rb +6 -0
  45. data/lib/tasks/tags_collate_utf8.rake +21 -0
  46. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +205 -195
  47. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +79 -81
  48. data/spec/acts_as_taggable_on/caching_spec.rb +83 -0
  49. data/spec/acts_as_taggable_on/default_parser_spec.rb +47 -0
  50. data/spec/acts_as_taggable_on/generic_parser_spec.rb +14 -0
  51. data/spec/acts_as_taggable_on/related_spec.rb +99 -0
  52. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +211 -0
  53. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
  54. data/spec/acts_as_taggable_on/tag_list_spec.rb +142 -62
  55. data/spec/acts_as_taggable_on/tag_spec.rb +274 -64
  56. data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
  57. data/spec/acts_as_taggable_on/taggable_spec.rb +704 -181
  58. data/spec/acts_as_taggable_on/tagger_spec.rb +134 -56
  59. data/spec/acts_as_taggable_on/tagging_spec.rb +54 -22
  60. data/spec/acts_as_taggable_on/tags_helper_spec.rb +39 -22
  61. data/spec/acts_as_taggable_on/utils_spec.rb +23 -0
  62. data/spec/internal/app/models/altered_inheriting_taggable_model.rb +3 -0
  63. data/spec/internal/app/models/cached_model.rb +3 -0
  64. data/spec/internal/app/models/cached_model_with_array.rb +5 -0
  65. data/spec/internal/app/models/company.rb +15 -0
  66. data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
  67. data/spec/internal/app/models/market.rb +2 -0
  68. data/spec/internal/app/models/models.rb +90 -0
  69. data/spec/internal/app/models/non_standard_id_taggable_model.rb +8 -0
  70. data/spec/internal/app/models/ordered_taggable_model.rb +4 -0
  71. data/spec/internal/app/models/other_cached_model.rb +3 -0
  72. data/spec/internal/app/models/other_taggable_model.rb +4 -0
  73. data/spec/internal/app/models/student.rb +2 -0
  74. data/spec/internal/app/models/taggable_model.rb +13 -0
  75. data/spec/internal/app/models/untaggable_model.rb +3 -0
  76. data/spec/internal/app/models/user.rb +3 -0
  77. data/spec/internal/config/database.yml.sample +19 -0
  78. data/spec/internal/db/schema.rb +97 -0
  79. data/spec/spec_helper.rb +12 -38
  80. data/spec/support/0-helpers.rb +32 -0
  81. data/spec/support/array.rb +9 -0
  82. data/spec/support/database.rb +42 -0
  83. data/spec/support/database_cleaner.rb +21 -0
  84. metadata +268 -73
  85. data/CHANGELOG +0 -25
  86. data/README.rdoc +0 -212
  87. data/VERSION +0 -1
  88. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +0 -56
  89. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +0 -97
  90. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +0 -220
  91. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +0 -29
  92. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +0 -101
  93. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +0 -64
  94. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -41
  95. data/lib/acts_as_taggable_on/acts_as_tagger.rb +0 -47
  96. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -6
  97. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  98. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +0 -31
  99. data/rails/init.rb +0 -1
  100. data/spec/bm.rb +0 -52
  101. data/spec/models.rb +0 -36
  102. data/spec/schema.rb +0 -42
data/README.md ADDED
@@ -0,0 +1,477 @@
1
+ # ActsAsTaggableOn
2
+ [![Gem Version](https://badge.fury.io/rb/acts-as-taggable-on.svg)](http://badge.fury.io/rb/acts-as-taggable-on)
3
+ [![Build Status](https://secure.travis-ci.org/mbleigh/acts-as-taggable-on.png)](http://travis-ci.org/mbleigh/acts-as-taggable-on)
4
+ [![Code Climate](https://codeclimate.com/github/mbleigh/acts-as-taggable-on.png)](https://codeclimate.com/github/mbleigh/acts-as-taggable-on)
5
+ [![Inline docs](http://inch-ci.org/github/mbleigh/acts-as-taggable-on.png)](http://inch-ci.org/github/mbleigh/acts-as-taggable-on)
6
+
7
+ This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
8
+ It has evolved substantially since that point, but all credit goes to him for the
9
+ initial tagging functionality that so many people have used.
10
+
11
+ For instance, in a social network, a user might have tags that are called skills,
12
+ interests, sports, and more. There is no real way to differentiate between tags and
13
+ so an implementation of this type is not possible with acts as taggable on steroids.
14
+
15
+ Enter Acts as Taggable On. Rather than tying functionality to a specific keyword
16
+ (namely `tags`), acts as taggable on allows you to specify an arbitrary number of
17
+ tag "contexts" that can be used locally or in combination in the same way steroids
18
+ was used.
19
+
20
+ ## Compatibility
21
+
22
+ Versions 2.x are compatible with Ruby 1.8.7+ and Rails 3.
23
+
24
+ Versions 2.4.1 and up are compatible with Rails 4 too (thanks to arabonradar and cwoodcox).
25
+
26
+ Versions >= 3.x are compatible with Ruby 1.9.3+ and Rails 3 and 4.
27
+
28
+ For an up-to-date roadmap, see https://github.com/mbleigh/acts-as-taggable-on/milestones
29
+
30
+ ## Installation
31
+
32
+ To use it, add it to your Gemfile:
33
+
34
+ ```ruby
35
+ gem 'acts-as-taggable-on', '~> 3.4'
36
+ ```
37
+
38
+ and bundle:
39
+
40
+ ```shell
41
+ bundle
42
+ ```
43
+
44
+ #### Post Installation
45
+
46
+ Install migrations
47
+
48
+ ```shell
49
+ # For the latest versions :
50
+ rake acts_as_taggable_on_engine:install:migrations
51
+ # For versions 2.4.1 and earlier :
52
+ rails generate acts_as_taggable_on:migration
53
+ ```
54
+
55
+ Review the generated migrations then migrate :
56
+ ```shell
57
+ rake db:migrate
58
+ ```
59
+
60
+ #### For MySql users
61
+ You can circumvent at any time the problem of special characters [issue 623](https://github.com/mbleigh/acts-as-taggable-on/issues/623) by setting in an initializer file:
62
+
63
+ ```ruby
64
+ ActsAsTaggableOn.force_binary_collation = true
65
+ ```
66
+
67
+ Or by running this rake task:
68
+
69
+ ```shell
70
+ rake acts_as_taggable_on_engine:tag_names:collate_bin
71
+ ```
72
+
73
+ See the Configuration section for more details, and a general note valid for older
74
+ version of the gem.
75
+
76
+ #### Upgrading
77
+
78
+ see [UPGRADING](UPGRADING.md)
79
+
80
+ ## Usage
81
+
82
+ Setup
83
+
84
+ ```ruby
85
+ class User < ActiveRecord::Base
86
+ acts_as_taggable # Alias for acts_as_taggable_on :tags
87
+ acts_as_taggable_on :skills, :interests
88
+ end
89
+
90
+ class UsersController < ApplicationController
91
+ def user_params
92
+ params.require(:user).permit(:name, :tag_list => []) ## Rails 4 strong params usage
93
+ end
94
+ end
95
+
96
+ @user = User.new(:name => "Bobby")
97
+ ```
98
+
99
+ Add and remove a single tag
100
+
101
+ ```ruby
102
+ @user.tag_list.add("awesome") # add a single tag. alias for <<
103
+ @user.tag_list.remove("awesome") # remove a single tag
104
+ ```
105
+
106
+ Add and remove multiple tags in an array
107
+
108
+ ```ruby
109
+ @user.tag_list.add("awesome", "slick")
110
+ @user.tag_list.remove("awesome", "slick")
111
+ ```
112
+
113
+ You can also add and remove tags in format of String. This would
114
+ be convenient in some cases such as handling tag input param in a String.
115
+
116
+ Pay attention you need to add `parse: true` as option in this case.
117
+
118
+ You may also want to take a look at delimiter in the string. The default
119
+ is comma `,` so you don't need to do anything here. However, if you made
120
+ a change on delimiter setting, make sure the string will match. See
121
+ [configuration](#configuration) for more about delimiter.
122
+
123
+ ```ruby
124
+ @user.tag_list.add("awesome, slick", parse: true)
125
+ @user.tag_list.remove("awesome, slick", parse: true)
126
+ ```
127
+
128
+ You can also add and remove tags by direct assignment. Note this will
129
+ remove existing tags so use it with attention.
130
+
131
+ ```ruby
132
+ @user.tag_list = "awesome, slick, hefty"
133
+ @user.save
134
+ @user.reload
135
+ @user.tags
136
+ => [#<ActsAsTaggableOn::Tag id: 1, name: "awesome", taggings_count: 1>,
137
+ #<ActsAsTaggableOn::Tag id: 2, name: "slick", taggings_count: 1>,
138
+ #<ActsAsTaggableOn::Tag id: 3, name: "hefty", taggings_count: 1>]
139
+ ```
140
+
141
+ With the defined context in model, you have multiple new methods at disposal
142
+ to manage and view the tags in the context. For example, with `:skill` context
143
+ these methods are added to the model: `skill_list`(and `skill_list.add`, `skill_list.remove`
144
+ `skill_list=`), `skills`(plural), `skill_counts`.
145
+
146
+ ```ruby
147
+ @user.skill_list = "joking, clowning, boxing"
148
+ @user.save
149
+ @user.reload
150
+ @user.skills
151
+ => [#<ActsAsTaggableOn::Tag id: 1, name: "joking", taggings_count: 1>,
152
+ #<ActsAsTaggableOn::Tag id: 2, name: "clowning", taggings_count: 1>,
153
+ #<ActsAsTaggableOn::Tag id: 3, name: "boxing", taggings_count: 1>]
154
+
155
+ @user.skill_list.add("coding")
156
+
157
+ @user.skill_list
158
+ # => ["joking", "clowning", "boxing", "coding"]
159
+
160
+ @another_user = User.new(:name => "Alice")
161
+ @another_user.skill_list.add("clowning")
162
+ @another_user.save
163
+
164
+ User.skill_counts
165
+ => [#<ActsAsTaggableOn::Tag id: 1, name: "joking", taggings_count: 1>,
166
+ #<ActsAsTaggableOn::Tag id: 2, name: "clowning", taggings_count: 2>,
167
+ #<ActsAsTaggableOn::Tag id: 3, name: "boxing", taggings_count: 1>]
168
+ ```
169
+
170
+ To preserve the order in which tags are created use `acts_as_ordered_taggable`:
171
+
172
+ ```ruby
173
+ class User < ActiveRecord::Base
174
+ # Alias for acts_as_ordered_taggable_on :tags
175
+ acts_as_ordered_taggable
176
+ acts_as_ordered_taggable_on :skills, :interests
177
+ end
178
+
179
+ @user = User.new(:name => "Bobby")
180
+ @user.tag_list = "east, south"
181
+ @user.save
182
+
183
+ @user.tag_list = "north, east, south, west"
184
+ @user.save
185
+
186
+ @user.reload
187
+ @user.tag_list # => ["north", "east", "south", "west"]
188
+ ```
189
+
190
+ ### Finding most or least used tags
191
+
192
+ You can find the most or least used tags by using:
193
+
194
+ ```ruby
195
+ ActsAsTaggableOn::Tag.most_used
196
+ ActsAsTaggableOn::Tag.least_used
197
+ ```
198
+
199
+ You can also filter the results by passing the method a limit, however the default limit is 20.
200
+
201
+ ```ruby
202
+ ActsAsTaggableOn::Tag.most_used(10)
203
+ ActsAsTaggableOn::Tag.least_used(10)
204
+ ```
205
+
206
+ ### Finding Tagged Objects
207
+
208
+ Acts As Taggable On uses scopes to create an association for tags.
209
+ This way you can mix and match to filter down your results.
210
+
211
+ ```ruby
212
+ class User < ActiveRecord::Base
213
+ acts_as_taggable_on :tags, :skills
214
+ scope :by_join_date, order("created_at DESC")
215
+ end
216
+
217
+ User.tagged_with("awesome").by_join_date
218
+ User.tagged_with("awesome").by_join_date.paginate(:page => params[:page], :per_page => 20)
219
+
220
+ # Find users that matches all given tags:
221
+ User.tagged_with(["awesome", "cool"], :match_all => true)
222
+
223
+ # Find users with any of the specified tags:
224
+ User.tagged_with(["awesome", "cool"], :any => true)
225
+
226
+ # Find users that has not been tagged with awesome or cool:
227
+ User.tagged_with(["awesome", "cool"], :exclude => true)
228
+
229
+ # Find users with any of the tags based on context:
230
+ User.tagged_with(['awesome', 'cool'], :on => :tags, :any => true).tagged_with(['smart', 'shy'], :on => :skills, :any => true)
231
+ ```
232
+
233
+ You can also use `:wild => true` option along with `:any` or `:exclude` option. It will be looking for `%awesome%` and `%cool%` in SQL.
234
+
235
+ __Tip:__ `User.tagged_with([])` or `User.tagged_with('')` will return `[]`, an empty set of records.
236
+
237
+
238
+ ### Relationships
239
+
240
+ You can find objects of the same type based on similar tags on certain contexts.
241
+ Also, objects will be returned in descending order based on the total number of
242
+ matched tags.
243
+
244
+ ```ruby
245
+ @bobby = User.find_by_name("Bobby")
246
+ @bobby.skill_list # => ["jogging", "diving"]
247
+
248
+ @frankie = User.find_by_name("Frankie")
249
+ @frankie.skill_list # => ["hacking"]
250
+
251
+ @tom = User.find_by_name("Tom")
252
+ @tom.skill_list # => ["hacking", "jogging", "diving"]
253
+
254
+ @tom.find_related_skills # => [<User name="Bobby">, <User name="Frankie">]
255
+ @bobby.find_related_skills # => [<User name="Tom">]
256
+ @frankie.find_related_skills # => [<User name="Tom">]
257
+ ```
258
+
259
+ ### Dynamic Tag Contexts
260
+
261
+ In addition to the generated tag contexts in the definition, it is also possible
262
+ to allow for dynamic tag contexts (this could be user generated tag contexts!)
263
+
264
+ ```ruby
265
+ @user = User.new(:name => "Bobby")
266
+ @user.set_tag_list_on(:customs, "same, as, tag, list")
267
+ @user.tag_list_on(:customs) # => ["same", "as", "tag", "list"]
268
+ @user.save
269
+ @user.tags_on(:customs) # => [<Tag name='same'>,...]
270
+ @user.tag_counts_on(:customs)
271
+ User.tagged_with("same", :on => :customs) # => [@user]
272
+ ```
273
+
274
+ ### Tag Parsers
275
+
276
+ If you want to change how tags are parsed, you can define a your own implementation:
277
+
278
+ ```ruby
279
+ class MyParser < ActsAsTaggableOn::GenericParser
280
+ def parse
281
+ ActsAsTaggableOn::TagList.new.tap do |tag_list|
282
+ tag_list.add @tag_list.split('|')
283
+ end
284
+ end
285
+ end
286
+ ```
287
+
288
+ Now you can use this parser, passing it as parameter:
289
+
290
+ ```ruby
291
+ @user = User.new(:name => "Bobby")
292
+ @user.tag_list = "east, south"
293
+ @user.tag_list.add("north|west", parser: MyParser)
294
+ @user.tag_list # => ["north", "east", "south", "west"]
295
+
296
+ # Or also:
297
+ @user.tag_list.parser = MyParser
298
+ @user.tag_list.add("north|west")
299
+ @user.tag_list # => ["north", "east", "south", "west"]
300
+ ```
301
+
302
+ Or change it globally:
303
+
304
+ ```ruby
305
+ ActsAsTaggable.default_parser = MyParser
306
+ @user = User.new(:name => "Bobby")
307
+ @user.tag_list = "east|south"
308
+ @user.tag_list # => ["east", "south"]
309
+ ```
310
+
311
+ ### Tag Ownership
312
+
313
+ Tags can have owners:
314
+
315
+ ```ruby
316
+ class User < ActiveRecord::Base
317
+ acts_as_tagger
318
+ end
319
+
320
+ class Photo < ActiveRecord::Base
321
+ acts_as_taggable_on :locations
322
+ end
323
+
324
+ @some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
325
+ @some_user.owned_taggings
326
+ @some_user.owned_tags
327
+ Photo.tagged_with("paris", :on => :locations, :owned_by => @some_user)
328
+ @some_photo.locations_from(@some_user) # => ["paris", "normandy"]
329
+ @some_photo.owner_tags_on(@some_user, :locations) # => [#<ActsAsTaggableOn::Tag id: 1, name: "paris">...]
330
+ @some_photo.owner_tags_on(nil, :locations) # => Ownerships equivalent to saying @some_photo.locations
331
+ @some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations, :skip_save => true) #won't save @some_photo object
332
+ ```
333
+
334
+ ### Dirty objects
335
+
336
+ ```ruby
337
+ @bobby = User.find_by_name("Bobby")
338
+ @bobby.skill_list # => ["jogging", "diving"]
339
+
340
+ @bobby.skill_list_changed? #=> false
341
+ @bobby.changes #=> {}
342
+
343
+ @bobby.skill_list = "swimming"
344
+ @bobby.changes.should == {"skill_list"=>["jogging, diving", ["swimming"]]}
345
+ @bobby.skill_list_changed? #=> true
346
+
347
+ @bobby.skill_list_change.should == ["jogging, diving", ["swimming"]]
348
+ ```
349
+
350
+ ### Tag cloud calculations
351
+
352
+ To construct tag clouds, the frequency of each tag needs to be calculated.
353
+ Because we specified `acts_as_taggable_on` on the `User` class, we can
354
+ get a calculation of all the tag counts by using `User.tag_counts_on(:customs)`. But what if we wanted a tag count for
355
+ a single user's posts? To achieve this we call tag_counts on the association:
356
+
357
+ ```ruby
358
+ User.find(:first).posts.tag_counts_on(:tags)
359
+ ```
360
+
361
+ A helper is included to assist with generating tag clouds.
362
+
363
+ Here is an example that generates a tag cloud.
364
+
365
+ Helper:
366
+
367
+ ```ruby
368
+ module PostsHelper
369
+ include ActsAsTaggableOn::TagsHelper
370
+ end
371
+ ```
372
+
373
+ Controller:
374
+
375
+ ```ruby
376
+ class PostController < ApplicationController
377
+ def tag_cloud
378
+ @tags = Post.tag_counts_on(:tags)
379
+ end
380
+ end
381
+ ```
382
+
383
+ View:
384
+
385
+ ```erb
386
+ <% tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| %>
387
+ <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
388
+ <% end %>
389
+ ```
390
+
391
+ CSS:
392
+
393
+ ```css
394
+ .css1 { font-size: 1.0em; }
395
+ .css2 { font-size: 1.2em; }
396
+ .css3 { font-size: 1.4em; }
397
+ .css4 { font-size: 1.6em; }
398
+ ```
399
+
400
+ ## Configuration
401
+
402
+ If you would like to remove unused tag objects after removing taggings, add:
403
+
404
+ ```ruby
405
+ ActsAsTaggableOn.remove_unused_tags = true
406
+ ```
407
+
408
+ If you want force tags to be saved downcased:
409
+
410
+ ```ruby
411
+ ActsAsTaggableOn.force_lowercase = true
412
+ ```
413
+
414
+ If you want tags to be saved parametrized (you can redefine to_param as well):
415
+
416
+ ```ruby
417
+ ActsAsTaggableOn.force_parameterize = true
418
+ ```
419
+
420
+ If you would like tags to be case-sensitive and not use LIKE queries for creation:
421
+
422
+ ```ruby
423
+ ActsAsTaggableOn.strict_case_match = true
424
+ ```
425
+
426
+ If you would like to have an exact match covering special characters with MySql:
427
+
428
+ ```ruby
429
+ ActsAsTaggableOn.force_binary_collation = true
430
+ ```
431
+
432
+ If you want to change the default delimiter (it defaults to ','). You can also pass in an array of delimiters such as ([',', '|']):
433
+
434
+ ```ruby
435
+ ActsAsTaggableOn.delimiter = ','
436
+ ```
437
+
438
+ *NOTE 1: SQLite by default can't upcase or downcase multibyte characters, resulting in unwanted behavior. Load the SQLite ICU extension for proper handle of such characters. [See docs](http://www.sqlite.org/src/artifact?ci=trunk&filename=ext/icu/README.txt)*
439
+
440
+ *NOTE 2: the option `force_binary_collation` is strongest than `strict_case_match` and when
441
+ set to true, the `strict_case_match` is ignored.
442
+ To roughly apply the `force_binary_collation` behaviour with a version of the gem <= 3.4.4, execute the following commands in the MySql console:*
443
+
444
+ ```shell
445
+ USE my_wonderful_app_db;
446
+ ALTER TABLE tags MODIFY name VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin;
447
+ ```
448
+
449
+ ## Contributors
450
+
451
+ We have a long list of valued contributors. [Check them all](https://github.com/mbleigh/acts-as-taggable-on/contributors)
452
+
453
+ ## Maintainer
454
+
455
+ * [Joost Baaij](https://github.com/tilsammans)
456
+
457
+ ## TODO
458
+
459
+ - Write benchmark script
460
+ - Resolve concurrency issues
461
+
462
+ ## Testing
463
+
464
+ Acts As Taggable On uses RSpec for its test coverage. Inside the gem
465
+ directory, you can run the specs with:
466
+
467
+ ```shell
468
+ bundle
469
+ rake spec
470
+ ```
471
+
472
+ You can run all the tests across all the Rails versions by running `rake appraise`. If you'd also like to [run the tests across all rubies and databases as configured for Travis CI, install and run `wwtd`](https://github.com/grosser/wwtd).
473
+
474
+
475
+ ## License
476
+
477
+ See [LICENSE](https://github.com/mbleigh/acts-as-taggable-on/blob/master/LICENSE.md)
data/Rakefile CHANGED
@@ -1,59 +1,21 @@
1
- begin
2
- # Rspec 1.3.0
3
- require 'spec/rake/spectask'
1
+ require 'rubygems'
2
+ require 'bundler/setup'
4
3
 
5
- desc 'Default: run specs'
6
- task :default => :spec
7
- Spec::Rake::SpecTask.new do |t|
8
- t.spec_files = FileList["spec/**/*_spec.rb"]
9
- end
4
+ import "./lib/tasks/tags_collate_utf8.rake"
10
5
 
11
- Spec::Rake::SpecTask.new('rcov') do |t|
12
- t.spec_files = FileList["spec/**/*_spec.rb"]
13
- t.rcov = true
14
- t.rcov_opts = ['--exclude', 'spec']
15
- end
16
-
17
- rescue LoadError
18
- # Rspec 2.0
19
- require 'rspec/core/rake_task'
6
+ desc 'Default: run specs'
7
+ task default: :spec
20
8
 
21
- desc 'Default: run specs'
22
- task :default => :spec
23
- Rspec::Core::RakeTask.new do |t|
24
- t.pattern = "spec/**/*_spec.rb"
25
- end
26
-
27
- Rspec::Core::RakeTask.new('rcov') do |t|
28
- t.pattern = "spec/**/*_spec.rb"
29
- t.rcov = true
30
- t.rcov_opts = ['--exclude', 'spec']
31
- end
32
-
33
- rescue LoadError
34
- puts "Rspec not available. Install it with: gem install rspec"
9
+ desc 'Copy sample spec database.yml over if not exists'
10
+ task :copy_db_config do
11
+ cp 'spec/internal/config/database.yml.sample', 'spec/internal/config/database.yml'
35
12
  end
36
13
 
37
- namespace 'rails2.3' do
38
- task :spec do
39
- gemfile = File.join(File.dirname(__FILE__), 'lib', 'acts_as_taggable_on', 'compatibility', 'Gemfile')
40
- ENV['BUNDLE_GEMFILE'] = gemfile
41
- Rake::Task['spec'].invoke
42
- end
14
+ task spec: [:copy_db_config]
15
+
16
+ require 'rspec/core/rake_task'
17
+ RSpec::Core::RakeTask.new do |t|
18
+ t.pattern = 'spec/**/*_spec.rb'
43
19
  end
44
20
 
45
- begin
46
- require 'jeweler'
47
- Jeweler::Tasks.new do |gemspec|
48
- gemspec.name = "acts-as-taggable-on"
49
- gemspec.summary = "ActsAsTaggableOn is a tagging plugin for Rails that provides multiple tagging contexts on a single model."
50
- gemspec.description = "With ActsAsTaggableOn, you could tag a single model on several contexts, such as skills, interests, and awards. It also provides other advanced functionality."
51
- gemspec.email = "michael@intridea.com"
52
- gemspec.homepage = "http://github.com/mbleigh/acts-as-taggable-on"
53
- gemspec.authors = ["Michael Bleigh"]
54
- gemspec.files = FileList["[A-Z]*", "{generators,lib,spec,rails}/**/*"] - FileList["**/*.log"]
55
- end
56
- Jeweler::GemcutterTasks.new
57
- rescue LoadError
58
- puts "Jeweler not available. Install it with: gem install jeweler"
59
- end
21
+ Bundler::GemHelper.install_tasks
data/UPGRADING.md ADDED
@@ -0,0 +1,8 @@
1
+ When upgrading
2
+
3
+ Re-run the migrations generator
4
+
5
+ rake acts_as_taggable_on_engine:install:migrations
6
+
7
+ This will create any new migrations and skip existing ones
8
+ Version 3.5.0 has a migration for mysql adapter
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'acts_as_taggable_on/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'acts-as-taggable-on'
8
+ gem.version = ActsAsTaggableOn::VERSION
9
+ gem.authors = ['Michael Bleigh', 'Joost Baaij']
10
+ gem.email = %w(michael@intridea.com joost@spacebabies.nl)
11
+ gem.description = %q{With ActsAsTaggableOn, you can tag a single model on several contexts, such as skills, interests, and awards. It also provides other advanced functionality.}
12
+ gem.summary = 'Advanced tagging for Rails.'
13
+ gem.homepage = 'https://github.com/mbleigh/acts-as-taggable-on'
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.test_files = gem.files.grep(%r{^spec/})
18
+ gem.require_paths = ['lib']
19
+ gem.required_ruby_version = '>= 1.9.3'
20
+
21
+ if File.exist?('UPGRADING.md')
22
+ gem.post_install_message = File.read('UPGRADING.md')
23
+ end
24
+
25
+ gem.add_runtime_dependency 'activerecord', ['>= 3.2', '< 5']
26
+
27
+ gem.add_development_dependency 'sqlite3'
28
+ gem.add_development_dependency 'mysql2', '~> 0.3.7'
29
+ gem.add_development_dependency 'pg'
30
+
31
+ gem.add_development_dependency 'rspec-rails'
32
+ gem.add_development_dependency 'rspec-its'
33
+ gem.add_development_dependency 'rspec'
34
+ gem.add_development_dependency 'barrier'
35
+ gem.add_development_dependency 'database_cleaner'
36
+ end
@@ -9,10 +9,12 @@ class ActsAsTaggableOnMigration < ActiveRecord::Migration
9
9
 
10
10
  # You should make sure that the column created is
11
11
  # long enough to store the required class names.
12
- t.references :taggable, :polymorphic => true
13
- t.references :tagger, :polymorphic => true
12
+ t.references :taggable, polymorphic: true
13
+ t.references :tagger, polymorphic: true
14
14
 
15
- t.string :context
15
+ # Limit is created to prevent MySQL error on index
16
+ # length for MyISAM table type: http://bit.ly/vgW2Ql
17
+ t.string :context, limit: 128
16
18
 
17
19
  t.datetime :created_at
18
20
  end
@@ -0,0 +1,19 @@
1
+ class AddMissingUniqueIndices < ActiveRecord::Migration
2
+ def self.up
3
+ add_index :tags, :name, unique: true
4
+
5
+ remove_index :taggings, :tag_id
6
+ remove_index :taggings, [:taggable_id, :taggable_type, :context]
7
+ add_index :taggings,
8
+ [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
9
+ unique: true, name: 'taggings_idx'
10
+ end
11
+
12
+ def self.down
13
+ remove_index :tags, :name
14
+
15
+ remove_index :taggings, name: 'taggings_idx'
16
+ add_index :taggings, :tag_id
17
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ class AddTaggingsCounterCacheToTags < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :tags, :taggings_count, :integer, default: 0
4
+
5
+ ActsAsTaggableOn::Tag.reset_column_information
6
+ ActsAsTaggableOn::Tag.find_each do |tag|
7
+ ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings)
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ remove_column :tags, :taggings_count
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ class AddMissingTaggableIndex < ActiveRecord::Migration
2
+ def self.up
3
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
4
+ end
5
+
6
+ def self.down
7
+ remove_index :taggings, [:taggable_id, :taggable_type, :context]
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # This migration is added to circumvent issue #623 and have special characters
2
+ # work properly
3
+ class ChangeCollationForTagNames < ActiveRecord::Migration
4
+ def up
5
+ if ActsAsTaggableOn::Utils.using_mysql?
6
+ execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", :github => "rails/rails", :branch => "3-2-stable"
6
+
7
+ group :local_development do
8
+ gem "guard"
9
+ gem "guard-rspec"
10
+ gem "appraisal"
11
+ gem "rake"
12
+ gem "byebug", :platform => :mri_21
13
+ end
14
+
15
+ gemspec :path => "../"