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 +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:
|