stacked 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.document +5 -0
  2. data/.gitignore +6 -0
  3. data/Gemfile +1 -0
  4. data/LICENSE +20 -0
  5. data/README.markdown +37 -0
  6. data/README.rdoc +18 -0
  7. data/Rakefile +51 -0
  8. data/VERSION +1 -0
  9. data/cleaner.rb +29 -0
  10. data/doc/Stacked.html +155 -0
  11. data/doc/Stacked/Answer.html +1394 -0
  12. data/doc/Stacked/Badge.html +480 -0
  13. data/doc/Stacked/Base.html +1124 -0
  14. data/doc/Stacked/Comment.html +1037 -0
  15. data/doc/Stacked/NotImplemented.html +162 -0
  16. data/doc/Stacked/Posttimeline.html +543 -0
  17. data/doc/Stacked/Question.html +1763 -0
  18. data/doc/Stacked/Reputation.html +606 -0
  19. data/doc/Stacked/Tag.html +267 -0
  20. data/doc/Stacked/User.html +2787 -0
  21. data/doc/Stacked/Usertimeline.html +630 -0
  22. data/doc/_index.html +246 -0
  23. data/doc/file.README.html +54 -0
  24. data/doc/index.html +54 -0
  25. data/doc/method_list.html +1203 -0
  26. data/doc/top-level-namespace.html +87 -0
  27. data/genddoc.sh +12 -0
  28. data/lib/stacked.rb +46 -0
  29. data/lib/stacked/answer.rb +43 -0
  30. data/lib/stacked/badge.rb +18 -0
  31. data/lib/stacked/base.rb +159 -0
  32. data/lib/stacked/comment.rb +37 -0
  33. data/lib/stacked/posttimeline.rb +10 -0
  34. data/lib/stacked/question.rb +85 -0
  35. data/lib/stacked/reputation.rb +15 -0
  36. data/lib/stacked/tag.rb +9 -0
  37. data/lib/stacked/user.rb +195 -0
  38. data/lib/stacked/usertimeline.rb +14 -0
  39. data/spec/sorted_by_spec.rb +24 -0
  40. data/spec/spec_helper.rb +16 -0
  41. data/spec/stacked/answer_spec.rb +31 -0
  42. data/spec/stacked/badge_spec.rb +17 -0
  43. data/spec/stacked/base_spec.rb +27 -0
  44. data/spec/stacked/comment_spec.rb +41 -0
  45. data/spec/stacked/question_spec.rb +110 -0
  46. data/spec/stacked/reputation_spec.rb +18 -0
  47. data/spec/stacked/tag_spec.rb +21 -0
  48. data/spec/stacked/user_spec.rb +187 -0
  49. data/spec/stacked/usertimeline_spec.rb +5 -0
  50. data/spec/support/aliases.rb +7 -0
  51. data/spec/support/sorted_by.rb +31 -0
  52. data/spec/support/within.rb +24 -0
  53. data/spec/within_spec.rb +19 -0
  54. data/stacked.gemspec +115 -0
  55. data/stylesheet.css +41 -0
  56. metadata +157 -0
