lonely_coder 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ 0.1.5 (April 04, 2012)
2
+ * Added ethnicity filter
1
3
  0.1.4 (March 27, 2012)
2
4
  * more consistent search results when paginating by using OKCupid server timestamps
3
5
  * Search#load_next_page returns boolean to deal with cases where pagination doesn't load new results
data/README.mdown CHANGED
@@ -20,8 +20,7 @@ Lonely coder seeks nice boy
20
20
  :min_age => 25,
21
21
  :max_age => 35, # just someone around my age, yo
22
22
  :gentation => 'Guys who like guys', # don't hate
23
- :order_by => 'Match %', # I want us to get along
24
- :match_limit => 80, # let's make it last
23
+ :order_by => 'Match %', # I want us to get along, let's make it last
25
24
  :last_login => 'last month',
26
25
  :location => maybe_here
27
26
  :radius => 25, # acceptable values are 25, 50, 100, 250, 500, nil
@@ -0,0 +1,13 @@
1
+ class OKCupid
2
+ class AgeFilter < Filter
3
+ def lookup(value)
4
+ "#{value[0]},#{value[1]}"
5
+ end
6
+ end
7
+
8
+ class Search
9
+ def add_age_option(value)
10
+ @filters << AgeFilter.new('age', value)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class OKCupid
2
+ class EthnicityFilter < Filter
3
+ def lookup(values)
4
+ # lookup the race values and sum them. I think OKC is doing some kind of base2 math on them
5
+ values.collect {|v| MagicNumbers::Ethnicity[v.downcase]}.inject(0, :+)
6
+ end
7
+ end
8
+
9
+ class Search
10
+ def add_ethnicity_option(values)
11
+ @filters << EthnicityFilter.new('ethnicity', values)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ class OKCupid
2
+ # Filter instances are used to build the query parameters for a search on OKCupid.
3
+ # OKCupid has specific coded values for many search values, you should check the magic_constants file
4
+ # for a list. Not all of them have been implemented yet.
5
+ #
6
+ # Adding a Filter takes one of three forms:
7
+ # 1) implementing a add_<filter_name>_option method on Search
8
+ # that pushes a new named Filter to the @filters array for unmamed query parts
9
+ #
10
+ # 2) Subclassing Filter for filters that have atypical parameterization behavior.
11
+ # where you'll implement a add_<filter_name>_option method on Search
12
+ # and provide a custom class overriding `lookup` or `to_param` to create
13
+ # a correct url query part.
14
+ #
15
+ # 3) Creating a new class to handle query parameters that are specifically named
16
+ # These are refered to as "Parameters" to contrast them with "Filters" which
17
+ # are parameterized in the "filterN=code,value" pattern (e.g. filter4=22,7).
18
+ # Parameters are not numbered and have specific names, e.g. "loc_id=1234567"
19
+ # You'll also implement a add_<filter_name>_option that adds an instance of this
20
+ # class to the @parameters array (not the @filters array).
21
+ #
22
+ # See the included Filter and Parameter classes for ideas on how to structure these objects.
23
+ #
24
+ # OKCupid's query system is a bit obtuse and the details aren't published anywhere.
25
+ # If you're implementing a new filter, you may need to spend some time figuring out
26
+ # what data they expect to receive and the kinds of results it will return.
27
+ class Filter
28
+ class NoSuchFilter < StandardError; end
29
+ class BadValue < StandardError; end
30
+
31
+ attr_reader :name, :value, :code
32
+
33
+ def initialize(name, value)
34
+ @code = MagicNumbers::Filters[name.to_s]
35
+ raise(NoSuchFilter, name) unless @code
36
+
37
+ @name = name.to_s
38
+ @value = value
39
+ @encoded_value = lookup(@value)
40
+ unless @encoded_value
41
+ raise(BadValue, "#{@value.inspect} is not a possible value for #{@name}. Try one of #{allowed_values.map(&:inspect).join(', ')}")
42
+ end
43
+ end
44
+
45
+ def allowed_values
46
+ MagicNumbers.const_get(@name.camelize).keys
47
+ end
48
+
49
+ def lookup(value)
50
+ MagicNumbers.const_get(@name.camelize)[value.downcase]
51
+ end
52
+
53
+ def to_param(n)
54
+ "filter#{n}=#{@code},#{@encoded_value}"
55
+ end
56
+ end
57
+
58
+ # All filters that follow the base Filter pattern are exposed here through a
59
+ # add_<filter_name>_option method. Custom filters and parameters are defined
60
+ # in their own files and include the appropriate add_<option_name>_option method.
61
+ class Search
62
+ def add_relationship_status_option(value)
63
+ @filters << Filter.new('relationship_status', value)
64
+ end
65
+
66
+ def add_gentation_option(value)
67
+ @filters << Filter.new('gentation', value)
68
+ end
69
+
70
+ def add_last_login_option(value)
71
+ @filters << Filter.new('last_login', value)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,25 @@
1
+ class OKCupid
2
+ class LocationParameter
3
+ def initialize(value)
4
+ @value = value
5
+ end
6
+
7
+ def to_param
8
+ if @value.is_a?(String)
9
+ if @value.downcase == 'near me'
10
+ "locid=0"
11
+ else
12
+ "locid=#{Search.location_id_for(@value)}&lquery=#{URI.escape(@value)}"
13
+ end
14
+ else
15
+ "locid=#{@value}"
16
+ end
17
+ end
18
+ end
19
+
20
+ class Search
21
+ def add_location_option(value)
22
+ @parameters << LocationParameter.new(value)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ class OKCupid
2
+ class OrderByParameter
3
+ def initialize(value)
4
+ @value = value
5
+ @encoded_value = MagicNumbers::OrderBy[value.downcase]
6
+ end
7
+
8
+ def to_param
9
+ "matchOrderBy=#{@encoded_value}"
10
+ end
11
+ end
12
+
13
+ # Reopen Search to accept order_by filters
14
+ class Search
15
+ def add_order_by_option(value)
16
+ @parameters << OrderByParameter.new(value)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ class OKCupid
2
+ # used to create the pagination part of a search url:
3
+ # low=1&count=10&ajax_load=1
4
+ # where low is the start value
5
+ # count is the number of items per page
6
+ class Paginator
7
+ attr_reader :page, :per_page
8
+
9
+ def initialize(options)
10
+ @per_page = options[:per_page]
11
+ @page = options[:page]
12
+ end
13
+
14
+ def low
15
+ @low = ((@page - 1) * @per_page) + 1
16
+ end
17
+
18
+ def next
19
+ @page +=1
20
+ self
21
+ end
22
+
23
+ def to_param
24
+ "low=#{low}&count=#{@per_page}"
25
+ end
26
+ end
27
+
28
+ class Search
29
+ def add_pagination_option(value)
30
+ @parameters << @pagination = Paginator.new(value)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ class OKCupid
2
+ class RadiusFilter < Filter
3
+ def lookup(value)
4
+ value.nil? ? '' : value
5
+ end
6
+
7
+ def to_param(n)
8
+ return nil if @encoded_value === ''
9
+ super
10
+ end
11
+ end
12
+
13
+ class Search
14
+ def add_radius_option(value)
15
+ @filters << RadiusFilter.new('radius', value)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ class OKCupid
2
+ class RequirePhotoFilter < Filter
3
+ def lookup(value)
4
+ value ? 1 : 0
5
+ end
6
+ end
7
+
8
+ class Search
9
+ def add_require_photo_option(value)
10
+ @filters << RequirePhotoFilter.new('require_photo', value)
11
+ end
12
+ end
13
+ end
@@ -1,15 +1,65 @@
1
1
  require 'uri'
