make_taggable 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +47 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +3 -0
  5. data/.standard.yml +18 -0
  6. data/.standard_todo.yml +5 -0
  7. data/.travis.yml +36 -0
  8. data/Appraisals +11 -0
  9. data/CHANGELOG.md +0 -0
  10. data/CODE_OF_CONDUCT.md +74 -0
  11. data/CONTRIBUTING.md +57 -0
  12. data/Gemfile +16 -0
  13. data/LICENSE.md +20 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +478 -0
  16. data/Rakefile +7 -0
  17. data/bin/console +14 -0
  18. data/bin/setup +8 -0
  19. data/db/migrate/1_create_make_taggable_tags.rb +10 -0
  20. data/db/migrate/2_create_make_taggable_taggings.rb +12 -0
  21. data/db/migrate/3_add_index_to_tags.rb +5 -0
  22. data/db/migrate/4_add_index_to_taggings.rb +12 -0
  23. data/gemfiles/rails_5.gemfile +9 -0
  24. data/gemfiles/rails_6.gemfile +9 -0
  25. data/gemfiles/rails_master.gemfile +9 -0
  26. data/lib/make_taggable.rb +134 -0
  27. data/lib/make_taggable/default_parser.rb +75 -0
  28. data/lib/make_taggable/engine.rb +4 -0
  29. data/lib/make_taggable/generic_parser.rb +19 -0
  30. data/lib/make_taggable/tag.rb +131 -0
  31. data/lib/make_taggable/tag_list.rb +102 -0
  32. data/lib/make_taggable/taggable.rb +100 -0
  33. data/lib/make_taggable/taggable/cache.rb +90 -0
  34. data/lib/make_taggable/taggable/collection.rb +183 -0
  35. data/lib/make_taggable/taggable/core.rb +323 -0
  36. data/lib/make_taggable/taggable/ownership.rb +137 -0
  37. data/lib/make_taggable/taggable/related.rb +71 -0
  38. data/lib/make_taggable/taggable/tag_list_type.rb +4 -0
  39. data/lib/make_taggable/taggable/tagged_with_query.rb +16 -0
  40. data/lib/make_taggable/taggable/tagged_with_query/all_tags_query.rb +111 -0
  41. data/lib/make_taggable/taggable/tagged_with_query/any_tags_query.rb +68 -0
  42. data/lib/make_taggable/taggable/tagged_with_query/exclude_tags_query.rb +81 -0
  43. data/lib/make_taggable/taggable/tagged_with_query/query_base.rb +61 -0
  44. data/lib/make_taggable/tagger.rb +89 -0
  45. data/lib/make_taggable/tagging.rb +32 -0
  46. data/lib/make_taggable/tags_helper.rb +15 -0
  47. data/lib/make_taggable/utils.rb +34 -0
  48. data/lib/make_taggable/version.rb +4 -0
  49. data/lib/tasks/tags_collate_utf8.rake +17 -0
  50. data/make_taggable.gemspec +26 -0
  51. data/spec/dummy/README.md +24 -0
  52. data/spec/dummy/Rakefile +6 -0
  53. data/spec/dummy/app/assets/config/manifest.js +2 -0
  54. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  55. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  57. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  58. data/spec/dummy/app/jobs/application_job.rb +7 -0
  59. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  60. data/spec/dummy/app/models/altered_inheriting_taggable_model.rb +5 -0
  61. data/spec/dummy/app/models/application_record.rb +3 -0
  62. data/spec/dummy/app/models/cached_model.rb +3 -0
  63. data/spec/dummy/app/models/cached_model_with_array.rb +11 -0
  64. data/spec/dummy/app/models/columns_override_model.rb +5 -0
  65. data/spec/dummy/app/models/company.rb +15 -0
  66. data/spec/dummy/app/models/concerns/.keep +0 -0
  67. data/spec/dummy/app/models/inheriting_taggable_model.rb +4 -0
  68. data/spec/dummy/app/models/market.rb +2 -0
  69. data/spec/dummy/app/models/non_standard_id_taggable_model.rb +8 -0
  70. data/spec/dummy/app/models/ordered_taggable_model.rb +4 -0
  71. data/spec/dummy/app/models/other_cached_model.rb +3 -0
  72. data/spec/dummy/app/models/other_taggable_model.rb +4 -0
  73. data/spec/dummy/app/models/student.rb +4 -0
  74. data/spec/dummy/app/models/taggable_model.rb +14 -0
  75. data/spec/dummy/app/models/untaggable_model.rb +3 -0
  76. data/spec/dummy/app/models/user.rb +3 -0
  77. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  78. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  79. data/spec/dummy/bin/rails +4 -0
  80. data/spec/dummy/bin/rake +4 -0
  81. data/spec/dummy/bin/setup +33 -0
  82. data/spec/dummy/config.ru +5 -0
  83. data/spec/dummy/config/application.rb +19 -0
  84. data/spec/dummy/config/boot.rb +5 -0
  85. data/spec/dummy/config/cable.yml +10 -0
  86. data/spec/dummy/config/credentials.yml.enc +1 -0
  87. data/spec/dummy/config/database.yml +25 -0
  88. data/spec/dummy/config/environment.rb +5 -0
  89. data/spec/dummy/config/environments/development.rb +52 -0
  90. data/spec/dummy/config/environments/production.rb +105 -0
  91. data/spec/dummy/config/environments/test.rb +49 -0
  92. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  93. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  94. data/spec/dummy/config/initializers/cors.rb +16 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  96. data/spec/dummy/config/initializers/inflections.rb +16 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  98. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  99. data/spec/dummy/config/locales/en.yml +33 -0
  100. data/spec/dummy/config/master.key +1 -0
  101. data/spec/dummy/config/puma.rb +38 -0
  102. data/spec/dummy/config/routes.rb +3 -0
  103. data/spec/dummy/config/spring.rb +6 -0
  104. data/spec/dummy/config/storage.yml +34 -0
  105. data/spec/dummy/db/migrate/20201119220853_create_taggable_models.rb +8 -0
  106. data/spec/dummy/db/migrate/20201119221037_create_columns_override_models.rb +9 -0
  107. data/spec/dummy/db/migrate/20201119221121_create_non_standard_id_taggable_models.rb +8 -0
  108. data/spec/dummy/db/migrate/20201119221228_create_untaggable_models.rb +8 -0
  109. data/spec/dummy/db/migrate/20201119221247_create_cached_models.rb +9 -0
  110. data/spec/dummy/db/migrate/20201119221314_create_other_cached_models.rb +11 -0
  111. data/spec/dummy/db/migrate/20201119221343_create_companies.rb +7 -0
  112. data/spec/dummy/db/migrate/20201119221416_create_users.rb +7 -0
  113. data/spec/dummy/db/migrate/20201119221434_create_other_taggable_models.rb +8 -0
  114. data/spec/dummy/db/migrate/20201119221507_create_ordered_taggable_models.rb +8 -0
  115. data/spec/dummy/db/migrate/20201119221530_create_cache_methods_injected_models.rb +7 -0
  116. data/spec/dummy/db/migrate/20201119221629_create_other_cached_with_array_models.rb +11 -0
  117. data/spec/dummy/db/migrate/20201119221746_create_taggable_model_with_jsons.rb +9 -0
  118. data/spec/dummy/db/migrate/20201119222429_create_make_taggable_tags.make_taggable_engine.rb +11 -0
  119. data/spec/dummy/db/migrate/20201119222430_create_make_taggable_taggings.make_taggable_engine.rb +13 -0
  120. data/spec/dummy/db/migrate/20201119222431_add_index_to_tags.make_taggable_engine.rb +6 -0
  121. data/spec/dummy/db/migrate/20201119222432_add_index_to_taggings.make_taggable_engine.rb +13 -0
  122. data/spec/dummy/db/schema.rb +117 -0
  123. data/spec/dummy/db/seeds.rb +7 -0
  124. data/spec/dummy/lib/tasks/.keep +0 -0
  125. data/spec/dummy/log/.keep +0 -0
  126. data/spec/dummy/public/robots.txt +1 -0
  127. data/spec/dummy/storage/.keep +0 -0
  128. data/spec/dummy/test/channels/application_cable/connection_test.rb +11 -0
  129. data/spec/dummy/test/controllers/.keep +0 -0
  130. data/spec/dummy/test/fixtures/.keep +0 -0
  131. data/spec/dummy/test/fixtures/files/.keep +0 -0
  132. data/spec/dummy/test/integration/.keep +0 -0
  133. data/spec/dummy/test/mailers/.keep +0 -0
  134. data/spec/dummy/test/models/.keep +0 -0
  135. data/spec/dummy/test/test_helper.rb +13 -0
  136. data/spec/dummy/vendor/.keep +0 -0
  137. data/spec/make_taggable/acts_as_tagger_spec.rb +112 -0
  138. data/spec/make_taggable/caching_spec.rb +123 -0
  139. data/spec/make_taggable/default_parser_spec.rb +45 -0
  140. data/spec/make_taggable/dirty_spec.rb +140 -0
  141. data/spec/make_taggable/generic_parser_spec.rb +13 -0
  142. data/spec/make_taggable/make_taggable_spec.rb +260 -0
  143. data/spec/make_taggable/related_spec.rb +93 -0
  144. data/spec/make_taggable/single_table_inheritance_spec.rb +220 -0
  145. data/spec/make_taggable/tag_list_spec.rb +169 -0
  146. data/spec/make_taggable/tag_spec.rb +297 -0
  147. data/spec/make_taggable/taggable_spec.rb +804 -0
  148. data/spec/make_taggable/tagger_spec.rb +149 -0
  149. data/spec/make_taggable/tagging_spec.rb +115 -0
  150. data/spec/make_taggable/tags_helper_spec.rb +43 -0
  151. data/spec/make_taggable/utils_spec.rb +22 -0
  152. data/spec/make_taggable_spec.rb +5 -0
  153. data/spec/spec_helper.rb +18 -0
  154. data/spec/support/array.rb +9 -0
  155. data/spec/support/helpers.rb +31 -0
  156. metadata +391 -0
