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,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> &raquo;
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>
@@ -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
@@ -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
@@ -0,0 +1,18 @@
1
+ module Stacked
2
+ class Badge < Base
3
+ attr_accessor :award_count,
4
+ :badge_id,
5
+ :class,
6
+ :description,
7
+ :name
8
+
9
+ collection :tags
10
+
11
+ alias_method :id, :badge_id
12
+
13
+ class << self
14
+ alias_method :name, :all
15
+ end
16
+
17
+ end
18
+ end
@@ -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,10 @@
1
+ module Stacked
2
+ class Posttimeline < Base
3
+ attr_accessor :action,
4
+ :action_user_id,
5
+ :creation_date,
6
+ :post_id,
7
+ :timeline_type,
8
+ :user_id
9
+ end
10
+ 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