2
+ require 'set'
3
+
4
+ require 'lonely_coder/search/magic_constants'
5
+ require 'lonely_coder/search/search_pagination_parser'
6
+ require 'lonely_coder/search/options/filter'
7
+
8
+ # these are the included filters. See Filter class documentation for adding your own.
9
+ require 'lonely_coder/search/options/age'
10
+ require 'lonely_coder/search/options/ethnicity'
11
+ require 'lonely_coder/search/options/order_by'
12
+ require 'lonely_coder/search/options/location'
13
+ require 'lonely_coder/search/options/paginator'
14
+ require 'lonely_coder/search/options/radius'
15
+ require 'lonely_coder/search/options/require_photo'
2
16
 
3
17
  class OKCupid
18
+ # Creates a new Search with the passed options to act as query parameters.
19
+ # A search will not trigger a query to OKCupid until `results` is called.
20
+ #
21
+ # @param [Hash] options a list of options for the search
22
+ # @option options [Integer] :min_age (18) Minimum age to search for.
23
+ # @option options [Integer] :max_age (99) Maximum age to search for.
24
+ # @option options [String] :gentation Gentation is OKCupid's portmanteau for 'gender and orientation'.
25
+ # Acceptable values are:
26
+ # "girls who like guys", "guys who like girls", "girls who like girls",
27
+ # "guys who like guys", "both who like bi guys", "both who like bi girls",
28
+ # "straight girls only", "straight guys only", "gay girls only",
29
+ # "gay guys only", "bi girls only", "bi guys only", "everybody"
30
+ # this option is required.
31
+ #
32
+ # @option options [String] :order_by ('match %') The sort order of the search results.
33
+ # Acceptable values are 'match %','friend %', 'enemy %',
34
+ # 'special blend', 'join', and 'last login'.
35
+ #
36
+ # @option options [Integer] :radius (25) The search radius, in miles.
37
+ # Acceptable values are 25, 50, 100, 250, 500.
38
+ # You must also specific a :location option.
39
+ # @option options [Integer, String] :location ('near me'). A specific search location.
40
+ # Acceptable values are 'near me', 'anywhere', a "City, State" pair
41
+ # (e.g. 'Chicago, Illinois') or OKCupid location id which can be
42
+ # obtained with Search#location_id_for("City, State").
43
+ # If specifiying a location other than 'near me' or 'anywhere'
44
+ # you may also provide a :radius option
45
+ #
46
+ # @option options [true, false] :require_photo (true). Search for profiles that have photos
47
+ # @option options [String] :relationship_status ('single'). Acceptable values are 'single', 'not single', 'any'
48
+ # @return [Search] A Search without results loaded. To trigger a query against OKCupid call `results`
4
49
  def search(options={})
