acts_as_word_cloud 0.0.1 → 0.0.2

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.
@@ -5,223 +5,143 @@ module ActsAsWordCloud
5
5
  end
6
6
 
7
7
  module ClassMethods
8
- # args:
9
- # using, takes an array of Symbols that refer to the methods we'll use on the model calling word_cloud
10
- # excluded models, takes in Class names of models we want to ignore that are associated to model calling word_cloud
11
- # skipped attributes, takes in Symbols referring to attributes that won't be pulled from model calling word_cloud
12
- # depth level, takes in an Integer referring to how deep of a recursive search we'll make
13
- # no_mixin_fields, is a default pre-set array of methods to use on models that do not include the mixin
14
- #
15
- def acts_as_word_cloud(args)
8
+ # Sets up the word_cloud method and takes arguments that control what it returns
9
+ #
10
+ # @param [Hash] args
11
+ # @param args [Array] :included_methods An array of method symbols used to create this model's word cloud
12
+ # @param args [Array] :excluded_methods An array of method symbols used to remove data from the word cloud. This should be used to remove database fields from the word cloud.
13
+ # @param args [Array] :excluded_models An array of models whose data should not be included in the word cloud
14
+ # @param args [Integer] :depth_level How many levels of associations to include
15
+ # @param args [Symbol] :object_name_method How to name the object when included in the word cloud as an association
16
+ def acts_as_word_cloud(args = {})
16
17
 
17
18
  # getter/setter methods for the module
18
- mattr_accessor :word_cloud_using unless respond_to? :word_cloud_using
19
- mattr_accessor :word_cloud_excluded unless respond_to? :word_cloud_excluded
20
- mattr_accessor :word_cloud_skipped unless respond_to? :word_cloud_skipped
21
- mattr_accessor :word_cloud_depth unless respond_to? :word_cloud_depth
22
- mattr_accessor :word_cloud_no_mixin_fields unless respond_to? :word_cloud_no_mixin_fields
23
-
24
- # set empty arrays to allow |= on values
25
- unless self.word_cloud_using.is_a?(Array)
26
- self.word_cloud_using = []
27
- end
28
- unless self.word_cloud_excluded.is_a?(Array)
29
- self.word_cloud_excluded = []
30
- end
31
- unless self.word_cloud_skipped.is_a?(Array)
32
- self.word_cloud_skipped = []
33
- end
34
- unless self.word_cloud_no_mixin_fields.is_a?(Array)
35
- self.word_cloud_no_mixin_fields = []
36
- end
37
-
38
- # default values if none set in the mixin call on the model
39
- self.word_cloud_using |= args[:methods_to_use].present? ? args[:methods_to_use] : []
40
- self.word_cloud_excluded |= args[:excluded_models].present? ? args[:excluded_models] : []
41
- self.word_cloud_skipped |= args[:skipped_attributes].present? ? args[:skipped_attributes] : []
42
- self.word_cloud_depth = args[:depth].present? ? args[:depth] : ActsAsWordCloud.config.min_depth
43
- self.word_cloud_no_mixin_fields = [:name, :title, :label] #ActsAsWordCloud.config.no_mixin_fields
19
+ mattr_accessor :word_cloud_attributes unless respond_to? :word_cloud_attributes
20
+ allowed_options = [:included_methods, :excluded_methods, :excluded_models, :depth, :object_name_methods]
21
+
22
+ # set defaults
23
+ args[:included_methods] ||= []
24
+ args[:excluded_methods] ||= []
25
+ args[:excluded_models] ||= []
26
+ args[:depth] ||= ::ActsAsWordCloud.config.default_search_depth
27
+ # note that the user passes in object_name_method and it is turned into the array object_name_methods
28
+ args[:object_name_methods] = args[:object_name_method] ? [args[:object_name_method]] : ::ActsAsWordCloud.config.object_name_methods
29
+
30
+ self.word_cloud_attributes = args.keep_if { |key| allowed_options.include?(key) }
44
31
 
45
32
  include ActsAsWordCloud::WordCloud::InstanceMethods
46
33
  end
47
34
  end
48
35
 
49
36
  module InstanceMethods
