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,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