mwilden-disqus 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ module Disqus
2
+
3
+ class Post
4
+ attr_reader :id, :forum, :thread, :created_at, :message, :parent_post, :shown, :is_anonymous, :author
5
+
6
+ def initialize(id, forum, thread, created_at, message, parent_post, shown, is_anonymous, author)
7
+ @id, @forum, @thread, @created_at, @message, @parent_post, @shown, @is_anonymous, @author = id.to_i, forum, thread, Time.parse(created_at.to_s), message, parent_post, shown, is_anonymous, author
8
+ end
9
+
10
+ # Returns an array of Post objects representing all posts belonging to the
11
+ # given thread. The array is sorted by the "created_at" date descending.
12
+ def self.list(thread)
13
+ response = Disqus::Api::get_thread_posts(:thread_id =>thread.id, :forum_api_key => thread.forum.key)
14
+ if response["succeeded"]
15
+ posts = response["message"].map do |post|
16
+ author = nil
17
+ if post["is_anonymous"]
18
+ author = AnonymousAuthor.new(
19
+ post["anonymous_author"]["name"],
20
+ post["anonymous_author"]["url"],
21
+ post["anonymous_author"]["email_hash"]
22
+ )
23
+ else
24
+ author = Author.new(
25
+ post["author"]["id"].to_i,
26
+ post["author"]["username"],
27
+ post["author"]["display_name"],
28
+ post["author"]["url"],
29
+ post["author"]["email_hash"],
30
+ post["author"]["has_avatar"]
31
+ )
32
+ end
33
+ Post.new(
34
+ post["id"],
35
+ thread.forum,
36
+ thread,
37
+ post["created_at"],
38
+ post["message"],
39
+ post["parent_post"],
40
+ post["shown"],
41
+ post["is_anonymous"],
42
+ author
43
+ )
44
+ end
45
+ posts.sort!{|a,b| a.created_at <=> b.created_at}
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
@@ -0,0 +1,73 @@
1
+ module Disqus
2
+
3
+ class Thread
4
+ attr_reader :id, :forum, :slug, :title, :created_at, :allow_comments, :url, :identifier, :forum
5
+
6
+ def initialize(id, forum, slug, title, created_at, allow_comments, url, identifier)
7
+ @id, @forum, @slug, @title, @created_at, @allow_comments, @url, @identifier = id.to_i, forum, slug, title, Time.parse(created_at.to_s), allow_comments, url, identifier
8
+ @posts = []
9
+ end
10
+
11
+ # Threads are equal if all their attributes are equal.
12
+ def ==(other_thread)
13
+ id == other_thread.id &&
14
+ forum == other_thread.forum &&
15
+ slug == other_thread.slug &&
16
+ title == other_thread.title &&
17
+ created_at == other_thread.created_at &&
18
+ allow_comments == other_thread.allow_comments &&
19
+ url == other_thread.url &&
20
+ identifier == other_thread.identifier
21
+ end
22
+
23
+ # Returns an array of Thread objects representing the threads belonging to the given Forum.
24
+ def self.list(forum, opts = {})
25
+ response = Disqus::Api::get_thread_list(opts.merge(:forum_id =>forum.id, :forum_api_key => forum.key))
26
+ if response["succeeded"]
27
+ list = response["message"].map do |thread|
28
+ Thread.new(
29
+ thread["id"],
30
+ forum,
31
+ thread["slug"],
32
+ thread["title"],
33
+ thread["created_at"],
34
+ thread["allow_comments"],
35
+ thread["url"],
36
+ thread["identifier"]
37
+ )
38
+ end
39
+ end
40
+ end
41
+
42
+ # Returns an array of posts belonging to this thread.
43
+ def posts(force_update = false)
44
+ if (@posts.nil? or @posts.empty? or force_update)
45
+ @posts = Disqus::Post.list(self)
46
+ end
47
+ @posts
48
+ end
49
+
50
+ # Sets the provided values on the thread object.
51
+ #
52
+ # Options:
53
+ #
54
+ # * :thread_id
55
+ # * :title
56
+ # * :slug
57
+ # * :url
58
+ # * :allow_comments
59
+ def update(opts = {})
60
+ result = Disqus::Api::update_thread(opts.merge(
61
+ :forum_api_key => forum.key,
62
+ :thread_id => id,
63
+ :title => title,
64
+ :slug => slug,
65
+ :url => url,
66
+ :allow_comments => allow_comments)
67
+ )
68
+ return result["succeeded"]
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,8 @@
1
+ module Disqus
2
+ module Version
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 3
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ # Shortcuts to access the widgets as simple functions as opposed to using
2
+ # their full qualified names.
3
+ %w[combo comment_counts popular_threads recent_comments thread top_commenters].each do |method|
4
+ eval(<<-EOM)
5
+ def disqus_#{method}(options = {})
6
+ Disqus::Widget.#{method}(options)
7
+ end
8
+ EOM
9
+ end
@@ -0,0 +1,185 @@
1
+ module Disqus
2
+
3
+ # Disqus Widget generator.
4
+ #
5
+ # All of the methods accept various options, and "account" is required for
6
+ # all of them. You can avoid having to pass in the account each time by
7
+ # setting it in the defaults like this:
8
+ #
9
+ # Disqus::defaults[:account] = "my_account"
10
+ class Widget
11
+
12
+ VALID_COLORS = ['blue', 'grey', 'green', 'red', 'orange']
13
+ VALID_NUM_ITEMS = 5..20
14
+ VALID_DEFAULT_TABS = ['people', 'recent', 'popular']
15
+ VALID_AVATAR_SIZES = [24, 32, 48, 92, 128]
16
+ VALID_ORIENTATIONS = ['horizontal', 'vertical']
17
+
18
+ ROOT_PATH = 'http://disqus.com/forums/%s/'
19
+ THREAD = ROOT_PATH + 'embed.js'
20
+ COMBO = ROOT_PATH + 'combination_widget.js?num_items=%d&color=%s&default_tab=%s'
21
+ RECENT = ROOT_PATH + 'recent_comments_widget.js?num_items=%d&avatar_size=%d'
22
+ POPULAR = ROOT_PATH + 'popular_threads_widget.js?num_items=%d'
23
+ TOP = ROOT_PATH + 'top_commenters_widget.js?num_items=%d&avatar_size=%d&orientation=%s'
24
+ class << self
25
+
26
+ # Show the main Disqus thread widget.
27
+ # Options:
28
+ # * <tt>account:</tt> Your Discus account (required).
29
+ def thread(opts = {})
30
+ opts = Disqus::defaults.merge(opts)
31
+ opts[:view_thread_text] ||= "View the discussion thread"
32
+ validate_opts!(opts)
33
+ s = ''
34
+ if opts[:developer]
35
+ s << '<script type="text/javascript">var disqus_developer = 1;</script>'
36
+ end
37
+ s << '<div id="disqus_thread"></div>'
38
+ s << '<script type="text/javascript" src="' + THREAD + '"></script>'
39
+ s << '<noscript><a href="http://%s.disqus.com/?url=ref">'
40
+ s << opts[:view_thread_text]
41
+ s << '</a></noscript>'
42
+ if opts[:show_powered_by]
43
+ s << '<a href="http://disqus.com" class="dsq-brlink">blog comments '
44
+ s << 'powered by <span class="logo-disqus">Disqus</span></a>'
45
+ end
46
+ s % [opts[:account], opts[:account]]
47
+ end
48
+
49
+ # Loads Javascript to show the number of comments for the page.
50
+ #
51
+ # The Javascript sets the inner html to the comment count for any links
52
+ # on the page that have the anchor "disqus_thread". For example, "View
53
+ # Comments" below would be replaced by "1 comment" or "23 comments" etc.
54
+ #
55
+ # <a href="http://my.website/article-permalink#disqus_thread">View Comments</a>
56
+ # <a href="http://my.website/different-permalink#disqus_thread">View Comments</a>
57
+ # Options:
58
+ # * <tt>account:</tt> Your Discus account (required).
59
+ def comment_counts(opts = {})
60
+ opts = Disqus::defaults.merge(opts)
61
+ validate_opts!(opts)
62
+ s = <<-WHIMPER
63
+ <script type="text/javascript">
64
+ //<![CDATA[
65
+ (function() {
66
+ var links = document.getElementsByTagName('a');
67
+ var query = '?';
68
+ for(var i = 0; i < links.length; i++) {
69
+ if(links[i].href.indexOf('#disqus_thread') >= 0) {
70
+ query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&';
71
+ }
72
+ }
73
+ document.write('<' + 'script type="text/javascript" src="#{ROOT_PATH}get_num_replies.js' + query + '"></' + 'script>');
74
+ })();
75
+ //]]>
76
+ </script>
77
+ WHIMPER
78
+ s % opts[:account]
79
+ end
80
+
81
+ # Show the top commenters Disqus thread widget.
82
+ # Options:
83
+ # * <tt>account:</tt> Your Discus account (required).
84
+ # * <tt>header:</tt> HTML snipper with header (default h2) tag and text.
85
+ # * <tt>show_powered_by:</tt> Show or hide the powered by Disqus text.
86
+ # * <tt>num_items:</tt>: How many items to show.
87
+ # * <tt>hide_mods:</tt> Don't show moderators.
88
+ # * <tt>hide_avatars:</tt> Don't show avatars.
89
+ # * <tt>avatar_size:</tt> Avatar size.
90
+ def top_commenters(opts = {})
91
+ opts = Disqus::defaults.merge(opts)
92
+ opts[:header] ||= '<h2 class="dsq-widget-title">Top Commenters</h2>'
93
+ validate_opts!(opts)
94
+ s = '<div id="dsq-topcommenters" class="dsq-widget">'
95
+ s << opts[:header]
96
+ s << '<script type="text/javascript" src="'
97
+ s << TOP
98
+ s << '&hide_avatars=1' if opts[:hide_avatars]
99
+ s << '&hide_mods=1' if opts[:hide_mods]
100
+ s << '"></script>'
101
+ s << '</div>'
102
+ if opts[:show_powered_by]
103
+ s << '<a href="http://disqus.com">Powered by Disqus</a>'
104
+ end
105
+ s % [opts[:account], opts[:num_items], opts[:avatar_size], opts[:orientation]]
106
+ end
107
+
108
+ # Show the popular threads Disqus widget.
109
+ # Options:
110
+ # * <tt>account:</tt> Your Discus account (required).
111
+ # * <tt>header:</tt> HTML snipper with header (default h2) tag and text.
112
+ # * <tt>num_items:</tt>: How many items to show.
113
+ # * <tt>hide_mods:</tt> Don't show moderators.
114
+ def popular_threads(opts = {})
115
+ opts = Disqus::defaults.merge(opts)
116
+ opts[:header] ||= '<h2 class="dsq-widget-title">Popular Threads</h2>'
117
+ validate_opts!(opts)
118
+ s = '<div id="dsq-popthreads" class="dsq-widget">'
119
+ s << opts[:header]
120
+ s << '<script type="text/javascript" src="'
121
+ s << POPULAR
122
+ s << '&hide_mods=1' if opts[:hide_mods]
123
+ s << '"></script>'
124
+ s << '</div>'
125
+ s << '<a href="http://disqus.com">Powered by Disqus</a>' if opts[:show_powered_by]
126
+ s % [opts[:account], opts[:num_items]]
127
+ end
128
+
129
+ # Show the recent comments Disqus widget.
130
+ # Options:
131
+ # * <tt>account:</tt> Your Discus account (required).
132
+ # * <tt>header:</tt> HTML snipper with header (default h2) tag and text.
133
+ # * <tt>num_items:</tt>: How many items to show.
134
+ # * <tt>hide_avatars:</tt> Don't show avatars.
135
+ # * <tt>avatar_size:</tt> Avatar size.
136
+ def recent_comments(opts = {})
137
+ opts = Disqus::defaults.merge(opts)
138
+ opts[:header] ||= '<h2 class="dsq-widget-title">Recent Comments</h2>'
139
+ validate_opts!(opts)
140
+ s = '<div id="dsq-recentcomments" class="dsq-widget">'
141
+ s << opts[:header]
142
+ s << '<script type="text/javascript" src="'
143
+ s << RECENT
144
+ s << '&hide_avatars=1' if opts[:hide_avatars]
145
+ s << '"></script>'
146
+ s << '</div>'
147
+ if opts[:show_powered_by]
148
+ s << '<a href="http://disqus.com">Powered by Disqus</a>'
149
+ end
150
+ s % [opts[:account], opts[:num_items], opts[:avatar_size]]
151
+ end
152
+
153
+ # Show the Disqus combo widget. This is a three-tabbed box with links
154
+ # popular threads, top posters, and recent threads.
155
+ # Options:
156
+ # * <tt>:account:</tt> Your Discus account (required).
157
+ # * <tt>:num_items:</tt> How many items to show.
158
+ # * <tt>:hide_mods:</tt> Don't show moderators.
159
+ # * <tt>:default_tab:</tt> Should be 'people', 'recent', or 'popular'.
160
+ def combo(opts = {})
161
+ opts = Disqus::defaults.merge(opts)
162
+ validate_opts!(opts)
163
+ s = '<script type="text/javascript" src="'
164
+ s << COMBO
165
+ s << '&hide_mods=1' if opts[:hide_mods]
166
+ s << '"></script>'
167
+ s % [opts[:account], opts[:num_items], opts[:color], opts[:default_tab]]
168
+ end
169
+
170
+ private
171
+
172
+ def validate_opts!(opts)
173
+ raise ArgumentError.new("You must specify an :account") if !opts[:account]
174
+ raise ArgumentError.new("Invalid color") if opts[:color] && !VALID_COLORS.include?(opts[:color])
175
+ raise ArgumentError.new("Invalid num_items") if opts[:num_items] && !VALID_NUM_ITEMS.include?(opts[:num_items])
176
+ raise ArgumentError.new("Invalid default_tab") if opts[:default_tab] && !VALID_DEFAULT_TABS.include?(opts[:default_tab])
177
+ raise ArgumentError.new("Invalid avatar size") if opts[:avatar_size] && !VALID_AVATAR_SIZES.include?(opts[:avatar_size])
178
+ raise ArgumentError.new("Invalid orientation") if opts[:orientation] && !VALID_ORIENTATIONS.include?(opts[:orientation])
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+
185
+ end
data/lib/disqus.rb ADDED
@@ -0,0 +1,52 @@
1
+ %w[api author forum post thread version view_helpers widget].each do |file|
2
+ require File.join(File.dirname(__FILE__), "disqus", file)
3
+ end
4
+
5
+ # == From the {Disqus Website}[http://disqus.com]:
6
+
7
+ # "Disqus, pronounced "discuss", is a service and tool for web comments and
8
+ # discussions. The Disqus comment system can be plugged into any website, blog,
9
+ # or application. Disqus makes commenting easier and more interactive, while
10
+ # connecting websites and commenters across a thriving discussion community."
11
+ #
12
+ # "Disqus is a free service to the general public with absolutely no inline
13
+ # advertisements."
14
+
15
+ # The Disqus gem helps you quickly and easily integrate Disqus's Javascript
16
+ # widgets into your Ruby-based website. Adding Disqus to your site literally
17
+ # takes only a few minutes. The Disqus gem also provides a complete
18
+ # implementation of the Disqus API for more complex applications.
19
+
20
+ # To use this code, please first create an account on Disqus[http://disqus.com].
21
+ module Disqus
22
+
23
+ @defaults = {
24
+ :api_key => "",
25
+ :account => "",
26
+ :developer => false,
27
+ :container_id => 'disqus_thread',
28
+ :avatar_size => 48,
29
+ :color => "grey",
30
+ :default_tab => "popular",
31
+ :hide_avatars => false,
32
+ :hide_mods => true,
33
+ :num_items => 15,
34
+ :show_powered_by => true,
35
+ :orientation => "horizontal"
36
+ }
37
+
38
+ # Disqus defaults:
39
+ # :account => "",
40
+ # :avatar_size => 48,
41
+ # :color => "grey",
42
+ # :default_tab => "popular",
43
+ # :hide_avatars => false,
44
+ # :hide_mods => true,
45
+ # :num_items => 15,
46
+ # :show_powered_by => true,
47
+ # :orientation => "horizontal"
48
+ def self.defaults
49
+ @defaults
50
+ end
51
+
52
+ end
data/test/api_test.rb ADDED
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ApiTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ Disqus.defaults[:api_key] = DISQUS_TEST["api_key"]
7
+ end
8
+
9
+ def test_create_post
10
+ mock_post_response('create_post.json')
11
+ the_post = Disqus::Api::create_post()
12
+ assert_equal "This is a mock post", the_post["message"]["message"]
13
+ end
14
+
15
+ def test_get_forum_list
16
+ mock_get_response('get_forum_list.json')
17
+ forum_list = Disqus::Api::get_forum_list
18
+ assert_equal "Disqus Test", forum_list["message"][0]["name"]
19
+ end
20
+
21
+ def test_get_forum_api_key
22
+ mock_get_response('get_forum_api_key.json')
23
+ forum_api_key = Disqus::Api::get_forum_api_key(:forum_id => 1234, :user_api_key=>"FAKE_KEY")
24
+ assert_equal "FAKE_FORUM_API_KEY", forum_api_key["message"]
25
+ end
26
+
27
+ def test_get_thread_list
28
+ mock_get_response('get_thread_list.json')
29
+ thread_list = Disqus::Api::get_thread_list(:forum_api_key=>"FAKE_KEY")
30
+ assert_equal "this_is_the_thread_identifier", thread_list["message"].first["identifier"]
31
+ end
32
+
33
+ def test_get_num_posts
34
+ mock_get_response('get_num_posts.json')
35
+ nums = Disqus::Api::get_num_posts(:thread_ids => [123,456], :forum_api_key=>"FAKE_KEY")
36
+ assert_equal [10,12], nums["message"][nums["message"].keys.first]
37
+ end
38
+
39
+ def test_get_thread_by_url
40
+ mock_get_response('get_thread_by_url.json')
41
+ thread = Disqus::Api::get_thread_by_url(:url => "FAKE_URL", :forum_api_key=>"FAKE_KEY")
42
+ assert_equal "test_thread", thread["message"]["slug"]
43
+ end
44
+
45
+ def test_get_thread_posts
46
+ mock_get_response('get_thread_posts.json')
47
+ thread_posts = Disqus::Api::get_thread_posts(:thread_id =>1234, :forum_api_key => "FAKE_KEY")
48
+ assert_equal "This is a mock post", thread_posts["message"].first["message"]
49
+ end
50
+
51
+ def test_thread_by_identifier
52
+ mock_post_response('thread_by_identifier.json')
53
+ thread = Disqus::Api::thread_by_identifier(:identifier =>'foo_bar', :title => "Foo Bar", :forum_api_key => "FAKE_KEY")
54
+ assert_equal "Test thread", thread["message"]["thread"]["title"]
55
+ end
56
+
57
+ def test_update_thread
58
+ mock_post_response('update_thread.json')
59
+ result = Disqus::Api::thread_by_identifier(:thread_id =>123, :title => "Foo Bar", :forum_api_key => "FAKE_KEY")
60
+ assert result["succeeded"]
61
+ end
62
+
63
+ def test_comment_form
64
+ c = Disqus::Api::comment_form("myforum", "mythread")
65
+ assert_match(/myforum/, c)
66
+ assert_match(/mythread/, c)
67
+ end
68
+
69
+ private
70
+
71
+ def mock_get_response(file)
72
+ Disqus::Api.expects(:get).returns(File.read(File.dirname(__FILE__) + "/responses/#{file}"))
73
+ end
74
+
75
+ def mock_post_response(file)
76
+ Disqus::Api.expects(:post).returns(File.read(File.dirname(__FILE__) + "/responses/#{file}"))
77
+ end
78
+
79
+ end