50
-
51
- # collects associations on a model as long as they're not nil
52
- #
53
- # @param [Symbol] the association type to look in
54
- # @returns [Array <Symbol>] association names under the passed in type
55
- #
56
- def word_cloud_get_associated(type)
57
- self.class.reflect_on_all_associations(type).select {|r| self.send(r.name).present? }.collect { |r| r.name }
58
- end
59
-
60
- # goes through array of objects or arrays (in the case of has_many association)
61
- #
62
- # @param [Symbol] the association type to fetch objects from
63
- # @returns [Array <Object>] that under association passed in
64
- #
65
- def word_cloud_associated_objects(type)
66
- objects = []
67
- associated = word_cloud_get_associated(type)
68
- associated.each do |o|
69
- if o.class == Array
70
- nested_array = self.send(o)
71
- nested_array.each do |a|
72
- objects << a
73
- end
74
- else
75
- objects << self.send(o)
76
- end
77
- end
78
- return objects
79
- end
80
-
81
- # removes objects that are in the list of objects to exclude
82
- #
83
- # @returns [Array <Object>] that are not in the excluded list
84
- #
85
- def word_cloud_exclude_words(objects)
86
- if objects.nil?
87
- return []
88
- else
89
- result = objects.flatten.reject { |x| self.word_cloud_excluded.include?(x.class) }
90
- return result.present? ? result : []
91
- end
92
- end
93
-
94
- # removes objects that include word_cloud mixin
95
- #
96
- # @returns [Array <Object>] that don't include the mixin
97
- #
98
- def word_cloud_no_mixin(objects)
99
- if objects.nil?
100
- return []
37
+ # Uses recursive word cloud to find text attributes, associations and included methods on the model
38
+ #
39
+ # @param [Symbol] return_type Whether to return an array or string, defaulting to :string
40
+ # @return [Array<String> or String] All processed values
41
+ def word_cloud(return_type = :string)
42
+ output = recursive_word_cloud(self.word_cloud_attributes[:depth]).uniq.flatten
43
+ if return_type == :string
44
+ return output.join(' ')
101
45
  else
102
- result = objects.flatten.reject { |n| n.respond_to?(:word_cloud) }
103
- result.present? ? result : []
46
+ return output
104
47
  end
105
48
  end
106
-
107
- # goes through each object passed in trying the included methods for each of those objects
108
- # keeps the first one to work and returns the value of that method called on the object
109
- #
110
- # @param [Array <Objects>] to look through for values
111
- # @returns [Array <String>] values returned by method
49
+
50
+ protected
51
+
52
+ # Finds all text attributes, associated objects, and included methods down to a specified depth
112
53
  #
113
- def word_cloud_process_words(objects)
54
+ # @param [Integer] depth How many layers of associations to search
55
+ # @return [Array] The word cloud for the specified object and depth
56
+ def recursive_word_cloud(depth)
57
+ # prepare an array of strings to be used as an output
114
58
  output = []
59
+
60
+ # list of database attributes and selected methods minus a list of excluded methods
61
+ output |= word_cloud_get_valid_strings
62
+
63
+ # array of objects for every association minus a list of excluded objects
64
+ objects = word_cloud_associated_objects
115
65
  objects.each do |obj|
116
- obj_calls = obj.word_cloud_using.select { |f| obj.respond_to?(f) }
117
- if obj_calls.first.present?
118
- output << obj.send(obj_calls.first)
119
- end
120
- end
121
- return output
122
- end
123
-
124
- # goes through each of the default fields to call on models that don't include mixin
125
- # and attempts to return the value of the first one to work, if none do return model class name
126
- #
127
- # @returns [String] value for object passed in
128
- #
129
- def word_cloud_apply_fields_to(no_mixin_model)
130
- # fields that should be tried on models that don't have the mixin, set with other attributes
131
- fields = self.word_cloud_no_mixin_fields
132
- fields.each do |f|
133
- if no_mixin_model.respond_to?(f)
134
- return no_mixin_model.send(f)
66
+ if obj.respond_to?(:recursive_word_cloud) && depth > 1
67
+ # if the object has a word cloud mixin and we can recurse
68
+ output |= obj.recursive_word_cloud(depth - 1)
69
+ else
70
+ # otherwise get the default name for the object
71
+ output |= [self.word_cloud_object_name(obj)]
135
72
  end
136
73
  end