5
50
  Search.new(options, @browser)
6
51
  end
7
52
 
53
+ # The OKCupid search object. Stores filters and query options and a results set. Correct useage is to obtain
54
+ # and instance of this class by using OKCupid#search(options).
55
+ # @see OKCupid#search
8
56
  class Search
9
57
  class FilterError < StandardError; end
10
58
 
11
59
  attr_reader :filters
12
60
 
61
+ # @param [String] A string query for a city and state pair, e.g. 'Little Rock, Arkansas'
62
+ # @return [Integer] The OKCupid location id for the query
13
63
  def self.location_id_for(query)
14
64
  uri = URI("http://www.okcupid.com/locquery?func=query&query=#{URI.encode(query)}")
15
65
  JSON.parse(Net::HTTP.get(uri))['results'][0]['locid'].to_s
@@ -24,62 +74,29 @@ class OKCupid
24
74
  def parse(options)
25
75
  check_for_required_options(options)
26
76
 
77
+ # :age appears as two options when creating a search
78
+ # but is combined into one for paramterizing.
27
79
  options[:age] = combine_ages(options)
28
80
 
29
-
81
+ # filters appear in the query string as filterN=code,value
82
+ # e.g. filter4=11,75
30
83
  @filters = []
84
+
85
+ # parameters appear in the query string as named query parameters
86
+ # e.g. loc_id=1234567
31
87
  @parameters = []
32
88
 
89
+
33
90
  options.each do |name,value|
34
91
  self.send("add_#{name}_option", value)
35
92
  end
36
93
 
37
94
  # OKC needs an initial time key of 1 to represent "waaaay in the past"
38
95
  # futures searches will use the OKC server value returned from the first
39
- # results set
96
+ # results set.
40
97
  @timekey = 1
41
98
  end
42
99
 
