disqus_rails 0.0.5

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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in disqus_rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Anton Kirichenko
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,296 @@
1
+ # DisqusRails
2
+
3
+ DisqusRails is a gem for including [Disqus](http://disqus.com/) service into Ruby on Rails application.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ `gem 'disqus_rails'`
10
+
11
+ And then execute:
12
+
13
+ `$ bundle`
14
+
15
+ Or install it yourself as:
16
+
17
+ `$ gem install disqus_rails`
18
+
19
+ And add to your javascript manifest file:
20
+
21
+ `//= require disqus_rails`
22
+
23
+ ## Usage
24
+ ### Getting started
25
+
26
+ Create new initializer, lets say 'disqus_rails.rb' in config/initializers directory with this:
27
+ ```ruby
28
+ DisqusRails.setup do |config|
29
+ config::SHORT_NAME = "your_short_name"
30
+ config::SECRET_KEY = "your_secret_disqus_key" #leave blank if not used
31
+ config::PUBLIC_KEY = "your public_disqus_key" #leave blank if not used
32
+ config::ACCESS_TOKEN = "your_access_token" #you got it, right? ;-)
33
+ end
34
+ ```
35
+ In your layout file place 'disqus_init' helper:
36
+ ```erb
37
+ <%= disqus_init %>
38
+ ```
39
+ In your view, where you want to display Disqus thread, place 'disqus_thread' helper:
40
+ ```erb
41
+ <%= disqus_thread %>
42
+ ```
43
+ And you are ready to go.
44
+
45
+ ####additional params:
46
+
47
+ You can omit creating initializer if you want - you can pass all those params right into 'disqus_init' helper as hash:
48
+ ```erb
49
+ <%= disqus_init :short_name => "short_name", :public_key => "public key", :secret_key => "secret_key", :access_token => "access_token" %>
50
+ ```
51
+ also you can pass reset option to 'disqus_init' to invoke 'DISQUS.reset' function to be triggered on 'onReady' disqus event:
52
+ ```erb
53
+ <%= disqus_init :reset => true %>
54
+ ```
55
+ This is for ajax-heavy sites, read more [here](http://help.disqus.com/customer/portal/articles/472107-using-disqus-on-ajax-sites)
56
+
57
+ 'disqus_thread' has two params - first is 'ident' identifier and the second is title:
58
+ ```erb
59
+ <%= disqus_thread 1, "some title, that will preferred to document.title" %>
60
+ ```
61
+
62
+ ###Api
63
+
64
+ ####Api calls
65
+
66
+ 'DisqusRails::Api' stands for [Disqus API](http://disqus.com/api/docs/) calls. Each entity of Disqus API (Posts, Users, Forums, etc...)
67
+ has its own class in 'DisqusRails::Api' module. Lets say you want to get all reactions for some forum with limit of 50 results, with desc order:
68
+ ```ruby
69
+ DisqusRails::Api::Reactions.list(:forum => "forum_id", :limit => 50, :order => "desc")
70
+ ```
71
+ or, for example to update some post:
72
+ ```ruby
73
+ DisqusRails::Api::Posts.update(:post => post_id, :message => "some updated message")
74
+ ```
75
+ Disqus return data as json, 'DisqusRails::Api' requests translates it to hash with symbolized keys.
76
+ All methods with required and optional params you can see in 'api.yml' file. If any of required params not passed - it will generate
77
+ exeption, so as if passed param neither required nor optional. Also, you have to initialize access_token for methods that has
78
+ 'require authentication' option set to true.
79
+
80
+ ####Models
81
+
82
+ For more flexibility and using Disqus entities as an ActiveRecord model there is 'DisqusRails::Model' class that is inherited by
83
+ 'DisqusRails::Forum', 'DisqusRails::Category', 'DisqusRails::Thread', 'DisqusRails::Post' and 'DisqusRails::User'.
84
+ Lets take previous example and turn it into model's presentation:
85
+ ```ruby
86
+ new_post = DisqusRails::Post.create(:message => "initial message")
87
+ new_post.update(:message => "updated message")
88
+ new_post.author.checkUsername("John Doe")
89
+ new_post.remove
90
+ new_post.restore
91
+ ```
92
+ If Disqus entity has 'details' api method, than model has 'find' singleton method
93
+ ```ruby
94
+ user = DisqusRails::User.find(:user => "user_id")
95
+ user_posts.follow()
96
+ ```
97
+ Each 'DisqusRails::Model' has number of attributes that you can find in model's code. For example, here is Posts:
98
+ ```ruby
99
+ class Post < Model
100
+ attr_accessor :id,
101
+ :isJuliaFlagged,
102
+ :isFlagged,
103
+ :parent,
104
+ :media,
105
+ :isApproved,
106
+ :dislikes,
107
+ :raw_message,
108
+ :points,
109
+ :createdAt,
110
+ :isEdited,
111
+ :message,
112
+ :isHighlighted,
113
+ :ipAddress,
114
+ :isSpam,
115
+ :isDeleted,
116
+ :likes,
117
+
118
+ :author,
119
+ :thread,
120
+ :forum
121
+ ```
122
+
123
+ ####Collections
124
+
125
+ Collections, as you may guess, is a list of similar Models. Its inherited from Enumerable. There is similar to models
126
+ number of collection - 'DisqusRails::Forums', 'DisqusRails::Categories', 'DisqusRails::Threads', 'DisqusRails::Posts' and 'DisqusRails::Users'.
127
+ Here some examples:
128
+ ```ruby
129
+ user = DisqusRails.User.find(:user => "user_id")
130
+ user.active_threads(:limit => 50)[15].posts.each do |post|
131
+ post.update(:message => "my post is nothing comparing to #{user.username} writings...")
132
+ end
133
+ ```
134
+ If there is 'list' method in Disqus entity - it transforms to 'where' singleton method of model that creates corresponding collection. For example:
135
+ ```ruby
136
+ forum_categories = DisqusRails::Category.where(:forum => "forum_id")
137
+ ```
138
+ Disqus API is designed in such way, that you can only get maximum 100 results for list query, and by default its 25. To get more
139
+ results you are given with 'cursor' object that have 'next' and 'prev' values, passing which into query you can walk through
140
+ results as if it was a list data structure. To define if there is more values to get, Disqus provides 'hasNext' and 'hasPrev' boolean
141
+ values. For example, to get first 225 posts you can use this code:
142
+ ```ruby
143
+ posts = DisqusRails::Post.where(:limit => 75)
144
+ 2.times do
145
+ if posts.has_cursor_next?
146
+ previous_items = posts.items
147
+ posts.cursor_next!
148
+ posts.items = previous_items + posts.items
149
+ end
150
+ end
151
+ ```
152
+ In future I, may be, will rewrite this to handle common cases for :limit attribute to be set to any number. As you saw, in
153
+ previous example were used 'cursor_next!' method. There is both 'cursor_next', 'cursor_next!' and 'cursor_prev', 'cursor_prev!' methods.
154
+ The difference is in returned values - method with bang in the end initializes new collection right in invoked instance,
155
+ when method without it - just returns new collection.
156
+ Also, each collection has singleton method 'find_all_#collection_class_name#!' that will get all results for query:
157
+ ```ruby
158
+ threads = Disqus::Thread.where(:forum => "forum_name", :limit => 100).find_all_threads!
159
+ ```
160
+
161
+ ###Connection to ActiveRecord models
162
+
163
+ ####acts_as_disqusable and disqus_thread
164
+
165
+ Lets say you have a 'Content' ActiveRecord model that implements logic for displaying some content information, and you add to that displaying
166
+ Disqus thread. Then you may want to match threads info with different Content model instances. For example you may want to know
167
+ how many comments and what is the last comment for each model instance.
168
+ For this you need to wright 'acts_as_disqusable' in models definition:
169
+ ```ruby
170
+ class Content < ActiveRecord::Base
171
+ acts_as_disqusable
172
+ ...
173
+ end
174
+ ```
175
+ And then all your model instances will be populated with 'disqus_thread' method that will return 'DisqusRails::Thread' instance
176
+ that is found by Disqus 'ident' identifier which you can pass to 'disqus_thread' helper in your view. Here is full example:
177
+
178
+ Your model:
179
+ ```ruby
180
+ class Content < ActiveRecord::Base
181
+ acts_as_disqusable
182
+ ...
183
+ end
184
+ ```
185
+ Your model's details view
186
+ ```erb
187
+ <%= disqus_thread @content.id %>
188
+
189
+ ```
190
+ And now, when you run this
191
+ ```ruby
192
+ disqus_thread = Content.first.disqus_thread #It will be thanslated to DisqusRails::Thread.find(:'thread:ident' => Content.first.id)
193
+ disqus_thread.posts_count #number of posts
194
+ disqus_thread.posts(:limit => 1).createdAt #last comment date
195
+ ```
196
+ This work also in opposite direction - after you include 'acts_as_disqusable' in your model definition, all 'DisqusRails::Thread'
197
+ instances you will have method 'disqusable' what will return your model instance that is linked to Disqus thread via 'ident' identificator.
198
+ As an example lets say that we want to get all threads from Disqus service and update comments_count attribute in Content model:
199
+ ```ruby
200
+ DisqusRails.Thread.where().find_all_threads!.each do |thread|
201
+ thread.disqusable.comments_count = thread.posts_count
202
+ thread.disqusable.save()
203
+ end
204
+ ```
205
+
206
+ ####acts_as_disquser and Single Sign On
207
+
208
+ Disqus provides [SSO service](http://help.disqus.com/customer/portal/articles/236206-integrating-single-sign-on) which gives
209
+ ability to link your local users info to Disqus users, read more in Disqus tutorial. To do this, as and for linking model to
210
+ Disqus thread - you have to add 'acts_as_disquser' line in your users model. You need pass there four attributes:
211
+ 'id', 'username', 'email' and 'avatar'(avatar is an optional field, so you can omit this). Here is example:
212
+ ```ruby
213
+ class User < ActiveRecord::Base
214
+ acts_as_disquser :username => :full_name, :email => :email, :avatar => Proc.new{ avatar.url }
215
+ ...
216
+ end
217
+ ```
218
+ As you see, you can pass there or symbols, or procs. First will try to get instance variable with such name from model's instance,
219
+ second will evaluate code inside Proc with context of model's instance. Important - only Proc are available for second way of
220
+ defining attribute, no lambdas.
221
+ Also, you may not implicitly pass `acts_as_disquser :id => :id` - it will try to get id automatically if it is not defined.
222
+ Next, you need to specify in disqus_init helper attributes 'disquser' with current user instance, and 'sso' as
223
+ boolean to enable or disable SSO.
224
+ ```erb
225
+ <%= disqus_init :disquser => current_user, :sso => true %>
226
+ ```
227
+ After this is done, when users will post comments via Disqus, their username, email and avatar will be taken from your site.
228
+
229
+ ###Javascript events
230
+
231
+ Disqus provides developer with set of events which could be used to implement some logic that depends on it. The problem is
232
+ that instead of triggering events we have to append function definitions in array for each of this events - for example look
233
+ at [this article](http://help.disqus.com/customer/portal/articles/466258-how-can-i-capture-disqus-commenting-activity-in-my-own-analytics-tool-).
234
+ I found that it might be a little bit more useful to set event listener for this, so I defined separate event for every Disqus event
235
+ that developer can implicitly create listener:
236
+ ```coffeescript
237
+ @callbacks.afterRender = [->
238
+ $(document).trigger "disqus:after_render"
239
+ ]
240
+ @callbacks.onInit = [->
241
+ $(document).trigger "disqus:on_init"
242
+ ]
243
+ @callbacks.onNewComment = [->
244
+ $(document).trigger "disqus:on_new_comment"
245
+ ]
246
+ @callbacks.onPaginate = [->
247
+ $(document).trigger "disqus:on_paginate"
248
+ ]
249
+ @callbacks.onReady = [->
250
+ $(document).trigger "disqus:on_ready"
251
+ ]
252
+ @callbacks.preData = [->
253
+ $(document).trigger "disqus:pre_data"
254
+ ]
255
+ @callbacks.preInit = [->
256
+ $(document).trigger "disqus:pre_init"
257
+ ]
258
+ @callbacks.preReset = [->
259
+ $(document).trigger "disqus:pre_reset"
260
+ ]
261
+ ```
262
+ For more information about coffeescript global class 'DisqusRails' look into 'disqus_rails.js.coffee' file.
263
+
264
+ ### Keeping data up to date
265
+ It is equally little hard to do that with Disqus for now, as for me. The problem is that you can not set some callback for user
266
+ actions - all you can is to set event listener for 'disqus:on_new_comment', but that will not be valid for all circumstances.
267
+ Lets say user deleted or created new post from his users admin page in Disqus site. Disqus does not provide any callback for setting url
268
+ where should query go, or some other futuristic way like web socket channel (sarcasm tag). So we should create some cron task for keeping data
269
+ that we need up to date. For example, lets go back to problem of getting comments count and last comment for each Disqus thread.
270
+ Here is example of such rake task that could be scheduled with whenever (or any else) gem:
271
+
272
+ ```ruby
273
+ require 'resque/tasks'
274
+
275
+ namespace :disqus do
276
+ desc "Refreshing local data about remote disqus comments"
277
+ task :refresh => :environment do
278
+ threads = DisqusRails::Thread.where(:forum => "forum_name", :limit => 100).find_all_threads!
279
+ threads.each do |thread|
280
+ if thread.disqusable_id && Content.find_by_id(thread.disqusable_id)
281
+ content = thread.disqusable
282
+ if (content.comments_count != thread.posts_count) || (thread.posts_count > 0 && thread.posts(:limit => 1).first.createdAt != content.last_comment_at)
283
+ content.comments_count = thread.posts_count
284
+ if thread.posts_count > 0
285
+ content.last_comment_at = DateTime.parse(thread.posts(:limit => 1).first.createdAt)
286
+ else
287
+ content.last_comment_at = nil
288
+ end
289
+ content.save
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end
295
+
296
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'disqus_rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "disqus_rails"
8
+ spec.version = DisqusRails::VERSION
9
+ spec.authors = ["Anton Kyrychenko"]
10
+ spec.email = ["kyrychenkoanton@gmail.com"]
11
+ spec.description = "Disqus 2012 Ruby on Rails wrapper"
12
+ spec.summary = "Integrates Disqus service into your Ruby on Rails application"
13
+ spec.homepage = "https://github.com/sandric/disqus_rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rails"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+
26
+ spec.add_dependency "rails"
27
+ end
@@ -0,0 +1,31 @@
1
+ require "disqus_rails/version"
2
+
3
+ %w[api helpers collection model user post thread forum category].each do |file|
4
+ require File.join(File.dirname(__FILE__), "disqus_rails", file)
5
+ end
6
+
7
+ %w[acts_as_disqusable acts_as_disquser].each do |file|
8
+ require File.join(File.dirname(__FILE__), "disqus_rails/active_record", file)
9
+ end
10
+
11
+ module DisqusRails
12
+ module Rails
13
+ class Engine < ::Rails::Engine
14
+ initializer 'acts_as_disqusable.extend_active_record' do
15
+ ::ActiveRecord::Base.extend DisqusRails::Disqusable::ActiveRecordMethods
16
+ end
17
+
18
+ initializer 'acts_as_disquser.extend_active_record' do
19
+ ::ActiveRecord::Base.extend DisqusRails::Disquser::ActiveRecordMethods
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.setup
25
+ yield self
26
+ end
27
+ end
28
+
29
+ module ApplicationHelper
30
+ include DisqusRails::Helpers
31
+ end
@@ -0,0 +1,24 @@
1
+ module DisqusRails
2
+ module Disqusable
3
+ module ActiveRecordMethods
4
+ def acts_as_disqusable
5
+ include ActsAsDisqusable
6
+ end
7
+ end
8
+
9
+ module ActsAsDisqusable
10
+
11
+ def disqus_thread
12
+ DisqusRails::Thread.find_by_ident(self.id)
13
+ end
14
+
15
+ def self.included(disqusable)
16
+ Thread.class_exec do
17
+ define_method :disqusable do
18
+ disqusable.find(@disqusable_id)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,48 @@
1
+ module DisqusRails
2
+ module Disquser
3
+ module ActiveRecordMethods
4
+ def acts_as_disquser(attributes={})
5
+
6
+ define_method :disqus_params do
7
+ data = {}
8
+
9
+ if attributes.has_key?(:id)
10
+ if attributes[:id].is_a? Proc
11
+ data[:id] = instance_eval &attributes[:id]
12
+ else
13
+ data[:id] = send attributes[:id]
14
+ end
15
+ else
16
+ data[:id] = self.id
17
+ end
18
+
19
+ if attributes.has_key?(:username)
20
+ if attributes[:username].is_a? Proc
21
+ data[:username] = instance_eval &attributes[:username]
22
+ else
23
+ data[:username] = send attributes[:username]
24
+ end
25
+ end
26
+
27
+ if attributes.has_key?(:email)
28
+ if attributes[:email].is_a? Proc
29
+ data[:email] = instance_eval &attributes[:email]
30
+ else
31
+ data[:email] = send attributes[:email]
32
+ end
33
+ end
34
+
35
+ if attributes.has_key?(:avatar)
36
+ if attributes[:avatar].is_a? Proc
37
+ data[:avatar] = instance_eval &attributes[:avatar]
38
+ else
39
+ data[:avatar] = send attributes[:avatar]
40
+ end
41
+ end
42
+
43
+ data
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end