137
- return no_mixin_model.class.name
138
- end
139
-
140
- # goes through models that don't include mixin trying to find a relevant value for each
141
- #
142
- # @returns [Array <String>] of values for objects passed in
143
- #
144
- def word_cloud_find_field(no_mixin)
145
- output = []
146
- flag = 0
147
- no_mixin.each do |n|
148
- output << word_cloud_apply_fields_to(n)
149
- end
150
74
  return output
151
- end
152
-
153
-
154
- # returns values from methods specified in using option
155
- # ignores string attributes that are listed in the skipped option
75
+ end
76
+
77
+ # returns values from methods specified in included_methods option
78
+ # ignores string attributes that are listed in the excluded_methods option
156
79
  #
157
- # @returns [Array <String>]
80
+ # Note that his uses content_columns to read the database data.
81
+ # At some point, we may want to increase the types that are allowed or use a blacklist instead of a whitelist
158
82
  #
83
+ # @return [Array <String>] The database fields and included methods with excluded methods removed
159
84
  def word_cloud_get_valid_strings
160
- output = []
161
- ignore = []
162
-
163
- self.word_cloud_using.each do |m|
164
- output << self.send(m)
165
- end
166
-
167
- self.word_cloud_skipped.each do |s|
168
- ignore << self.send(s)
169
- end
85
+ # database fields
86
+ string_attributes = self.class.content_columns.collect { |c| c.name.to_sym if c.type == :string || c.type == :text }.compact
87
+ # included methods
88
+ methods = self.word_cloud_attributes[:included_methods] | string_attributes
89
+ # excluded methods
90
+ methods -= self.word_cloud_attributes[:excluded_methods]
170
91
 
171
- # current model's string attributes
172
- output += self.attributes.select {|k,v| v.class == String}.values
173
- output -= ignore
174
-
175
- return output
92
+ return methods.map { |m| self.send(m) }
176
93
  end
177
94
 
178
- # finds string attributes on model
179
- # processes associations on model, either fetching word_cloud results if they include mixin or default information if they don't
180
- # if depth is said to something higher than one the word_cloud results on each associated model then makes more recursive calls (BFS)
181
- #
182
- # @params Integer, recursive depth for method; Symbol for the type of result (array or string)
183
- # @returns [Array <String>] all processed values
95
+ # Gets a list of objects associated to the calling object
96
+ # Uses rails reflect_on_all_associations methods. Only pulls from belongs_to, has_one, and has_many for now.
97
+ # Each association should return nil, a single object, or an array of objects
184
98
  #
185
- def word_cloud( depth = self.word_cloud_depth, type = :string )
186
-
187
- output = []
99
+ # @return [Array] List of models associated to the calling object
100
+ def word_cloud_associated_objects
188
101
  objects = []
189
- no_mixin = []
190
-
191
- # string attributes in current model
192
- output = word_cloud_get_valid_strings
102
+ # get associations
103
+ [:belongs_to, :has_one, :has_many].each do |association_type|
104
+ word_cloud_association_names_by_type(association_type).each do |association|
105
+ objects << self.send(association)
106
+ end
107
+ end
108
+ objects.flatten!
109
+ # remove excluded associations
110
+ objects = objects.delete_if { |o| self.word_cloud_attributes[:excluded_models].include?(o.class) }
111
+ return objects
112
+ end
113
+
114
+ # @return [Array <Symbol>] Collect the names of associations of a specific typeI
115
+ def word_cloud_association_names_by_type(type)
116
+ self.class.reflect_on_all_associations(type).collect(&:name)
117
+ end
193
118
 