@@ -0,0 +1,15 @@
1
+ module Stacked
2
+ class Reputation < Base
3
+ attr_accessor :negative_rep,
4
+ :on_date,
5
+ :positive_rep,
6
+ :post_id,
7
+ :post_type,
8
+ :title
9
+
10
+ # Return the cumulative score of this reputation.
11
+ def score
12
+ positive_rep - negative_rep
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Stacked
2
+ class Tag < Base
3
+ attr_accessor :count,
4
+ :name
5
+
6
+ collection :popular, :name, :recent
7
+
8
+ end
9
+ end
@@ -0,0 +1,195 @@
1
+ module Stacked
2
+ class User < Base
3
+ attr_accessor :about_me,
4
+ :accept_rate,
5
+ :age,
6
+ :answer_count,
7
+ :creation_date,
8
+ :display_name,
9
+ :down_vote_count,
10
+ :email_hash,
11
+ :last_access_date,
12
+ :location,
13
+ :question_count,
14
+ :reputation,
15
+ :up_vote_count,
16
+ :user_id,
17
+ :user_type,
18
+ :view_count,
19
+ :website_url
20
+
21
+ collection :newest, :oldest, :name
22
+
23
+ class << self
24
+ alias_method :reputation, :all
25
+
26
+ # Returns users matching the given filter.
27
+ def filter(filter, options={})
28
+ options.reverse_merge!(:filter => filter)
29
+ records(path, options)
30
+ end
31
+ end
32
+
33
+ ###########
34
+ # Answers #
35
+ ###########
36
+
37
+ # Paginated answers for this user.
38
+ def answers(options={})
39
+ parse_answers(request(singular(id) + "answers", options))
40
+ end
41
+
42
+ # Paginated answers by views for this user.
43
+ def answers_by_views(options={})
44
+ parse_answers(request(singular(id) + "answers/views", options))
45
+ end
46
+
47
+ # Paginated answers by votes for this user.
48
+ def answers_by_votes(options={})
49
+ parse_answers(request(singular(id) + "answers/votes", options))
50
+ end
51
+
52
+ # Newest answers for this user.
53
+ def newest_answers(options={})
54
+ parse_answers(request(singular(id) + "answers/newest", options))
55
+ end
56
+
57
+ ############
58
+ # Comments #
59
+ ############
60
+
61
+ # Paginated comments for this user.
62
+ def comments(options={})
63
+ parse_comments(request(singular(id) + "comments", options))
64
+ end
65
+
66
+ # Find a comment directed at +other_user_id+.
67
+ # Works only if you specify it the correct range.
68
+ # This method's a bit funny. http://dev.meta.stackoverflow.com/questions/34747/comments-api-method
69
+ def directed_at(other_user_id, options={})
70
+ parse_comments(request(singular(id) + "comments" + other_user_id.to_s, options))
71
+ end
72
+
73
+ # All comments directed at +other_user_id+ ordered by creation date in descending order.
74
+ def directed_at_by_date(other_user_id, options={})
75
+ parse_comments(request(singular(id) + "comments" + other_user_id.to_s + "recent", options))
76
+ end
77
+
78
+ # All comments directed at +other_user_id+ ordered by votes.
79
+ def directed_at_by_score(other_user_id, options={})
80
+ parse_comments(request(singular(id) + "comments" + other_user_id.to_s + "score", options))
81
+ end
82
+
83
+ # Paginated comments for this user ordered by score.
84
+ def comments_by_score(options={})
85
+ parse_comments(request(singular(id) + "comments/score", options))
86
+ end
87
+
88
+ alias_method :popular_comments, :comments_by_score
89
+
90
+ # Paginated comments for this user ordered by date created.
91
+ def recent_comments(options={})
92
+ parse_comments(request(singular(id) + "comments/recent", options))
93
+ end
94
+
95
+ ##############
96
+ # Favourites #
97
+ ##############
98
+
99
+ # Paginated favorites in the order they were added.
100
+ def added_favorites(options={})
101
+ parse_questions(request(singular(id) + "favorites/added", options))
102
+ end
103
+
104
+ # Paginated favorites.
105
+ def favorites(options={})
106
+ parse_questions(request(singular(id) + "favorites", options))
107
+ end
108
+
109
+ def newest_favorites(options={})
110
+ parse_questions(request(singular(id) + "favorites/newest", options))
111
+ end
112
+
113
+ def popular_favorites(options={})
114
+ parse_questions(request(singular(id) + "favorites/views", options))
115
+ end
116
+
117
+ alias_method :favorites_by_views, :popular_favorites
118
+ alias_method :favourites_by_views, :popular_favorites
119
+
120
+ def recent_favorites(options={})
121
+ parse_questions(request(singular(id) + "favorites/recent", options))
122
+ end
123
+
124
+ # Silly Americans.
125
+ alias_method :added_favourites, :added_favorites
126
+ alias_method :favourites, :favorites
127
+ alias_method :popular_favourites, :popular_favorites
128
+ alias_method :recent_favourites, :favorites
129
+ alias_method :newest_favourites, :newest_favorites
130
+
131
+ #############
132
+ # Questions #
133
+ #############
134
+
135
+ def newest_questions(options={})
136
+ parse_questions(request(singular(id) + "questions/newest", options))
137
+ end
138
+
139
+ def questions(options={})
140
+ parse_questions(request(singular(id) + "questions", options))
141
+ end
142
+
143
+ def questions_by_views(options={})
144
+ parse_questions(request(singular(id) + "questions/views", options))
145
+ end
146
+
147
+ def questions_by_votes(options={})
148
+ parse_questions(request(singular(id) + "questions/votes", options))
149
+ end
150
+
151
+ alias_method :popular_questions, :questions_by_votes
152
+
153
+ def recent_questions(options={})
154
+ parse_questions(request(singular(id) + "questions/recent", options))
155
+ end
156
+
157
+ ###############
158
+ # Other stuff #
159
+ ###############
160
+
161
+ def reputations(options={})
162
+ parse_reputations(request(singular(id) + "reputation", options))
163
+ end
164
+
165
+ def mentioned(options={})
166
+ parse_comments(request(singular(id) + "mentioned", options))
167
+ end
168
+
169
+ def badges(options={})
170
+ parse_badges(request(singular(id) + "badges", options))
171
+ end
172
+
173
+ def tags(options={})
174
+ parse_tags(request(singular(id) + "tags", options))
175
+ end
176
+
177
+ alias_method :mentions, :mentioned
178
+
179
+ def timeline(options={})
180
+ parse_user_timeline(request(singular(id) + "timeline", options))
181
+ end
182
+
183
+ alias_method :created_at, :creation_date
184
+ alias_method :down_votes, :down_vote_count
185
+ alias_method :gravatar, :email_hash
186
+ alias_method :id, :user_id
187
+ alias_method :name, :display_name
188
+ alias_method :recent_answers, :answers
189
+ alias_method :type, :user_type
190
+ alias_method :up_votes, :up_vote_count
191
+ alias_method :views, :view_count
192
+
193
+
194
+ end
195
+ end
@@ -0,0 +1,14 @@
1
+ module Stacked
2
+ # I would really *love* to call this UserTimeline but alas, the API fails me.
3
+ class Usertimeline < Base
4
+ attr_accessor :action,
5
+ :comment_id,
6
+ :creation_date,
7
+ :description,
8
+ :detail,
9
+ :post_id,
10
+ :timeline_type
11
+
12
+ alias_method :created_at, :creation_date
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Spec::Matchers::Sortedby do
4
+ before do
5
+ @questions = []
6
+ @questions << Stacked::Question.new(:last_edit_date => Time.now.to_i)
7
+ @questions << Stacked::Question.new(:last_edit_date => (Time.now + 5.days).to_i)
8
+ end
9
+
10
+ subject { @questions }
11
+
12
+ it "sorted by" do
13
+ subject.should be_sorted_by(:last_edit_date)
14
+ end
15
+
16
+ it "sorted by reverse" do
17
+ subject.reverse.should be_sorted_by(:last_edit_date, :desc)
18
+ end
19
+
20
+ it "fails with an understandable error message" do
21
+ lambda { subject.reverse.should be_sorted_by(:last_edit_date, :asc) }.should raise_error("Expected result set to be sorted by last_edit_date in ascending order, but was not.")
22
+ end
23
+
24
+ end
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'stacked'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+ require 'fakeweb'
7
+
8
+ Dir["spec/support/**/*.rb"].each { |f| require f }
9
+
10
+ # Uncomment only for debugging as we only ever want to test against the real API.
11
+ # At least, until it becomes stable.
12
+ # FakeWeb.allow_net_connect = false
13
+
14
+ Spec::Runner.configure do |config|
15
+
16
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Stacked::Answer do
4
+ context "class methods" do
5
+ it "raises an error if all is called" do
6
+ lambda { Stacked::Answer.all }.should raise_error(Stacked::NotImplemented, "The requested action is not available in the API.")
7
+ end
8
+ end
9
+
10
+ context "instance methods" do
11
+ subject { Stacked::Answer.find(1237127, :body => true) }
12
+ it "finds the user for an answer" do
13
+ subject.user.display_name.should eql("Daniel Vandersluis")
14
+ end
15
+
16
+ it "finds the question for an answer" do
17
+ subject.question.should be_is_a(Stacked::Question)
18
+ end
19
+
20
+ aliases(:views => :view_count,
21
+ :up_votes => :up_vote_count,
22
+ :answer_id => :id,
23
+ :created_at => :creation_date,
24
+ :updated_at => :last_edit_date)
25
+
26
+ it "has a body" do
27
+ subject.body.should_not be_blank
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Stacked::Badge do
4
+ subject { Stacked::Badge }
5
+ it "finds all badges" do
6
+ subject.all.first.should be_is_a(Stacked::Badge)
7
+ end
8
+
9
+ it "finds all badges ordered by name" do
10
+ subject.name.first.should be_is_a(Stacked::Badge)
11
+ end
12
+
13
+ it "finds all badges based on tags" do
14
+ subject.tags.first.name.should eql(".net")
15
+ end
16
+
17
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Stacked::Base do
4
+ context "common methods" do
5
+ # We cannot use Stacked::Base here obviously because SO does not have Bases!
6
+ # So we use User instead.
7
+ subject { Stacked::User }
8
+
9
+ it "retrieves a list of a resource" do
10
+ subject.all.all? { |u| u.is_a?(subject) }.should be_true
11
+ end
12
+
13
+ it "paginates through the resource" do
14
+ subject.all.first.should_not eql(subject.all(:page => 2).first)
15
+ end
16
+
17
+ it "limits the results" do
18
+ subject.all(:pagesize => 1).size.should eql(1)
19
+ end
20
+
21
+ it "stats" do
22
+ Stacked::Base.stats.keys.sort.should eql(["answers_per_minute", "api_version", "badges_per_minute", "questions_per_minute", "total_answers", "total_badges", "total_comments", "total_questions", "total_unanswered", "total_users", "total_votes"])
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Stacked::Comment do
4
+ subject { Stacked::Comment }
5
+
6
+ context "class methods" do
7
+
8
+ it "raises an error if all is called" do
9
+ lambda { Stacked::Comment.all }.should raise_error(Stacked::NotImplemented, "The requested action is not available in the API.")
10
+ end
11
+
12
+ it "finds a single comment" do
13
+ comment = Stacked::Comment.find(2561833)
14
+ comment.should be_is_a(Stacked::Comment)
15
+ comment.owner_user_id.should eql(22656)
16
+ end
17
+ end
18
+
19
+ context "instance methods" do
20
+ subject { Stacked::Comment.find(2561833) }
21
+
22
+ aliases(:comment_id => :id,
23
+ :created_at => :creation_date,
24
+ :owner => :user,
25
+ :edits => :edit_count)
26
+
27
+ it "finds the related post for this comment" do
28
+ subject.post.should be_is_a(Stacked::Answer)
29
+ end
30
+
31
+ it "finds who the comment was directed at, or nobody if nobody" do
32
+ Stacked::Comment.find(2561833).reply_to.should be_is_a(Stacked::User)
33
+ Stacked::Comment.find(1063043).reply_to.should be_nil
34
+ end
35
+
36
+ it "finds the user who wrote the comment" do
37
+ subject.owner.should be_is_a(Stacked::User)
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,110 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Stacked::Question do
4
+ context "class methods" do
5
+ subject { Stacked::Question }
6
+ it "gets a list of all questions" do
7
+ subject.all.all? { |q| q.is_a?(subject) }.should be_true
8
+ end
9
+
10
+ it "gets a list of questions with comments" do
11
+ pending("Cannot currently get a list of questions with comments")
12
+ subject.all(:comments => true)
13
+ end
14
+
15
+ it "active" do
16
+ subject.active(:pagesize => 1).first.last_activity_date.should be_within(1.day)
17
+ end
18
+
19
+ it "newest" do
20
+ subject.newest(:pagesize => 1).first.creation_date.should be_within(1.day)
21
+ end
22
+
23
+ it "featured" do
24
+ (subject.featured(:pagesize => 1).first.bounty_closes_date > Time.now.to_i).should be_true
25
+ end
26
+
27
+ it "hot" do
28
+ # It was 3555 as of 3rd April 2010.
29
+ (subject.hot(:pagesize => 1).first.view_count > 3555).should be_true
30
+ end
31
+
32
+ it "month" do
33
+ subject.month(:pagesize => 1).first.creation_date.should be_within(1.month)
34
+ end
35
+
36
+ it "week" do
37
+ subject.week(:pagesize => 1).first.creation_date.should be_within(1.week)
38
+ end
39
+
40
+ it "votes" do
41
+ subject.votes(:pagesize => 1).first.up_vote_count.should eql(1190)
42
+ end
43
+
44
+ it "unanswered by votes" do
45
+ question = subject.unanswered_by_votes(:pagesize => 1).first
46
+ question.should be_is_a(Stacked::Question)
47
+ question.answers.should be_blank
48
+ end
49
+
50
+ it "tagged" do
51
+ question = subject.tagged(:tags => ["ruby", "ruby-on-rails"], :pagesize => 1).first
52
+ question.tags.map(&:name).should include("ruby")
53
+ question.tags.map(&:name).should include("ruby-on-rails")
54
+ end
55
+ end
56
+
57
+ context "instance methods" do
58
+ subject { Stacked::Question.find(1236996, :comments => true, :body => true) }
59
+
60
+ aliases(
61
+ :created_at => :creation_date,
62
+ :down_votes => :down_vote_count,
63
+ :favorites => :favorite_count,
64
+ :favourites => :favorite_count,
65
+ :id => :question_id,
66
+ :updated_at => :last_edit_date,
67
+ :up_votes => :up_vote_count,
68
+ :user => :owner,
69
+ :views => :view_count
70
+ )
71
+
72
+
73
+ it "is the right question" do
74
+ subject.title.should eql("Calculating the distance between two times")
75
+ end
76
+
77
+ it "has a body" do
78
+ subject.body.should_not be_blank
79
+ end
80
+
81
+ it "retreives comments" do
82
+ subject.comments.should_not be_empty
83
+ subject.comments.first.should be_is_a(Stacked::Comment)
84
+ end
85
+
86
+ it "retreives answers" do
87
+ subject.answers.should_not be_empty
88
+ subject.answers.first.should be_is_a(Stacked::Answer)
89
+ end
90
+
91
+ it "retreives tags" do
92
+ subject.tags.should_not be_empty
93
+ subject.tags.first.should be_is_a(Stacked::Tag)
94
+ end
95
+
96
+ it "finds the user for a question" do
97
+ subject.user.should be_is_a(Stacked::User)
98
+ end
99
+
100
+ it "finds the accepted answer" do
101
+ subject.accepted_answer.should be_is_a(Stacked::Answer)
102
+ end
103
+
104
+ it "shows the timeline of the question" do
105
+ subject.timeline.first.should be_is_a(Stacked::Posttimeline)
106
+ end
107
+
108
+ end
109
+
110
+ end