lonely_coder 0.1.4 → 0.1.5
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.
- data/CHANGELOG +2 -0
- data/README.mdown +1 -2
- data/lib/lonely_coder/{magic_constants.rb → search/magic_constants.rb} +0 -0
- data/lib/lonely_coder/search/options/age.rb +13 -0
- data/lib/lonely_coder/search/options/ethnicity.rb +14 -0
- data/lib/lonely_coder/search/options/filter.rb +74 -0
- data/lib/lonely_coder/search/options/location.rb +25 -0
- data/lib/lonely_coder/search/options/order_by.rb +19 -0
- data/lib/lonely_coder/search/options/paginator.rb +33 -0
- data/lib/lonely_coder/search/options/radius.rb +18 -0
- data/lib/lonely_coder/search/options/require_photo.rb +13 -0
- data/lib/lonely_coder/{search_pagination_parser.rb → search/search_pagination_parser.rb} +0 -0
- data/lib/lonely_coder/search.rb +72 -179
- data/lib/lonely_coder.rb +1 -3
- data/lonely_coder.gemspec +1 -1
- data/spec/ethnicity_filter_spec.rb +21 -0
- metadata +15 -4
data/CHANGELOG
CHANGED
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
|
File without changes
|
@@ -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
|
File without changes
|
data/lib/lonely_coder/search.rb
CHANGED
@@ -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
|
-
#
|
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,
|
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
|
130
|
-
#
|
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
|
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
|
-
|
162
|
-
#
|
163
|
-
#
|
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.
|
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
@@ -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
|
+
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-
|
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/
|
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:
|