livefyre 0.0.1

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.
@@ -0,0 +1,186 @@
1
+ module Livefyre
2
+ # Public: Proxy for a Livefyre domain resource
3
+ class Domain
4
+ attr_accessor :client
5
+
6
+ def initialize(client = nil)
7
+ @client = client || Livefyre.client
8
+ end
9
+
10
+ # Public: Get a list of sites for this domain
11
+ #
12
+ # Returns [Array<Site>] An array of {Site sites}
13
+ # Raises [APIException] when response is not valid
14
+ def sites
15
+ response = client.get "/sites/?actor_token=#{CGI.escape client.system_token}"
16
+ if response.success?
17
+ JSON.parse(response.body).map do |site|
18
+ Site.new(site["id"], client, site)
19
+ end
20
+ else
21
+ raise APIException.new(response.body)
22
+ end
23
+ end
24
+
25
+ # Public: Get a list of users on this domain
26
+ #
27
+ # Returns [Array<User>] An array of {User users}
28
+ # Raises [APIException] when response is not valid
29
+ def users
30
+ response = client.get "/profiles/?actor_token=#{CGI.escape client.system_token}"
31
+ if response.success?
32
+ JSON.parse(response.body).map do |site|
33
+ User.new(site["id"], client, site["display_name"])
34
+ end
35
+ else
36
+ raise APIException.new(response.body)
37
+ end
38
+ end
39
+
40
+ # Public: Push a user profile to this domain
41
+ #
42
+ # profile - [Hash] Hash of user data to publish per the Livefyre profile schema
43
+ #
44
+ # Returns [Bool] true on success
45
+ # Raises [APIException] when response is not valid
46
+ def add_user(profile)
47
+ raise "Invalid ID" if profile["id"].nil?
48
+ response = client.post "/profiles/?actor_token=#{CGI.escape client.system_token}&id=#{CGI.escape profile["id"]}", profile
49
+ if response.success?
50
+ true
51
+ else
52
+ raise APIException.new(response.body)
53
+ end
54
+ end
55
+
56
+ # Public: Create a new site on this domain
57
+ #
58
+ # Returns [Site] A new {Site site}.
59
+ # Raises [APIException] when response is not valid
60
+ def create_site(url)
61
+ response = client.post "/sites/?actor_token=#{CGI.escape client.system_token}&url=#{CGI.escape url}"
62
+ if response.success?
63
+ opts = JSON.parse response.body
64
+ Site.new(opts["id"], client, opts)
65
+ else
66
+ raise APIException.new(response.body)
67
+ end
68
+ end
69
+
70
+ # Public: Retrieve a list of owners associated with this domain
71
+ #
72
+ # Returns [Array<User>] An array of {User users}
73
+ # Raises [APIException] when response is not valid
74
+ def owners
75
+ response = client.get "/owners/", {:actor_token => client.system_token}
76
+ if response.success?
77
+ JSON.parse(response.body).map do |u|
78
+ client.user u.split("@", 2).first
79
+ end
80
+ else
81
+ raise APIException.new(response.body)
82
+ end
83
+ end
84
+
85
+ # Public: Adds a user to the list of owners for this domain
86
+ #
87
+ # user - [String, User, Integer] User or user ID to add as an owner
88
+ #
89
+ # Returns [Bool] true on success
90
+ # Raises [APIException] when response is not valid
91
+ def add_owner(user)
92
+ user = User.get_user(user, client)
93
+ response = client.put "/owners/?actor_token=#{CGI.escape user.token}"
94
+ if response.success?
95
+ true
96
+ else
97
+ raise APIException.new(response.body)
98
+ end
99
+ end
100
+
101
+ # Public: Removes a user from the list of owners for this domain
102
+ #
103
+ # user - [String, User, Integer] User or user ID to remove as an owner
104
+ #
105
+ # Returns [Bool] true on success
106
+ # Raises [APIException] when response is not valid
107
+ def remove_owner(user)
108
+ user = User.get_user(user, client)
109
+ response = client.delete "/owners/?actor_token=#{CGI.escape user.token}"
110
+ if response.success?
111
+ true
112
+ else
113
+ raise APIException.new(response.body)
114
+ end
115
+ end
116
+
117
+ # Public: Retrieve a list of owners associated with this domain
118
+ #
119
+ # Returns [Array<User>] An array of {User users}
120
+ # Raises [APIException] when response is not valid
121
+ def admins
122
+ response = client.get "/admins/", {:actor_token => client.system_token}
123
+ if response.success?
124
+ JSON.parse(response.body).map do |u|
125
+ client.user u.split("@", 2).first
126
+ end
127
+ else
128
+ raise APIException.new(response.body)
129
+ end
130
+ end
131
+
132
+ # Public: Adds a user to the list of owners for this domain
133
+ #
134
+ # user - [String, User, Integer] User or user ID to add as an admin
135
+ #
136
+ # Returns [Bool] true on success
137
+ # Raises [APIException] when response is not valid
138
+ def add_admin(user)
139
+ user = User.get_user(user, client)
140
+ response = client.post "/admins/?actor_token=#{CGI.escape user.token}", {:jid => user.jid}
141
+ if response.success?
142
+ true
143
+ else
144
+ raise APIException.new(response.body)
145
+ end
146
+ end
147
+
148
+ # Public: Removes a user from the list of owners for this domain
149
+ #
150
+ # user - [String, User, Integer] User or user ID to remove as an admin
151
+ #
152
+ # Returns [Bool] true on success
153
+ # Raises [APIException] when response is not valid
154
+ def remove_admin(user)
155
+ user = User.get_user(user, client)
156
+ response = client.delete "/admin/#{user.jid}/?actor_token=#{CGI.escape client.system_token}"
157
+ if response.success?
158
+ true
159
+ else
160
+ raise APIException.new(response.body)
161
+ end
162
+ end
163
+
164
+ # Public: Sets the profile pull URL for the entire network.
165
+ #
166
+ # url - A URL template that includes the string "{{id}}" in it somewhere
167
+ #
168
+ # Returns [Bool] true on success
169
+ # Raises APIException if the request failed
170
+ def set_pull_url(url)
171
+ result = client.post "/", {:pull_profile_url => url, :actor_token => client.system_token}
172
+ if result.success?
173
+ return true
174
+ else
175
+ raise APIException.new(result.body)
176
+ end
177
+ end
178
+
179
+ # Internal: Returns a cleaner string representation of this object
180
+ #
181
+ # Returns [String] representation of this class
182
+ def to_s
183
+ "#<#{self.class.name}:0x#{object_id.to_s(16).rjust(14, "0")} host='#{client.host}'>"
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,6 @@
1
+ module Livefyre
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,56 @@
1
+ module Livefyre
2
+ # Public: View helpers for Livefyre
3
+ module Helpers
4
+
5
+ # Public: Add a Livefyre comment form to this page.
6
+ #
7
+ # id - [String, Integer] identifier to use for this conversation. Likely a post ID.
8
+ # title - [String] Title of this post or conversation
9
+ # link - [String] Link to this post or conversation
10
+ # tags - [Array, String] Optional array or comma-delimited list of tags on this conversation.
11
+ # options - [Hash] Additional options to pass to the created div tag.
12
+ #
13
+ # Returns [String] div element for insertion into your view
14
+ def livefyre_comments(id, title, link, tags = nil, options = {})
15
+ meta = livefyre_conversation_metadata(id, title, link, tags)
16
+ options.merge!(
17
+ :id => "livefyre_comments",
18
+ :data => {
19
+ :checksum => meta[:checksum],
20
+ :"collection-meta" => meta[:collectionMeta],
21
+ :"site-id" => meta[:siteId],
22
+ :"article-id" => meta[:articleId],
23
+ :network => Livefyre.client.host,
24
+ :root => Livefyre.config[:domain]
25
+ }
26
+ )
27
+ content_tag(:div, "", options)
28
+ end
29
+
30
+ private
31
+
32
+ # Internal: Generate a metadata hash from the given attributes.
33
+ #
34
+ # Returns [Hash]
35
+ def livefyre_conversation_metadata(id, title, link, tags)
36
+ tags = tags.join(",") if tags.is_a? Array
37
+
38
+ metadata = {
39
+ :title => title,
40
+ :url => link,
41
+ :tags => tags
42
+ }
43
+ metadata[:checksum] = Digest::MD5.hexdigest(metadata.to_json)
44
+ metadata[:articleId] = id
45
+ post_meta = JWT.encode(metadata, Livefyre.config[:site_key])
46
+
47
+ {
48
+ :el => "livefyre_comments",
49
+ :checksum => metadata[:checksum],
50
+ :collectionMeta => post_meta,
51
+ :siteId => Livefyre.config[:site_id],
52
+ :articleId => id.to_s
53
+ }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,76 @@
1
+ module Livefyre
2
+ module Model
3
+ extend ActiveSupport::Concern
4
+
5
+ # Resque worker for updating Livefyre users via ping-to-pull
6
+ class RequestPull
7
+ @queue = :livefyre
8
+
9
+ # Public: Pings Livefyre, requesting that the user identified by the passed ID is refreshed.
10
+ def self.perform(id)
11
+ Livefyre::User.new( id ).refresh
12
+ end
13
+ end
14
+
15
+ # Public: Ping Livefyre to refresh this user's record
16
+ #
17
+ # defer - If true, will use Resque to process the update
18
+ def refresh_livefyre(defer = false)
19
+ livefyre_id = self._livefyre_id
20
+ if defer
21
+ if defined?(Resque)
22
+ Resque.enqueue Livefyre::Model::RequestPull, livefyre_id
23
+ else
24
+ raise ":defer was passed, but Resque was not found"
25
+ end
26
+ else
27
+ Livefyre::Model::RequestPull.perform livefyre_id
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def update_livefyre_if_fields_changed
34
+ if updates = _livefyre_options[:update_on]
35
+ updates.each do |field|
36
+ if send("#{field}_changed?")
37
+ refresh_livefyre _livefyre_options[:defer]
38
+ break
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def _livefyre_options
45
+ self.class.instance_variable_get("@livefyre_options")
46
+ end
47
+
48
+ def _livefyre_id
49
+ livefyre_id = self.id
50
+ if _livefyre_options[:id]
51
+ livefyre_id = self.send(_livefyre_options[:id])
52
+ end
53
+ livefyre_id
54
+ end
55
+
56
+ public
57
+
58
+ module ClassMethods
59
+ # Public: Adds callback handlers and additional methods for treating this record as a user record.
60
+ #
61
+ # options - [Hash] of options to initialize behavior with
62
+ # :update_on - [Array<Symbol>] List of fields which should trigger a Livefyre update when they're updated.
63
+ # :id - [Symbol] Name of the method to use for determining this record's livefyre ID. If not given, #id is used.
64
+ #
65
+ # Examples
66
+ #
67
+ # livefyre_user :update_on => [:email, :first_name, :last_name, :username, :picture_url], :id => :custom_livefyre_id
68
+ #
69
+ # Returns [nil]
70
+ def livefyre_user(options = {})
71
+ @livefyre_options = options
72
+ after_save :update_livefyre_if_fields_changed
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,138 @@
1
+ module Livefyre
2
+ # Public: An object representing a Livefyre site belonging to a Livefyre domain
3
+ class Site
4
+ attr_accessor :client, :secret, :options, :id
5
+
6
+ # Public: Create a new Site
7
+ def initialize(id, client = nil, options = {})
8
+ @id = id
9
+ @client = client || Livefyre.client
10
+ @options = options
11
+ @secret = options["api_secret"]
12
+ end
13
+
14
+ # Public: Get a list of properties for this site
15
+ #
16
+ # Returns [Hash] Site properties
17
+ # Raises [APIException] when response is not valid
18
+ def properties
19
+ return @options unless @options.nil? or @options.empty?
20
+ response = client.get "/site/#{id}/", {:actor_token => client.system_token}
21
+ if response.success?
22
+ @options = JSON.parse response.body if @options.nil? or @options.empty?
23
+ @secret = options["api_secret"]
24
+ else
25
+ raise APIException.new(response.body)
26
+ end
27
+ end
28
+
29
+ # Public: Set the postback URL for actions on this site
30
+ # See: https://github.com/Livefyre/livefyre-docs/wiki/Accessing-Site-Comment-Data
31
+ #
32
+ # url - [String] URL to use as the postback URL for actions
33
+ #
34
+ # Returns [Bool] true on success
35
+ # Raises: [APIException] when response is not valid
36
+ def set_postback_url(url)
37
+ response = client.post "/site/#{id}/", {:actor_token => client.system_token, :postback_url => url}
38
+ if response.success?
39
+ true
40
+ else
41
+ raise APIException.new(response.body)
42
+ end
43
+ end
44
+
45
+ # Public: Retrieve a list of owners associated with this site
46
+ #
47
+ # Returns [Array<Livefyre::User>] A list of {Livefyre::User users}
48
+ # Raises: APIException when response is not valid
49
+ def owners
50
+ response = client.get "/site/#{id}/owners/", {:actor_token => client.system_token}
51
+ if response.success?
52
+ JSON.parse(response.body).map do |u|
53
+ client.user u.split("@", 2).first
54
+ end
55
+ else
56
+ raise APIException.new(response.body)
57
+ end
58
+ end
59
+
60
+ # Public: Adds a user to the list of owners for this site
61
+ #
62
+ # Returns [Bool] true on success
63
+ # Raises [APIException] when response is not valid
64
+ def add_owner(user)
65
+ uid = User.get_user_id(user)
66
+ response = client.post "/site/#{id}/owners/?actor_token=#{CGI.escape client.system_token}", {:jid => client.jid(uid)}
67
+ if response.success?
68
+ true
69
+ else
70
+ raise APIException.new(response.body)
71
+ end
72
+ end
73
+
74
+ # Public: Removes a user from the list of owners for this site
75
+ #
76
+ # Returns [Bool] true on success
77
+ # Raises [APIException] when response is not valid
78
+ def remove_owner(user)
79
+ uid = User.get_user_id(user)
80
+ response = client.delete "/site/#{id}/owner/#{client.jid uid}?actor_token=#{CGI.escape client.system_token}"
81
+ if response.success?
82
+ true
83
+ else
84
+ raise APIException.new(response.body)
85
+ end
86
+ end
87
+
88
+ # Public: Retrieve a list of owners associated with this site
89
+ #
90
+ # Returns [Array<Livefyre::User>] A list of {Livefyre::User users}
91
+ # Raises: [APIException] when response is not valid
92
+ def admins
93
+ response = client.get "/site/#{id}/admins/", {:actor_token => client.system_token}
94
+ if response.success?
95
+ JSON.parse(response.body).map do |u|
96
+ client.user u.split("@", 2).first
97
+ end
98
+ else
99
+ raise APIException.new(response.body)
100
+ end
101
+ end
102
+
103
+ # Public: Adds a user to the list of admins for this site
104
+ #
105
+ # Returns [Bool] true on success
106
+ # Raises [APIException] when response is not valid
107
+ def add_admin(user)
108
+ uid = User.get_user_id(user)
109
+ response = client.post "/site/#{id}/admins/?actor_token=#{CGI.escape client.system_token}", {:jid => client.jid(uid)}
110
+ if response.success?
111
+ true
112
+ else
113
+ raise APIException.new(response.body)
114
+ end
115
+ end
116
+
117
+ # Public: Removes a user from the list of admins for this site
118
+ #
119
+ # Returns [Bool] true on success
120
+ # Raises [APIException] when response is not valid
121
+ def remove_admin(user)
122
+ uid = User.get_user_id(user)
123
+ response = client.delete "/site/#{id}/admin/#{client.jid uid}?actor_token=#{CGI.escape client.system_token}"
124
+ if response.success?
125
+ true
126
+ else
127
+ raise APIException.new(response.body)
128
+ end
129
+ end
130
+
131
+ # Internal: Returns a cleaner string representation of this object
132
+ #
133
+ # Returns [String] representation of this class
134
+ def to_s
135
+ "#<#{self.class.name}:0x#{object_id.to_s(16).rjust(14, "0")} id='#{id}' secret='#{secret}' options=#{options.inspect}>"
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,110 @@
1
+ module Livefyre
2
+ # Public: Interface for dealing with Livefyre users by User ID.
3
+ class User
4
+ attr_accessor :id, :client, :display_name
5
+
6
+ # Public: Create a new Livefyre User proxy.
7
+ #
8
+ # id - [String] ID of the user to proxy
9
+ # client - [Livefyre::Client] an instance of Livefyre::Client. If nil, the default client is used.
10
+ # display_name - [String] The display name for this user (optional)
11
+ def initialize(id, client = nil, display_name = nil)
12
+ @id = id
13
+ @client = client || Livefyre.client
14
+ @display_name = display_name
15
+ end
16
+
17
+ # Internal - Fetch an internal Jabber-style ID for this user
18
+ #
19
+ # Returns [String] representation of this user
20
+ def jid
21
+ "#{id}@#{client.host}"
22
+ end
23
+
24
+ # Public: Creates a signed JWT token for this user
25
+ #
26
+ # max_age - [Integer] Expiry time for this token in seconds (default: 86400)
27
+ #
28
+ # Returns [String] token
29
+ def token(max_age = 86400)
30
+ data = {
31
+ :domain => client.host,
32
+ :user_id => id,
33
+ :expires => Time.now.to_i + max_age
34
+ }.tap do |opts|
35
+ opts[:display_name] = @display_name unless @display_name.nil?
36
+ end
37
+ JWT.encode(data, client.key)
38
+ end
39
+
40
+ # Public: Update this user's profile on Livefyre
41
+ #
42
+ # data - [Hash] A hash of user data as defined by the Livefyre user profile schema
43
+ #
44
+ # Returns [Bool] true on success
45
+ # Raises [APIException] if the request failed
46
+ def push(data)
47
+ result = client.post "/profiles/?actor_token=#{CGI.escape client.system_token}&id=#{id}", {:data => data.to_json}
48
+ if result.success?
49
+ true
50
+ else
51
+ raise APIException.new(result.body)
52
+ end
53
+ end
54
+
55
+ # Public: Invoke Livefyre ping-to-pull to refresh this user's data
56
+ #
57
+ # Returns [Bool] true on success
58
+ # Raises [APIException] if the request failed
59
+ def refresh
60
+ result = client.post "/api/v3_0/user/#{id}/refresh", {:lftoken => client.system_token}
61
+ if result.success?
62
+ true
63
+ else
64
+ raise APIException.new(result.body)
65
+ end
66
+ end
67
+
68
+ # Public: Coerce a string or [User] into a user ID
69
+ #
70
+ # userish - [String/User/Int]A [User] or user ID
71
+ #
72
+ # Returns [String] User ID
73
+ # Raises Exception when value can't be coerced
74
+ def self.get_user_id(userish)
75
+ case userish
76
+ when String
77
+ userish.split("@", 2).first
78
+ when Fixnum
79
+ userish
80
+ when User
81
+ userish.id
82
+ else
83
+ raise "Invalid user ID"
84
+ end
85
+ end
86
+
87
+ # Public: Fetch a Livefyre::User from a user record or ID
88
+ #
89
+ # userish - [String/User/Int] A User or user ID
90
+ # client - [Livefyre::Client] Client to bind to the User record
91
+ #
92
+ # Returns [User]
93
+ def self.get_user(userish, client)
94
+ case userish
95
+ when User
96
+ userish.client = client
97
+ userish
98
+ else
99
+ new get_user_id(userish), client
100
+ end
101
+ end
102
+
103
+ # Internal: Returns a cleaner string representation of this object
104
+ #
105
+ # Returns [String] representation of this class
106
+ def to_s
107
+ "#<#{self.class.name}:0x#{object_id.to_s(16).rjust(14, "0")} id='#{id}' display_name='#{display_name}'>"
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ module Livefyre
2
+ VERSION = "0.0.1"
3
+ end
data/lib/livefyre.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'jwt'
2
+ require 'faraday'
3
+ require 'forwardable'
4
+
5
+ # Public: Toplevel Livefyre namespace
6
+ module Livefyre
7
+ # Public: Exception thrown when the Livefyre API does not return a success value
8
+ # #message will be the response body from the Livefyre API.
9
+ class APIException < ::Exception; end
10
+
11
+ # Public: Set the default configuration object for Livefyre clients
12
+ #
13
+ # Returns [nil]
14
+ def self.config=(config)
15
+ config.keys.each do |key|
16
+ config[(key.to_sym rescue key) || key] = config.delete(key)
17
+ end if config.is_a? Hash
18
+ @@config = config
19
+ @@client = nil
20
+ end
21
+
22
+ # Public: Get the configuration object for default clients
23
+ #
24
+ # Returns [Hash] configuration hash
25
+ def self.config
26
+ @@config
27
+ end
28
+
29
+ # Public: Retreive a singleton instance of the Livefyre client
30
+ #
31
+ # Returns [Livefyre::Client] instance configured with the default settings
32
+ # Raises Exception if #config is nil
33
+ def self.client
34
+ raise "Invalid configuration" if @@config.nil?
35
+ @@client ||= Livefyre::Client.new(@@config)
36
+ end
37
+ end
38
+
39
+ require File.expand_path("livefyre/client", File.dirname(__FILE__))
40
+ require File.expand_path("livefyre/user", File.dirname(__FILE__))
41
+ require File.expand_path("livefyre/domain", File.dirname(__FILE__))
42
+ require File.expand_path("livefyre/site", File.dirname(__FILE__))
43
+
44
+ if defined?(Rails)
45
+ require File.expand_path("livefyre/controller_extensions", File.dirname(__FILE__))
46
+ require File.expand_path("livefyre/helpers", File.dirname(__FILE__))
47
+ require File.expand_path("livefyre/model_extensions", File.dirname(__FILE__))
48
+ require File.expand_path("../railties/railtie", File.dirname(__FILE__))
49
+ require File.expand_path("livefyre/engine", File.dirname(__FILE__))
50
+ end
data/livefyre.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/livefyre/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Mashable"]
6
+ gem.email = ["cheald@mashable.com"]
7
+ gem.description = %q{Interface library for Livefyre's comment API with Rails helpers}
8
+ gem.summary = %q{Interface library for Livefyre's comment API with Rails helpers}
9
+ gem.homepage = "http://github.com/mashable/livefyre"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(spec)/})
14
+ gem.name = "livefyre"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Livefyre::VERSION
17
+
18
+ gem.add_dependency "faraday"
19
+ gem.add_dependency "jwt"
20
+
21
+ gem.add_development_dependency "rspec"
22
+ gem.add_development_dependency "simplecov"
23
+ gem.add_development_dependency "simplecov-rcov"
24
+ gem.add_development_dependency "rails"
25
+ gem.add_development_dependency "resque"
26
+ end
@@ -0,0 +1,9 @@
1
+ module Livefyre
2
+ class Railtie < Rails::Railtie
3
+ initializer "livefyre.initializer" do
4
+ ActionController::Base.send :include, Livefyre::Controller
5
+ ActionView::Base.send :include, Livefyre::Helpers
6
+ ActiveRecord::Base.send :include, Livefyre::Model if defined?(ActiveRecord)
7
+ end
8
+ end
9
+ end