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