43
- def add_order_by_option(value)
44
- @parameters << OrderByParameter.new(value)
45
- end
46
-
47
- def add_last_login_option(value)
48
- @filters << Filter.new('last_login', value)
49
- end
50
-
51
- def add_location_option(value)
52
- @parameters << LocationParameter.new(value)
53
- end
54
-
55
- def add_radius_option(value)
56
- @filters << RadiusFilter.new('radius', value)
57
- end
58
-
59
- def add_require_photo_option(value)
60
- @filters << RequirePhotoFilter.new('require_photo', value)
61
- end
62
-
63
- def add_relationship_status_option(value)
64
- @filters << Filter.new('relationship_status', value)
65
- end
66
-
67
- def add_gentation_option(value)
68
- @filters << Filter.new('gentation', value)
69
- end
70
-
71
- def add_age_option(value)
72
- @filters << AgeFilter.new('age', value)
73
- end
74
-
75
- def add_pagination_option(value)
76
- @parameters << @pagination = Paginator.new(value)
77
- end
78
-
79
- def add_match_limit_option(value)
80
- # TODO.
81
- end
82
-
83
100
  def check_for_required_options(options)
84
101
  raise(FilterError, 'gentation is a required option') unless options.has_key?(:gentation)
85
102
  end
@@ -89,30 +106,19 @@ class OKCupid
89
106
  options[:age] = age
90
107
  end
91
108
 
92
- # Default values for search:
93
- # match_limit 80
94
- # min_age 18
95
- # max_age 99
96
- # order_by 'match %'
97
- # last_login 'last month'
98
- # location 'Near me'
99
- # to search 'anywhere', use 'Near me' and omit a radius
100
- # radius 25
101
- # require_photo true
102
- # relationship_status 'single'
109
+ #
103
110
  def defaults
104
111
  {
105
112
  :pagination => {
106
113
  :page => 1,
107
114
  :per_page => 10
108
115
  },
109
- :match_limit => 80,
110
116
  :min_age => 18,
111
117
  :max_age => 99,
112
118
  :order_by => 'Match %',
113
119
  :last_login => 'last month',
114
120
  :location => 'Near me',
115
- :radius => 25, # acceptable values are 25, 50, 100, 250, 500, nil
121
+ :radius => 25,
116
122
  :require_photo => true,
117
123
  :relationship_status => 'single'
118
124
  }
@@ -124,10 +130,13 @@ class OKCupid
124
130
  # the first results request has to receive a full HTML page.
125
131
  # subseqent calls can make json requests
126
132
  page = @browser.get(url)
133
+
134
+ # Stores the OKCupid server timestamp. Without this, pagination returns
135
+ # inconsistent results.
127
136
  @timekey = page.search('script')[0].text.match(/CurrentGMT = new Date\(([\d]+)\*[\d]+\)/).captures[0]
128
137
 
129
- # OKCupid will return previous profiles if there aren't enough
130
- # profiles to fill a query, so we stop that with a set.
138
+ # OKCupid may return previously found profiles if there aren't enough
139
+ # to fill a query or pagination, so we stop that with a set.
131
140
  @results = Set.new
132
141
  @results += page.search('.match_row').collect do |node|
133
142
  OKCupid::Profile.from_search_result(node)
@@ -143,24 +152,25 @@ class OKCupid
143
152
  # update_prefs=1
144
153
  # using_saved_search=0
145
154
  # mygender=m
155
+ #
146
156
  # no idea what the following parameters do, but without them, the search
147
157
  # behaves erratically
148
158
  # &custom_search=0
149
159
  #
150
- # OKCupid timestamps searches for pagniation. The first search gets a timestamp
160
+ # OKCupid timestamps searches for pagination. The first search gets a timestamp
151
161
  # of 1 (e.g. 1 second into the epoch) and future searches are stamped with
152
162
  # some server cache value. If that server value isn't submitted, the results
153
163
  # for pagniation don't quite match what you'd expect: you'll get duplicates,
154
164
  # or lower numbers than expected.
155
165
  # &timekey=1
156
-
157
166
  def magic_params_not_truly_understood
158
167
  "timekey=#{@timekey}&custom_search=0"
159
168
  end
160
169
 