194
- # objects on all associations collected
195
- objects += self.word_cloud_associated_objects(:belongs_to)
196
- objects += self.word_cloud_associated_objects(:has_one)
197
- objects += self.word_cloud_associated_objects(:has_many)
198
-
199
- # remove objects that have been explicitly excluded on current model
200
- objects = word_cloud_exclude_words(objects)
201
- # find associated models that do not include mixin
202
- no_mixin = word_cloud_no_mixin(objects)
203
- objects -= no_mixin
204
-
205
- # process object using set methods for each model with mixin, or try preset general methods for models without
206
- output += self.word_cloud_process_words(objects) + word_cloud_find_field(no_mixin)
207
-
208
- # recursive step, if depth > 1, calls word cloud on objects array (which already excludes objects that don't have the mixin)
209
- if depth < 1
210
- return "depth for word cloud can't be less than 0"
211
- elsif depth == 1
212
- # recursive steps got a bit more complicated with :array/:string addition, that last line gets rid of duplicate results in the 'long' strings being returned
213
- return ( type == :array ? output.uniq : output.uniq.join(' ') )
119
+ # Name an object that is included in the word cloud
120
+ #
121
+ # When the depth level is low or an included gem does not use the word cloud mixin
122
+ # use a special rule to determine the name of the object. The word cloud attribute
123
+ # object_name_methods is used for objects that use the word cloud. Otherwise, the
124
+ # default config option is used. If none of the methods are found on the object, an
125
+ # empty string is returned.
126
+ #
127
+ # @param [ActiveRecord::Base] object The object to name
128
+ # @param [String] The name of the object or a blank string
129
+ def word_cloud_object_name(object)
130
+ output = ""
131
+ method_list = []
132
+ if object.respond_to?(:word_cloud_attributes)
133
+ method_list = object.word_cloud_attributes[:object_name_methods]
214
134
  else
215
- if type == :array
216
- output |= objects.collect { |o| o.word_cloud(depth-1, :array) }
217
- return output.flatten.uniq
218
- else
219
- output |= objects.collect { |o| o.word_cloud(depth-1) }
220
- return output.flatten.join(' ').split(' ').uniq.join(' ')
135
+ method_list = ::ActsAsWordCloud.config.object_name_methods
136
+ end
137
+ method_list.each do |method|
138
+ if object.respond_to?(method)
139
+ output = object.send(method)
140
+ break
221
141
  end
222
142
  end
223
- end
143
+ return output
144
+ end
224
145
  end
225
146
  end
226
147
  end
227
-
@@ -1,7 +1,12 @@
1
1
  ActsAsWordCloud.configure do |config|
2
- #the default setting for smallest recursive step for calling wordcloud on models
3
- config.min_depth = 1
2
+ # The default recursion depth when looking at data in associations.
3
+ # A min_depth of 1 will look at all fields on the current model and the name of each association.
4
+ # A min_depth of 2 will look at all fields on the current model, all fields on the associations, and all names of the association's associations
5
+ # config.default_search_depth = 1
4
6
 
5
- #methods whose values we'll attempt to get for models that don't include the mixin
6
- config.no_mixin_fields = [:name, :title, :label]
7
+ # This option is useful when associations do not use the acts_as_word_cloud mixin.
8
+ # When word_cloud is called, the system tries to guess a reasonable name for the associated models.
9
+ # It calls the methods in order meaning it will look for name first.
10
+ # Make sure that the final method in this list is on every model
11
+ # config.default_object_names = [:name, :title, :label, :to_s]
7
12
  end
@@ -1,109 +1,172 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "ActsAsWordCloud" do
4
- before(:all) do
5
- @lone_article = Article.make
6
- @publisher = Publisher.make(:name => "publisher", :location => "location")
7
- @site = Site.make( :name => "site name", :domain => "uff.us", :genre => "site genre", :publisher => @publisher)
8
- @author = Author.make(:name => "author", :genre => "author genre", :publisher => @publisher, :site => @site)
9
- @article = Article.make(:title => "article title", :genre => "genre", :content => "article text", :author => @author, :publisher => @publisher, :site => @site)
10
- @reader = Reader.make(:username => "reader", :site => @site)
11
- @following = Following.make(:article => @article, :reader => @reader, :special_name => "following1")
4
+ before(:each) do
5
+ @article = Article.new
12
6
  end
13
-
7
+
14
8
  describe "word_cloud" do
