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.
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