161
- # returns true if it worked, false if not.
162
- # OKCupid searches will return duplicate results if you attempt to
163
- # page beyond the availble results.
170
+
171
+ # Loads the next page of possible results. Will return `true` if
172
+ # additional results were available or `false` if not
173
+ # @return [true,false]
164
174
  def load_next_page
165
175
  @browser.pluggable_parser.html = SearchPaginationParser
166
176
 
@@ -194,121 +204,4 @@ class OKCupid
194
204
  filters.compact.to_enum(:each_with_index).map {|filter,index| filter.to_param(index+1)}.join('&')
195
205
  end
196
206
  end
197
-
198
- # used to create the pagination part of a search url:
199
- # low=1&count=10&ajax_load=1
200
- # where low is the start value
201
- # count is the number of items per page
202
- class Paginator
203
- attr_reader :page, :per_page
204
-
205
- def initialize(options)
206
- @per_page = options[:per_page]
207
- @page = options[:page]
208
- end
209
-
210
- def low
211
- @low = ((@page - 1) * @per_page) + 1
212
- end
213
-
214
- def next
215
- @page +=1
216
- self
217
- end
218
-
219
- def to_param
220
- "low=#{low}&count=#{@per_page}"
221
- end
222
- end
223
-
224
- class Filter
225
- class NoSuchFilter < StandardError; end
226
- class BadValue < StandardError; end
227
-
228
- attr_reader :name, :value, :code
229
-
230
- def initialize(name, value)
231
- @code = MagicNumbers::Filters[name.to_s]
232
- raise(NoSuchFilter, name) unless @code
233
-
234
- @name = name.to_s
235
- @value = value
236
- @encoded_value = lookup(@value)
237
- unless @encoded_value
238
- raise(BadValue, "#{@value.inspect} is not a possible value for #{@name}. Try one of #{allowed_values.map(&:inspect).join(', ')}")
239
- end
240
- end
241
-
242
- def allowed_values
243
- MagicNumbers.const_get(@name.camelize).keys
244
- end
245
-
246
- def lookup(value)
247
- MagicNumbers.const_get(@name.camelize)[value.downcase]
248
- end
249
-
250
- def to_param(n)
251
- "filter#{n}=#{@code},#{@encoded_value}"
252
- end
253
- end
254
-
255
- class EthnicityFilter < Filter
256
- def lookup(values)
257
- # lookup the race values and sum them. I think OKC is doing some kind of base2 math on them
258
- values.collect {|v| MagicNumbers::Ethnicity[v.downcase]}.inject(0, :+)
259
- end
260
- end
261
-
262
- class LocationParameter
263
- def initialize(value)
264
- @value = value
265
- end
266
-
267
- def to_param
268
- # to do: 'anywhere' needs to remove the radius filter
269
- if @value.is_a?(String)
270
- if @value.downcase == 'near me'
271
- "locid=0"
272
- else
273
- "locid=#{Search.location_id_for(@value)}&lquery=#{URI.escape(@value)}"
274
- end
275
- else
276
- "locid=#{@value}"
277
- end
278
- end
279
- end
280
-
281
- class OrderByParameter
282
- def initialize(value)
283
- @value = value
284
- @encoded_value = MagicNumbers::OrderBy[value.downcase]
285
- end
286
-
287
- def to_param
288
- "matchOrderBy=#{@encoded_value}"
289
- end
290
- end
291
-
292
- class RequirePhotoFilter < Filter
293
- def lookup(value)
294
- value ? 1 : 0
295
- end
296
- end
297
-
298
- class RadiusFilter < Filter
299
- def lookup(value)
300
- value.nil? ? '' : value
301
- end
302
-
303
- def to_param(n)
304
- return nil if @encoded_value === ''
305
- super
306
- end
307
- end
308
-
309
- class AgeFilter < Filter
310
- def lookup(value)
311
- "#{value[0]},#{value[1]}"
312
- end
313
- end
314
207
  end
data/lib/lonely_coder.rb CHANGED
@@ -11,7 +11,7 @@ require 'mechanize'
11
11
 
12
12
  class OKCupid
13
13
  BaseUrl = 'http://www.okcupid.com'