15
- describe "class method use_word_cloud" do
16
- it "should contain set or default values in attributes" do
17
- @article.word_cloud_using.should == [:title]
18
- @article.word_cloud_excluded.should == []
19
- @article.word_cloud_depth.should == 1
20
- @article.word_cloud_no_mixin_fields.should == [:name, :title, :label]
21
- end
22
- end
23
-
24
- it "should return the right association models" do
25
- @lone_article.word_cloud.should == [@lone_article.title, @lone_article.genre, @lone_article.content, @lone_article.author.name, @lone_article.publisher.name, @lone_article.site.domain ].join(' ')
26
- @article.word_cloud.should == [@article.title, @article.genre, @article.content, @article.author.name, @article.publisher.name, @article.site.domain, @article.readers.first.username, "Following" ].join(' ')
27
-
28
- @publisher2 = Publisher.make(:name => "publisher2", :location => "location2")
29
- @site2 = Site.make( :name => "site2 name", :domain => "uff.us2", :genre => "site2 genre", :publisher => @publisher2)
30
- @author2 = Author.make(:name => "author2", :genre => "author2 genre", :publisher => @publisher2, :site => @site2)
31
- @article2 = Article.make(:title => "article2 title", :genre => "genre2", :content => "article2 text", :author => @author2, :publisher => @publisher2, :site => @site2)
32
- @following2 = Following.make(:article => @article2, :reader => @reader , :special_name => "following2")
33
-
34
- @reader.word_cloud(2, :array).should == [@reader.username, @reader.site.domain, @article.title, @article2.title, "Following", @site.name, @site.genre, @site.publisher.name, @site.authors.first.name, @article.genre, @article.content, @article2.genre, @article2.content, @article2.author.name, @article2.publisher.name, @article2.site.domain ]
9
+ before(:each) do
10
+ @article.stub(:recursive_word_cloud).and_return([])
11
+ end
12
+
13
+ it "should call recursive_word_cloud" do
14
+ @article.should_receive(:recursive_word_cloud).and_return([])
15
+ @article.word_cloud
16
+ end
17
+
18
+ it "should return a string by default" do
19
+ @article.word_cloud.should be_a_kind_of String
20
+ end
21
+
22
+ it "should return a string if :string is passed as an argument" do
23
+ @article.word_cloud(:string).should be_a_kind_of String
24
+ end
25
+
26
+ it "should return an array if :array is passed as an argument" do
27
+ @article.word_cloud(:array).should be_a_kind_of Array
35
28
  end
36
29
  end
37
-
38
- describe "instance methods:" do
39
- describe "word_cloud_associated_objects" do
40
- it "should return correct models when called with belongs_to" do
41
- @article.word_cloud_associated_objects(:belongs_to).should == [@article.author, @article.publisher, @article.site]
42
- end
43
-
44
- it "should return correct models when called with has_many" do
45
- @article.word_cloud_associated_objects(:has_many).should == [@article.followings, @article.readers]
46
- end
30
+
31
+ describe "recursive_word_cloud" do
32
+ before(:each) do
33
+ @article.stub(:word_cloud_get_valid_strings).and_return([])
34
+ @article.stub(:word_cloud_associated_objects).and_return([])
35
+ end
36
+ it "should call word_cloud_get_valid_strings" do
37
+ @article.should_receive(:word_cloud_get_valid_strings).and_return([])
38
+ @article.send(:recursive_word_cloud, 1)
39
+ end
40
+
41
+ it "should call word_cloud_associated_objects" do
42
+ @article.should_receive(:word_cloud_associated_objects).and_return([])
43
+ @article.send(:recursive_word_cloud, 1)
44
+ end
45
+
46
+ it "should call rescursive_word_cloud on an associated object if the depth is more than 1" do
47
+ object_with_word_cloud = mock(Author)
48
+ object_with_word_cloud.should_receive(:recursive_word_cloud).with(1).and_return([])
49
+
50
+ @article.stub(:word_cloud_associated_objects).and_return([object_with_word_cloud])
51
+ @article.send(:recursive_word_cloud, 2)
52
+ end
53
+
54
+ it "should call word_cloud_object_name on an associated object if the depth is 1 or less" do
55
+ object_with_word_cloud = mock(Author)
56
+ object_with_word_cloud.stub(:recursive_word_cloud).and_return([])
57
+ @article.stub(:word_cloud_associated_objects).and_return([object_with_word_cloud])
58
+
59
+ @article.should_receive(:word_cloud_object_name).with(object_with_word_cloud)
60
+ @article.send(:recursive_word_cloud, 1)
61
+ end
62
+
63
+ it "should call word_cloud_object_name on an associated object if the object does not have the word cloud mixin" do
64
+ object_without_word_cloud = mock(Author)
65
+ @article.stub(:word_cloud_associated_objects).and_return([object_without_word_cloud])
66
+
67
+ @article.should_receive(:word_cloud_object_name).with(object_without_word_cloud)
68
+ @article.send(:recursive_word_cloud, 2)
69
+ end
70
+
71
+ it "should return an array of strings" do
72
+ @article.stub(:word_cloud_get_valid_strings).and_return(["hello"])
73
+ results = @article.send(:recursive_word_cloud, 1)
74
+ results.should be_a_kind_of Array
75
+ results.each { |r| r.should be_a_kind_of String }
76
+ end
77
+ end
78
+
79
+ describe "word_cloud_get_valid_strings" do
80
+ before(:each) do
81
+ @article.word_cloud_attributes[:included_methods] = [:sample_method]
82
+ @article.word_cloud_attributes[:excluded_methods] = [:sample_method]
83
+ end
84
+
85
+ it "should read the database structure and return text and string fields only" do
86
+ @article.should_receive(:title)
87
+ @article.should_receive(:content)
88
+ @article.should_not_receive(:created_at)
89
+ @article.send(:word_cloud_get_valid_strings)
47
90
  end
