make_taggable 0.6.3

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.
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,123 @@
1
+ require "spec_helper"
2
+
3
+ describe "Acts As Taggable On" do
4
+ describe "Caching" do
5
+ before(:each) do
6
+ @taggable = CachedModel.new(name: "Bob Jones")
7
+ @another_taggable = OtherCachedModel.new(name: "John Smith")
8
+ end
9
+
10
+ it "should add saving of tag lists and cached tag lists to the instance" do
11
+ expect(@taggable).to respond_to(:save_cached_tag_list)
12
+ expect(@another_taggable).to respond_to(:save_cached_tag_list)
13
+
14
+ expect(@taggable).to respond_to(:save_tags)
15
+ end
16
+
17
+ it "should add cached tag lists to the instance if cached column is not present" do
18
+ expect(TaggableModel.new(name: "Art Kram")).to_not respond_to(:save_cached_tag_list)
19
+ end
20
+
21
+ it "should generate a cached column checker for each tag type" do
22
+ expect(CachedModel).to respond_to(:caching_tag_list?)
23
+ expect(OtherCachedModel).to respond_to(:caching_language_list?)
24
+ end
25
+
26
+ it "should not have cached tags" do
27
+ expect(@taggable.cached_tag_list).to be_blank
28
+ expect(@another_taggable.cached_language_list).to be_blank
29
+ end
30
+
31
+ it "should cache tags" do
32
+ @taggable.update(tag_list: "awesome, epic")
33
+ expect(@taggable.cached_tag_list).to eq("awesome, epic")
34
+
35
+ @another_taggable.update(language_list: "ruby, .net")
36
+ expect(@another_taggable.cached_language_list).to eq("ruby, .net")
37
+ end
38
+
39
+ it "should keep the cache" do
40
+ @taggable.update(tag_list: "awesome, epic")
41
+ @taggable = CachedModel.find(@taggable.id)
42
+ @taggable.save!
43
+ expect(@taggable.cached_tag_list).to eq("awesome, epic")
44
+ end
45
+
46
+ it "should update the cache" do
47
+ @taggable.update(tag_list: "awesome, epic")
48
+ @taggable.update(tag_list: "awesome")
49
+ expect(@taggable.cached_tag_list).to eq("awesome")
50
+ end
51
+
52
+ it "should remove the cache" do
53
+ @taggable.update(tag_list: "awesome, epic")
54
+ @taggable.update(tag_list: "")
55
+ expect(@taggable.cached_tag_list).to be_blank
56
+ end
57
+
58
+ it "should have a tag list" do
59
+ @taggable.update(tag_list: "awesome, epic")
60
+ @taggable = CachedModel.find(@taggable.id)
61
+ expect(@taggable.tag_list.sort).to eq(%w[awesome epic].sort)
62
+ end
63
+
64
+ it "should keep the tag list" do
65
+ @taggable.update(tag_list: "awesome, epic")
66
+ @taggable = CachedModel.find(@taggable.id)
67
+ @taggable.save!
68
+ expect(@taggable.tag_list.sort).to eq(%w[awesome epic].sort)
69
+ end
70
+
71
+ it "should clear the cache on reset_column_information" do
72
+ CachedModel.column_names
73
+ CachedModel.reset_column_information
74
+ expect(CachedModel.instance_variable_get(:@make_taggable_cache_columns)).to eql(nil)
75
+ end
76
+
77
+ it "should not override a user-defined columns method" do
78
+ expect(ColumnsOverrideModel.columns.map(&:name)).not_to include("ignored_column")
79
+ ColumnsOverrideModel.acts_as_taggable
80
+ expect(ColumnsOverrideModel.columns.map(&:name)).not_to include("ignored_column")
81
+ end
82
+ end
83
+
84
+ describe "with a custom delimiter" do
85
+ before(:each) do
86
+ @taggable = CachedModel.new(name: "Bob Jones")
87
+ @another_taggable = OtherCachedModel.new(name: "John Smith")
88
+ MakeTaggable.delimiter = ";"
89
+ end
90
+
91
+ after(:all) do
92
+ MakeTaggable.delimiter = ","
93
+ end
94
+
95
+ it "should cache tags with custom delimiter" do
96
+ @taggable.update(tag_list: "awesome; epic")
97
+ expect(@taggable.tag_list).to eq(["awesome", "epic"])
98
+ expect(@taggable.cached_tag_list).to eq("awesome; epic")
99
+
100
+ @taggable = CachedModel.find_by_name("Bob Jones")
101
+ expect(@taggable.tag_list).to eq(["awesome", "epic"])
102
+ expect(@taggable.cached_tag_list).to eq("awesome; epic")
103
+ end
104
+ end
105
+
106
+ describe "Cache methods initialization on new models" do
107
+ before(:all) do
108
+ ActiveRecord::Base.connection.execute(
109
+ "INSERT INTO cache_methods_injected_models (cached_tag_list) VALUES ('ciao')"
110
+ )
111
+ class CacheMethodsInjectedModel < ActiveRecord::Base
112
+ acts_as_taggable
113
+ end
114
+ end
115
+ after(:all) { Object.send(:remove_const, :CacheMethodsInjectedModel) }
116
+
117
+ it "cached_tag_list_on? get injected correctly" do
118
+ expect {
119
+ CacheMethodsInjectedModel.first.tag_list
120
+ }.not_to raise_error
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ describe MakeTaggable::DefaultParser do
4
+ it "#parse should return empty array if empty array is passed" do
5
+ parser = MakeTaggable::DefaultParser.new([])
6
+ expect(parser.parse).to be_empty
7
+ end
8
+
9
+ describe "Multiple Delimiter" do
10
+ before do
11
+ @old_delimiter = MakeTaggable.delimiter
12
+ end
13
+
14
+ after do
15
+ MakeTaggable.delimiter = @old_delimiter
16
+ end
17
+
18
+ it "should separate tags by delimiters" do
19
+ MakeTaggable.delimiter = [",", " ", '\|']
20
+ parser = MakeTaggable::DefaultParser.new("cool, data|I have")
21
+ expect(parser.parse.to_s).to eq("cool, data, I, have")
22
+ end
23
+
24
+ it "should escape quote" do
25
+ MakeTaggable.delimiter = [",", " ", '\|']
26
+ parser = MakeTaggable::DefaultParser.new("'I have'|cool, data")
27
+ expect(parser.parse.to_s).to eq('"I have", cool, data')
28
+
29
+ parser = MakeTaggable::DefaultParser.new('"I, have"|cool, data')
30
+ expect(parser.parse.to_s).to eq('"I, have", cool, data')
31
+ end
32
+
33
+ it "should work for utf8 delimiter and long delimiter" do
34
+ MakeTaggable.delimiter = [",", "的", "可能是"]
35
+ parser = MakeTaggable::DefaultParser.new("我的东西可能是不见了,还好有备份")
36
+ expect(parser.parse.to_s).to eq("我, 东西, 不见了, 还好有备份")
37
+ end
38
+
39
+ it "should work for multiple quoted tags" do
40
+ MakeTaggable.delimiter = [","]
41
+ parser = MakeTaggable::DefaultParser.new('"Ruby Monsters","eat Katzenzungen"')
42
+ expect(parser.parse.to_s).to eq("Ruby Monsters, eat Katzenzungen")
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,140 @@
1
+ require "spec_helper"
2
+
3
+ describe "Dirty behavior of taggable objects" do
4
+ context "with un-contexted tags" do
5
+ before(:each) do
6
+ @taggable = TaggableModel.create(tag_list: "awesome, epic")
7
+ end
8
+
9
+ context "when tag_list changed" do
10
+ before(:each) do
11
+ expect(@taggable.changes).to be_empty
12
+ @taggable.tag_list = "one"
13
+ end
14
+
15
+ it "should show changes of dirty object" do
16
+ expect(@taggable.changes).to eq({"tag_list" => [["awesome", "epic"], ["one"]]})
17
+ end
18
+
19
+ it "should show changes of freshly initialized dirty object" do
20
+ taggable = TaggableModel.find(@taggable.id)
21
+ taggable.tag_list = "one"
22
+ expect(taggable.changes).to eq({"tag_list" => [["awesome", "epic"], ["one"]]})
23
+ end
24
+
25
+ if Rails.version >= "5.1"
26
+ it "flags tag_list as changed" do
27
+ expect(@taggable.will_save_change_to_tag_list?).to be_truthy
28
+ end
29
+ end
30
+
31
+ it "preserves original value" do
32
+ expect(@taggable.tag_list_was).to eq(["awesome", "epic"])
33
+ end
34
+
35
+ it "shows what the change was" do
36
+ expect(@taggable.tag_list_change).to eq([["awesome", "epic"], ["one"]])
37
+ end
38
+
39
+ context "without order" do
40
+ it "should not mark attribute if order change " do
41
+ taggable = TaggableModel.create(name: "Dirty Harry", tag_list: %w[d c b a])
42
+ taggable.tag_list = %w[a b c d]
43
+ expect(taggable.tag_list_changed?).to be_falsey
44
+ end
45
+ end
46
+
47
+ context "with order" do
48
+ it "should mark attribute if order change" do
49
+ taggable = OrderedTaggableModel.create(name: "Clean Harry", tag_list: "d,c,b,a")
50
+ taggable.save
51
+ taggable.tag_list = %w[a b c d]
52
+ expect(taggable.tag_list_changed?).to be_truthy
53
+ end
54
+ end
55
+ end
56
+
57
+ context "when tag_list is the same" do
58
+ before(:each) do
59
+ @taggable.tag_list = "awesome, epic"
60
+ end
61
+
62
+ it "is not flagged as changed" do
63
+ expect(@taggable.tag_list_changed?).to be_falsy
64
+ end
65
+
66
+ it "does not show any changes to the taggable item" do
67
+ expect(@taggable.changes).to be_empty
68
+ end
69
+
70
+ context "and using a delimiter different from a ','" do
71
+ before do
72
+ @old_delimiter = MakeTaggable.delimiter
73
+ MakeTaggable.delimiter = ";"
74
+ end
75
+
76
+ after do
77
+ MakeTaggable.delimiter = @old_delimiter
78
+ end
79
+
80
+ it "does not show any changes to the taggable item when using array assignments" do
81
+ @taggable.tag_list = %w[awesome epic]
82
+ expect(@taggable.changes).to be_empty
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ context "with context tags" do
89
+ before(:each) do
90
+ @taggable = TaggableModel.create("language_list" => "awesome, epic")
91
+ end
92
+
93
+ context "when language_list changed" do
94
+ before(:each) do
95
+ expect(@taggable.changes).to be_empty
96
+ @taggable.language_list = "one"
97
+ end
98
+
99
+ it "should show changes of dirty object" do
100
+ expect(@taggable.changes).to eq({"language_list" => [["awesome", "epic"], ["one"]]})
101
+ end
102
+
103
+ it "flags language_list as changed" do
104
+ expect(@taggable.language_list_changed?).to be_truthy
105
+ end
106
+
107
+ it "preserves original value" do
108
+ expect(@taggable.language_list_was).to eq(["awesome", "epic"])
109
+ end
110
+
111
+ it "shows what the change was" do
112
+ expect(@taggable.language_list_change).to eq([["awesome", "epic"], ["one"]])
113
+ end
114
+ end
115
+
116
+ context "when language_list is the same" do
117
+ before(:each) do
118
+ @taggable.language_list = "awesome, epic"
119
+ end
120
+
121
+ it "is not flagged as changed" do
122
+ expect(@taggable.language_list_changed?).to be_falsy
123
+ end
124
+
125
+ it "does not show any changes to the taggable item" do
126
+ expect(@taggable.changes).to be_empty
127
+ end
128
+ end
129
+
130
+ context "when language_list changed by association" do
131
+ let(:tag) { MakeTaggable::Tag.new(name: "one") }
132
+
133
+ it "flags language_list as changed" do
134
+ expect(@taggable.changes).to be_empty
135
+ @taggable.languages << tag
136
+ expect(@taggable.language_list_changed?).to be_truthy
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ describe MakeTaggable::GenericParser do
4
+ it "#parse should return empty array if empty tag string is passed" do
5
+ tag_list = MakeTaggable::GenericParser.new("")
6
+ expect(tag_list.parse).to be_empty
7
+ end
8
+
9
+ it "#parse should separate tags by comma" do
10
+ tag_list = MakeTaggable::GenericParser.new("cool,data,,I,have")
11
+ expect(tag_list.parse).to eq(%w[cool data I have])
12
+ end
13
+ end
@@ -0,0 +1,260 @@
1
+ require "spec_helper"
2
+
3
+ describe "Acts As Taggable On" do
4
+ it "should provide a class method 'taggable?' that is false for untaggable models" do
5
+ expect(UntaggableModel).to_not be_taggable
6
+ end
7
+
8
+ describe "Taggable Method Generation To Preserve Order" do
9
+ before(:each) do
10
+ TaggableModel.tag_types = []
11
+ TaggableModel.preserve_tag_order = false
12
+ TaggableModel.acts_as_ordered_taggable_on(:ordered_tags)
13
+ @taggable = TaggableModel.new(name: "Bob Jones")
14
+ end
15
+
16
+ it "should respond 'true' to preserve_tag_order?" do
17
+ expect(@taggable.class.preserve_tag_order?).to be_truthy
18
+ end
19
+ end
20
+
21
+ describe "Taggable Method Generation" do
22
+ before(:each) do
23
+ TaggableModel.tag_types = []
24
+ TaggableModel.make_taggable(:tags, :languages, :skills, :needs, :offerings)
25
+ @taggable = TaggableModel.new(name: "Bob Jones")
26
+ end
27
+
28
+ it "should respond 'true' to taggable?" do
29
+ expect(@taggable.class).to be_taggable
30
+ end
31
+
32
+ it "should create a class attribute for tag types" do
33
+ expect(@taggable.class).to respond_to(:tag_types)
34
+ end
35
+
36
+ it "should create an instance attribute for tag types" do
37
+ expect(@taggable).to respond_to(:tag_types)
38
+ end
39
+
40
+ it "should have all tag types" do
41
+ expect(@taggable.tag_types).to eq([:tags, :languages, :skills, :needs, :offerings])
42
+ end
43
+
44
+ it "should create a class attribute for preserve tag order" do
45
+ expect(@taggable.class).to respond_to(:preserve_tag_order?)
46
+ end
47
+
48
+ it "should create an instance attribute for preserve tag order" do
49
+ expect(@taggable).to respond_to(:preserve_tag_order?)
50
+ end
51
+
52
+ it "should respond 'false' to preserve_tag_order?" do
53
+ expect(@taggable.class.preserve_tag_order?).to be_falsy
54
+ end
55
+
56
+ it "should generate an association for each tag type" do
57
+ expect(@taggable).to respond_to(:tags, :skills, :languages)
58
+ end
59
+
60
+ it "should add tagged_with and tag_counts to singleton" do
61
+ expect(TaggableModel).to respond_to(:tagged_with, :tag_counts)
62
+ end
63
+
64
+ it "should generate a tag_list accessor/setter for each tag type" do
65
+ expect(@taggable).to respond_to(:tag_list, :skill_list, :language_list)
66
+ expect(@taggable).to respond_to(:tag_list=, :skill_list=, :language_list=)
67
+ end
68
+
69
+ it "should generate a tag_list accessor, that includes owned tags, for each tag type" do
70
+ expect(@taggable).to respond_to(:all_tags_list, :all_skills_list, :all_languages_list)
71
+ end
72
+ end
73
+
74
+ describe "Matching Contexts" do
75
+ it "should find objects with tags of matching contexts" do
76
+ taggable1 = TaggableModel.create!(name: "Taggable 1")
77
+ taggable2 = TaggableModel.create!(name: "Taggable 2")
78
+ taggable3 = TaggableModel.create!(name: "Taggable 3")
79
+
80
+ taggable1.offering_list = "one, two"
81
+ taggable1.save!
82
+
83
+ taggable2.need_list = "one, two"
84
+ taggable2.save!
85
+
86
+ taggable3.offering_list = "one, two"
87
+ taggable3.save!
88
+
89
+ expect(taggable1.find_matching_contexts(:offerings, :needs)).to include(taggable2)
90
+ expect(taggable1.find_matching_contexts(:offerings, :needs)).to_not include(taggable3)
91
+ end
92
+
93
+ it "should find other related objects with tags of matching contexts" do
94
+ taggable1 = TaggableModel.create!(name: "Taggable 1")
95
+ taggable2 = OtherTaggableModel.create!(name: "Taggable 2")
96
+ taggable3 = OtherTaggableModel.create!(name: "Taggable 3")
97
+
98
+ taggable1.offering_list = "one, two"
99
+ taggable1.save
100
+
101
+ taggable2.need_list = "one, two"
102
+ taggable2.save
103
+
104
+ taggable3.offering_list = "one, two"
105
+ taggable3.save
106
+
107
+ expect(taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs)).to include(taggable2)
108
+ expect(taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs)).to_not include(taggable3)
109
+ end
110
+
111
+ it "should not include the object itself in the list of related objects with tags of matching contexts" do
112
+ taggable1 = TaggableModel.create!(name: "Taggable 1")
113
+ taggable2 = TaggableModel.create!(name: "Taggable 2")
114
+
115
+ taggable1.offering_list = "one, two"
116
+ taggable1.need_list = "one, two"
117
+ taggable1.save
118
+
119
+ taggable2.need_list = "one, two"
120
+ taggable2.save
121
+
122
+ expect(taggable1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(taggable2)
123
+ expect(taggable1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(taggable1)
124
+ end
125
+
126
+ it "should ensure joins to multiple taggings maintain their contexts when aliasing" do
127
+ taggable1 = TaggableModel.create!(name: "Taggable 1")
128
+
129
+ taggable1.offering_list = "one"
130
+ taggable1.need_list = "two"
131
+
132
+ taggable1.save
133
+
134
+ column = TaggableModel.connection.quote_column_name("context")
135
+ offer_alias = TaggableModel.connection.quote_table_name(MakeTaggable.taggings_table)
136
+ need_alias = TaggableModel.connection.quote_table_name("need_taggings_taggable_models_join")
137
+
138
+ expect(TaggableModel.joins(:offerings, :needs).to_sql).to include "#{offer_alias}.#{column}"
139
+ expect(TaggableModel.joins(:offerings, :needs).to_sql).to include "#{need_alias}.#{column}"
140
+ end
141
+ end
142
+
143
+ describe "Tagging Contexts" do
144
+ it "should eliminate duplicate tagging contexts " do
145
+ TaggableModel.make_taggable(:skills, :skills)
146
+ expect(TaggableModel.tag_types.freq[:skills]).to eq(1)
147
+ end
148
+
149
+ it "should not contain embedded/nested arrays" do
150
+ TaggableModel.make_taggable([:array], [:array])
151
+ expect(TaggableModel.tag_types.freq[[:array]]).to eq(0)
152
+ end
153
+
154
+ it "should _flatten_ the content of arrays" do
155
+ TaggableModel.make_taggable([:array], [:array])
156
+ expect(TaggableModel.tag_types.freq[:array]).to eq(1)
157
+ end
158
+
159
+ it "should not raise an error when passed nil" do
160
+ expect(-> {
161
+ TaggableModel.make_taggable
162
+ }).to_not raise_error
163
+ end
164
+
165
+ it "should not raise an error when passed [nil]" do
166
+ expect(-> {
167
+ TaggableModel.make_taggable([nil])
168
+ }).to_not raise_error
169
+ end
170
+
171
+ it "should include dynamic contexts in tagging_contexts" do
172
+ taggable = TaggableModel.create!(name: "Dynamic Taggable")
173
+ taggable.set_tag_list_on :colors, "tag1, tag2, tag3"
174
+ expect(taggable.tagging_contexts).to eq(%w[tags languages skills needs offerings array colors])
175
+ taggable.save
176
+ taggable = TaggableModel.where(name: "Dynamic Taggable").first
177
+ expect(taggable.tagging_contexts).to eq(%w[tags languages skills needs offerings array colors])
178
+ end
179
+ end
180
+
181
+ context 'when tagging context ends in an "s" when singular (ex. "status", "glass", etc.)' do
182
+ describe "caching" do
183
+ before { @taggable = OtherCachedModel.new(name: "John Smith") }
184
+ subject { @taggable }
185
+
186
+ it { should respond_to(:save_cached_tag_list) }
187
+
188
+ it { expect(@taggable.cached_language_list).to eq nil }
189
+ it { expect(@taggable.cached_status_list).to eq nil }
190
+ it { expect(@taggable.cached_glass_list).to eq nil }
191
+
192
+ context "language taggings cache after update" do
193
+ before { @taggable.update(language_list: "ruby, .net") }
194
+ subject { @taggable }
195
+
196
+ it { expect(@taggable.language_list).to eq ["ruby", ".net"] }
197
+ it { expect(@taggable.cached_language_list).to eq "ruby, .net" }
198
+ end
199
+
200
+ context "status taggings cache after update" do
201
+ before { @taggable.update(status_list: "happy, married") }
202
+ subject { @taggable }
203
+
204
+ it { expect(@taggable.status_list).to eq ["happy", "married"] }
205
+ it { expect(@taggable.cached_status_list).to eq "happy, married" }
206
+ end
207
+
208
+ context "glass taggings cache after update" do
209
+ before do
210
+ @taggable.update(glass_list: "rectangle, aviator")
211
+ end
212
+
213
+ subject { @taggable }
214
+ it { expect(@taggable.glass_list).to eq ["rectangle", "aviator"] }
215
+ it { expect(@taggable.cached_glass_list).to eq "rectangle, aviator" }
216
+ end
217
+ end
218
+ end
219
+
220
+ describe "taggings" do
221
+ before(:each) do
222
+ @taggable = TaggableModel.new(name: "Art Kram")
223
+ end
224
+
225
+ it "should return no taggings" do
226
+ expect(@taggable.taggings).to be_empty
227
+ end
228
+ end
229
+
230
+ describe "@@remove_unused_tags" do
231
+ before do
232
+ @taggable = TaggableModel.create(name: "Bob Jones")
233
+ @tag = MakeTaggable::Tag.create(name: "awesome")
234
+
235
+ @tagging = MakeTaggable::Tagging.create(taggable: @taggable, tag: @tag, context: "tags")
236
+ end
237
+
238
+ context "if set to true" do
239
+ before do
240
+ MakeTaggable.remove_unused_tags = true
241
+ end
242
+
243
+ it "should remove unused tags after removing taggings" do
244
+ @tagging.destroy
245
+ expect(MakeTaggable::Tag.find_by_name("awesome")).to be_nil
246
+ end
247
+ end
248
+
249
+ context "if set to false" do
250
+ before do
251
+ MakeTaggable.remove_unused_tags = false
252
+ end
253
+
254
+ it "should not remove unused tags after removing taggings" do
255
+ @tagging.destroy
256
+ expect(MakeTaggable::Tag.find_by_name("awesome")).to eq(@tag)
257
+ end
258
+ end
259
+ end
260
+ end