@@ -0,0 +1,93 @@
1
+ require "spec_helper"
2
+
3
+ describe "Acts As Taggable On" do
4
+ describe "Related Objects" do
5
+ # TODO, shared example
6
+ it "should find related objects based on tag names on context" do
7
+ taggable1 = TaggableModel.create!(name: "Taggable 1", tag_list: "one, two")
8
+ taggable2 = TaggableModel.create!(name: "Taggable 2", tag_list: "three, four")
9
+ taggable3 = TaggableModel.create!(name: "Taggable 3", tag_list: "one, four")
10
+
11
+ expect(taggable1.find_related_tags).to include(taggable3)
12
+ expect(taggable1.find_related_tags).to_not include(taggable2)
13
+ end
14
+
15
+ it "finds related tags for ordered taggable on" do
16
+ taggable1 = OrderedTaggableModel.create!(name: "Taggable 1", colour_list: "one, two")
17
+ taggable2 = OrderedTaggableModel.create!(name: "Taggable 2", colour_list: "three, four")
18
+ taggable3 = OrderedTaggableModel.create!(name: "Taggable 3", colour_list: "one, four")
19
+
20
+ expect(taggable1.find_related_colours).to include(taggable3)
21
+ expect(taggable1.find_related_colours).to_not include(taggable2)
22
+ end
23
+
24
+ it "should find related objects based on tag names on context - non standard id" do
25
+ taggable1 = NonStandardIdTaggableModel.create!(name: "Taggable 1", tag_list: "one, two")
26
+ taggable2 = NonStandardIdTaggableModel.create!(name: "Taggable 2", tag_list: "three, four")
27
+ taggable3 = NonStandardIdTaggableModel.create!(name: "Taggable 3", tag_list: "one, four")
28
+
29
+ expect(taggable1.find_related_tags).to include(taggable3)
30
+ expect(taggable1.find_related_tags).to_not include(taggable2)
31
+ end
32
+
33
+ it "should find other related objects based on tag names on context" do
34
+ taggable1 = TaggableModel.create!(name: "Taggable 1", tag_list: "one, two")
35
+ taggable2 = OtherTaggableModel.create!(name: "Taggable 2", tag_list: "three, four")
36
+ taggable3 = OtherTaggableModel.create!(name: "Taggable 3", tag_list: "one, four")
37
+
38
+ expect(taggable1.find_related_tags_for(OtherTaggableModel)).to include(taggable3)
39
+ expect(taggable1.find_related_tags_for(OtherTaggableModel)).to_not include(taggable2)
40
+ end
41
+
42
+ it "should find other related objects based on tags only from particular context" do
43
+ taggable1 = TaggableModel.create!(name: "Taggable 1", tag_list: "one, two")
44
+ taggable2 = TaggableModel.create!(name: "Taggable 2", tag_list: "three, four", skill_list: "one, two")
45
+ taggable3 = TaggableModel.create!(name: "Taggable 3", tag_list: "one, four")
46
+
47
+ expect(taggable1.find_related_tags).to include(taggable3)
48
+ expect(taggable1.find_related_tags).to_not include(taggable2)
49
+ end
50
+
51
+ shared_examples "a collection" do
52
+ it do
53
+ taggable1 = described_class.create!(name: "Taggable 1", tag_list: "one")
54
+ taggable2 = described_class.create!(name: "Taggable 2", tag_list: "one, two")
55
+
56
+ expect(taggable1.find_related_tags).to include(taggable2)
57
+ expect(taggable1.find_related_tags).to_not include(taggable1)
58
+ end
59
+ end
60
+
61
+ # it 'should not include the object itself in the list of related objects' do
62
+ describe TaggableModel do
63
+ it_behaves_like "a collection"
64
+ end
65
+
66
+ # it 'should not include the object itself in the list of related objects - non standard id' do
67
+ describe NonStandardIdTaggableModel do
68
+ it_behaves_like "a collection"
69
+ end
70
+
71
+ context "Ignored Tags" do
72
+ let(:taggable1) { TaggableModel.create!(name: "Taggable 1", tag_list: "one, two, four") }
73
+ let(:taggable2) { TaggableModel.create!(name: "Taggable 2", tag_list: "two, three") }
74
+ let(:taggable3) { TaggableModel.create!(name: "Taggable 3", tag_list: "one, three") }
75
+
76
+ it "should not include ignored tags in related search" do
77
+ expect(taggable1.find_related_tags(ignore: "two")).to_not include(taggable2)
78
+ expect(taggable1.find_related_tags(ignore: "two")).to include(taggable3)
79
+ end
80
+
81
+ it "should accept array of ignored tags" do
82
+ taggable4 = TaggableModel.create!(name: "Taggable 4", tag_list: "four")
83
+
84
+ expect(taggable1.find_related_tags(ignore: ["two", "four"])).to_not include(taggable2)
85
+ expect(taggable1.find_related_tags(ignore: ["two", "four"])).to_not include(taggable4)
86
+ end
87
+
88
+ it "should accept symbols as ignored tags" do
89
+ expect(taggable1.find_related_tags(ignore: :two)).to_not include(taggable2)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,220 @@
1
+ require "spec_helper"
2
+
3
+ describe "Single Table Inheritance" do
4
+ let(:taggable) { TaggableModel.new(name: "taggable model") }
5
+
6
+ let(:inheriting_model) { InheritingTaggableModel.new(name: "Inheriting Taggable Model") }
7
+ let(:altered_inheriting) { AlteredInheritingTaggableModel.new(name: "Altered Inheriting Model") }
8
+
9
+ 1.upto(4) do |n|
10
+ let(:"inheriting_#{n}") { InheritingTaggableModel.new(name: "Inheriting Model #{n}") }
11
+ end
12
+
13
+ let(:student) { Student.create! }
14
+
15
+ describe "tag contexts" do
16
+ it "should pass on to STI-inherited models" do
17
+ expect(inheriting_model).to respond_to(:tag_list, :skill_list, :language_list)
18
+ expect(altered_inheriting).to respond_to(:tag_list, :skill_list, :language_list)
19
+ end
20
+
21
+ it "should pass on to altered STI models" do
22
+ expect(altered_inheriting).to respond_to(:part_list)
23
+ end
24
+ end
25
+
26
+ context "matching contexts" do
27
+ before do
28
+ inheriting_1.offering_list = "one, two"
29
+ inheriting_1.need_list = "one, two"
30
+ inheriting_1.save!
31
+
32
+ inheriting_2.need_list = "one, two"
33
+ inheriting_2.save!
34
+
35
+ inheriting_3.offering_list = "one, two"
36
+ inheriting_3.save!
37
+
38
+ inheriting_4.tag_list = "one, two, three, four"
39
+ inheriting_4.save!
40
+
41
+ taggable.need_list = "one, two"
42
+ taggable.save!
43
+ end
44
+
45
+ it "should find objects with tags of matching contexts" do
46
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to include(inheriting_2)
47
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_3)
48
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_4)
49
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(taggable)
50
+
51
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(inheriting_2)
52
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_3)
53
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_4)
54
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(taggable)
55
+ end
56
+
57
+ it "should not include the object itself in the list of related objects with tags of matching contexts" do
58
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_1)
59
+ expect(inheriting_1.find_matching_contexts_for(InheritingTaggableModel, :offerings, :needs)).to_not include(inheriting_1)
60
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_1)
61
+ end
62
+ end
63
+
64
+ context "find related tags" do
65
+ before do
66
+ inheriting_1.tag_list = "one, two"
67
+ inheriting_1.save
68
+
69
+ inheriting_2.tag_list = "three, four"
70
+ inheriting_2.save
71
+
72
+ inheriting_3.tag_list = "one, four"
73
+ inheriting_3.save
74
+
75
+ taggable.tag_list = "one, two, three, four"
76
+ taggable.save
77
+ end
78
+
79
+ it "should find related objects based on tag names on context" do
80
+ expect(inheriting_1.find_related_tags).to include(inheriting_3)
81
+ expect(inheriting_1.find_related_tags).to_not include(inheriting_2)
82
+ expect(inheriting_1.find_related_tags).to_not include(taggable)
83
+
84
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(inheriting_3)
85
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_2)
86
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(taggable)
87
+ end
88
+
89
+ it "should not include the object itself in the list of related objects" do
90
+ expect(inheriting_1.find_related_tags).to_not include(inheriting_1)
91
+ expect(inheriting_1.find_related_tags_for(InheritingTaggableModel)).to_not include(inheriting_1)
92
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_1)
93
+ end
94
+ end
95
+
96
+ describe "tag list" do
97
+ before do
98
+ @inherited_same = InheritingTaggableModel.new(name: "inherited same")
99
+ @inherited_different = AlteredInheritingTaggableModel.new(name: "inherited different")
100
+ end
101
+
102
+ # TODO, shared example
103
+ it "should be able to save tags for inherited models" do
104
+ inheriting_model.tag_list = "bob, kelso"
105
+ inheriting_model.save
106
+ expect(InheritingTaggableModel.tagged_with("bob").first).to eq(inheriting_model)
107
+ end
108
+
109
+ it "should find STI tagged models on the superclass" do
110
+ inheriting_model.tag_list = "bob, kelso"
111
+ inheriting_model.save
112
+ expect(TaggableModel.tagged_with("bob").first).to eq(inheriting_model)
113
+ end
114
+
115
+ it "should be able to add on contexts only to some subclasses" do
116
+ altered_inheriting.part_list = "fork, spoon"
117
+ altered_inheriting.save
118
+ expect(InheritingTaggableModel.tagged_with("fork", on: :parts)).to be_empty
119
+ expect(AlteredInheritingTaggableModel.tagged_with("fork", on: :parts).first).to eq(altered_inheriting)
120
+ end
121
+
122
+ it "should have different tag_counts_on for inherited models" do
123
+ inheriting_model.tag_list = "bob, kelso"
124
+ inheriting_model.save!
125
+ altered_inheriting.tag_list = "fork, spoon"
126
+ altered_inheriting.save!
127
+
128
+ expect(InheritingTaggableModel.tag_counts_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso])
129
+ expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[fork spoon])
130
+ expect(TaggableModel.tag_counts_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso fork spoon])
131
+ end
132
+
133
+ it "should have different tags_on for inherited models" do
134
+ inheriting_model.tag_list = "bob, kelso"
135
+ inheriting_model.save!
136
+ altered_inheriting.tag_list = "fork, spoon"
137
+ altered_inheriting.save!
138
+
139
+ expect(InheritingTaggableModel.tags_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso])
140
+ expect(AlteredInheritingTaggableModel.tags_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[fork spoon])
141
+ expect(TaggableModel.tags_on(:tags, order: "#{MakeTaggable.tags_table}.id").map(&:name)).to eq(%w[bob kelso fork spoon])
142
+ end
143
+
144
+ it "should store same tag without validation conflict" do
145
+ taggable.tag_list = "one"
146
+ taggable.save!
147
+
148
+ inheriting_model.tag_list = "one"
149
+ inheriting_model.save!
150
+
151
+ inheriting_model.update! name: "foo"
152
+ end
153
+
154
+ it "should only join with taggable table to check type for inherited models" do
155
+ expect(TaggableModel.tag_counts_on(:tags).to_sql).to_not match(/INNER JOIN taggable_models ON/)
156
+ expect(InheritingTaggableModel.tag_counts_on(:tags).to_sql).to match(/INNER JOIN taggable_models ON/)
157
+ end
158
+ end
159
+
160
+ describe "ownership" do
161
+ it "should have taggings" do
162
+ student.tag(taggable, with: "ruby,scheme", on: :tags)
163
+ expect(student.owned_taggings.count).to eq(2)
164
+ end
165
+
166
+ it "should have tags" do
167
+ student.tag(taggable, with: "ruby,scheme", on: :tags)
168
+ expect(student.owned_tags.count).to eq(2)
169
+ end
170
+
171
+ it "should return tags for the inheriting tagger" do
172
+ student.tag(taggable, with: "ruby, scheme", on: :tags)
173
+ expect(taggable.tags_from(student)).to eq(%w[ruby scheme])
174
+ end
175
+
176
+ it "returns all owner tags on the taggable" do
177
+ student.tag(taggable, with: "ruby, scheme", on: :tags)
178
+ student.tag(taggable, with: "skill_one", on: :skills)
179
+ student.tag(taggable, with: "english, spanish", on: :language)
180
+ expect(taggable.owner_tags(student).count).to eq(5)
181
+ expect(taggable.owner_tags(student).sort == %w[english ruby scheme skill_one spanish])
182
+ end
183
+
184
+ it "returns owner tags on the tagger" do
185
+ student.tag(taggable, with: "ruby, scheme", on: :tags)
186
+ expect(taggable.owner_tags_on(student, :tags).count).to eq(2)
187
+ end
188
+
189
+ it "returns owner tags on the taggable for an array of contexts" do
190
+ student.tag(taggable, with: "ruby, scheme", on: :tags)
191
+ student.tag(taggable, with: "skill_one, skill_two", on: :skills)
192
+ expect(taggable.owner_tags_on(student, [:tags, :skills]).count).to eq(4)
193
+ expect(taggable.owner_tags_on(student, [:tags, :skills]).sort == %w[ruby scheme skill_one skill_two])
194
+ end
195
+
196
+ it "should scope objects returned by tagged_with by owners" do
197
+ student.tag(taggable, with: "ruby, scheme", on: :tags)
198
+ expect(TaggableModel.tagged_with(%w[ruby scheme], owned_by: student).count).to eq(1)
199
+ end
200
+ end
201
+
202
+ describe "a subclass of Tag" do
203
+ let(:company) { Company.new(name: "Dewey, Cheatham & Howe") }
204
+ let(:user) { User.create! }
205
+
206
+ subject { Market.create! name: "finance" }
207
+
208
+ it "sets STI type through string list" do
209
+ company.market_list = "law, accounting"
210
+ company.save!
211
+ expect(Market.count).to eq(2)
212
+ end
213
+
214
+ it "does not interfere with a normal Tag context on the same model" do
215
+ company.location_list = "cambridge"
216
+ company.save!
217
+ expect(MakeTaggable::Tag.where(name: "cambridge")).to_not be_empty
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,169 @@
1
+ require "spec_helper"
2
+
3
+ describe MakeTaggable::TagList do
4
+ let(:tag_list) { MakeTaggable::TagList.new("awesome", "radical") }
5
+ let(:another_tag_list) { MakeTaggable::TagList.new("awesome", "crazy", "alien") }
6
+
7
+ it { should be_kind_of Array }
8
+
9
+ describe "#add" do
10
+ it "should be able to be add a new tag word" do
11
+ tag_list.add("cool")
12
+ expect(tag_list.include?("cool")).to be_truthy
13
+ end
14
+
15
+ it "should be able to add delimited lists of words" do
16
+ tag_list.add("cool, wicked", parse: true)
17
+ expect(tag_list).to include("cool", "wicked")
18
+ end
19
+
20
+ it "should be able to add delimited list of words with quoted delimiters" do
21
+ tag_list.add("'cool, wicked', \"really cool, really wicked\"", parse: true)
22
+ expect(tag_list).to include("cool, wicked", "really cool, really wicked")
23
+ end
24
+
25
+ it "should be able to handle other uses of quotation marks correctly" do
26
+ tag_list.add("john's cool car, mary's wicked toy", parse: true)
27
+ expect(tag_list).to include("john's cool car", "mary's wicked toy")
28
+ end
29
+
30
+ it "should be able to add an array of words" do
31
+ tag_list.add(%w[cool wicked], parse: true)
32
+ expect(tag_list).to include("cool", "wicked")
33
+ end
34
+
35
+ it "should quote escape tags with commas in them" do
36
+ tag_list.add("cool", "rad,bodacious")
37
+ expect(tag_list.to_s).to eq("awesome, radical, cool, \"rad,bodacious\"")
38
+ end
39
+ end
40
+
41
+ describe "#remove" do
42
+ it "should be able to remove words" do
43
+ tag_list.remove("awesome")
44
+ expect(tag_list).to_not include("awesome")
45
+ end
46
+
47
+ it "should be able to remove delimited lists of words" do
48
+ tag_list.remove("awesome, radical", parse: true)
49
+ expect(tag_list).to be_empty
50
+ end
51
+
52
+ it "should be able to remove an array of words" do
53
+ tag_list.remove(%w[awesome radical], parse: true)
54
+ expect(tag_list).to be_empty
55
+ end
56
+ end
57
+
58
+ describe "#+" do
59
+ it "should not have duplicate tags" do
60
+ new_tag_list = tag_list + another_tag_list
61
+ expect(tag_list).to eq(%w[awesome radical])
62
+ expect(another_tag_list).to eq(%w[awesome crazy alien])
63
+ expect(new_tag_list).to eq(%w[awesome radical crazy alien])
64
+ end
65
+
66
+ it "should have class : MakeTaggable::TagList" do
67
+ new_tag_list = tag_list + another_tag_list
68
+ expect(new_tag_list.class).to eq(MakeTaggable::TagList)
69
+ end
70
+ end
71
+
72
+ describe "#concat" do
73
+ it "should not have duplicate tags" do
74
+ expect(tag_list.concat(another_tag_list)).to eq(%w[awesome radical crazy alien])
75
+ end
76
+
77
+ it "should have class : MakeTaggable::TagList" do
78
+ new_tag_list = tag_list.concat(another_tag_list)
79
+ expect(new_tag_list.class).to eq(MakeTaggable::TagList)
80
+ end
81
+
82
+ context "without duplicates" do
83
+ let(:arr) { ["crazy", "alien"] }
84
+ let(:another_tag_list) { MakeTaggable::TagList.new(*arr) }
85
+ it "adds other list" do
86
+ expect(tag_list.concat(another_tag_list)).to eq(%w[awesome radical crazy alien])
87
+ end
88
+
89
+ it "adds other array" do
90
+ expect(tag_list.concat(arr)).to eq(%w[awesome radical crazy alien])
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "#to_s" do
96
+ it "should give a delimited list of words when converted to string" do
97
+ expect(tag_list.to_s).to eq("awesome, radical")
98
+ end
99
+
100
+ it "should be able to call to_s on a frozen tag list" do
101
+ tag_list.freeze
102
+ expect(-> { tag_list.add("cool", "rad,bodacious") }).to raise_error(RuntimeError)
103
+ expect(-> { tag_list.to_s }).to_not raise_error
104
+ end
105
+ end
106
+
107
+ describe "cleaning" do
108
+ it "should parameterize if force_parameterize is set to true" do
109
+ MakeTaggable.force_parameterize = true
110
+ tag_list = MakeTaggable::TagList.new("awesome()", "radical)(cc")
111
+
112
+ expect(tag_list.to_s).to eq("awesome, radical-cc")
113
+ MakeTaggable.force_parameterize = false
114
+ end
115
+
116
+ it "should lowercase if force_lowercase is set to true" do
117
+ MakeTaggable.force_lowercase = true
118
+
119
+ tag_list = MakeTaggable::TagList.new("aweSomE", "RaDicaL", "Entrée")
120
+ expect(tag_list.to_s).to eq("awesome, radical, entrée")
121
+
122
+ MakeTaggable.force_lowercase = false
123
+ end
124
+
125
+ it "should ignore case when removing duplicates if strict_case_match is false" do
126
+ tag_list = MakeTaggable::TagList.new("Junglist", "JUNGLIST", "Junglist", "Massive", "MASSIVE", "MASSIVE")
127
+
128
+ expect(tag_list.to_s).to eq("Junglist, Massive")
129
+ end
130
+
131
+ it "should not ignore case when removing duplicates if strict_case_match is true" do
132
+ MakeTaggable.strict_case_match = true
133
+ tag_list = MakeTaggable::TagList.new("Junglist", "JUNGLIST", "Junglist", "Massive", "MASSIVE", "MASSIVE")
134
+
135
+ expect(tag_list.to_s).to eq("Junglist, JUNGLIST, Massive, MASSIVE")
136
+ MakeTaggable.strict_case_match = false
137
+ end
138
+ end
139
+
140
+ describe "custom parser" do
141
+ let(:parser) { double(parse: %w[cool wicked]) }
142
+ let(:parser_class) { stub_const("MyParser", Class) }
143
+
144
+ it "should use a the default parser if none is set as parameter" do
145
+ allow(MakeTaggable.default_parser).to receive(:new).and_return(parser)
146
+ MakeTaggable::TagList.new("cool, wicked", parse: true)
147
+
148
+ expect(parser).to have_received(:parse)
149
+ end
150
+
151
+ it "should use the custom parser passed as parameter" do
152
+ allow(parser_class).to receive(:new).and_return(parser)
153
+
154
+ MakeTaggable::TagList.new("cool, wicked", parser: parser_class)
155
+
156
+ expect(parser).to have_received(:parse)
157
+ end
158
+
159
+ it "should use the parser setted as attribute" do
160
+ allow(parser_class).to receive(:new).with("new, tag").and_return(parser)
161
+
162
+ tag_list = MakeTaggable::TagList.new("example")
163
+ tag_list.parser = parser_class
164
+ tag_list.add("new, tag", parse: true)
165
+
166
+ expect(parser).to have_received(:parse)
167
+ end
168
+ end
169
+ end