48
-
49
- describe "word_cloud_get_associated" do
50
- it "should return collectoin of non-nil associated models" do
51
- @article.word_cloud_get_associated(:belongs_to).should == [:author, :publisher, :site]
52
- @lone_article.word_cloud_get_associated(:has_many).should == []
53
- @article.word_cloud_get_associated(:has_many).should == [:followings, :readers]
54
- end
55
- end
56
-
57
- describe "word_cloud_exclude_words" do
58
- it "should not keep excluded models" do
59
- @article.stub!(:word_cloud_excluded).and_return([Publisher])
60
- objects = @article.word_cloud_associated_objects(:belongs_to)
61
- @article.word_cloud_exclude_words(objects).should == [@article.author, @article.site]
62
- end
63
- end
64
-
65
- describe "word_cloud_no_mixin" do
66
- it "should only have models that don't have the mixin" do
67
- objects = @article.word_cloud_associated_objects(:has_many)
68
- @article.word_cloud_no_mixin(objects).should == @article.followings
69
- end
70
- end
71
-
72
- describe "word_cloud_process_words" do
73
- it "should return result of first included method to work on objects with mixin" do
74
- objects = []
75
- objects += @article.word_cloud_associated_objects(:belongs_to)
76
- objects += @article.word_cloud_associated_objects(:has_many)
77
- objects = @article.word_cloud_exclude_words(objects)
78
- no_mixin = @article.word_cloud_no_mixin(objects)
79
- objects -= no_mixin
80
- @article.word_cloud_process_words(objects).should == [@article.author.name, @article.publisher.name, @article.site.domain, @article.readers.first.username]
81
- end
82
- end
83
-
84
- describe "word_cloud_apply_fields_to" do
85
- it "return the value found in the first method to work on object passed in, if there's none return class name" do
86
- @article.word_cloud_apply_fields_to(@article.followings.first).should == "Following"
87
- @article.stub!(:word_cloud_no_mixin_fields).and_return([:special_name])
88
- @article.word_cloud_apply_fields_to(@article.followings.first).should == @article.followings.first.special_name
89
- end
90
- end
91
-
92
- describe "word_cloud_find_field" do
93
- it "should return result of first included method to work on objects with mixin or their class names" do
94
- @article.word_cloud_find_field(@article.followings).should == ["Following"]
95
- end
96
- end
97
-
98
- describe "word_cloud_get_valid_strings" do
99
- it "should return used methods on model and string attributes not in skipped list" do
100
- @article.stub!(:word_cloud_skipped).and_return([:title, :content])
101
- @article.word_cloud_get_valid_strings.should_not include "article title"
102
- @article.word_cloud_get_valid_strings.should_not include "article text"
103
- @article.word_cloud_get_valid_strings.should include "genre"
104
- end
105
- end
106
-
91
+
92
+ it "should add included methods to the output and call them" do
93
+ @article.word_cloud_attributes[:excluded_methods] = []
94
+ @article.should_receive(:sample_method)
95
+ @article.send(:word_cloud_get_valid_strings)
96
+ end
97
+
98
+ it "should remove excluded methods from the output and not call them" do
99
+ @article.should_not_receive(:sample_method)
100
+ @article.send(:word_cloud_get_valid_strings)
101
+ end
107
102
  end
