stacked 0.5.0
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/.document +5 -0
- data/.gitignore +6 -0
- data/Gemfile +1 -0
- data/LICENSE +20 -0
- data/README.markdown +37 -0
- data/README.rdoc +18 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/cleaner.rb +29 -0
- data/doc/Stacked.html +155 -0
- data/doc/Stacked/Answer.html +1394 -0
- data/doc/Stacked/Badge.html +480 -0
- data/doc/Stacked/Base.html +1124 -0
- data/doc/Stacked/Comment.html +1037 -0
- data/doc/Stacked/NotImplemented.html +162 -0
- data/doc/Stacked/Posttimeline.html +543 -0
- data/doc/Stacked/Question.html +1763 -0
- data/doc/Stacked/Reputation.html +606 -0
- data/doc/Stacked/Tag.html +267 -0
- data/doc/Stacked/User.html +2787 -0
- data/doc/Stacked/Usertimeline.html +630 -0
- data/doc/_index.html +246 -0
- data/doc/file.README.html +54 -0
- data/doc/index.html +54 -0
- data/doc/method_list.html +1203 -0
- data/doc/top-level-namespace.html +87 -0
- data/genddoc.sh +12 -0
- data/lib/stacked.rb +46 -0
- data/lib/stacked/answer.rb +43 -0
- data/lib/stacked/badge.rb +18 -0
- data/lib/stacked/base.rb +159 -0
- data/lib/stacked/comment.rb +37 -0
- data/lib/stacked/posttimeline.rb +10 -0
- data/lib/stacked/question.rb +85 -0
- data/lib/stacked/reputation.rb +15 -0
- data/lib/stacked/tag.rb +9 -0
- data/lib/stacked/user.rb +195 -0
- data/lib/stacked/usertimeline.rb +14 -0
- data/spec/sorted_by_spec.rb +24 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/stacked/answer_spec.rb +31 -0
- data/spec/stacked/badge_spec.rb +17 -0
- data/spec/stacked/base_spec.rb +27 -0
- data/spec/stacked/comment_spec.rb +41 -0
- data/spec/stacked/question_spec.rb +110 -0
- data/spec/stacked/reputation_spec.rb +18 -0
- data/spec/stacked/tag_spec.rb +21 -0
- data/spec/stacked/user_spec.rb +187 -0
- data/spec/stacked/usertimeline_spec.rb +5 -0
- data/spec/support/aliases.rb +7 -0
- data/spec/support/sorted_by.rb +31 -0
- data/spec/support/within.rb +24 -0
- data/spec/within_spec.rb +19 -0
- data/stacked.gemspec +115 -0
- data/stylesheet.css +41 -0
- metadata +157 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta name="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>Top Level Namespace</title>
|
7
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
|
8
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
|
9
|
+
|
10
|
+
<script type="text/javascript" charset="utf-8">
|
11
|
+
relpath = '';
|
12
|
+
if (relpath != '') relpath += '/';
|
13
|
+
</script>
|
14
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
15
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
16
|
+
|
17
|
+
</head>
|
18
|
+
<body>
|
19
|
+
<script type="text/javascript" charset="utf-8">
|
20
|
+
if (window.top.frames.main) document.body.className = 'frames';
|
21
|
+
</script>
|
22
|
+
|
23
|
+
<div id="header">
|
24
|
+
<div id="menu">
|
25
|
+
|
26
|
+
<a href="_index.html">Index</a> »
|
27
|
+
|
28
|
+
|
29
|
+
<span class="title">Top Level Namespace</span>
|
30
|
+
|
31
|
+
|
32
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="search">
|
36
|
+
<a id="class_list_link" href="#">Class List</a>
|
37
|
+
<a id="method_list_link" href="#">Method List</a>
|
38
|
+
<a id ="file_list_link" href="#">File List</a>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div class="clear"></div>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<iframe id="search_frame"></iframe>
|
45
|
+
|
46
|
+
<div id="content"><h1>Top Level Namespace
|
47
|
+
|
48
|
+
|
49
|
+
</h1>
|
50
|
+
|
51
|
+
<dl class="box">
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
</dl>
|
61
|
+
<div class="clear"></div>
|
62
|
+
|
63
|
+
<h2>Defined Under Namespace</h2>
|
64
|
+
<p class="children">
|
65
|
+
|
66
|
+
|
67
|
+
<strong class="modules">Modules:</strong> <a href="Stacked.html" title="Stacked (module)">Stacked</a>
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
</p>
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
</div>
|
79
|
+
|
80
|
+
<div id="footer">
|
81
|
+
Generated on Sun Apr 4 18:16:35 2010 by
|
82
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool">yard</a>
|
83
|
+
0.5.4 (ruby-1.8.7).
|
84
|
+
</div>
|
85
|
+
|
86
|
+
</body>
|
87
|
+
</html>
|
data/genddoc.sh
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
yardoc lib/**/*.rb &&
|
3
|
+
rm -rf ../stacked-doc &&
|
4
|
+
mkdir -p ../stacked-doc &&
|
5
|
+
mv doc/* ../stacked-doc &&
|
6
|
+
git checkout gh-pages &&
|
7
|
+
rm -rf * &&
|
8
|
+
mv ../stacked-doc/* . &&
|
9
|
+
git add . &&
|
10
|
+
git commit -m "Updated documentation." &&
|
11
|
+
git push origin gh-pages &&
|
12
|
+
git checkout master
|
data/lib/stacked.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
rescue LoadError
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.setup!
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'active_support'
|
9
|
+
|
10
|
+
module Stacked
|
11
|
+
# TODO: Use this coupled with autoload_under when AS 3.0 becomes "stable":
|
12
|
+
# extend ActiveSupport::Autoload
|
13
|
+
# Will not then need to implement it ourselves.
|
14
|
+
#
|
15
|
+
# Implement some AS 3.0 code for automatically determining autoload path.
|
16
|
+
# Allows us to do:
|
17
|
+
#
|
18
|
+
# autoload :Base
|
19
|
+
#
|
20
|
+
# Instead of:
|
21
|
+
#
|
22
|
+
# autoload :Base, 'stacked/base'
|
23
|
+
class << self
|
24
|
+
def autoload(klass)
|
25
|
+
super(klass, "stacked/#{klass.to_s.underscore}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
autoload :Answer
|
29
|
+
autoload :Base
|
30
|
+
autoload :Badge
|
31
|
+
autoload :Comment
|
32
|
+
autoload :Posttimeline
|
33
|
+
autoload :Question
|
34
|
+
autoload :Reputation
|
35
|
+
autoload :Tag
|
36
|
+
autoload :User
|
37
|
+
autoload :Usertimeline
|
38
|
+
|
39
|
+
class NotImplemented < StandardError
|
40
|
+
def message
|
41
|
+
"The requested action is not available in the API."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Stacked
|
2
|
+
class Answer < Base
|
3
|
+
attr_accessor :accepted,
|
4
|
+
:answer_id,
|
5
|
+
:body,
|
6
|
+
:comments,
|
7
|
+
:community_owned,
|
8
|
+
:creation_date,
|
9
|
+
:down_vote_count,
|
10
|
+
:last_edit_date,
|
11
|
+
:owner_display_name,
|
12
|
+
:owner_user_id,
|
13
|
+
:question_id,
|
14
|
+
:score,
|
15
|
+
:title,
|
16
|
+
:up_vote_count,
|
17
|
+
:view_count
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def all(*args)
|
21
|
+
raise Stacked::NotImplemented
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# A Stacked::User object representing the owner of the answer.
|
26
|
+
def owner
|
27
|
+
@owner ||= User.find(owner_user_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
# A Stacked::Question object representing the question the answer is in response to.
|
31
|
+
def question
|
32
|
+
@question ||= Question.find(question_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :created_at, :creation_date
|
36
|
+
alias_method :updated_at, :last_edit_date
|
37
|
+
alias_method :id, :answer_id
|
38
|
+
alias_method :up_votes, :up_vote_count
|
39
|
+
alias_method :views, :view_count
|
40
|
+
alias_method :user, :owner
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/stacked/base.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Stacked
|
5
|
+
class Base
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
delegate :request, :singular, :parse, :to => "self.class"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Return the stats provided by the API.
|
13
|
+
def stats
|
14
|
+
request(base + "stats")["stats"]
|
15
|
+
end
|
16
|
+
|
17
|
+
# All the first group (depends on pagesize) of records for current class.
|
18
|
+
def all(options = {})
|
19
|
+
records(path, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# A single record belonging to the current class.
|
23
|
+
def find(id, options={})
|
24
|
+
self.new(request(singular(id), options)[resource.singularize])
|
25
|
+
end
|
26
|
+
|
27
|
+
# All records for a given request path.
|
28
|
+
def records(p = path, options = {})
|
29
|
+
parse(request(p, options)[resource])
|
30
|
+
end
|
31
|
+
|
32
|
+
# Raw Hash of request.
|
33
|
+
def request(p = path, options = {})
|
34
|
+
get(p, :query => { :key => key }.merge!(options))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Define collection methods, such as newest.
|
38
|
+
def collection(*names)
|
39
|
+
# Forgive me Matz for I have sinned.
|
40
|
+
for name in names
|
41
|
+
eval <<-EVAL
|
42
|
+
def self.#{name}(options = {})
|
43
|
+
records(path + "#{name}", options)
|
44
|
+
end
|
45
|
+
EVAL
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Defines association methods for things such as comments on questions.
|
50
|
+
def association(assoc)
|
51
|
+
instance_eval do
|
52
|
+
assoc = assoc.to_s
|
53
|
+
define_method("#{assoc}=") do |records|
|
54
|
+
instance_variable_set("@#{assoc}", records.map { |record| "Stacked::#{assoc.classify}".constantize.new(record) })
|
55
|
+
end
|
56
|
+
|
57
|
+
define_method(assoc) { instance_variable_get("@#{assoc}") }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# The path to the singular resource.
|
62
|
+
def singular(id)
|
63
|
+
path + id.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# The root URL of the API,
|
69
|
+
def base
|
70
|
+
Pathname.new("http://api.stackoverflow.com/0.5/")
|
71
|
+
end
|
72
|
+
|
73
|
+
# The key to let us in.
|
74
|
+
def key
|
75
|
+
"knockknock"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convert the records into actual objects.
|
79
|
+
def parse(records, klass=self)
|
80
|
+
records.map { |record| klass.new(record) }
|
81
|
+
end
|
82
|
+
|
83
|
+
# The path to this particular part of the API.
|
84
|
+
# Example if the class is Stacked::Question:
|
85
|
+
#
|
86
|
+
# http://api.stackoverflow.com/0.5/questions
|
87
|
+
|
88
|
+
def path
|
89
|
+
base + resource
|
90
|
+
end
|
91
|
+
|
92
|
+
# The Stack Overflow friendly version of the class, pluralized.
|
93
|
+
def resource
|
94
|
+
self.to_s.demodulize.downcase.pluralize
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Convert an answers result into a collection of Stacked::Answer objects.
|
99
|
+
def parse_answers(result)
|
100
|
+
parse_type(result, "answer")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Convert a badges result into a collection of Stacked::Badge objects.
|
104
|
+
def parse_badges(result)
|
105
|
+
parse_type(result, "badge")
|
106
|
+
end
|
107
|
+
|
108
|
+
# Convert a comments result into a collection of Stacked::Comment objects.
|
109
|
+
def parse_comments(result)
|
110
|
+
parse_type(result, "comment")
|
111
|
+
end
|
112
|
+
|
113
|
+
# Convert a post timeline result into a collection of Stacked::Posttimeline objects.
|
114
|
+
def parse_post_timeline(result)
|
115
|
+
parse_type(result, "posttimeline")
|
116
|
+
end
|
117
|
+
|
118
|
+
# Convert a questions result into a collection of Stacked::Question objects.
|
119
|
+
def parse_questions(result)
|
120
|
+
parse_type(result, "question")
|
121
|
+
end
|
122
|
+
|
123
|
+
# Convert a reputation result into a collection of Stacked::Reputation objects.
|
124
|
+
def parse_reputations(result)
|
125
|
+
parse_type(result, "reputation")
|
126
|
+
end
|
127
|
+
|
128
|
+
# Convert a tags result into a collection of Stacked::Tag objects.
|
129
|
+
def parse_tags(result)
|
130
|
+
parse_type(result, "tag")
|
131
|
+
end
|
132
|
+
|
133
|
+
# Convert a user timeline result into a collection of Stacked::Usertimeline objects.
|
134
|
+
def parse_user_timeline(result)
|
135
|
+
parse_type(result, "usertimeline")
|
136
|
+
end
|
137
|
+
|
138
|
+
# Converts the specified result into objects of the +type+ class.
|
139
|
+
def parse_type(result, type)
|
140
|
+
parse(result[type.pluralize], "Stacked::#{type.classify}".constantize)
|
141
|
+
end
|
142
|
+
|
143
|
+
public
|
144
|
+
|
145
|
+
# Finds a post based on the +post_type+ and +post_id+
|
146
|
+
def post
|
147
|
+
"Stacked::#{post_type.classify}".constantize.find(post_id)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Creates a new object of the given class based on the attributes passed in.
|
151
|
+
def initialize(attributes)
|
152
|
+
# p self
|
153
|
+
# p attributes.keys.sort.map { |t| t.to_sym }
|
154
|
+
attributes.each do |k, v|
|
155
|
+
self.send("#{k}=", v)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Stacked
|
2
|
+
class Comment < Base
|
3
|
+
attr_accessor :body,
|
4
|
+
:comment_id,
|
5
|
+
:creation_date,
|
6
|
+
:edit_count,
|
7
|
+
:owner_display_name,
|
8
|
+
:owner_user_id,
|
9
|
+
:post_id,
|
10
|
+
:post_type,
|
11
|
+
:reply_to_user_id,
|
12
|
+
:score
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def all(*args)
|
16
|
+
raise Stacked::NotImplemented
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Finds the user this comment was in reply to.
|
21
|
+
# nil if no user.
|
22
|
+
def reply_to
|
23
|
+
Stacked::User.find(reply_to_user_id) if reply_to_user_id
|
24
|
+
end
|
25
|
+
|
26
|
+
# The owner of this comment.
|
27
|
+
def owner
|
28
|
+
@owner ||= Stacked::User.find(owner_user_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :created_at, :creation_date
|
32
|
+
alias_method :id, :comment_id
|
33
|
+
alias_method :user, :owner
|
34
|
+
alias_method :edits, :edit_count
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Stacked
|
2
|
+
class Question < Base
|
3
|
+
attr_accessor :accepted_answer_id,
|
4
|
+
:answer_count,
|
5
|
+
:answers,
|
6
|
+
:body,
|
7
|
+
:bounty_closes_date,
|
8
|
+
:community_owned,
|
9
|
+
:creation_date,
|
10
|
+
:down_vote_count,
|
11
|
+
:favorite_count,
|
12
|
+
:last_activity_date,
|
13
|
+
:last_edit_date,
|
14
|
+
:owner_display_name,
|
15
|
+
:owner_user_id,
|
16
|
+
:question_id,
|
17
|
+
:score,
|
18
|
+
:tags,
|
19
|
+
:title,
|
20
|
+
:up_vote_count,
|
21
|
+
:view_count
|
22
|
+
|
23
|
+
# TODO: Combine this into a "has_many"-esque method when I'm not jetlagged.
|
24
|
+
association :comments
|
25
|
+
association :answers
|
26
|
+
|
27
|
+
collection :active,
|
28
|
+
:featured,
|
29
|
+
:hot,
|
30
|
+
:month,
|
31
|
+
:newest,
|
32
|
+
:unanswered,
|
33
|
+
:votes,
|
34
|
+
:week
|
35
|
+
|
36
|
+
# The Stacked::Answer representing the accepted answer.
|
37
|
+
# nil if none accepted
|
38
|
+
def accepted_answer
|
39
|
+
Answer.find(accepted_answer_id) if accepted_answer_id
|
40
|
+
end
|
41
|
+
|
42
|
+
# The Stacked::User representation of the owner.
|
43
|
+
def owner
|
44
|
+
@owner ||= User.find(owner_user_id)
|
45
|
+
end
|
46
|
+
|
47
|
+
# A timeline of the question.
|
48
|
+
def timeline(options={})
|
49
|
+
parse_post_timeline(request(singular(id) + "timeline", options))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Helper method for creating Stacked::Tag objects when initializing new Stacked::Question objects.
|
53
|
+
def tags=(tags)
|
54
|
+
@tags = tags.map { |name| Tag.new(:name => name) }
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :created_at, :creation_date
|
58
|
+
alias_method :down_votes, :down_vote_count
|
59
|
+
alias_method :favorites, :favorite_count
|
60
|
+
alias_method :favourites, :favorite_count
|
61
|
+
alias_method :id, :question_id
|
62
|
+
alias_method :updated_at, :last_edit_date
|
63
|
+
alias_method :up_votes, :up_vote_count
|
64
|
+
alias_method :user, :owner
|
65
|
+
alias_method :views, :view_count
|
66
|
+
|
67
|
+
class << self
|
68
|
+
alias_method :newest_unanswered, :unanswered
|
69
|
+
|
70
|
+
# All unanswered questions ordered by votes.
|
71
|
+
def unanswered_by_votes(options={})
|
72
|
+
records(path + "unanswered/votes", options)
|
73
|
+
end
|
74
|
+
|
75
|
+
# All questions tagged with the given tags.
|
76
|
+
# Accepts tags as either +:tags+ or +:tagged+
|
77
|
+
# Must be an Array of tags.
|
78
|
+
def tagged(options={})
|
79
|
+
options[:tagged] ||= []
|
80
|
+
options[:tagged] = (options[:tagged] << options[:tags]).join(", ")
|
81
|
+
records(path + "tagged", options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|