stack_exchange 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Sam Coles
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,310 @@
1
+ = stack_exchange
2
+
3
+ A thin Ruby wrapper for the Stack Exchange API. Designed to match the API as closely as possible.
4
+
5
+ == Examples:
6
+
7
+ > s = StackExchange::Base.new
8
+ > s.questions(2877955, :answers)
9
+ > pp s.questions(2877955, :body => 'true')
10
+
11
+ {
12
+ "total": 1,
13
+ "page": 1,
14
+ "pagesize": 30,
15
+ "questions": [
16
+ {
17
+ "tags": [
18
+ "java",
19
+ "concurrency"
20
+ ],
21
+ "answer_count": 5,
22
+ "answers": [
23
+ {
24
+ "answer_id": 2878021,
25
+ "accepted": false,
26
+ "answer_comments_url": "/answers/2878021/comments",
27
+ "question_id": 2877955,
28
+ "owner": {
29
+ "user_id": 142446,
30
+ "user_type": "registered",
31
+ "display_name": "bkail",
32
+ "reputation": 1268,
33
+ "email_hash": "0e8190d89907e24bf6b4438eaef71af4"
34
+ },
35
+ "creation_date": 1274391221,
36
+ "last_edit_date": 1274396565,
37
+ "last_activity_date": 1274396565,
38
+ "up_vote_count": 2,
39
+ "down_vote_count": 1,
40
+ "view_count": 80,
41
+ "score": 1,
42
+ "community_owned": false,
43
+ "title": "Controlling race condition at startup.",
44
+ "body": "<p>A <code>synchronized</code> block will automatically block other threads. Just use a simple lock object + status variable:</p>\n\n<pre><code>public class MyClass {\n private static boolean initialised;\n private static final Object lockObject = new Object();\n\n public void initialise() {\n synchronized (lockObject) {\n if (!initialised) {\n initStuff();\n initialised = true;\n }\n }\n }\n\n public void doStuff() {\n initialise();\n doOtherStuff();\n }\n}\n</code></pre>\n"
45
+ },
46
+ {
47
+ "answer_id": 2878026,
48
+ "accepted": false,
49
+ "answer_comments_url": "/answers/2878026/comments",
50
+ "question_id": 2877955,
51
+ "owner": {
52
+ "user_id": 326480,
53
+ "user_type": "registered",
54
+ "display_name": "mdma",
55
+ "reputation": 4572,
56
+ "email_hash": "12778253a95d10ba54e696ffab85103d"
57
+ },
58
+ "creation_date": 1274391233,
59
+ "last_edit_date": 1274391655,
60
+ "last_activity_date": 1274391655,
61
+ "up_vote_count": 0,
62
+ "down_vote_count": 0,
63
+ "view_count": 80,
64
+ "score": 0,
65
+ "community_owned": false,
66
+ "title": "Controlling race condition at startup.",
67
+ "body": "<p>You're using <code>AtomicBoolean</code> always from inside a synchronized block. There's not much point to that since only one thread can access it. Atomic variables are intended for use in lock-free solutions - you can get and set the value as an uninterruptable unit.</p>\n\n<p>I guess you are looking for a lock free solution once the intiialization has happened:</p>\n\n<pre><code>public class MyClass {\n private static final AtomicBoolean initialised = new AtomicBoolean(false);\n\n public void initialise() {\n if (!intialized.get())\n {\n synchornized (this)\n {\n if (!initialized.getAndSet(true))\n doInitialize();\n }\n }\n }\n\n public void doStuff() {\n initialize();\n doOtherStuff();\n }\n</code></pre>\n\n<p>You could also do this with a simple <code>volatile boolean</code> which is actually a little more efficient than an AtomicBoolean.</p>\n"
68
+ },
69
+ {
70
+ "answer_id": 2878031,
71
+ "accepted": false,
72
+ "answer_comments_url": "/answers/2878031/comments",
73
+ "question_id": 2877955,
74
+ "owner": {
75
+ "user_id": 162273,
76
+ "user_type": "registered",
77
+ "display_name": "Jason",
78
+ "reputation": 855,
79
+ "email_hash": "ca27a5c424b2e8f1b9f9ea3747305751"
80
+ },
81
+ "creation_date": 1274391259,
82
+ "last_activity_date": 1274391259,
83
+ "up_vote_count": 0,
84
+ "down_vote_count": 0,
85
+ "view_count": 80,
86
+ "score": 0,
87
+ "community_owned": false,
88
+ "title": "Controlling race condition at startup.",
89
+ "body": "<p>It this is right at startup, why not wait to start the other threads until the initialization is complete?</p>\n\n<p>Also, you can do a thread-synchronized IsComplete boolean that is set to false until it is set to true by the initialization routine.</p>\n"
90
+ },
91
+ {
92
+ "answer_id": 2878040,
93
+ "accepted": true,
94
+ "answer_comments_url": "/answers/2878040/comments",
95
+ "question_id": 2877955,
96
+ "owner": {
97
+ "user_id": 3474,
98
+ "user_type": "registered",
99
+ "display_name": "erickson",
100
+ "reputation": 34431,
101
+ "email_hash": "ffbf4e85b8ffbae4e9039b9c0cf07bc8"
102
+ },
103
+ "creation_date": 1274391344,
104
+ "last_activity_date": 1274391344,
105
+ "up_vote_count": 4,
106
+ "down_vote_count": 0,
107
+ "view_count": 80,
108
+ "score": 4,
109
+ "community_owned": false,
110
+ "title": "Controlling race condition at startup.",
111
+ "body": "<p>That's a strange mix of library and built-in concurrency controls. Something like this is much cleaner:</p>\n\n<pre><code>public class MyClass {\n\n private static final CountDownLatch latch = new CountDownLatch(1);\n\n public void initialise() {\n initStuff();\n latch.countDown();\n }\n\n public void doStuff() {\n try {\n latch.await();\n } catch (InterruptedException ex) {\n throw new RuntimeException(\"Uh oh!\", ex);\n }\n doOtherStuff();\n }\n\n}\n</code></pre>\n"
112
+ },
113
+ {
114
+ "answer_id": 2878440,
115
+ "accepted": false,
116
+ "answer_comments_url": "/answers/2878440/comments",
117
+ "question_id": 2877955,
118
+ "owner": {
119
+ "user_id": 115432,
120
+ "user_type": "registered",
121
+ "display_name": "Kathy Van Stone",
122
+ "reputation": 3675,
123
+ "email_hash": "eca023f50940b65931dfe75a03e6e00a"
124
+ },
125
+ "creation_date": 1274395987,
126
+ "last_activity_date": 1274395987,
127
+ "up_vote_count": 0,
128
+ "down_vote_count": 0,
129
+ "view_count": 80,
130
+ "score": 0,
131
+ "community_owned": false,
132
+ "title": "Controlling race condition at startup.",
133
+ "body": "<p>The best may be to use a static initializer (as mentioned by SB):</p>\n\n<pre><code>public class MyClass {\n\n public static void doInitialize() {\n ...\n }\n\n public void doStuff() {\n doOtherStuff();\n }\n\n static {\n doInitialize();\n }\n}\n</code></pre>\n\n<p>This will get executed once before any other code is allowed to be called. If you will always have to initialize anytime the class is used then there is no performance hit as the class will not be loaded until it is used. See the answers to <a href=\"http://stackoverflow.com/questions/878577/are-java-static-initializers-thread-safe\">this question</a> for more details.</p>\n"
134
+ }
135
+ ],
136
+ "accepted_answer_id": 2878040,
137
+ "favorite_count": 3,
138
+ "question_timeline_url": "/questions/2877955/timeline",
139
+ "question_comments_url": "/questions/2877955/comments",
140
+ "question_answers_url": "/questions/2877955/answers",
141
+ "question_id": 2877955,
142
+ "owner": {
143
+ "user_id": 13663,
144
+ "user_type": "registered",
145
+ "display_name": "Will Hartung",
146
+ "reputation": 13650,
147
+ "email_hash": "92f0c7fe372960ea92560487665a8720"
148
+ },
149
+ "creation_date": 1274390502,
150
+ "last_edit_date": 1274393269,
151
+ "last_activity_date": 1274396565,
152
+ "up_vote_count": 3,
153
+ "down_vote_count": 0,
154
+ "view_count": 80,
155
+ "score": 3,
156
+ "community_owned": false,
157
+ "title": "Controlling race condition at startup.",
158
+ "body": "<p>I have some code that I want to have some one time initialisation performed. But this code doesn't have a definite lifecycle, so my logic can be potentially invoked by multiple threads before my initialisation is done. So, I want to basically ensure that my logic code \"waits\" until initialisation is done.</p>\n\n<p>This is my first cut.</p>\n\n<pre><code>public class MyClass {\n private static final AtomicBoolean initialised = new AtomicBoolean(false);\n\n public void initialise() {\n synchronized(initialised) {\n initStuff();\n initialised.getAndSet(true);\n initialised.notifyAll();\n }\n }\n\n public void doStuff() {\n synchronized(initialised) {\n if (!initialised.get()) {\n try {\n initialised.wait();\n } catch (InterruptedException ex) {\n throw new RuntimeException(\"Uh oh!\", ex);\n }\n }\n }\n\n doOtherStuff();\n }\n}\n</code></pre>\n\n<p>I basically want to make sure this is going to do what I think it's going to do -- block doStuff until the initialised is true, and that I'm not missing a race condition where doStuff might get stuck on a Object.wait() that will never arrive.</p>\n\n<p>Edit:</p>\n\n<p>I have no control over the threads. And I want to be able to control when all of the initialisation is done, which is why doStuff() can't call initialise().</p>\n\n<p>I used an AtomicBoolean as it was a combination of a value holder, and an object I could synchronize. I could have also simply had a \"public static final Object lock = new Object();\" and a simple boolean flag. AtomicBoolean conveniently gave me both. A Boolean can not be modified.</p>\n\n<p>The CountDownLatch is exactly what I was looking for. I also considered using a Sempahore with 0 permits. But the CountDownLatch is perfect for just this task.</p>\n"
159
+ }
160
+ ]
161
+ }
162
+ {"questions"=>
163
+ [{"question_id"=>2877955,
164
+ "up_vote_count"=>3,
165
+ "last_activity_date"=>1274396565,
166
+ "body"=>
167
+ "<p>I have some code that I want to have some one time initialisation performed. But this code doesn't have a definite lifecycle, so my logic can be potentially invoked by multiple threads before my initialisation is done. So, I want to basically ensure that my logic code \"waits\" until initialisation is done.</p>\n\n<p>This is my first cut.</p>\n\n<pre><code>public class MyClass {\n private static final AtomicBoolean initialised = new AtomicBoolean(false);\n\n public void initialise() {\n synchronized(initialised) {\n initStuff();\n initialised.getAndSet(true);\n initialised.notifyAll();\n }\n }\n\n public void doStuff() {\n synchronized(initialised) {\n if (!initialised.get()) {\n try {\n initialised.wait();\n } catch (InterruptedException ex) {\n throw new RuntimeException(\"Uh oh!\", ex);\n }\n }\n }\n\n doOtherStuff();\n }\n}\n</code></pre>\n\n<p>I basically want to make sure this is going to do what I think it's going to do -- block doStuff until the initialised is true, and that I'm not missing a race condition where doStuff might get stuck on a Object.wait() that will never arrive.</p>\n\n<p>Edit:</p>\n\n<p>I have no control over the threads. And I want to be able to control when all of the initialisation is done, which is why doStuff() can't call initialise().</p>\n\n<p>I used an AtomicBoolean as it was a combination of a value holder, and an object I could synchronize. I could have also simply had a \"public static final Object lock = new Object();\" and a simple boolean flag. AtomicBoolean conveniently gave me both. A Boolean can not be modified.</p>\n\n<p>The CountDownLatch is exactly what I was looking for. I also considered using a Sempahore with 0 permits. But the CountDownLatch is perfect for just this task.</p>\n",
168
+ "title"=>"Controlling race condition at startup.",
169
+ "down_vote_count"=>0,
170
+ "creation_date"=>1274390502,
171
+ "question_answers_url"=>"/questions/2877955/answers",
172
+ "favorite_count"=>3,
173
+ "answer_count"=>5,
174
+ "tags"=>["java", "concurrency"],
175
+ "last_edit_date"=>1274393269,
176
+ "accepted_answer_id"=>2878040,
177
+ "question_comments_url"=>"/questions/2877955/comments",
178
+ "community_owned"=>false,
179
+ "score"=>3,
180
+ "view_count"=>80,
181
+ "owner"=>
182
+ {"reputation"=>13650,
183
+ "user_type"=>"registered",
184
+ "email_hash"=>"92f0c7fe372960ea92560487665a8720",
185
+ "user_id"=>13663,
186
+ "display_name"=>"Will Hartung"},
187
+ "question_timeline_url"=>"/questions/2877955/timeline",
188
+ "answers"=>
189
+ [{"question_id"=>2877955,
190
+ "up_vote_count"=>2,
191
+ "last_activity_date"=>1274396565,
192
+ "body"=>
193
+ "<p>A <code>synchronized</code> block will automatically block other threads. Just use a simple lock object + status variable:</p>\n\n<pre><code>public class MyClass {\n private static boolean initialised;\n private static final Object lockObject = new Object();\n\n public void initialise() {\n synchronized (lockObject) {\n if (!initialised) {\n initStuff();\n initialised = true;\n }\n }\n }\n\n public void doStuff() {\n initialise();\n doOtherStuff();\n }\n}\n</code></pre>\n",
194
+ "title"=>"Controlling race condition at startup.",
195
+ "down_vote_count"=>1,
196
+ "creation_date"=>1274391221,
197
+ "last_edit_date"=>1274396565,
198
+ "community_owned"=>false,
199
+ "answer_id"=>2878021,
200
+ "score"=>1,
201
+ "answer_comments_url"=>"/answers/2878021/comments",
202
+ "accepted"=>false,
203
+ "view_count"=>80,
204
+ "owner"=>
205
+ {"reputation"=>1268,
206
+ "user_type"=>"registered",
207
+ "email_hash"=>"0e8190d89907e24bf6b4438eaef71af4",
208
+ "user_id"=>142446,
209
+ "display_name"=>"bkail"}},
210
+ {"question_id"=>2877955,
211
+ "up_vote_count"=>0,
212
+ "last_activity_date"=>1274391655,
213
+ "body"=>
214
+ "<p>You're using <code>AtomicBoolean</code> always from inside a synchronized block. There's not much point to that since only one thread can access it. Atomic variables are intended for use in lock-free solutions - you can get and set the value as an uninterruptable unit.</p>\n\n<p>I guess you are looking for a lock free solution once the intiialization has happened:</p>\n\n<pre><code>public class MyClass {\n private static final AtomicBoolean initialised = new AtomicBoolean(false);\n\n public void initialise() {\n if (!intialized.get())\n {\n synchornized (this)\n {\n if (!initialized.getAndSet(true))\n doInitialize();\n }\n }\n }\n\n public void doStuff() {\n initialize();\n doOtherStuff();\n }\n</code></pre>\n\n<p>You could also do this with a simple <code>volatile boolean</code> which is actually a little more efficient than an AtomicBoolean.</p>\n",
215
+ "title"=>"Controlling race condition at startup.",
216
+ "down_vote_count"=>0,
217
+ "creation_date"=>1274391233,
218
+ "last_edit_date"=>1274391655,
219
+ "community_owned"=>false,
220
+ "answer_id"=>2878026,
221
+ "score"=>0,
222
+ "answer_comments_url"=>"/answers/2878026/comments",
223
+ "accepted"=>false,
224
+ "view_count"=>80,
225
+ "owner"=>
226
+ {"reputation"=>4572,
227
+ "user_type"=>"registered",
228
+ "email_hash"=>"12778253a95d10ba54e696ffab85103d",
229
+ "user_id"=>326480,
230
+ "display_name"=>"mdma"}},
231
+ {"question_id"=>2877955,
232
+ "up_vote_count"=>0,
233
+ "last_activity_date"=>1274391259,
234
+ "body"=>
235
+ "<p>It this is right at startup, why not wait to start the other threads until the initialization is complete?</p>\n\n<p>Also, you can do a thread-synchronized IsComplete boolean that is set to false until it is set to true by the initialization routine.</p>\n",
236
+ "title"=>"Controlling race condition at startup.",
237
+ "down_vote_count"=>0,
238
+ "creation_date"=>1274391259,
239
+ "community_owned"=>false,
240
+ "answer_id"=>2878031,
241
+ "score"=>0,
242
+ "answer_comments_url"=>"/answers/2878031/comments",
243
+ "accepted"=>false,
244
+ "view_count"=>80,
245
+ "owner"=>
246
+ {"reputation"=>855,
247
+ "user_type"=>"registered",
248
+ "email_hash"=>"ca27a5c424b2e8f1b9f9ea3747305751",
249
+ "user_id"=>162273,
250
+ "display_name"=>"Jason"}},
251
+ {"question_id"=>2877955,
252
+ "up_vote_count"=>4,
253
+ "last_activity_date"=>1274391344,
254
+ "body"=>
255
+ "<p>That's a strange mix of library and built-in concurrency controls. Something like this is much cleaner:</p>\n\n<pre><code>public class MyClass {\n\n private static final CountDownLatch latch = new CountDownLatch(1);\n\n public void initialise() {\n initStuff();\n latch.countDown();\n }\n\n public void doStuff() {\n try {\n latch.await();\n } catch (InterruptedException ex) {\n throw new RuntimeException(\"Uh oh!\", ex);\n }\n doOtherStuff();\n }\n\n}\n</code></pre>\n",
256
+ "title"=>"Controlling race condition at startup.",
257
+ "down_vote_count"=>0,
258
+ "creation_date"=>1274391344,
259
+ "community_owned"=>false,
260
+ "answer_id"=>2878040,
261
+ "score"=>4,
262
+ "answer_comments_url"=>"/answers/2878040/comments",
263
+ "accepted"=>true,
264
+ "view_count"=>80,
265
+ "owner"=>
266
+ {"reputation"=>34431,
267
+ "user_type"=>"registered",
268
+ "email_hash"=>"ffbf4e85b8ffbae4e9039b9c0cf07bc8",
269
+ "user_id"=>3474,
270
+ "display_name"=>"erickson"}},
271
+ {"question_id"=>2877955,
272
+ "up_vote_count"=>0,
273
+ "last_activity_date"=>1274395987,
274
+ "body"=>
275
+ "<p>The best may be to use a static initializer (as mentioned by SB):</p>\n\n<pre><code>public class MyClass {\n\n public static void doInitialize() {\n ...\n }\n\n public void doStuff() {\n doOtherStuff();\n }\n\n static {\n doInitialize();\n }\n}\n</code></pre>\n\n<p>This will get executed once before any other code is allowed to be called. If you will always have to initialize anytime the class is used then there is no performance hit as the class will not be loaded until it is used. See the answers to <a href=\"http://stackoverflow.com/questions/878577/are-java-static-initializers-thread-safe\">this question</a> for more details.</p>\n",
276
+ "title"=>"Controlling race condition at startup.",
277
+ "down_vote_count"=>0,
278
+ "creation_date"=>1274395987,
279
+ "community_owned"=>false,
280
+ "answer_id"=>2878440,
281
+ "score"=>0,
282
+ "answer_comments_url"=>"/answers/2878440/comments",
283
+ "accepted"=>false,
284
+ "view_count"=>80,
285
+ "owner"=>
286
+ {"reputation"=>3675,
287
+ "user_type"=>"registered",
288
+ "email_hash"=>"eca023f50940b65931dfe75a03e6e00a",
289
+ "user_id"=>115432,
290
+ "display_name"=>"Kathy Van Stone"}}]}],
291
+ "total"=>1,
292
+ "page"=>1,
293
+ "pagesize"=>30}
294
+
295
+
296
+
297
+
298
+ == Note on Patches/Pull Requests
299
+
300
+ * Fork the project.
301
+ * Make your feature addition or bug fix.
302
+ * Add tests for it. This is important so I don't break it in a
303
+ future version unintentionally.
304
+ * Commit, do not mess with rakefile, version, or history.
305
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
306
+ * Send me a pull request. Bonus points for topic branches.
307
+
308
+ == Copyright
309
+
310
+ Copyright (c) 2010 Sam Coles. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "stack_exchange"
8
+ gem.summary = %Q{A Ruby interface to the Stack Exchange API}
9
+ gem.description = %Q{Access any Stack Exchange site (stackoverflow.com, ect.) from Ruby.}
10
+ gem.email = "sam@coredumplings.com"
11
+ gem.homepage = "http://github.com/coredumplings/stack_exchange"
12
+ gem.authors = ["Sam Coles"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/test_*.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ #task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "stack_exchange #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'stack_exchange/base'
3
+
4
+ module StackExchange
5
+ VERSION = '0.1'
6
+ end
@@ -0,0 +1,42 @@
1
+ require 'open-uri'
2
+ require 'uri'
3
+ require 'json'
4
+ require 'zlib'
5
+ require 'stringio'
6
+
7
+ module StackExchange
8
+ class Base
9
+ DEFAULT_OPTIONS = {
10
+ :site => 'stackoverflow.com',
11
+ :version => '0.8',
12
+ :http_basic_authentication => nil,
13
+ 'Accept-Encoding' => 'gzip',
14
+ 'User-Agent' => "Ruby StackExchange Client/#{VERSION}"
15
+ }
16
+
17
+ attr_reader :base_uri
18
+ attr_reader :http_options
19
+
20
+ def initialize(opts={})
21
+ opts = DEFAULT_OPTIONS.merge(opts)
22
+ @base_uri = URI.parse("http://api.#{opts.delete(:site)}/#{opts.delete(:version)}/")
23
+ @http_options = opts
24
+ end
25
+
26
+ def hash_to_querystring(hash)
27
+ hash.keys.inject('') do |query_string, key|
28
+ query_string << '&' unless key == hash.keys.first
29
+ query_string << "#{URI.encode(key.to_s)}=#{URI.encode(hash[key])}"
30
+ end
31
+ end
32
+
33
+ def method_missing(method_name, *args)
34
+ query = hash_to_querystring(args.pop) if args && args.last.is_a?(Hash)
35
+ uri = base_uri + ([method_name.to_s] + args).join('/')
36
+ uri.query = query if defined?(query)
37
+ response = open( uri, http_options ).read
38
+ uncompressed_response = Zlib::GzipReader.new(StringIO.new( response )).read
39
+ JSON.parse( uncompressed_response )
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{stack_exchange}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Sam Coles"]
12
+ s.date = %q{2010-05-24}
13
+ s.description = %q{Access any Stack Exchange site (stackoverflow.com, ect.) from Ruby.}
14
+ s.email = %q{sam@coredumplings.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/stack_exchange.rb",
27
+ "lib/stack_exchange/base.rb",
28
+ "stack_exchange.gemspec",
29
+ "test/helper.rb",
30
+ "test/test_stack_exchange.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/coredumplings/stack_exchange}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{A Ruby interface to the Stack Exchange API}
37
+ s.test_files = [
38
+ "test/helper.rb",
39
+ "test/test_stack_exchange.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ else
48
+ end
49
+ else
50
+ end
51
+ end
52
+
data/test/helper.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'stack_exchange'
7
+
8
+ class Test::Unit::TestCase
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ class TestStackExchange < Test::Unit::TestCase
4
+ def test_uri_generation
5
+ answers = StackExchange::Base.new.questions(1, :answers)
6
+ puts(answers)
7
+ assert("http://api.stackoverflow.com/0.8/questions/1/answers", answers)
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stack_exchange
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Sam Coles
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-24 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Access any Stack Exchange site (stackoverflow.com, ect.) from Ruby.
23
+ email: sam@coredumplings.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ - README.rdoc
31
+ files:
32
+ - .document
33
+ - .gitignore
34
+ - LICENSE
35
+ - README.rdoc
36
+ - Rakefile
37
+ - VERSION
38
+ - lib/stack_exchange.rb
39
+ - lib/stack_exchange/base.rb
40
+ - stack_exchange.gemspec
41
+ - test/helper.rb
42
+ - test/test_stack_exchange.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/coredumplings/stack_exchange
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --charset=UTF-8
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.7
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: A Ruby interface to the Stack Exchange API
77
+ test_files:
78
+ - test/helper.rb
79
+ - test/test_stack_exchange.rb