108
- end
109
103
 
104
+ describe "word_cloud_associated_objects" do
105
+ before(:each) do
106
+ @article.stub(:word_cloud_association_names_by_type).and_return([])
107
+ end
108
+ it "should get objects in belongs to, has ony, and has many relationships" do
109
+ @article.should_receive(:word_cloud_association_names_by_type).with(:belongs_to).and_return([])
110
+ @article.should_receive(:word_cloud_association_names_by_type).with(:has_one).and_return([])
111
+ @article.should_receive(:word_cloud_association_names_by_type).with(:has_many).and_return([])
112
+ @article.send(:word_cloud_associated_objects)
113
+ end
114
+
115
+ it "should remove objects that are included in excluded_models" do
116
+ author = Author.new
117
+ @article.stub(:author).and_return(author)
118
+ @article.stub(:word_cloud_association_names_by_type).with(:belongs_to).and_return([:author])
119
+ @article.word_cloud_attributes[:excluded_models] = [Author]
120
+ @article.send(:word_cloud_associated_objects).should be_empty
121
+ end
122
+
123
+ it "should return an array of rails models" do
124
+ author = Author.new
125
+ @article.stub(:author).and_return(author)
126
+ @article.stub(:word_cloud_association_names_by_type).with(:belongs_to).and_return([:author])
127
+ @article.word_cloud_attributes[:excluded_models] = []
128
+ @article.send(:word_cloud_associated_objects).should == [author]
129
+ end
130
+ end
131
+
132
+ describe "word_cloud_association_names_by_type" do
133
+ # this one is tough to test because we are just calling a single rails method
134
+ it "should return an array of symbols" do
135
+ associations = @article.send(:word_cloud_association_names_by_type, :belongs_to)
136
+ associations.should be_a_kind_of Array
137
+ associations.each { |a| a.should be_a_kind_of Symbol }
138
+ end
139
+ end
140
+
141
+ describe "word_cloud_object_name" do
142
+ it "should use the object's word cloud attribute for object name if set" do
143
+ @article.word_cloud_attributes[:object_name_methods] = [:hello]
144
+ @article.should_receive(:hello).and_return("hello")
145
+ @article.send(:word_cloud_object_name, @article).should == "hello"
146
+ end
147
+
148
+ it "should call each member of the method list until it finds one that is runnable on the object" do
149
+ publisher = Publisher.new
150
+
151
+ ActsAsWordCloud.config.should_receive(:object_name_methods).and_return([:hello, :world])
152
+ publisher.stub(:respond_to?).with(:word_cloud_attributes).and_return(false)
153
+
154
+ publisher.stub(:respond_to?).with(:hello).and_return(false)
155
+ publisher.should_not_receive(:hello)
156
+
157
+ publisher.stub(:respond_to?).with(:world).and_return(true)
158
+ publisher.should_receive(:world).and_return("world")
159
+
160
+ @article.send(:word_cloud_object_name, publisher).should == "world"
161
+ end
162
+
163
+ it "should return an empty string if no methods are found" do
164
+ publisher = Publisher.new
165
+
166
+ ActsAsWordCloud.config.should_receive(:object_name_methods).and_return([])
167
+ publisher.stub(:respond_to?).with(:word_cloud_attributes).and_return(false)
168
+
169
+ @article.send(:word_cloud_object_name, publisher).should == ""
170
+ end
171
+ end
172
+ end
@@ -1,9 +1,11 @@
1
1
  class Article < ActiveRecord::Base
2
- acts_as_word_cloud :methods_to_use => [:title], :excluded_models => [], :skipped_attributes => [], :depth => 1
2
+ acts_as_word_cloud :included_methods => [:truncated_title], :excluded_methods => [:genre], :excluded_models => [ArticleReader, Reader], :depth => 2, :object_name_method => :title
3
3
 
4
4
  belongs_to :author
5
- belongs_to :publisher
6
- belongs_to :site
7
- has_many :followings
8
- has_many :readers, :through => :followings
5
+ has_many :article_readers
6
+ has_many :readers, :through => :article_readers
7
+
8
+ def truncated_title
9
+ title[0..5] if title
10
+ end
9
11
  end