pilha 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pilha.rb ADDED
@@ -0,0 +1,92 @@
1
+ path = File.expand_path(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
3
+
4
+ require 'open-uri'
5
+ require 'json'
6
+ require 'forwardable'
7
+
8
+ require 'pilha/stack_overflow/statistics'
9
+ require 'pilha/stack_overflow/badge'
10
+ require 'pilha/stack_overflow/user'
11
+
12
+ module StackExchange
13
+
14
+ module StackOverflow
15
+ class Client
16
+ URL = 'http://api.stackoverflow.com/'
17
+ API_VERSION = '0.8'
18
+
19
+ attr_reader :url
20
+ attr_reader :api_version
21
+ attr_reader :api_key
22
+
23
+ class << self
24
+ def config &block
25
+ options = {}
26
+ yield options if block_given?
27
+ client = new(options)
28
+ include_client(client, Badge, Statistics, User)
29
+ end
30
+
31
+ def include_client(client, *classes)
32
+ classes.each do |klass|
33
+ klass.instance_variable_set(:@client, client)
34
+ end
35
+ end
36
+ end
37
+
38
+ def initialize(options = {})
39
+ @url = normalize(options[:url] || URL )
40
+ @api_version = options[:api_version] || options[:version] || API_VERSION
41
+ @api_key = options[:api_key] || options[:key]
42
+ end
43
+
44
+ def api_method_path(pattern, options = {})
45
+ pattern << '/' unless pattern.end_with? '/'
46
+ parts = pattern.split('/').select { |part| part =~ /^:/ }
47
+
48
+ parts.each do |part|
49
+ key = part.sub(':', '').intern
50
+ pattern.sub!(part, options[key].to_s)
51
+ options.delete key
52
+ end
53
+
54
+ pattern
55
+ end
56
+
57
+ def api_method_url(method, options = {})
58
+ options.merge! :api_key => api_key if api_key
59
+ root_path + api_method_path(method, options) + query_string(options)
60
+ end
61
+
62
+ def get(url)
63
+ stream = open(url) { |stream| stream.read }
64
+ JSON.parse stream
65
+ end
66
+
67
+ def root_path
68
+ url + api_version
69
+ end
70
+
71
+ private
72
+ def key?
73
+ !!@api_key
74
+ end
75
+
76
+ def query_string(options)
77
+ params = options[:query]
78
+ if params
79
+ '?' + params.inject([]) do |arr, (key, value)|
80
+ arr << "#{key}=#{value}"
81
+ end.join('&')
82
+ else
83
+ ''
84
+ end
85
+ end
86
+
87
+ def normalize(url)
88
+ url.end_with?('/') ? url : url + '/'
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,29 @@
1
+ module StackExchange
2
+ module StackOverflow
3
+ class Badge
4
+
5
+ extend Forwardable
6
+
7
+ def_delegators :@struct, :badge_id, :rank, :name, :description,
8
+ :award_count, :tag_based, :badges_recipients_url
9
+
10
+ class << self
11
+ attr_reader :client
12
+
13
+ def all
14
+ response = client.get client.api_method_url('/badges')
15
+ badges = response['badges']
16
+ badges.map { |badge| Badge.new badge }
17
+ end
18
+ end
19
+
20
+ def initialize(hash)
21
+ @struct = OpenStruct.new hash
22
+ end
23
+
24
+ def id
25
+ @struct.badge_id
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module StackExchange
2
+ module StackOverflow
3
+
4
+ class Statistics
5
+
6
+ extend Forwardable
7
+
8
+ def_delegators :@struct, :total_questions, :total_unanswered, :total_answers,
9
+ :total_comments, :total_votes, :total_badges,
10
+ :total_users, :questions_per_minute, :answers_per_minute,
11
+ :badges_per_minute, :api_version
12
+
13
+ def initialize(hash)
14
+ @struct = OpenStruct.new hash
15
+ end
16
+
17
+ class << self
18
+ attr_reader :client
19
+
20
+ def all
21
+ response = client.get client.api_method_url('/stats')
22
+ stats = response['statistics'].first
23
+ Statistics.new stats
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ module StackExchange
2
+ module StackOverflow
3
+
4
+ class User
5
+ extend Forwardable
6
+
7
+ def_delegators :@struct, :user_id, :user_type, :creation_date, :display_name,
8
+ :reputation, :email_hash, :age, :last_access_date,
9
+ :website_url, :location, :about_me, :question_count,
10
+ :answer_count, :view_count, :up_vote_count, :down_vote_count,
11
+ :user_questions_url, :user_answers_url, :user_favorites_url,
12
+ :user_tags_url, :user_badges_url, :user_timeline_url,
13
+ :user_mentioned_url, :user_comments_url, :user_reputation_url,
14
+ :badge_counts
15
+
16
+ def initialize(hash)
17
+ @struct = OpenStruct.new hash
18
+ end
19
+
20
+ class << self
21
+
22
+ attr_reader :client
23
+
24
+ def find_by_badge_id(id, query_options = {})
25
+ response = client.get client.api_method_url('/badges/:id', :id => id, :query => query_options)
26
+ users = response['users'].map { |user| User.new(user) }
27
+ response['users'] = users
28
+ OpenStruct.new response
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
data/pilha.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "pilha"
3
+ gem.version = "0.1.1"
4
+ gem.authors = ["Dalto Curvelano Junior"]
5
+ gem.description = "A ruby wrapper to the StackExchange (StackOverflow and friends) API."
6
+ gem.summary = "A ruby wrapper to the StackExchange (StackOverflow and friends) API."
7
+ gem.files = [
8
+ "pilha.gemspec",
9
+ "lib/pilha/stack_overflow/badge.rb",
10
+ "lib/pilha/stack_overflow/statistics.rb",
11
+ "lib/pilha/stack_overflow/user.rb",
12
+ "lib/pilha.rb",
13
+ "spec/fixtures/badges.json",
14
+ "spec/fixtures/badges_by_id.json",
15
+ "spec/fixtures/badges_by_id_page2.json",
16
+ "spec/fixtures/badges_name.json",
17
+ "spec/fixtures/stats.json",
18
+ "spec/pilha/stack_overflow/badge_spec.rb",
19
+ "spec/pilha/stack_overflow/stack_overflow_spec.rb",
20
+ "spec/pilha/stack_overflow/statistics_spec.rb",
21
+ "spec/pilha/stack_overflow/user_spec.rb",
22
+ "spec/spec.opts",
23
+ "spec/spec_helper.rb"
24
+ ]
25
+ gem.homepage = "http://github.com/dlt/stack_exchange"
26
+ end
@@ -0,0 +1,400 @@
1
+ {
2
+ "badges": [
3
+ {
4
+ "badge_id": 9,
5
+ "rank": "bronze",
6
+ "name": "Autobiographer",
7
+ "description": "Completed all user profile fields",
8
+ "award_count": 28065,
9
+ "tag_based": false,
10
+ "badges_recipients_url": "/badges/9"
11
+ },
12
+ {
13
+ "badge_id": 30,
14
+ "rank": "silver",
15
+ "name": "Beta",
16
+ "description": "Actively participated in the Stack Overflow private beta",
17
+ "award_count": 2606,
18
+ "tag_based": false,
19
+ "badges_recipients_url": "/badges/30"
20
+ },
21
+ {
22
+ "badge_id": 8,
23
+ "rank": "bronze",
24
+ "name": "Citizen Patrol",
25
+ "description": "First flagged post",
26
+ "award_count": 5689,
27
+ "tag_based": false,
28
+ "badges_recipients_url": "/badges/8"
29
+ },
30
+ {
31
+ "badge_id": 32,
32
+ "rank": "silver",
33
+ "name": "Civic Duty",
34
+ "description": "Voted 300 or more times",
35
+ "award_count": 4693,
36
+ "tag_based": false,
37
+ "badges_recipients_url": "/badges/32"
38
+ },
39
+ {
40
+ "badge_id": 4,
41
+ "rank": "bronze",
42
+ "name": "Cleanup",
43
+ "description": "First rollback",
44
+ "award_count": 3812,
45
+ "tag_based": false,
46
+ "badges_recipients_url": "/badges/4"
47
+ },
48
+ {
49
+ "badge_id": 31,
50
+ "rank": "bronze",
51
+ "name": "Commentator",
52
+ "description": "Left 10 comments",
53
+ "award_count": 31090,
54
+ "tag_based": false,
55
+ "badges_recipients_url": "/badges/31"
56
+ },
57
+ {
58
+ "badge_id": 7,
59
+ "rank": "bronze",
60
+ "name": "Critic",
61
+ "description": "First down vote",
62
+ "award_count": 25323,
63
+ "tag_based": false,
64
+ "badges_recipients_url": "/badges/7"
65
+ },
66
+ {
67
+ "badge_id": 37,
68
+ "rank": "bronze",
69
+ "name": "Disciplined",
70
+ "description": "Deleted own post with score of 3 or higher",
71
+ "award_count": 1665,
72
+ "tag_based": false,
73
+ "badges_recipients_url": "/badges/37"
74
+ },
75
+ {
76
+ "badge_id": 3,
77
+ "rank": "bronze",
78
+ "name": "Editor",
79
+ "description": "First edit",
80
+ "award_count": 63219,
81
+ "tag_based": false,
82
+ "badges_recipients_url": "/badges/3"
83
+ },
84
+ {
85
+ "badge_id": 155,
86
+ "rank": "gold",
87
+ "name": "Electorate",
88
+ "description": "Voted on 600 questions and 25% or more of total votes are on questions",
89
+ "award_count": 276,
90
+ "tag_based": false,
91
+ "badges_recipients_url": "/badges/155"
92
+ },
93
+ {
94
+ "badge_id": 19,
95
+ "rank": "silver",
96
+ "name": "Enlightened",
97
+ "description": "First answer was accepted with score of 10 or more",
98
+ "award_count": 11403,
99
+ "tag_based": false,
100
+ "badges_recipients_url": "/badges/19"
101
+ },
102
+ {
103
+ "badge_id": 71,
104
+ "rank": "silver",
105
+ "name": "Enthusiast",
106
+ "description": "Visited the site each day for 30 consecutive days",
107
+ "award_count": 6940,
108
+ "tag_based": false,
109
+ "badges_recipients_url": "/badges/71"
110
+ },
111
+ {
112
+ "badge_id": 145,
113
+ "rank": "silver",
114
+ "name": "Epic",
115
+ "description": "Hit the daily reputation cap on 50 days",
116
+ "award_count": 86,
117
+ "tag_based": false,
118
+ "badges_recipients_url": "/badges/145"
119
+ },
120
+ {
121
+ "badge_id": 28,
122
+ "rank": "gold",
123
+ "name": "Famous Question",
124
+ "description": "Asked a question with 10,000 views",
125
+ "award_count": 1063,
126
+ "tag_based": false,
127
+ "badges_recipients_url": "/badges/28"
128
+ },
129
+ {
130
+ "badge_id": 83,
131
+ "rank": "gold",
132
+ "name": "Fanatic",
133
+ "description": "Visited the site each day for 100 consecutive days",
134
+ "award_count": 937,
135
+ "tag_based": false,
136
+ "badges_recipients_url": "/badges/83"
137
+ },
138
+ {
139
+ "badge_id": 33,
140
+ "rank": "silver",
141
+ "name": "Favorite Question",
142
+ "description": "Question favorited by 25 users",
143
+ "award_count": 1082,
144
+ "tag_based": false,
145
+ "badges_recipients_url": "/badges/33"
146
+ },
147
+ {
148
+ "badge_id": 15,
149
+ "rank": "silver",
150
+ "name": "Generalist",
151
+ "description": "Provided non-wiki answers of 15 total score in 20 of top 40 tags",
152
+ "award_count": 133,
153
+ "tag_based": false,
154
+ "badges_recipients_url": "/badges/15"
155
+ },
156
+ {
157
+ "badge_id": 24,
158
+ "rank": "silver",
159
+ "name": "Good Answer",
160
+ "description": "Answer score of 25 or more",
161
+ "award_count": 9093,
162
+ "tag_based": false,
163
+ "badges_recipients_url": "/badges/24"
164
+ },
165
+ {
166
+ "badge_id": 21,
167
+ "rank": "silver",
168
+ "name": "Good Question",
169
+ "description": "Question score of 25 or more",
170
+ "award_count": 2267,
171
+ "tag_based": false,
172
+ "badges_recipients_url": "/badges/21"
173
+ },
174
+ {
175
+ "badge_id": 25,
176
+ "rank": "gold",
177
+ "name": "Great Answer",
178
+ "description": "Answer score of 100 or more",
179
+ "award_count": 1020,
180
+ "tag_based": false,
181
+ "badges_recipients_url": "/badges/25"
182
+ },
183
+ {
184
+ "badge_id": 22,
185
+ "rank": "gold",
186
+ "name": "Great Question",
187
+ "description": "Question score of 100 or more",
188
+ "award_count": 196,
189
+ "tag_based": false,
190
+ "badges_recipients_url": "/badges/22"
191
+ },
192
+ {
193
+ "badge_id": 18,
194
+ "rank": "silver",
195
+ "name": "Guru",
196
+ "description": "Accepted answer and score of 40 or more",
197
+ "award_count": 1128,
198
+ "tag_based": false,
199
+ "badges_recipients_url": "/badges/18"
200
+ },
201
+ {
202
+ "badge_id": 146,
203
+ "rank": "gold",
204
+ "name": "Legendary",
205
+ "description": "Hit the daily reputation cap on 150 days",
206
+ "award_count": 18,
207
+ "tag_based": false,
208
+ "badges_recipients_url": "/badges/146"
209
+ },
210
+ {
211
+ "badge_id": 144,
212
+ "rank": "bronze",
213
+ "name": "Mortarboard",
214
+ "description": "Hit the daily reputation cap for the first time",
215
+ "award_count": 4128,
216
+ "tag_based": false,
217
+ "badges_recipients_url": "/badges/144"
218
+ },
219
+ {
220
+ "badge_id": 17,
221
+ "rank": "silver",
222
+ "name": "Necromancer",
223
+ "description": "Answered a question more than 60 days later with score of 5 or more",
224
+ "award_count": 4894,
225
+ "tag_based": false,
226
+ "badges_recipients_url": "/badges/17"
227
+ },
228
+ {
229
+ "badge_id": 23,
230
+ "rank": "bronze",
231
+ "name": "Nice Answer",
232
+ "description": "Answer score of 10 or more",
233
+ "award_count": 55506,
234
+ "tag_based": false,
235
+ "badges_recipients_url": "/badges/23"
236
+ },
237
+ {
238
+ "badge_id": 20,
239
+ "rank": "bronze",
240
+ "name": "Nice Question",
241
+ "description": "Question score of 10 or more",
242
+ "award_count": 13354,
243
+ "tag_based": false,
244
+ "badges_recipients_url": "/badges/20"
245
+ },
246
+ {
247
+ "badge_id": 27,
248
+ "rank": "silver",
249
+ "name": "Notable Question",
250
+ "description": "Asked a question with 2,500 views",
251
+ "award_count": 14371,
252
+ "tag_based": false,
253
+ "badges_recipients_url": "/badges/27"
254
+ },
255
+ {
256
+ "badge_id": 5,
257
+ "rank": "bronze",
258
+ "name": "Organizer",
259
+ "description": "First retag",
260
+ "award_count": 12069,
261
+ "tag_based": false,
262
+ "badges_recipients_url": "/badges/5"
263
+ },
264
+ {
265
+ "badge_id": 38,
266
+ "rank": "bronze",
267
+ "name": "Peer Pressure",
268
+ "description": "Deleted own post with score of -3 or lower",
269
+ "award_count": 2965,
270
+ "tag_based": false,
271
+ "badges_recipients_url": "/badges/38"
272
+ },
273
+ {
274
+ "badge_id": 26,
275
+ "rank": "bronze",
276
+ "name": "Popular Question",
277
+ "description": "Asked a question with 1,000 views",
278
+ "award_count": 51931,
279
+ "tag_based": false,
280
+ "badges_recipients_url": "/badges/26"
281
+ },
282
+ {
283
+ "badge_id": 62,
284
+ "rank": "gold",
285
+ "name": "Populist",
286
+ "description": "Answer outscored an accepted answer with score of more than 10 by more than 2x",
287
+ "award_count": 344,
288
+ "tag_based": false,
289
+ "badges_recipients_url": "/badges/62"
290
+ },
291
+ {
292
+ "badge_id": 94,
293
+ "rank": "silver",
294
+ "name": "Pundit",
295
+ "description": "Left 10 comments with score of 10 or more",
296
+ "award_count": 83,
297
+ "tag_based": false,
298
+ "badges_recipients_url": "/badges/94"
299
+ },
300
+ {
301
+ "badge_id": 95,
302
+ "rank": "gold",
303
+ "name": "Reversal",
304
+ "description": "Provided answer of +20 score to a question of -5 score",
305
+ "award_count": 38,
306
+ "tag_based": false,
307
+ "badges_recipients_url": "/badges/95"
308
+ },
309
+ {
310
+ "badge_id": 10,
311
+ "rank": "bronze",
312
+ "name": "Scholar",
313
+ "description": "Asked a question and accepted an answer",
314
+ "award_count": 69346,
315
+ "tag_based": false,
316
+ "badges_recipients_url": "/badges/10"
317
+ },
318
+ {
319
+ "badge_id": 14,
320
+ "rank": "bronze",
321
+ "name": "Self-Learner",
322
+ "description": "Answered your own question with score of 3 or more",
323
+ "award_count": 3841,
324
+ "tag_based": false,
325
+ "badges_recipients_url": "/badges/14"
326
+ },
327
+ {
328
+ "badge_id": 36,
329
+ "rank": "gold",
330
+ "name": "Stellar Question",
331
+ "description": "Question favorited by 100 users",
332
+ "award_count": 153,
333
+ "tag_based": false,
334
+ "badges_recipients_url": "/badges/36"
335
+ },
336
+ {
337
+ "badge_id": 12,
338
+ "rank": "silver",
339
+ "name": "Strunk & White",
340
+ "description": "Edited 100 entries",
341
+ "award_count": 649,
342
+ "tag_based": false,
343
+ "badges_recipients_url": "/badges/12"
344
+ },
345
+ {
346
+ "badge_id": 2,
347
+ "rank": "bronze",
348
+ "name": "Student",
349
+ "description": "Asked first question with at least one up vote",
350
+ "award_count": 85741,
351
+ "tag_based": false,
352
+ "badges_recipients_url": "/badges/2"
353
+ },
354
+ {
355
+ "badge_id": 6,
356
+ "rank": "bronze",
357
+ "name": "Supporter",
358
+ "description": "First up vote",
359
+ "award_count": 61316,
360
+ "tag_based": false,
361
+ "badges_recipients_url": "/badges/6"
362
+ },
363
+ {
364
+ "badge_id": 11,
365
+ "rank": "silver",
366
+ "name": "Taxonomist",
367
+ "description": "Created a tag used by 50 questions",
368
+ "award_count": 2082,
369
+ "tag_based": false,
370
+ "badges_recipients_url": "/badges/11"
371
+ },
372
+ {
373
+ "badge_id": 1,
374
+ "rank": "bronze",
375
+ "name": "Teacher",
376
+ "description": "Answered first question with score of 1 or more",
377
+ "award_count": 71521,
378
+ "tag_based": false,
379
+ "badges_recipients_url": "/badges/1"
380
+ },
381
+ {
382
+ "badge_id": 63,
383
+ "rank": "bronze",
384
+ "name": "Tumbleweed",
385
+ "description": "Asked a question with no answers, no comments, and low views for a week",
386
+ "award_count": 23224,
387
+ "tag_based": false,
388
+ "badges_recipients_url": "/badges/63"
389
+ },
390
+ {
391
+ "badge_id": 13,
392
+ "rank": "silver",
393
+ "name": "Yearling",
394
+ "description": "Active member for a year, earning at least 200 reputation",
395
+ "award_count": 19443,
396
+ "tag_based": false,
397
+ "badges_recipients_url": "/badges/13"
398
+ }
399
+ ]
400
+ }