14
- VERSION = '0.1.4'
14
+ VERSION = '0.1.5'
15
15
 
16
16
  def initialize(username=nil, password=nil)
17
17
  @browser = Mechanize.new
@@ -31,9 +31,7 @@ end
31
31
 
32
32
  require 'active_support/core_ext/string/inflections'
33
33
 
34
- require 'lonely_coder/magic_constants'
35
34
  require 'lonely_coder/profile'
36
35
  require 'lonely_coder/search'
37
- require 'lonely_coder/search_pagination_parser'
38
36
  require 'lonely_coder/authentication'
39
37
  require 'lonely_coder/mailbox'
data/lonely_coder.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |s|
3
3
  s.name = "lonely_coder"
4
- s.version = '0.1.4'
4
+ s.version = '0.1.5'
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.authors = ["Trek Glowacki"]
7
7
  s.email = ["trek.glowacki@gmail.com"]
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe "EthnicityFilter" do
4
+ it "adds ethnicity as an numbered filter to the query url" do
5
+ @search = OKCupid::Search.new({
6
+ :gentation => 'guys who like guys',
7
+ :ethnicity => ['human']
8
+ })
9
+ @search.url.should match(/filter[\d]=9,512/)
10
+ end
11
+
12
+ it "supports mulitple ethnicity values" do
13
+ @search = OKCupid::Search.new({
14
+ :gentation => 'guys who like guys',
15
+ :ethnicity => ['white', 'black']
16
+ })
17
+ # White + Black
18
+ # 256 + 8
19
+ @search.url.should match(/filter[\d]=9,264/)
20
+ end
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lonely_coder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-27 00:00:00.000000000 Z
12
+ date: 2012-04-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mechanize
@@ -55,11 +55,19 @@ files:
55
55
  - README.mdown
56
56
  - lib/lonely_coder.rb
57
57
  - lib/lonely_coder/authentication.rb
58
- - lib/lonely_coder/magic_constants.rb
59
58
  - lib/lonely_coder/mailbox.rb
60
59
  - lib/lonely_coder/profile.rb
61
60
  - lib/lonely_coder/search.rb
62
- - lib/lonely_coder/search_pagination_parser.rb
61
+ - lib/lonely_coder/search/magic_constants.rb
62
+ - lib/lonely_coder/search/options/age.rb
63
+ - lib/lonely_coder/search/options/ethnicity.rb
64
+ - lib/lonely_coder/search/options/filter.rb
65
+ - lib/lonely_coder/search/options/location.rb
66
+ - lib/lonely_coder/search/options/order_by.rb
67
+ - lib/lonely_coder/search/options/paginator.rb
68
+ - lib/lonely_coder/search/options/radius.rb
69
+ - lib/lonely_coder/search/options/require_photo.rb
70
+ - lib/lonely_coder/search/search_pagination_parser.rb
63
71
  - lonely_coder.gemspec
64
72
  - spec/authentication_spec.rb
65
73
  - spec/cassettes/failed_authentication.yml
@@ -75,6 +83,7 @@ files:
75
83
  - spec/cassettes/search_finding_no_results.yml
76
84
  - spec/cassettes/search_that_includes_a_location.yml
77
85
  - spec/cassettes/successful_authentication.yml
86
+ - spec/ethnicity_filter_spec.rb
78
87
  - spec/helper_spec.rb
79
88
  - spec/location_id_spec.rb
80
89
  - spec/mailbox_spec.rb
@@ -123,6 +132,7 @@ test_files:
123
132
  - spec/cassettes/search_finding_no_results.yml
124
133
  - spec/cassettes/search_that_includes_a_location.yml
125
134
  - spec/cassettes/successful_authentication.yml
135
+ - spec/ethnicity_filter_spec.rb
126
136
  - spec/helper_spec.rb
127
137
  - spec/location_id_spec.rb
128
138
  - spec/mailbox_spec.rb
@@ -131,3 +141,4 @@ test_files:
131
141
  - spec/profile_spec.rb
132
142
  - spec/search_spec.rb
133
143
  - spec/spec_helper.rb
144
+ has_rdoc: