lonely_coder 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mdown +6 -0
- data/lib/lonely_coder/authentication.rb +2 -2
- data/lib/lonely_coder/magic_constants.rb +1 -1
- data/lib/lonely_coder/search.rb +132 -33
- data/lib/lonely_coder.rb +1 -1
- data/lonely_coder.gemspec +34 -0
- data/spec/cassettes/load_profile_from_search.yml +902 -0
- data/spec/cassettes/paginate_search_results_by_10.yml +1015 -0
- data/spec/cassettes/search_by_filters.yml +741 -1456
- data/spec/pagination_spec.rb +4 -2
- data/spec/paginator_spec.rb +44 -0
- data/spec/profile_spec.rb +7 -7
- data/spec/search_spec.rb +5 -5
- metadata +15 -11
- data/Gemfile.lock +0 -58
- data/spec/cassettes/paginate_search_results.yml +0 -879
data/README.mdown
CHANGED
@@ -40,6 +40,12 @@ How does it work?
|
|
40
40
|
-----------------------------
|
41
41
|
You'll need an OKCupid account (it's free) because most of the site is behind authentication. You can create a new connection to the site.
|
42
42
|
|
43
|
+
Install as a gem
|
44
|
+
|
45
|
+
gem install lonely_coder
|
46
|
+
|
47
|
+
Use your account
|
48
|
+
|
43
49
|
require 'lonely_coder'
|
44
50
|
okc = OKCupid.new('yourusername', 'yourpassword')
|
45
51
|
|
@@ -12,7 +12,7 @@ class OKCupid
|
|
12
12
|
username: username,
|
13
13
|
password: password
|
14
14
|
})
|
15
|
-
|
15
|
+
|
16
16
|
@success = browser.page.uri.path == '/home'
|
17
17
|
|
18
18
|
restore_default_parser(browser)
|
@@ -35,7 +35,7 @@ class OKCupid
|
|
35
35
|
# We're only using page uri to determine successful login, so
|
36
36
|
# there's not a lot of value in passing a body string to nokogiri
|
37
37
|
def initialize(uri = nil, response = nil, body = nil, code =nil)
|
38
|
-
super(uri, response,
|
38
|
+
super(uri, response, '', code)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
data/lib/lonely_coder/search.rb
CHANGED
@@ -22,23 +22,62 @@ class OKCupid
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def parse(options)
|
25
|
-
combine_ages(options)
|
26
25
|
check_for_required_options(options)
|
27
|
-
|
26
|
+
|
27
|
+
options[:age] = combine_ages(options)
|
28
|
+
|
28
29
|
|
29
30
|
@filters = []
|
31
|
+
@parameters = []
|
32
|
+
|
30
33
|
options.each do |name,value|
|
31
|
-
|
32
|
-
if OKCupid.const_defined?("#{name.to_s.camelize}Filter")
|
33
|
-
|
34
|
-
else
|
35
|
-
|
36
|
-
end
|
34
|
+
self.send("add_#{name}_option", value)
|
35
|
+
# if OKCupid.const_defined?("#{name.to_s.camelize}Filter")
|
36
|
+
# @filters << OKCupid.const_get("#{name.to_s.camelize}Filter").new(name, value)
|
37
|
+
# else
|
38
|
+
# @filters << Filter.new(name, value)
|
39
|
+
# end
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
|
-
def
|
41
|
-
@
|
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.
|
42
81
|
end
|
43
82
|
|
44
83
|
def check_for_required_options(options)
|
@@ -63,6 +102,10 @@ class OKCupid
|
|
63
102
|
# relationship_status 'single'
|
64
103
|
def defaults
|
65
104
|
{
|
105
|
+
:pagination => {
|
106
|
+
:page => 1,
|
107
|
+
:per_page => 10
|
108
|
+
},
|
66
109
|
:match_limit => 80,
|
67
110
|
:min_age => 18,
|
68
111
|
:max_age => 99,
|
@@ -78,24 +121,87 @@ class OKCupid
|
|
78
121
|
def results
|
79
122
|
return @results if @results
|
80
123
|
|
81
|
-
|
124
|
+
@browser.pluggable_parser.html = SearchPaginationParser
|
125
|
+
page = @browser.get(ajax_url)
|
126
|
+
|
82
127
|
@results = page.search('.match_row').collect do |node|
|
83
128
|
OKCupid::Profile.from_search_result(node)
|
84
129
|
end
|
130
|
+
|
131
|
+
@browser.pluggable_parser.html = Mechanize::Page
|
132
|
+
|
133
|
+
@results
|
134
|
+
end
|
135
|
+
|
136
|
+
# no idea what the following parameters do. They don't appear to have
|
137
|
+
# an effect:
|
138
|
+
# sort_type=0
|
139
|
+
# fromWhoOnline=0
|
140
|
+
# update_prefs=1
|
141
|
+
# using_saved_search=0
|
142
|
+
# mygender=m
|
143
|
+
# no idea what the following parameters do, but without them, the search
|
144
|
+
# behaves erratically
|
145
|
+
# &timekey=1
|
146
|
+
# &custom_search=0
|
147
|
+
def magic_params_not_truly_understood
|
148
|
+
"timekey=1&custom_search=0"
|
85
149
|
end
|
86
150
|
|
87
151
|
def load_next_page
|
88
152
|
@browser.pluggable_parser.html = SearchPaginationParser
|
89
|
-
|
153
|
+
|
154
|
+
page = @browser.get("#{ajax_url}&#{@pagination.next.to_param}")
|
155
|
+
|
90
156
|
@results += page.search('.match_row').collect do |node|
|
91
157
|
OKCupid::Profile.from_search_result(node)
|
92
158
|
end
|
159
|
+
|
93
160
|
@browser.pluggable_parser.html = Mechanize::Page
|
161
|
+
|
94
162
|
self
|
95
163
|
end
|
96
164
|
|
97
165
|
def url
|
98
|
-
|
166
|
+
"/match?#{filters_as_query}&#{parameters_as_query}&#{magic_params_not_truly_understood}"
|
167
|
+
end
|
168
|
+
|
169
|
+
def ajax_url
|
170
|
+
"#{url}&ajax_load=1"
|
171
|
+
end
|
172
|
+
|
173
|
+
def parameters_as_query
|
174
|
+
@parameters.collect {|param| param.to_param }.join('&')
|
175
|
+
end
|
176
|
+
|
177
|
+
def filters_as_query
|
178
|
+
filters.compact.to_enum(:each_with_index).map {|filter,index| filter.to_param(index+1)}.join('&')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# used to create the pagination part of a search url:
|
183
|
+
# low=1&count=10&ajax_load=1
|
184
|
+
# where low is the start value
|
185
|
+
# count is the number of items per page
|
186
|
+
class Paginator
|
187
|
+
attr_reader :page, :per_page
|
188
|
+
|
189
|
+
def initialize(options)
|
190
|
+
@per_page = options[:per_page]
|
191
|
+
@page = options[:page]
|
192
|
+
end
|
193
|
+
|
194
|
+
def low
|
195
|
+
@low = ((@page - 1) * @per_page) + 1
|
196
|
+
end
|
197
|
+
|
198
|
+
def next
|
199
|
+
@page +=1
|
200
|
+
self
|
201
|
+
end
|
202
|
+
|
203
|
+
def to_param
|
204
|
+
"low=#{low}&count=#{@per_page}"
|
99
205
|
end
|
100
206
|
end
|
101
207
|
|
@@ -137,13 +243,12 @@ class OKCupid
|
|
137
243
|
end
|
138
244
|
end
|
139
245
|
|
140
|
-
class
|
141
|
-
def
|
142
|
-
|
246
|
+
class LocationParameter
|
247
|
+
def initialize(value)
|
248
|
+
@value = value
|
143
249
|
end
|
144
250
|
|
145
|
-
def to_param
|
146
|
-
|
251
|
+
def to_param
|
147
252
|
# to do: 'anywhere' needs to remove the radius filter
|
148
253
|
if @value.is_a?(String)
|
149
254
|
if @value.downcase == 'near me'
|
@@ -156,24 +261,18 @@ class OKCupid
|
|
156
261
|
end
|
157
262
|
end
|
158
263
|
end
|
159
|
-
|
160
|
-
class
|
161
|
-
def
|
264
|
+
|
265
|
+
class OrderByParameter
|
266
|
+
def initialize(value)
|
267
|
+
@value = value
|
268
|
+
@encoded_value = MagicNumbers::OrderBy[value.downcase]
|
269
|
+
end
|
270
|
+
|
271
|
+
def to_param
|
162
272
|
"matchOrderBy=#{@encoded_value}"
|
163
273
|
end
|
164
274
|
end
|
165
|
-
|
166
|
-
# we fake this by paginating results ourselves.
|
167
|
-
# class MatchLimitFilter < Filter
|
168
|
-
# def lookup(value)
|
169
|
-
# 'MATCH'
|
170
|
-
# end
|
171
|
-
#
|
172
|
-
# def to_param(n)
|
173
|
-
# nil
|
174
|
-
# end
|
175
|
-
# end
|
176
|
-
|
275
|
+
|
177
276
|
class RequirePhotoFilter < Filter
|
178
277
|
def lookup(value)
|
179
278
|
value ? 1 : 0
|
data/lib/lonely_coder.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "lonely_coder"
|
4
|
+
s.version = '0.1.1'
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.authors = ["Trek Glowacki"]
|
7
|
+
s.email = ["trek.glowacki@gmail.com"]
|
8
|
+
s.homepage = "http://github.com/trek/lonely_coder"
|
9
|
+
s.summary = %q{A gem for interacting with OKCupid as if it had an API}
|
10
|
+
s.description = %q{A gem for interacting with OKCupid as if it had an API.}
|
11
|
+
|
12
|
+
s.add_dependency 'mechanize', '= 2.0.1'
|
13
|
+
s.add_dependency 'activesupport', '>= 3.2.1'
|
14
|
+
|
15
|
+
s.post_install_message = %q{
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
,d88b.d88b,
|
20
|
+
88888888888
|
21
|
+
`Y8888888Y'
|
22
|
+
`Y888Y'
|
23
|
+
`Y'
|
24
|
+
|
25
|
+
Good luck out there.
|
26
|
+
|
27
|
+
|
28
|
+
}
|
29
|
+
|
30
|
+
s.files = `git ls-files`.split("\n")
|
31
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
32
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
end
|